atlas-0.46.0/0000775000175000017500000000000015160212070013063 5ustar alastairalastairatlas-0.46.0/VERSION0000664000175000017500000000000715160212070014130 0ustar alastairalastair0.46.0 atlas-0.46.0/hic/0000775000175000017500000000000015160212070013626 5ustar alastairalastairatlas-0.46.0/hic/tests/0000775000175000017500000000000015160212070014770 5ustar alastairalastairatlas-0.46.0/hic/tests/test_hicsparse.cc0000664000175000017500000002371615160212070020330 0ustar alastairalastair/* * (C) Copyright 2024- 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 "hic/hic.h" #include "hic/hicsparse.h" // ----------------------------------------------------------------------------- int test_hicsparseCreate() { std::cout << "--- " << __FUNCTION__ << std::endl; hicsparseHandle_t handle; HICSPARSE_CALL(hicsparseCreate(&handle)); HICSPARSE_CALL(hicsparseDestroy(handle)); std::cout << "--- " << __FUNCTION__ << " SUCCEEDED " << std::endl; return 0; // success } // ----------------------------------------------------------------------------- int test_hicsparseSpMV() { std::cout << "--- " << __FUNCTION__ << std::endl; // Create a sparse matrix const int rows = 3; const int cols = 3; const int nnz = 3; double values[nnz] = {1.0, 2.0, 3.0}; int row_offsets[rows + 1] = {0, 1, 2, 3}; int column_indices[nnz] = {0, 1, 2}; // Put the sparse matrix onto the device double* dvalues; int* drow_offsets; int* dcolumn_indices; HIC_CALL(hicMalloc((void**)&dvalues, nnz * sizeof(double))); HIC_CALL(hicMalloc((void**)&drow_offsets, (rows + 1) * sizeof(int))); HIC_CALL(hicMalloc((void**)&dcolumn_indices, nnz * sizeof(int))); HIC_CALL(hicMemcpy(dvalues, values, nnz * sizeof(double), hicMemcpyHostToDevice)); HIC_CALL(hicMemcpy(drow_offsets, row_offsets, (rows + 1) * sizeof(int), hicMemcpyHostToDevice)); HIC_CALL(hicMemcpy(dcolumn_indices, column_indices, nnz * sizeof(int), hicMemcpyHostToDevice)); // Create a dense vector const int N = 3; double x[N] = {1.0, 2.0, 3.0}; double y[N] = {0.0, 0.0, 0.0}; // Put the dense vector onto the device double* dx; double* dy; HIC_CALL(hicMalloc((void**)&dx, N * sizeof(double))); HIC_CALL(hicMalloc((void**)&dy, N * sizeof(double))); HIC_CALL(hicMemcpy(dx, x, N * sizeof(double), hicMemcpyHostToDevice)); HIC_CALL(hicMemcpy(dy, y, N * sizeof(double), hicMemcpyHostToDevice)); // Create sparse library handle hicsparseHandle_t handle; HICSPARSE_CALL(hicsparseCreate(&handle)); // Create a sparse matrix descriptor hicsparseConstSpMatDescr_t matA; HICSPARSE_CALL( hicsparseCreateConstCsr( &matA, rows, cols, nnz, drow_offsets, dcolumn_indices, dvalues, HICSPARSE_INDEX_32I, HICSPARSE_INDEX_32I, HICSPARSE_INDEX_BASE_ZERO, HIC_R_64F) ); // Create dense matrix descriptors hicsparseConstDnVecDescr_t vecX; HICSPARSE_CALL( hicsparseCreateConstDnVec( &vecX, N, dx, HIC_R_64F) ); hicsparseDnVecDescr_t vecY; HICSPARSE_CALL( hicsparseCreateDnVec( &vecY, N, dy, HIC_R_64F) ); // Set parameters const double alpha = 1.0; const double beta = 0.0; // Determine buffer size size_t bufferSize = 0; HICSPARSE_CALL( hicsparseSpMV_bufferSize( handle, HICSPARSE_OPERATION_NON_TRANSPOSE, &alpha, matA, vecX, &beta, vecY, HIC_R_64F, HICSPARSE_SPMV_ALG_DEFAULT, &bufferSize) ); // Allocate buffer char* buffer; HIC_CALL(hicMalloc(&buffer, bufferSize)); // Perform SpMV // y = alpha * A * x + beta * y HICSPARSE_CALL( hicsparseSpMV( handle, HICSPARSE_OPERATION_NON_TRANSPOSE, &alpha, matA, vecX, &beta, vecY, HIC_R_64F, HICSPARSE_SPMV_ALG_DEFAULT, buffer) ); // Copy result back to host HIC_CALL(hicMemcpy(y, dy, N * sizeof(double), hicMemcpyDeviceToHost)); // Check result const double expected_y[N] = {1.0, 4.0, 9.0}; for (int i = 0; i < N; ++i) { if (y[i] != expected_y[i]) { throw std::runtime_error("Error: y[" + std::to_string(i) + "] = " + std::to_string(y[i]) + " != " + std::to_string(expected_y[i])); } } // Clean up HIC_CALL(hicFree(dy)); HIC_CALL(hicFree(dx)); HIC_CALL(hicFree(dcolumn_indices)); HIC_CALL(hicFree(drow_offsets)); HIC_CALL(hicFree(dvalues)); HICSPARSE_CALL(hicsparseDestroyDnVec(vecY)); HICSPARSE_CALL(hicsparseDestroyDnVec(vecX)); HICSPARSE_CALL(hicsparseDestroySpMat(matA)); HICSPARSE_CALL(hicsparseDestroy(handle)); std::cout << "--- " << __FUNCTION__ << " SUCCEEDED " << std::endl; return 0; // success } // ----------------------------------------------------------------------------- int test_hicsparseSpMM() { std::cout << "--- " << __FUNCTION__ << std::endl; // Create a sparse matrix const int rows = 3; const int cols = 3; const int nnz = 3; double values[nnz] = {1.0, 2.0, 3.0}; int row_offsets[rows + 1] = {0, 1, 2, 3}; int column_indices[nnz] = {0, 1, 2}; // Put the sparse matrix onto the device double* dvalues; int* drow_offsets; int* dcolumn_indices; HIC_CALL(hicMalloc((void**)&dvalues, nnz * sizeof(double))); HIC_CALL(hicMalloc((void**)&drow_offsets, (rows + 1) * sizeof(int))); HIC_CALL(hicMalloc((void**)&dcolumn_indices, nnz * sizeof(int))); HIC_CALL(hicMemcpy(dvalues, values, nnz * sizeof(double), hicMemcpyHostToDevice)); HIC_CALL(hicMemcpy(drow_offsets, row_offsets, (rows + 1) * sizeof(int), hicMemcpyHostToDevice)); HIC_CALL(hicMemcpy(dcolumn_indices, column_indices, nnz * sizeof(int), hicMemcpyHostToDevice)); // Create dense matrices const int B_rows = 3; const int B_cols = 3; double B[B_rows * B_cols] = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0}; double C[rows * B_cols] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; // Put the dense matrices onto the device double* dB; double* dC; HIC_CALL(hicMalloc((void**)&dB, B_rows * B_cols * sizeof(double))); HIC_CALL(hicMalloc((void**)&dC, rows * B_cols * sizeof(double))); HIC_CALL(hicMemcpy(dB, B, B_rows * B_cols * sizeof(double), hicMemcpyHostToDevice)); HIC_CALL(hicMemcpy(dC, C, rows * B_cols * sizeof(double), hicMemcpyHostToDevice)); // Create sparse library handle hicsparseHandle_t handle; HICSPARSE_CALL(hicsparseCreate(&handle)); // Create a sparse matrix descriptor hicsparseConstSpMatDescr_t matA; HICSPARSE_CALL(hicsparseCreateConstCsr( &matA, rows, cols, nnz, drow_offsets, dcolumn_indices, dvalues, HICSPARSE_INDEX_32I, HICSPARSE_INDEX_32I, HICSPARSE_INDEX_BASE_ZERO, HIC_R_64F)); // Create dense matrix descriptors hicsparseConstDnMatDescr_t matB; HICSPARSE_CALL(hicsparseCreateConstDnMat( &matB, B_rows, B_cols, B_cols, dB, HIC_R_64F, HICSPARSE_ORDER_ROW)); hicsparseDnMatDescr_t matC; HICSPARSE_CALL(hicsparseCreateDnMat( &matC, rows, B_cols, B_cols, dC, HIC_R_64F, HICSPARSE_ORDER_ROW)); // Set parameters const double alpha = 1.0; const double beta = 0.0; // Determine buffer size size_t bufferSize = 0; HICSPARSE_CALL(hicsparseSpMM_bufferSize( handle, HICSPARSE_OPERATION_NON_TRANSPOSE, HICSPARSE_OPERATION_NON_TRANSPOSE, &alpha, matA, matB, &beta, matC, HIC_R_64F, HICSPARSE_SPMM_ALG_DEFAULT, &bufferSize)); // Allocate buffer char* buffer; HIC_CALL(hicMalloc(&buffer, bufferSize)); // Perform SpMM HICSPARSE_CALL(hicsparseSpMM( handle, HICSPARSE_OPERATION_NON_TRANSPOSE, HICSPARSE_OPERATION_NON_TRANSPOSE, &alpha, matA, matB, &beta, matC, HIC_R_64F, HICSPARSE_SPMM_ALG_DEFAULT, buffer)); // Copy result back to host HIC_CALL(hicMemcpy(C, dC, rows * B_cols * sizeof(double), hicMemcpyDeviceToHost)); // Check result const double expected_C[rows * B_cols] = {1.0, 2.0, 3.0, 8.0, 10.0, 12.0, 21.0, 24.0, 27.0}; for (int i = 0; i < rows * B_cols; ++i) { if (C[i] != expected_C[i]) { throw std::runtime_error("Error: C[" + std::to_string(i) + "] = " + std::to_string(C[i]) + " != " + std::to_string(expected_C[i])); } } // Clean up HIC_CALL(hicFree(buffer)); HIC_CALL(hicFree(dC)); HIC_CALL(hicFree(dB)); HIC_CALL(hicFree(dcolumn_indices)); HIC_CALL(hicFree(drow_offsets)); HIC_CALL(hicFree(dvalues)); HICSPARSE_CALL(hicsparseDestroyDnMat(matC)); HICSPARSE_CALL(hicsparseDestroyDnMat(matB)); HICSPARSE_CALL(hicsparseDestroySpMat(matA)); HICSPARSE_CALL(hicsparseDestroy(handle)); std::cout << "--- " << __FUNCTION__ << " SUCCEEDED " << std::endl; return 0; // success } // ----------------------------------------------------------------------------- std::vector> tests = { test_hicsparseCreate, test_hicsparseSpMV, test_hicsparseSpMM, }; int main(int argc, char* argv[]) { int num_devices = 0; hicGetDeviceCount(&num_devices); if (num_devices == 0) { std::ignore = hicGetLastError(); std::cout << "TEST IGNORED, hicGetDeviceCount -> 0" << std::endl; return 0; } std::cout << "hicGetDeviceCount -> " << num_devices << std::endl; int error = 0; for (auto& test : tests) { try { error += test(); } catch (std::exception& e) { error += 1; std::cout << e.what() << std::endl; } } return error; } atlas-0.46.0/hic/tests/test_hic_dummy.cc0000664000175000017500000000251115160212070020313 0ustar alastairalastair/* * (C) Copyright 2024- 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 "hic/hic.h" int test_throw() { std::cout << "--- " << __FUNCTION__ << std::endl; // For the dummy backend we expect to have an error thrown when used void* p; try { std::ignore = hicMalloc(&p, 8); std::ignore = hicFree(p); } catch (std::runtime_error& e) { std::string expected_message = "hicMalloc is using the dummy backend and should not be called"; if (e.what() == expected_message) { std::cout << "--- " << __FUNCTION__ << " SUCCEEDED " << std::endl; return 0; // success } } std::cout << "--- " << __FUNCTION__ << " SUCCEEDED " << std::endl; return 1; // fail } std::vector> tests = {test_throw}; int main(int /*argc*/, char** /*argv*/) { int error = 0; for (auto& test : tests) { error += test(); } return error; } atlas-0.46.0/hic/tests/test_hic.cc0000664000175000017500000000360615160212070017106 0ustar alastairalastair/* * (C) Copyright 2024- 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 "hic/hic.h" // ----------------------------------------------------------------------------- int test_hicMalloc() { std::cout << "--- " << __FUNCTION__ << std::endl; void* p; HIC_CALL(hicMalloc(&p, 8)); HIC_CALL(hicFree(p)); std::cout << "--- " << __FUNCTION__ << " SUCCEEDED " << std::endl; return 0; // success } // ----------------------------------------------------------------------------- int test_hicMallocManaged() { std::cout << "--- " << __FUNCTION__ << std::endl; void* p; HIC_CALL(hicMallocManaged(&p, 8)); HIC_CALL(hicFree(p)); std::cout << "--- " << __FUNCTION__ << " SUCCEEDED " << std::endl; return 0; // success } // ----------------------------------------------------------------------------- std::vector> tests = { test_hicMalloc, test_hicMallocManaged, }; int main(int argc, char* argv[]) { int num_devices = 0; hicGetDeviceCount(&num_devices); if (num_devices == 0) { std::ignore = hicGetLastError(); std::cout << "TEST IGNORED, hicGetDeviceCount -> 0" << std::endl; return 0; } std::cout << "hicGetDeviceCount -> " << num_devices << std::endl; int error = 0; for (auto& test : tests) { try { error += test(); } catch (std::exception& e) { error += 1; std::cout << e.what() << std::endl; } } return error; } atlas-0.46.0/hic/tests/CMakeLists.txt0000664000175000017500000000137515160212070017536 0ustar alastairalastair# (C) Copyright 2024 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. ecbuild_add_test( TARGET hic_test_dummy SOURCES test_hic_dummy.cc LIBS hic DEFINITIONS HIC_BACKEND_DUMMY=1 ) if( HAVE_HIP OR HAVE_CUDA ) ecbuild_add_test( TARGET hic_test_hic SOURCES test_hic.cc LIBS hic ) ecbuild_add_test( TARGET hic_test_hicsparse SOURCES test_hicsparse.cc LIBS hicsparse ) endif() add_subdirectory(test_find_hic) atlas-0.46.0/hic/tests/test_find_hic/0000775000175000017500000000000015160212070017572 5ustar alastairalastairatlas-0.46.0/hic/tests/test_find_hic/find_hic/0000775000175000017500000000000015160212070021335 5ustar alastairalastairatlas-0.46.0/hic/tests/test_find_hic/find_hic/CMakeLists.txt0000664000175000017500000000016515160212070024077 0ustar alastairalastair cmake_minimum_required( VERSION 3.18 FATAL_ERROR ) project( find_hic LANGUAGES CXX ) find_package( hic REQUIRED ) atlas-0.46.0/hic/tests/test_find_hic/test.sh.in0000775000175000017500000000154515160212070021522 0ustar alastairalastair#!/usr/bin/env bash # Description: # Build downstream example projects # each individually with make/install # # Usage: # test-individual.sh [CMAKE_ARGUMENTS] SOURCE=@PROJECT_SOURCE_DIR@/tests/test_find_hic/find_hic BUILD=@CMAKE_CURRENT_BINARY_DIR@/build # Error handling function test_failed { EXIT_CODE=$? { set +ex; } 2>/dev/null if [ $EXIT_CODE -ne 0 ]; then echo "+++++++++++++++++" echo "Test failed" echo "+++++++++++++++++" fi exit $EXIT_CODE } trap test_failed EXIT set -e -o pipefail set -x # Start with clean build rm -rf $BUILD export hic_ROOT=@PROJECT_BINARY_DIR@ export ecbuild_DIR=@ecbuild_DIR@ # Build mkdir -p $BUILD && cd $BUILD cmake $SOURCE \ -DCMAKE_BUILD_TYPE=RelWithDebInfo \ "$@" # make VERBOSE=1 # ctest -VV { set +ex; } 2>/dev/null echo "+++++++++++++++++" echo "Test passed" echo "+++++++++++++++++" atlas-0.46.0/hic/tests/test_find_hic/CMakeLists.txt0000664000175000017500000000202015160212070022324 0ustar alastairalastair # This test builds a package that requires fypp processing # It uses the overriding of compile flags, like IFS is using. # # Test created to avoid regression after fixing issue FCKIT-19, # where compile flags were not propagated to fypp-generated files. if( HAVE_TESTS ) configure_file( test.sh.in ${CMAKE_CURRENT_BINARY_DIR}/test.sh @ONLY ) unset( _test_args ) if( CMAKE_TOOLCHAIN_FILE ) if( NOT IS_ABSOLUTE ${CMAKE_TOOLCHAIN_FILE}) set( CMAKE_TOOLCHAIN_FILE "${CMAKE_BINARY_DIR}/${CMAKE_TOOLCHAIN_FILE}" ) endif() list( APPEND _test_args "-DCMAKE_TOOLCHAIN_FILE=${CMAKE_TOOLCHAIN_FILE}" ) endif() foreach( lang CXX ) if( CMAKE_${lang}_COMPILER ) list( APPEND _test_args "-DCMAKE_${lang}_COMPILER=${CMAKE_${lang}_COMPILER}" ) endif() if( CMAKE_${lang}_FLAGS ) list( APPEND _test_args "-DCMAKE_${lang}_FLAGS=${CMAKE_${lang}_FLAGS}" ) endif() endforeach() add_test( NAME hic_test_find_hic COMMAND ${CMAKE_CURRENT_BINARY_DIR}/test.sh ${_test_args} ) endif() atlas-0.46.0/hic/src/0000775000175000017500000000000015160212070014415 5ustar alastairalastairatlas-0.46.0/hic/src/hic/0000775000175000017500000000000015160212070015160 5ustar alastairalastairatlas-0.46.0/hic/src/hic/hic_config.h.in0000664000175000017500000000217215160212070020030 0ustar alastairalastair/* * (C) Copyright 2024- 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 #if defined(HIC_BACKEND_DUMMY) && HIC_BACKEND_DUMMY==1 #define HIC_BACKEND_CUDA 0 #define HIC_BACKEND_HIP 0 #else #cmakedefine01 HIC_BACKEND_CUDA #cmakedefine01 HIC_BACKEND_HIP #cmakedefine01 HIC_BACKEND_DUMMY #endif #if (defined(__CUDACC__) || defined(__HIPCC__)) #define HIC_COMPILER 1 #define HIC_HOST_DEVICE __host__ __device__ #define HIC_DEVICE __device__ #define HIC_HOST __host__ #define HIC_GLOBAL __global__ #else #define HIC_COMPILER 0 #define HIC_HOST_DEVICE #define HIC_DEVICE #define HIC_HOST #define HIC_GLOBAL #endif #if defined(__CUDA_ARCH__) || defined(__HIP_DEVICE_COMPILE__) #define HIC_HOST_COMPILE 0 #define HIC_DEVICE_COMPILE 1 #else #define HIC_HOST_COMPILE 1 #define HIC_DEVICE_COMPILE 0 #endif atlas-0.46.0/hic/src/hic/hicsparse.h0000664000175000017500000001533315160212070017317 0ustar alastairalastair/* * (C) Copyright 2024- 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 "hic/hic_config.h" #include "hic/hic_library_types.h" #include "hic/hic_namespace_macro.h" #if HIC_BACKEND_CUDA #define HICSPARSE_BACKEND_PREFIX cu #define HICSPARSE_BACKEND_PREFIX_CAPS CU #include #if CUSPARSE_VERSION < 12 * 1000 + 0 * 100 + 0 // the "Const" versions only appeared with CUDA 12 #define cusparseConstDnVecDescr_t cusparseDnVecDescr_t #define cusparseConstDnMatDescr_t cusparseDnMatDescr_t #define cusparseConstSpVecDescr_t cusparseSpVecDescr_t #define cusparseConstSpMatDescr_t cusparseSpMatDescr_t cusparseStatus_t cusparseCreateConstCsr(cusparseConstSpMatDescr_t* spMatDescr, int64_t rows, int64_t cols, int64_t nnz, const void* csrRowOffsets, const void* csrColInd, const void* csrValues, cusparseIndexType_t csrRowOffsetsType, cusparseIndexType_t csrColIndType, cusparseIndexBase_t idxBase, cudaDataType valueType) { return cusparseCreateCsr(spMatDescr, rows, cols, nnz, const_cast(csrRowOffsets), const_cast(csrColInd), const_cast(csrValues), csrRowOffsetsType, csrColIndType, idxBase, valueType); } cusparseStatus_t cusparseCreateConstDnVec(cusparseConstDnVecDescr_t* dnVecDescr, int64_t size, const void* values, cudaDataType valueType) { return cusparseCreateDnVec(dnVecDescr, size, const_cast(values), valueType); } cusparseStatus_t cusparseCreateConstDnMat(cusparseConstDnMatDescr_t* dnMatDescr, int64_t rows, int64_t cols, int64_t ld, const void* values, cudaDataType valueType, cusparseOrder_t order) { return cusparseCreateDnMat(dnMatDescr, rows, cols, ld, const_cast(values), valueType, order); } cusparseStatus_t cusparseCreateConstSpVec(cusparseConstSpVecDescr_t* spVecDescr, int64_t size, int64_t nnz, const void* indices, const void* values, cusparseIndexType_t idxType, cusparseIndexBase_t idxBase, cudaDataType valueType) { return cusparseCreateSpVec(spVecDescr, size, nnz, const_cast(indices), const_cast(values), idxType, idxBase, valueType); } #endif #elif HIC_BACKEND_HIP #define HICSPARSE_BACKEND_PREFIX hip #define HICSPARSE_BACKEND_PREFIX_CAPS HIP #include #elif HIC_BACKEND_DUMMY #define HICSPARSE_BACKEND_PREFIX dummy #define HICSPARSE_BACKEND_PREFIX_CAPS dummy #include "hic/hic_dummy/hicsparse_dummy.h" #else #error Unsupported hic backend. Please define HIC_BACKEND_CUDA or HIC_BACKEND_HIP or HIC_BACKEND_DUMMY #endif #define HIC_PREFIX hic #define HIC_PREFIX_CAPS HIC #define HIC_CONCAT_(A, B) A##B #define HIC_CONCAT(A, B) HIC_CONCAT_(A, B) #define HIC_SYMBOL(API) HIC_CONCAT(HIC_PREFIX, API) #define HIC_SYMBOL_CAPS(API) HIC_CONCAT(HIC_PREFIX_CAPS, API) #define HIC_TYPE(TYPE) using HIC_SYMBOL(TYPE) = HIC_CONCAT(HICSPARSE_BACKEND_PREFIX, TYPE); #define HIC_FUNCTION(FUNCTION) \ template \ inline auto HIC_SYMBOL(FUNCTION)(Args && ... args) \ -> decltype(HIC_CONCAT(HICSPARSE_BACKEND_PREFIX, FUNCTION)(std::forward(args)...)) { \ return HIC_CONCAT(HICSPARSE_BACKEND_PREFIX, FUNCTION)(std::forward(args)...); \ } #define HIC_VALUE(VALUE) \ constexpr decltype(HIC_CONCAT(HICSPARSE_BACKEND_PREFIX_CAPS, VALUE)) HIC_SYMBOL_CAPS(VALUE) = \ HIC_CONCAT(HICSPARSE_BACKEND_PREFIX_CAPS, VALUE); //------------------------------------------------ HIC_NAMESPACE_BEGIN //------------------------------------------------ HIC_TYPE(sparseHandle_t) HIC_TYPE(sparseStatus_t) HIC_TYPE(sparseIndexType_t) HIC_TYPE(sparseOrder_t) HIC_TYPE(sparseConstDnVecDescr_t) HIC_TYPE(sparseDnVecDescr_t) HIC_TYPE(sparseConstDnMatDescr_t) HIC_TYPE(sparseDnMatDescr_t) HIC_TYPE(sparseConstSpVecDescr_t) HIC_TYPE(sparseSpVecDescr_t) HIC_TYPE(sparseConstSpMatDescr_t) HIC_TYPE(sparseSpMatDescr_t) HIC_TYPE(sparseSpMVAlg_t) HIC_TYPE(sparseSpMMAlg_t) HIC_FUNCTION(sparseGetErrorString) HIC_FUNCTION(sparseCreate) HIC_FUNCTION(sparseDestroy) HIC_FUNCTION(sparseCreateConstDnVec) HIC_FUNCTION(sparseCreateDnVec) HIC_FUNCTION(sparseDestroyDnVec) HIC_FUNCTION(sparseCreateConstDnMat) HIC_FUNCTION(sparseCreateDnMat) HIC_FUNCTION(sparseDestroyDnMat) HIC_FUNCTION(sparseCreateConstSpVec) HIC_FUNCTION(sparseCreateSpVec) HIC_FUNCTION(sparseDestroySpVec) HIC_FUNCTION(sparseCreateCsr) HIC_FUNCTION(sparseCreateConstCsr) HIC_FUNCTION(sparseDestroySpMat) HIC_FUNCTION(sparseSpMV_bufferSize) HIC_FUNCTION(sparseSpMV_preprocess) HIC_FUNCTION(sparseSpMV) HIC_FUNCTION(sparseSpMM_bufferSize) HIC_FUNCTION(sparseSpMM_preprocess) HIC_FUNCTION(sparseSpMM) HIC_VALUE(SPARSE_STATUS_SUCCESS) HIC_VALUE(SPARSE_ORDER_COL) HIC_VALUE(SPARSE_ORDER_ROW) HIC_VALUE(SPARSE_INDEX_32I) HIC_VALUE(SPARSE_INDEX_64I) HIC_VALUE(SPARSE_INDEX_BASE_ZERO) HIC_VALUE(SPARSE_INDEX_BASE_ONE) HIC_VALUE(SPARSE_SPMV_ALG_DEFAULT) HIC_VALUE(SPARSE_SPMM_ALG_DEFAULT) HIC_VALUE(SPARSE_OPERATION_NON_TRANSPOSE) HIC_VALUE(SPARSE_OPERATION_TRANSPOSE) HIC_VALUE(SPARSE_OPERATION_CONJUGATE_TRANSPOSE) #if HIC_BACKEND_DUMMY #define HICSPARSE_CALL(val) #else #define HICSPARSE_CALL(val) hicsparse_assert((val), #val, __FILE__, __LINE__) #endif inline void hicsparse_assert(hicsparseStatus_t status, const char* const func, const char* const file, const int line) { if (status != HICSPARSE_STATUS_SUCCESS) { std::ostringstream msg; msg << "HIC Runtime Error [code=" << status << "] at: " << file << " + " << line << " : " << func << "\n"; msg << " Reason: " << hicsparseGetErrorString(status); throw std::runtime_error(msg.str()); } } //------------------------------------------------ HIC_NAMESPACE_END //------------------------------------------------ #undef HIC_FUNCTION #undef HIC_TYPE #undef HIC_VALUE #undef HIC_PREFIX #undef HIC_PREFIX_CAPS #undef HIC_CONCAT #undef HIC_CONCAT_ #undef HIC_SYMBOL #undef HIC_SYMBOL_CAPS #undef HICSPARSE_BACKEND_PREFIX #undef HICSPARSE_BACKEND_PREFIX_CAPS atlas-0.46.0/hic/src/hic/hic.h0000664000175000017500000000267015160212070016101 0ustar alastairalastair/* * (C) Copyright 2024- 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 "hic/hic_config.h" #include "hic/hic_runtime.h" #include #include #if HIC_BACKEND_DUMMY #define HIC_CALL(val) #else #define HIC_CALL(val) hic_assert((val), #val, __FILE__, __LINE__) #endif inline void hic_assert(hicError_t err, const char* const func, const char* const file, const int line) { // Ignore errors when HIP/CUDA runtime is unloaded or deinitialized. // This happens when calling HIP/CUDA after main has ended, e.g. in teardown of static variables calling `hicFree` // --> ignore hicErrorDeinitialized (a.k.a. cudaErrorCudartUnloading / hipErrorDeinitialized) if (err != hicSuccess && err != hicErrorDeinitialized) { std::ostringstream msg; msg << "HIC Runtime Error [code=" << err << "] at: " << file << " + " << line << " : " << func << "\n"; msg << " Reason: " << hicGetErrorString(err); throw std::runtime_error(msg.str()); } } #define HIC_CHECK_KERNEL_LAUNCH() HIC_CALL(hicPeekAtLastError()) #define HIC_CHECK_LAST_ERROR() HIC_CALL(hicGetLastError()) atlas-0.46.0/hic/src/hic/hic_runtime.h0000664000175000017500000001366015160212070017645 0ustar alastairalastair/* * (C) Copyright 2024- 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 "hic/hic_namespace_macro.h" #if HIC_BACKEND_CUDA #define HIC_BACKEND cuda #include #elif HIC_BACKEND_HIP #define HIC_BACKEND hip #pragma push_macro("DEPRECATED") #undef DEPRECATED #include #pragma pop_macro("DEPRECATED") #if HIP_VERSION_MAJOR < 6 enum hicMemoryType { hicMemoryTypeUnregistered = 0, hicMemoryTypeHost = 1, hicMemoryTypeDevice = 2, hicMemoryTypeManaged = 3 }; struct hicPointerAttributes { hicMemoryType type; int device; void* devicePointer; void* hostPointer; }; [[nodiscard]] inline hipError_t hicPointerGetAttributes(hicPointerAttributes* attributes, const void* ptr) { hipPointerAttribute_t attr_; auto err = hipPointerGetAttributes(&attr_, ptr); if (err != hipSuccess) { attributes->device = 0; attributes->devicePointer = nullptr; attributes->hostPointer = nullptr; return err; } const auto& type = attr_.memoryType; if (attr_.isManaged) { attributes->type = hicMemoryTypeManaged; } else if (type == hipMemoryTypeHost) { attributes->type = hicMemoryTypeHost; } else if (type == hipMemoryTypeDevice) { attributes->type = hicMemoryTypeDevice; } else if (type == hipMemoryTypeUnified) { attributes->type = hicMemoryTypeManaged; } else { attributes->type = hicMemoryTypeUnregistered; } attributes->device = attr_.device; attributes->devicePointer = attr_.devicePointer; attributes->hostPointer = attr_.hostPointer; return err; }; #endif #elif HIC_BACKEND_DUMMY #define HIC_BACKEND dummy #include "hic/hic_dummy/hic_dummy_runtime.h" #else #error Unsupported hic backend. Please define HIC_BACKEND_CUDA or HIC_BACKEND_HIP or HIC_BACKEND_DUMMY #endif #define HIC_PREFIX hic #define HIC_CONCAT_(A, B) A##B #define HIC_CONCAT(A, B) HIC_CONCAT_(A, B) #define HIC_SYMBOL(API) HIC_CONCAT(HIC_PREFIX, API) #define HIC_BACKEND_SYMBOL(API) HIC_CONCAT(HIC_BACKEND, API) #define HIC_TYPE(TYPE) using HIC_SYMBOL(TYPE) = HIC_BACKEND_SYMBOL(TYPE); #define HIC_FUNCTION(FUNCTION) \ template \ inline auto HIC_SYMBOL(FUNCTION)(Args && ... args) \ -> decltype(HIC_BACKEND_SYMBOL(FUNCTION)(std::forward(args)...)) { \ return HIC_BACKEND_SYMBOL(FUNCTION)(std::forward(args)...); \ } #define HIC_VALUE(VALUE) constexpr decltype(HIC_BACKEND_SYMBOL(VALUE)) HIC_SYMBOL(VALUE) = HIC_BACKEND_SYMBOL(VALUE); //------------------------------------------------ HIC_NAMESPACE_BEGIN //------------------------------------------------ HIC_TYPE(HostFn_t) HIC_TYPE(Error_t) HIC_TYPE(Event_t) HIC_TYPE(Stream_t) #if !HIC_BACKEND_HIP HIC_TYPE(PointerAttributes) #elif HIP_VERSION_MAJOR >= 6 using HIC_SYMBOL(PointerAttributes) = HIC_BACKEND_SYMBOL(PointerAttribute_t); #endif #if HIC_BACKEND_CUDA // From CUDA 13.0 (released August 2025), the signature of cudaMemPrefetchAsync has changed // With hic, we are staying for now compatible with CUDA < 13.0. #if CUDART_VERSION >= 13 * 1000 + 0 * 10 inline hicError_t hicMemPrefetchAsync (const void* devPtr, size_t count, int dstDevice, hicStream_t stream = 0) { if (dstDevice == cudaCpuDeviceId) { cudaMemLocation location; location.type = cudaMemLocationTypeHostNumaCurrent; unsigned int flags = 0; // Unused in CUDA 13.0, reserved for future return cudaMemPrefetchAsync(devPtr, count, location, flags, stream); } else { cudaMemLocation location; location.type = cudaMemLocationTypeDevice; location.id = dstDevice; unsigned int flags = 0; // Unused in CUDA 13.0, reserved for future return cudaMemPrefetchAsync(devPtr, count, location, flags, stream); } } #endif #endif HIC_FUNCTION(DeviceSynchronize) HIC_FUNCTION(Free) HIC_FUNCTION(FreeAsync) HIC_FUNCTION(GetDeviceCount) HIC_FUNCTION(GetErrorString) HIC_FUNCTION(GetLastError) HIC_FUNCTION(HostGetDevicePointer) HIC_FUNCTION(LaunchHostFunc) HIC_FUNCTION(HostRegister) HIC_FUNCTION(HostUnregister) HIC_FUNCTION(Malloc) HIC_FUNCTION(MallocAsync) HIC_FUNCTION(MallocManaged) HIC_FUNCTION(Memcpy) HIC_FUNCTION(Memcpy2D) HIC_FUNCTION(MemcpyAsync) HIC_FUNCTION(Memcpy2DAsync) #if !HIC_BACKEND_CUDA HIC_FUNCTION(MemPrefetchAsync) #elif CUDART_VERSION < 13 * 1000 + 0 * 10 HIC_FUNCTION(MemPrefetchAsync) #endif HIC_FUNCTION(PeekAtLastError) #if !HIC_BACKEND_HIP HIC_FUNCTION(PointerGetAttributes) #elif HIP_VERSION_MAJOR >= 6 HIC_FUNCTION(PointerGetAttributes) #endif HIC_FUNCTION(StreamCreate) HIC_FUNCTION(StreamDestroy) HIC_FUNCTION(StreamSynchronize) HIC_VALUE(CpuDeviceId) HIC_VALUE(HostRegisterMapped) #if !HIC_BACKEND_HIP || (HIC_BACKEND_HIP && HIP_VERSION_MAJOR >= 6) HIC_VALUE(MemoryTypeDevice) HIC_VALUE(MemoryTypeHost) HIC_VALUE(MemoryTypeUnregistered) HIC_VALUE(MemoryTypeManaged) #endif HIC_VALUE(MemcpyDeviceToHost) HIC_VALUE(MemcpyHostToDevice) HIC_VALUE(Success) #if HIC_BACKEND_CUDA constexpr hicError_t hicErrorDeinitialized = cudaErrorCudartUnloading; #elif HIC_BACKEND_HIP constexpr hicError_t hicErrorDeinitialized = hipErrorDeinitialized; #else constexpr hicError_t hicErrorDeinitialized = 4; #endif //------------------------------------------------ HIC_NAMESPACE_END //------------------------------------------------ #undef HIC_FUNCTION #undef HIC_TYPE #undef HIC_VALUE #undef HIC_CONCAT #undef HIC_CONCAT_ #undef HIC_PREFIX #undef HIC_SYMBOL #undef HIC_BACKEND_SYMBOL atlas-0.46.0/hic/src/hic/hic_library_types.h0000664000175000017500000000302515160212070021044 0ustar alastairalastair/* * (C) Copyright 2024- 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 "hic/hic_config.h" #include "hic/hic_namespace_macro.h" #if HIC_BACKEND_CUDA #include #elif HIC_BACKEND_HIP #include #elif HIC_BACKEND_DUMMY // intentionally blank #else #error Unsupported hic backend. Please define HIC_BACKEND_CUDA or HIC_BACKEND_HIP or HIC_BACKEND_DUMMY #endif //------------------------------------------------ HIC_NAMESPACE_BEGIN //------------------------------------------------ #if HIC_BACKEND_CUDA constexpr decltype(CUDA_R_32F) HIC_R_32F = CUDA_R_32F; constexpr decltype(CUDA_R_64F) HIC_R_64F = CUDA_R_64F; using hicDataType = cudaDataType; #elif HIC_BACKEND_HIP constexpr decltype(HIP_R_32F) HIC_R_32F = HIP_R_32F; constexpr decltype(HIP_R_64F) HIC_R_64F = HIP_R_64F; using hicDataType = hipDataType; #elif HIC_BACKEND_DUMMY constexpr int HIC_R_32F = 0; constexpr int HIC_R_64F = 0; using hicDataType = int; #else #error Unsupported hic backend. Please define HIC_BACKEND_CUDA or HIC_BACKEND_HIP or HIC_BACKEND_DUMMY #endif //------------------------------------------------ HIC_NAMESPACE_END //------------------------------------------------atlas-0.46.0/hic/src/hic/hic_namespace_macro.h0000664000175000017500000000114615160212070021273 0ustar alastairalastair/* * (C) Copyright 2024- 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 #if !defined(HIC_NAMESPACE) #define HIC_NAMESPACE #define HIC_NAMESPACE_BEGIN #define HIC_NAMESPACE_END #else #define HIC_NAMESPACE_BEGIN namespace HIC_NAMESPACE { #define HIC_NAMESPACE_END } #endifatlas-0.46.0/hic/src/hic/hic_dummy/0000775000175000017500000000000015160212070017136 5ustar alastairalastairatlas-0.46.0/hic/src/hic/hic_dummy/hicsparse_dummy.h0000664000175000017500000000530315160212070022504 0ustar alastairalastair/* * (C) Copyright 2024- 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 "hic/hic_dummy/dummyShouldNotBeCalled.h" #define DUMMY_SHOULD_NOT_BE_CALLED(SYMBOL) dummyShouldNotBeCalled(#SYMBOL) #define DUMMY_FUNCTION(SYMBOL) \ template \ inline dummysparseStatus_t dummy##SYMBOL(Args&&... args) { \ DUMMY_SHOULD_NOT_BE_CALLED(hic##SYMBOL); \ return dummysparseStatus_t{0}; \ } #define DUMMY_VALUE(SYMBOL) constexpr int dummy##SYMBOL = 0; namespace { using dummysparseHandle_t = int; using dummysparseStatus_t = int; using dummysparseIndexType_t = int; using dummysparseOrder_t = int; using dummysparseConstDnVecDescr_t = void*; using dummysparseDnVecDescr_t = void*; using dummysparseConstDnMatDescr_t = void*; using dummysparseDnMatDescr_t = void*; using dummysparseConstSpVecDescr_t = void*; using dummysparseSpVecDescr_t = void*; using dummysparseConstSpMatDescr_t = void*; using dummysparseSpMatDescr_t = void*; using dummysparseSpMVAlg_t = int; using dummysparseSpMMAlg_t = int; DUMMY_FUNCTION(sparseGetErrorString) DUMMY_FUNCTION(sparseCreate) DUMMY_FUNCTION(sparseDestroy) DUMMY_FUNCTION(sparseCreateConstDnVec) DUMMY_FUNCTION(sparseCreateDnVec) DUMMY_FUNCTION(sparseDestroyDnVec) DUMMY_FUNCTION(sparseCreateConstDnMat) DUMMY_FUNCTION(sparseCreateDnMat) DUMMY_FUNCTION(sparseDestroyDnMat) DUMMY_FUNCTION(sparseCreateConstSpVec) DUMMY_FUNCTION(sparseCreateSpVec) DUMMY_FUNCTION(sparseDestroySpVec) DUMMY_FUNCTION(sparseCreateConstCsr) DUMMY_FUNCTION(sparseDestroySpMat) DUMMY_FUNCTION(sparseSpMV_bufferSize) DUMMY_FUNCTION(sparseSpMV_preprocess) DUMMY_FUNCTION(sparseSpMV) DUMMY_FUNCTION(sparseSpMM_bufferSize) DUMMY_FUNCTION(sparseSpMM_preprocess) DUMMY_FUNCTION(sparseSpMM) DUMMY_VALUE(SPARSE_STATUS_SUCCESS) DUMMY_VALUE(SPARSE_ORDER_COL) DUMMY_VALUE(SPARSE_ORDER_ROW) DUMMY_VALUE(SPARSE_INDEX_32I) DUMMY_VALUE(SPARSE_INDEX_64I) DUMMY_VALUE(SPARSE_INDEX_BASE_ZERO) DUMMY_VALUE(SPARSE_INDEX_BASE_ONE) DUMMY_VALUE(SPARSE_SPMV_ALG_DEFAULT) DUMMY_VALUE(SPARSE_SPMM_ALG_DEFAULT) DUMMY_VALUE(SPARSE_OPERATION_NON_TRANSPOSE) DUMMY_VALUE(SPARSE_OPERATION_TRANSPOSE) DUMMY_VALUE(SPARSE_OPERATION_CONJUGATE_TRANSPOSE) } // namespace #undef DUMMY_FUNCTION #undef DUMMY_VALUE #undef DUMMY_SHOULD_NOT_BE_CALLEDatlas-0.46.0/hic/src/hic/hic_dummy/dummyShouldNotBeCalled.h0000664000175000017500000000121015160212070023650 0ustar alastairalastair/* * (C) Copyright 2024- 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 namespace { [[noreturn]] void dummyShouldNotBeCalled(const char* symbol) { throw std::runtime_error(std::string(symbol) + " is using the dummy backend and should not be called"); } } // namespaceatlas-0.46.0/hic/src/hic/hic_dummy/hic_dummy_runtime.h0000664000175000017500000000455115160212070023035 0ustar alastairalastair/* * (C) Copyright 2024- 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 "hic/hic_dummy/dummyShouldNotBeCalled.h" #define DUMMY_SHOULD_NOT_BE_CALLED(SYMBOL) dummyShouldNotBeCalled(#SYMBOL) #define DUMMY_FUNCTION(SYMBOL) \ template \ inline dummyError_t dummy##SYMBOL(Args&&...) { \ DUMMY_SHOULD_NOT_BE_CALLED(hic##SYMBOL); \ return dummyError_t{0}; \ } #define DUMMY_VALUE(SYMBOL) constexpr int dummy##SYMBOL = 0; namespace { using dummyError_t = int; using dummyEvent_t = void*; using dummyHostFn_t = void*; using dummyStream_t = void*; inline const char* dummyGetErrorString(dummyError_t) { DUMMY_SHOULD_NOT_BE_CALLED(hicGetErrorString); } inline dummyError_t dummyGetLastError(void) { DUMMY_SHOULD_NOT_BE_CALLED(hicGetLastError); } inline dummyError_t dummyPeekAtLastError(void) { DUMMY_SHOULD_NOT_BE_CALLED(hicPeekAtLastError); } struct dummyPointerAttributes { int type{0}; int device{-2}; void* hostPointer{nullptr}; void* devicePointer{nullptr}; }; DUMMY_FUNCTION(DeviceSynchronize) DUMMY_FUNCTION(Free) DUMMY_FUNCTION(FreeAsync) DUMMY_FUNCTION(GetDeviceCount) DUMMY_FUNCTION(GetErrorString) DUMMY_FUNCTION(GetLastError) DUMMY_FUNCTION(HostGetDevicePointer) DUMMY_FUNCTION(HostRegister) DUMMY_FUNCTION(HostUnregister) DUMMY_FUNCTION(Malloc) DUMMY_FUNCTION(MallocAsync) DUMMY_FUNCTION(MallocManaged) DUMMY_FUNCTION(Memcpy) DUMMY_FUNCTION(Memcpy2D) DUMMY_FUNCTION(MemcpyAsync) DUMMY_FUNCTION(Memcpy2DAsync) DUMMY_FUNCTION(MemPrefetchAsync) DUMMY_FUNCTION(StreamCreate) DUMMY_FUNCTION(StreamDestroy) DUMMY_FUNCTION(StreamSynchronize) DUMMY_FUNCTION(PointerGetAttributes) DUMMY_VALUE(CpuDeviceId) DUMMY_VALUE(HostRegisterMapped) DUMMY_VALUE(MemoryTypeDevice) DUMMY_VALUE(MemoryTypeHost) DUMMY_VALUE(MemoryTypeUnregistered) DUMMY_VALUE(MemoryTypeManaged) DUMMY_VALUE(MemcpyDeviceToHost) DUMMY_VALUE(MemcpyHostToDevice) DUMMY_VALUE(Success) } // namespace #undef DUMMY_FUNCTION #undef DUMMY_VALUE #undef DUMMY_SHOULD_NOT_BE_CALLEDatlas-0.46.0/hic/src/CMakeLists.txt0000664000175000017500000000417615160212070017165 0ustar alastairalastair# (C) Copyright 2024- 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. set(HIC_BACKEND_CUDA 0) set(HIC_BACKEND_HIP 0) set(HIC_BACKEND_DUMMY 0) if( HAVE_CUDA ) set(HIC_BACKEND_CUDA 1) elseif( HAVE_HIP ) set(HIC_BACKEND_HIP 1) else() set(HIC_BACKEND_DUMMY 1) endif() configure_file(${PROJECT_SOURCE_DIR}/src/hic/hic_config.h.in ${PROJECT_BINARY_DIR}/src/hic/hic_config.h @ONLY ) ecbuild_add_library( TARGET hic TYPE INTERFACE PUBLIC_INCLUDES $ $ $ ) ecbuild_add_library( TARGET hicsparse TYPE INTERFACE PUBLIC_INCLUDES $ $ $ ) install( FILES ${PROJECT_BINARY_DIR}/src/hic/hic_config.h DESTINATION include/hic ) install( FILES hic/hic.h DESTINATION include/hic ) install( FILES hic/hic_runtime.h DESTINATION include/hic ) install( FILES hic/hic_namespace_macro.h DESTINATION include/hic ) install( FILES hic/hic_library_types.h DESTINATION include/hic ) install( FILES hic/hicsparse.h DESTINATION include/hic ) install( FILES hic/hic_dummy/dummyShouldNotBeCalled.h DESTINATION include/hic/hic_dummy ) install( FILES hic/hic_dummy/hic_dummy_runtime.h DESTINATION include/hic/hic_dummy ) install( FILES hic/hic_dummy/hicsparse_dummy.h DESTINATION include/hic/hic_dummy ) if( HAVE_CUDA ) target_link_libraries( hic INTERFACE CUDA::cudart ) target_link_libraries( hicsparse INTERFACE hic CUDA::cusparse ) elseif( HAVE_HIP ) target_link_libraries( hic INTERFACE hip::host ) target_link_libraries( hicsparse INTERFACE hic roc::hipsparse ) endif() atlas-0.46.0/hic/README.md0000664000175000017500000000005615160212070015106 0ustar alastairalastairhic === HIC: An abstraction to HIP and CUDA atlas-0.46.0/hic/CHANGELOG.md0000664000175000017500000000000015160212070015425 0ustar alastairalastairatlas-0.46.0/hic/LICENSE0000664000175000017500000002500315160212070014633 0ustar alastairalastair Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS Copyright 1996-2018 ECMWF Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. atlas-0.46.0/hic/cmake/0000775000175000017500000000000015160212070014706 5ustar alastairalastairatlas-0.46.0/hic/cmake/hic_fetchcontent_ecbuild.cmake0000664000175000017500000000066215160212070022712 0ustar alastairalastairinclude(FetchContent) set( HIC_ECBUILD_VERSION 3.13.1 ) message(STATUS "Downloading ecbuild version ${HIC_ECBUILD_VERSION} to ${CMAKE_BINARY_DIR}/ecbuild") FetchContent_Populate( ecbuild URL https://github.com/ecmwf/ecbuild/archive/refs/tags/${HIC_ECBUILD_VERSION}.tar.gz SOURCE_DIR ${CMAKE_BINARY_DIR}/ecbuild QUIET ) find_package( ecbuild ${HIC_ECBUILD_VERSION} REQUIRED HINTS ${CMAKE_BINARY_DIR} ) atlas-0.46.0/hic/cmake/hic-import.cmake.in0000664000175000017500000000035115160212070020367 0ustar alastairalastair include( CMakeFindDependencyMacro ) set( hic_HAVE_CUDA @hic_HAVE_CUDA@ ) set( hic_HAVE_HIP @hic_HAVE_HIP@ ) if( hic_HAVE_CUDA ) find_dependency( CUDAToolkit ) endif() if( hic_HAVE_HIP ) find_dependency( hip CONFIG ) endif() atlas-0.46.0/hic/AUTHORS0000664000175000017500000000014215160212070014673 0ustar alastairalastairAuthors ======= - Willem Deconinck Thanks for contributions from ============================= atlas-0.46.0/hic/CMakeLists.txt0000664000175000017500000000563515160212070016377 0ustar alastairalastair# (C) Copyright 2024- 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. ############################################################################################ # hic cmake_minimum_required( VERSION 3.18 FATAL_ERROR ) find_package( ecbuild 3.4 QUIET HINTS ${CMAKE_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/../ecbuild ${CMAKE_CURRENT_SOURCE_DIR}/../../ecbuild ) if( NOT ecbuild_FOUND ) include(cmake/hic_fetchcontent_ecbuild.cmake) endif() ################################################################################ # Initialise project if( NOT DEFINED atlas_VERSION ) set( atlas_VERSION 0.0.0 ) endif() project( hic VERSION ${atlas_VERSION} LANGUAGES CXX ) set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD_REQUIRED ON) ################################################################################ # Features that can be enabled / disabled with -DENABLE_ ecbuild_add_option( FEATURE CUDA DESCRIPTION "Use CUDA as backend to HIC" DEFAULT OFF ) ecbuild_add_option( FEATURE HIP DESCRIPTION "Use HIP as backend to HIC" DEFAULT OFF ) if( HAVE_CUDA ) find_package(CUDAToolkit REQUIRED) elseif( HAVE_HIP ) find_package(hip CONFIG REQUIRED) find_package(hipsparse CONFIG REQUIRED) endif() ecbuild_add_option( FEATURE WARNINGS DEFAULT ON DESCRIPTION "Add warnings to compiler" ) if(HAVE_WARNINGS) ecbuild_add_cxx_flags("-Wall" NO_FAIL NAME hic_cxx_Wall QUIET) ecbuild_add_cxx_flags("-Wextra" NO_FAIL NAME hic_cxx_Wextra QUIET) ecbuild_add_cxx_flags("-Wpedantic" NO_FAIL NAME hic_cxx_Wpedantic QUIET) endif() ecbuild_add_option( FEATURE WARNING_AS_ERROR DEFAULT OFF DESCRIPTION "Treat compile warning as error" ) if(HAVE_WARNING_AS_ERROR) ecbuild_add_cxx_flags("-Werror" NO_FAIL NAME hic_cxx_warning_as_error QUIET) endif() if( CMAKE_CXX_COMPILER_ID STREQUAL Intel ) ecbuild_add_cxx_flags("-diag-disable=10441" NO_FAIL) # Deprecated classic compiler endif() add_subdirectory( src ) add_subdirectory( tests ) ################################################################################ # Export and summarize ecbuild_add_resources( TARGET hic-others SOURCES_PACK README.md CHANGELOG.md LICENSE ) if (ECBUILD_2_COMPAT) # Manually add hic and hicsparse to the list hic_ALL_LIBS to get exported. # Only when ECBUILD_2_COMPAT=ON, INTERFACE libraries like hic,hicsparse # are not added to the list of libraries to be exported within ecbuild_add_library. set( hic_ALL_LIBS hic hicsparse ) endif() ecbuild_install_project( NAME hic ) ecbuild_print_summary() atlas-0.46.0/src/0000775000175000017500000000000015160212070013652 5ustar alastairalastairatlas-0.46.0/src/sandbox/0000775000175000017500000000000015160212070015310 5ustar alastairalastairatlas-0.46.0/src/sandbox/fortran_object/0000775000175000017500000000000015160212070020311 5ustar alastairalastairatlas-0.46.0/src/sandbox/fortran_object/CMakeLists.txt0000664000175000017500000000100315160212070023043 0ustar alastairalastair# (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. if( CMAKE_Fortran_COMPILER_LOADED ) ecbuild_add_executable( TARGET atlas-sandbox-fortran-object SOURCES fortran_object.F90 ) endif() atlas-0.46.0/src/sandbox/fortran_object/fortran_object.F900000664000175000017500000000407615160212070023601 0ustar alastairalastair! (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. ! Purpose of this program is to test if the compiler can support ! the final keyword for specifying a destructor for a derived type. ! Unfortunately gfortran version < 4.9 does not support it, ! ! This means a destructor needs to be called manually, ! and be public. module atlas_sandbox_fortran_object_module implicit none private public :: AnimalType, AnimalType__dtor type :: AnimalType private integer :: m_age contains procedure :: age procedure :: speak ! Finalization may not be supported by compiler ! Only since gfortran >= 4.9 !final :: AnimalType__dtor end type ! Declare constructor as interface with same name as type interface AnimalType module procedure AnimalType__ctor end interface contains function AnimalType__ctor(age) result(self) type(AnimalType) :: self integer :: age write(0,'(A)') "Constructor Animal" self%m_age = age end function subroutine AnimalType__dtor(self) type(AnimalType), intent(inout) :: self write(0,'(A)') "Destroying animal" end subroutine function age(self) class(AnimalType), intent(inout) :: self integer :: age age = self%m_age end function subroutine speak(self) class(AnimalType), intent(in) :: self write(0,'(A)') "Animal::speak not overridden" end subroutine end program atlas_sandbox_fortran_object use atlas_sandbox_fortran_object_module implicit none type(AnimalType) :: animal animal = AnimalType(8) write(0,'(A,I0)') "age = ",animal%age() call animal%speak() ! In case Finalization is supported by compiler, following ! statement is not required call AnimalType__dtor(animal) end program atlas-0.46.0/src/sandbox/grid_distribution/0000775000175000017500000000000015160212070021034 5ustar alastairalastairatlas-0.46.0/src/sandbox/grid_distribution/atlas-grid-distribution.cc0000664000175000017500000001375215160212070026117 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ // file deepcode ignore MissingOpenCheckOnFile: False positive #include #include #include #include #include #include #include #include #include "eckit/exception/Exceptions.h" #include "eckit/filesystem/PathName.h" #include "atlas/grid.h" #include "atlas/grid/Distribution.h" #include "atlas/grid/Partitioner.h" #include "atlas/output/Gmsh.h" #include "atlas/output/detail/GmshIO.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/runtime/AtlasTool.h" #include "atlas/runtime/Log.h" #include "atlas/util/Config.h" //------------------------------------------------------------------------------ using namespace atlas; using namespace atlas::grid; using atlas::util::Config; using eckit::PathName; //------------------------------------------------------------------------------ class Tool : public AtlasTool { 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"; } std::string usage() override { return name() + " (--grid.name=name|--grid.json=path) [OPTION]... OUTPUT [--help]"; } public: Tool(int argc, char** argv); private: std::string key; PathName path_in; PathName path_out; }; //----------------------------------------------------------------------------- Tool::Tool(int argc, char** argv): AtlasTool(argc, argv) { add_option(new SimpleOption( "grid.name", "Grid unique identifier\n" + indent() + " Example values: N80, F40, O24, L32")); add_option(new SimpleOption("grid.json", "Grid described by json file")); add_option(new SimpleOption("partitioner", "Partitioner to be used")); add_option(new SimpleOption("partitions", "Number of partitions")); } //----------------------------------------------------------------------------- int Tool::execute(const Args& args) { key = ""; args.get("grid.name", key); std::string path_in_str = ""; if (args.get("grid.json", path_in_str)) { path_in = path_in_str; } StructuredGrid grid; if (key.size()) { try { grid = Grid(key); } catch (eckit::Exception&) { return failed(); } } else if (path_in.path().size()) { Log::info() << "Creating grid from file " << path_in << std::endl; Log::debug() << Config(path_in) << std::endl; try { grid = Grid(Config(path_in)); } catch (eckit::Exception& e) { return failed(); } } else { Log::error() << "No grid specified." << std::endl; } if (!grid) { return failed(); } Log::debug() << "Domain: " << grid.domain() << std::endl; Log::debug() << "Periodic: " << grid.periodic() << std::endl; std::string partitioner_type; if (not args.get("partitioner", partitioner_type)) { partitioner_type = "equal_regions"; } long N = mpi::comm().size(); args.get("partitions", N); if (mpi::comm().rank() == 0) { grid::Partitioner partitioner(partitioner_type, N); grid::Distribution distribution = partitioner.partition(grid); Log::info() << distribution << std::endl; std::vector> x(N); std::vector> y(N); for (long p = 0; p < N; ++p) { size_t nb_pts = distribution.nb_pts()[p]; x[p].reserve(nb_pts); y[p].reserve(nb_pts); } size_t n = 0; for (PointXY pxy : grid.xy()) { size_t p = distribution.partition(n++); x[p].push_back(pxy.x()); y[p].push_back(pxy.y()); } std::ofstream f("grid-distribution.py", std::ios::trunc); f << "\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" ""; for (long p = 0; p < N; ++p) { f << "\n" "x = ["; for (const double& _x : x[p]) { f << _x << ", "; } f << "]"; f << "\n" "y = ["; for (const double& _y : y[p]) { f << _y << ", "; } f << "]"; f << "\n" "c = cycol()"; f << "\n" "ax.scatter(x, y, color=c, marker='o')"; f << "\n" ""; } f << "\n" "ax.set_xlim( 0-5, 360+5)" "\n" "ax.set_ylim(-90-5, 90+5)" "\n" "ax.set_xticks([0,45,90,135,180,225,270,315,360])" "\n" "ax.set_yticks([-90,-45,0,45,90])" "\n" "plt.grid()" "\n" "plt.show()"; } return success(); } //------------------------------------------------------------------------------ int main(int argc, char** argv) { Tool tool(argc, argv); return tool.start(); } atlas-0.46.0/src/sandbox/grid_distribution/CMakeLists.txt0000664000175000017500000000100015160212070023563 0ustar alastairalastair# (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. ecbuild_add_executable( TARGET atlas-grid-distribution SOURCES atlas-grid-distribution.cc LIBS atlas NOINSTALL ) atlas-0.46.0/src/sandbox/interpolation/0000775000175000017500000000000015160212070020177 5ustar alastairalastairatlas-0.46.0/src/sandbox/interpolation/atlas-conservative-interpolation.cc0000664000175000017500000003664615160212070027224 0ustar alastairalastair/* * (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 #include #include #include #include "eckit/geometry/Sphere.h" #include "eckit/log/Bytes.h" #include "eckit/log/JSON.h" #include "eckit/types/FloatCompare.h" #include "atlas/array.h" #include "atlas/array/MakeView.h" #include "atlas/field.h" #include "atlas/grid.h" #include "atlas/grid/Spacing.h" #include "atlas/grid/detail/spacing/CustomSpacing.h" #include "atlas/interpolation/Interpolation.h" #include "atlas/interpolation/method/unstructured/ConservativeSphericalPolygonInterpolation.h" #include "atlas/mesh.h" #include "atlas/mesh/Mesh.h" #include "atlas/mesh/actions/Build2DCellCentres.h" #include "atlas/meshgenerator.h" #include "atlas/option.h" #include "atlas/output/Gmsh.h" #include "atlas/runtime/AtlasTool.h" #include "atlas/util/Config.h" #include "atlas/util/function/MDPI_functions.h" #include "atlas/util/function/SolidBodyRotation.h" #include "atlas/util/function/SphericalHarmonic.h" #include "atlas/util/function/VortexRollup.h" #include "tests/AtlasTestEnvironment.h" namespace atlas { class AtlasParallelInterpolation : public AtlasTool { int execute(const AtlasTool::Args& args) override; std::string briefDescription() override { return "Demonstration of parallel conservative interpolation"; } std::string usage() override { return name() + "[OPTIONS]... [--help]"; } int numberOfPositionalArguments() override { return -1; } int minimumPositionalArguments() override { return 0; } public: AtlasParallelInterpolation(int argc, char* argv[]): AtlasTool(argc, argv) { // Grid options add_option(new eckit::option::Separator("Grid options")); add_option(new SimpleOption("source.grid", "source gridname")); add_option(new SimpleOption("source.partitioner", "source partitioner name (spherical-polygon, lonlat-polygon, brute-force)")); add_option(new SimpleOption("target.grid", "target gridname")); add_option(new SimpleOption("target.partitioner", "target partitioner name (equal_regions, regular_bands, equal_bands)")); add_option(new SimpleOption("source.functionspace", "source functionspace, to override source grid default")); add_option(new SimpleOption("target.functionspace", "target functionspace, to override target grid default")); add_option(new SimpleOption("source.halo", "default=1")); add_option(new SimpleOption("target.halo", "default=0 for CellColumns and 1 for NodeColumns")); // Interpolation options add_option(new eckit::option::Separator("Interpolation options")); add_option(new SimpleOption("order", "Interpolation order. Supported: 1, 2 (default=1)")); add_option(new SimpleOption("normalise_intersections", "Normalize polygon intersections so that interpolation weights sum to 1.")); add_option(new SimpleOption("validate", "Enable extra validations at cost of performance. For debugging purpose.")); add_option(new SimpleOption("matrix_free", "Do not store matrix for consecutive interpolations")); // Interpolation statistics options add_option(new eckit::option::Separator("Interpolation statistics options")); add_option(new SimpleOption("statistics.all", "Enable all statistics")); add_option( new SimpleOption("statistics.timings", "Enable statistics on interpolation setup and execution timings")); add_option( new SimpleOption("statistics.intersection", "Enable statistics on polygon intersections")); add_option(new SimpleOption("statistics.accuracy", "Enable statistics w.r.t. an analytical solution")); add_option( new SimpleOption("statistics.conservation", "Enable statistics on mass conservation")); // Output options add_option(new eckit::option::Separator("Output options")); add_option(new SimpleOption( "output-gmsh", "Output gmsh files src_mesh.msh, tgt_mesh.msh, src_field.msh, tgt_field.msh")); add_option(new SimpleOption("gmsh.coordinates", "Mesh coordinates [xy,lonlat,xyz]")); add_option(new SimpleOption("gmsh.ghost", "output of ghost")); add_option(new SimpleOption("output-json", "Output json file with run information")); add_option(new SimpleOption("json.file", "File path for json output")); // Initial condition options add_option(new eckit::option::Separator("Initial condition options")); add_option(new SimpleOption( "init", "Setup initial source field [ constant, spherical_harmonic, vortex_rollup (default), solid_body_rotation_wind_magnitude ]")); add_option(new SimpleOption("solid_body_rotation.angle", "Angle of solid body rotation (default = 0.)")); add_option(new SimpleOption("vortex_rollup.t", "Value that controls vortex rollup (default = 0.5)")); add_option(new SimpleOption("constant.value", "Value that is assigned in case init==constant)")); add_option(new SimpleOption("spherical_harmonic.n", "total wave number 'n' of a spherical harmonic")); add_option(new SimpleOption("spherical_harmonic.m", "zonal wave number 'm' of a spherical harmonic")); } struct Timers { using StopWatch = atlas::runtime::trace::StopWatch; StopWatch target_setup; StopWatch source_setup; StopWatch initial_condition; StopWatch interpolation_setup; StopWatch interpolation_execute; } timers; }; std::function get_init(const eckit::LocalConfiguration& args) { std::string init; args.get("init", init = "vortex_rollup"); if (init == "vortex_rollup") { double t; args.get("vortex_rollup.t", t = 1.); return [t](const PointLonLat& p) { return util::function::vortex_rollup(p.lon(), p.lat(), t); }; } else if (init == "spherical_harmonic") { int n = 2; int m = 2; args.get("spherical_harmonic.n", n); args.get("spherical_harmonic.m", m); bool caching = true; // true -> warning not thread-safe util::function::SphericalHarmonic Y(n, m, caching); return [Y](const PointLonLat& p) { return Y(p.lon(), p.lat()); }; } else if (init == "constant") { double value; args.get("constant.value", value = 1.); return [value](const PointLonLat&) { return value; }; } else if (init == "solid_body_rotation_wind_magnitude") { double beta; args.get("solid_body_rotation.angle", beta = 0.); util::function::SolidBodyRotation sbr(beta); return [sbr](const PointLonLat& p) { return sbr.windMagnitude(p.lon(), p.lat()); }; } else if (init == "MDPI_sinusoid") { auto sbr = util::function::MDPI_sinusoid; return [sbr](const PointLonLat& p) { return sbr(p.lon(), p.lat()); }; } else if (init == "MDPI_harmonic") { auto sbr = util::function::MDPI_harmonic; return [sbr](const PointLonLat& p) { return sbr(p.lon(), p.lat()); }; } else if (init == "MDPI_vortex") { auto sbr = util::function::MDPI_vortex; return [sbr](const PointLonLat& p) { return sbr(p.lon(), p.lat()); }; } else if (init == "MDPI_gulfstream") { auto sbr = util::function::MDPI_gulfstream; return [sbr](const PointLonLat& p) { return sbr(p.lon(), p.lat()); }; } else { if (args.has("init")) { Log::error() << "Bad value for \"init\": \"" << init << "\" not recognised." << std::endl; ATLAS_NOTIMPLEMENTED; } } ATLAS_THROW_EXCEPTION("Should not be here"); } int AtlasParallelInterpolation::execute(const AtlasTool::Args& args) {\ eckit::LocalConfiguration config(args); auto get_grid = [](std::string grid_name) { int grid_number = std::atoi( grid_name.substr(1, grid_name.size()).c_str() ); if (grid_name.at(0) == 'P') { Log::info() << "P-grid number: " << grid_number << std::endl; ATLAS_ASSERT(grid_number > 3); std::vector y = {90, 89.9999, 0, -90}; auto xspace = StructuredGrid::XSpace( grid::LinearSpacing(0, 360, grid_number, false) ); auto yspace = StructuredGrid::YSpace( new grid::spacing::CustomSpacing( y.size(), y.data() ) ); return StructuredGrid(xspace, yspace); } else { return StructuredGrid{grid_name}; } }; auto src_grid = get_grid(config.getString("source.grid", "H32")); auto tgt_grid = get_grid(config.getString("target.grid", "H32")); auto create_functionspace = [&](Mesh& mesh, int halo, std::string type) -> FunctionSpace { if (type.empty()) { type = "NodeColumns"; if (mesh.grid().type() == "healpix" || mesh.grid().type() == "cubedsphere") { type = "CellColumns"; } } if (type == "CellColumns") { auto fspace = functionspace::CellColumns(mesh, option::halo(halo)); if (! mesh.cells().has_field("lonlat")) { mesh::actions::Build2DCellCentres{"lonlat"}(mesh); } return fspace; } else if (type == "NodeColumns") { return functionspace::NodeColumns(mesh, option::halo(std::max(1, halo))); } ATLAS_THROW_EXCEPTION("FunctionSpace " << type << " is not recognized."); }; timers.target_setup.start(); int tgt_halo = config.getLong("target.halo", 0); auto tgt_mesh = Mesh{tgt_grid, grid::Partitioner(config.getString("target.partitioner", "regular_bands"))}; auto tgt_functionspace = create_functionspace(tgt_mesh, tgt_halo, config.getString("target.functionspace", "")); auto tgt_field = tgt_functionspace.createField(); timers.target_setup.stop(); timers.source_setup.start(); int src_halo = config.getLong("source.halo", 1); auto src_meshgenerator = MeshGenerator{src_grid.meshgenerator() | option::halo(src_halo) | util::Config("pole_elements", "")}; auto src_partitioner = grid::MatchingPartitioner{tgt_mesh, util::Config("partitioner", config.getString("source.partitioner", "spherical-polygon"))}; auto src_mesh = src_meshgenerator.generate(src_grid, src_partitioner); auto src_functionspace = create_functionspace(src_mesh, src_halo, config.getString("source.functionspace", "")); auto src_field = src_functionspace.createField(); timers.source_setup.stop(); { ATLAS_TRACE("Initial condition"); timers.initial_condition.start(); const auto lonlat = array::make_view(src_functionspace.lonlat()); auto src_view = array::make_view(src_field); auto f = get_init(config); for (idx_t n = 0; n < lonlat.shape(0); ++n) { src_view(n) = f(PointLonLat{lonlat(n, LON), lonlat(n, LAT)}); } src_field.set_dirty(true); timers.initial_condition.stop(); } timers.interpolation_setup.start(); auto interpolation = Interpolation(option::type("conservative-spherical-polygon") | config, src_functionspace, tgt_functionspace); Log::info() << interpolation << std::endl; timers.interpolation_setup.stop(); timers.interpolation_execute.start(); auto metadata = interpolation.execute(src_field, tgt_field); tgt_field.haloExchange(); timers.interpolation_execute.stop(); Field src_conservation_field; { using Statistics = interpolation::method::ConservativeSphericalPolygonInterpolation::Statistics; Statistics stats(metadata); if (config.getBool("statistics.accuracy", false) || config.getBool("statistics.all", false)) { stats.compute_accuracy(interpolation, tgt_field, get_init(config), &metadata); } } Log::info() << "interpolation metadata: \n"; { eckit::JSON json(Log::info(), eckit::JSON::Formatting::indent(2)); json << metadata; } Log::info() << std::endl; if (config.getBool("output-gmsh", false)) { if (config.getBool("gmsh.ghost", false)) { ATLAS_TRACE("halo exchange target"); tgt_field.haloExchange(); } util::Config gmsh_config(config.getSubConfiguration("gmsh")); output::Gmsh{"src_mesh.msh", gmsh_config}.write(src_mesh); output::Gmsh{"src_field.msh", gmsh_config}.write(src_field); output::Gmsh{"tgt_mesh.msh", gmsh_config}.write(tgt_mesh); output::Gmsh{"tgt_field.msh", gmsh_config}.write(tgt_field); if (src_conservation_field) { output::Gmsh{"src_conservation_field.msh", config}.write(src_conservation_field); } } if (config.getBool("output-json", false)) { util::Config output; output.set("setup.source.grid", config.getString("source.grid")); output.set("setup.target.grid", config.getString("target.grid")); output.set("setup.source.functionspace", src_functionspace.type()); output.set("setup.target.functionspace", tgt_functionspace.type()); output.set("setup.source.halo", config.getLong("source.halo", 1)); output.set("setup.target.halo", config.getLong("target.halo", 0)); output.set("setup.interpolation.order", config.getInt("order", 1)); output.set("setup.interpolation.normalise_intersections", config.getBool("normalise_intersections", false)); output.set("setup.interpolation.validate", config.getBool("validate", false)); output.set("setup.interpolation.matrix_free", config.getBool("matrix-free", false)); output.set("setup.init", config.getString("init", "vortex_rollup")); output.set("runtime.mpi", mpi::size()); output.set("runtime.omp", atlas_omp_get_max_threads()); output.set("atlas.build_type", ATLAS_BUILD_TYPE); output.set("timings.target.setup", timers.target_setup.elapsed()); output.set("timings.source.setup", timers.source_setup.elapsed()); output.set("timings.initial_condition", timers.initial_condition.elapsed()); output.set("timings.interpolation.setup", timers.interpolation_setup.elapsed()); output.set("timings.interpolation.execute", timers.interpolation_execute.elapsed()); output.set("interpolation", metadata); eckit::PathName json_filepath(config.getString("json.file", "out.json")); std::ostringstream ss; eckit::JSON json(ss, eckit::JSON::Formatting::indent(4)); json << output; eckit::FileStream file(json_filepath, "w"); std::string str = ss.str(); file.write(str.data(), str.size()); file.close(); } return success(); } } // namespace atlas int main(int argc, char* argv[]) { atlas::AtlasParallelInterpolation tool(argc, argv); return tool.start(); } atlas-0.46.0/src/sandbox/interpolation/ScripIO.h0000664000175000017500000000134515160212070021663 0ustar alastairalastair/* * (C) Copyright 2025- 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/linalg/sparse.h" #include "atlas/util/Config.h" namespace atlas { class ScripIO { public: explicit ScripIO(const util::Config& = util::NoConfig()) {} static atlas::linalg::SparseMatrixStorage read(const std::string&); static void write(const linalg::SparseMatrixStorage&, const std::string&); }; } atlas-0.46.0/src/sandbox/interpolation/PartitionedMesh.h0000664000175000017500000000252415160212070023452 0ustar alastairalastair/* * (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.h" #include "atlas/grid.h" #include "atlas/mesh.h" #include "atlas/meshgenerator.h" namespace atlas { namespace interpolation { struct PartitionedMesh { typedef grid::Partitioner Partitioner; PartitionedMesh(const std::string& partitioner, const std::string& generator, bool meshGeneratorTriangulate = false, double meshGeneratorAngle = 0, bool patchPole = true); virtual ~PartitionedMesh() {} const Mesh& mesh() const { return mesh_; } Mesh& mesh() { return mesh_; } void writeGmsh(const std::string& fileName, const FieldSet& fields = FieldSet()); void partition(const Grid&); void partition(const Grid&, const PartitionedMesh&); protected: const std::string optionPartitioner_; const std::string optionGenerator_; MeshGenerator::Parameters generatorParams_; Partitioner partitioner_; Mesh mesh_; }; } // namespace interpolation } // namespace atlas atlas-0.46.0/src/sandbox/interpolation/ScripIO.cc0000664000175000017500000000771615160212070022031 0ustar alastairalastair/* * (C) Copyright 2025- 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 "ScripIO.h" #include #if ATLAS_HAVE_NETCDF #include #endif #include "atlas/linalg/sparse/SparseMatrixToTriplets.h" #include "atlas/linalg/sparse/MakeEckitSparseMatrix.h" #include "atlas/runtime/Exception.h" namespace atlas { using SparseMatrixStorage = linalg::SparseMatrixStorage; using scrip_index = int; using scrip_value = double; using scrip_size = size_t; SparseMatrixStorage ScripIO::read(const std::string& matrix_name) { #if ATLAS_HAVE_NETCDF == 0 ATLAS_THROW_EXCEPTION("Cannot read SCRIP files: Atlas not compiled with NetCDF support"); #else size_t Nr = 0; size_t Nc = 0; size_t nnz = 0; std::vector rows; std::vector cols; std::vector vals; // read SCRIP file try { netCDF::NcFile f(matrix_name, netCDF::NcFile::read); Nr = f.getDim("dst_grid_size").getSize(); Nc = f.getDim("src_grid_size").getSize(); nnz = f.getDim("num_links").getSize(); ATLAS_ASSERT(Nr > 0); ATLAS_ASSERT(Nc > 0); ATLAS_ASSERT(nnz > 0); auto var_rows = f.getVar("dst_address"); ATLAS_ASSERT(var_rows.getDimCount() == 1 && var_rows.getDim(0).getSize() == nnz); rows.resize(nnz); // NOTE: not compressed var_rows.getVar(rows.data()); auto var_cols = f.getVar("src_address"); ATLAS_ASSERT(var_cols.getDimCount() == 1 && var_cols.getDim(0).getSize() == nnz); cols.resize(nnz); var_cols.getVar(cols.data()); auto var_vals = f.getVar("remap_matrix"); ATLAS_ASSERT(var_vals.getDimCount() == 2 && var_vals.getDim(0).getSize() == nnz && var_vals.getDim(1).getSize() == 1); vals.resize(nnz); var_vals.getVar(vals.data()); } catch (netCDF::exceptions::NcException& e) { ATLAS_THROW_EXCEPTION("SCRIP reading failed : " << e.what()); } constexpr scrip_index scrip_base = 1; constexpr bool is_sorted = false; return atlas::linalg::make_sparse_matrix_storage_from_rows_columns_values(Nr, Nc, rows, cols, vals, scrip_base, is_sorted); #endif // ATLAS_HAVE_NETCDF } void ScripIO::write(const SparseMatrixStorage& matrix, const std::string& matrix_name) { #if ATLAS_HAVE_NETCDF == 0 ATLAS_THROW_EXCEPTION("Cannot write SCRIP file: Atlas not compiled with NetCDF support"); #else scrip_size Nr = matrix.rows(); scrip_size Nc = matrix.cols(); scrip_size nnz = matrix.nnz(); std::vector ia(nnz); std::vector ja(nnz); std::vector a(nnz); atlas::linalg::sparse_matrix_to_rows_columns_values(matrix, ia, ja, a); constexpr scrip_index scrip_index_base = 1; for (size_t i = 0; i < matrix.nnz(); ++i) { ja[i] += scrip_index_base; ia[i] += scrip_index_base; } try { netCDF::NcFile f(matrix_name, netCDF::NcFile::replace); f.addDim("dst_grid_size", Nr); f.addDim("src_grid_size", Nc); std::vector dims; dims.push_back(f.addDim("num_links", nnz)); netCDF::NcVar nc_dstaddr = f.addVar("dst_address", netCDF::ncInt, dims); netCDF::NcVar nc_srcaddr = f.addVar("src_address", netCDF::ncInt, dims); dims.push_back(f.addDim("num_wgts", 1)); netCDF::NcVar nc_rmatrix = f.addVar("remap_matrix", netCDF::ncDouble, dims); nc_dstaddr.putVar(ia.data()); nc_srcaddr.putVar(ja.data()); nc_rmatrix.putVar(a.data()); f.close(); } catch (netCDF::exceptions::NcException& e) { ATLAS_THROW_EXCEPTION("SCRIP writing failed : " << e.what()); } #endif } } atlas-0.46.0/src/sandbox/interpolation/atlas-parallel-interpolation.cc0000664000175000017500000003326315160212070026300 0ustar alastairalastair/* * (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/config/Resource.h" #include "eckit/log/Plural.h" #include "PartitionedMesh.h" #include "atlas/field.h" #include "atlas/functionspace.h" #include "atlas/interpolation.h" #include "atlas/linalg/sparse/Backend.h" #include "atlas/runtime/AtlasTool.h" #include "atlas/runtime/Log.h" #include "atlas/util/function/VortexRollup.h" using namespace atlas; class AtlasParallelInterpolation : public AtlasTool { 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() override { return -1; } int minimumPositionalArguments() override { return 0; } public: AtlasParallelInterpolation(int argc, char* argv[]): AtlasTool(argc, argv) { add_option(new SimpleOption("log-rank", "use specific MPI rank for logging (default 0)")); add_option(new SimpleOption("log-statistics", "show simple statistics on source/target (default false)")); add_option(new SimpleOption("output-polygons", "Output Python script that plots partitions polygons")); add_option(new SimpleOption("method", "interpolation method (default finite-element)")); add_option( new SimpleOption("backward-method", "backward interpolation method (default finite-element)")); add_option(new SimpleOption("backend", "linear algebra backend")); add_option(new SimpleOption("k-nearest-neighbours", "k-nearest neighbours (default 1)")); add_option(new SimpleOption("with-backward", "Also do backward interpolation (default false)")); add_option(new SimpleOption("source-gridname", "source gridname")); add_option(new SimpleOption("source-mesh-partitioner", "source mesh partitioner (equal_regions (default), ...)")); add_option( new SimpleOption("source-mesh-generator", "source mesh generator (default structured)")); add_option(new SimpleOption("source-mesh-generator-triangulate", "source mesh generator triangulate option (default false)")); add_option( new SimpleOption("source-mesh-generator-angle", "source mesh generator angle option (default 0.)")); add_option(new SimpleOption("source-mesh-halo", "source mesh halo size (default 1)")); add_option(new SimpleOption("target-gridname", "target gridname")); add_option(new SimpleOption("target-mesh-partitioner", "target mesh partitioner " "(spherical-polygon, " "lonlat-polygon, brute-force)")); add_option(new SimpleOption("target-mesh-generator", "target mesh generator (default structured)")); add_option(new SimpleOption("target-mesh-generator-triangulate", "target mesh generator triangulate option (default false)")); add_option( new SimpleOption("target-mesh-generator-angle", "target mesh generator angle option (default 0.)")); add_option(new SimpleOption("target-mesh-halo", "target mesh halo size (default 1)")); add_option( new SimpleOption("forward-interpolator-output", "Output forward interpolator's points and weights")); add_option(new SimpleOption("backward-interpolator-output", "Output backward interpolator's points and weights")); add_option(new SimpleOption("skip-halo-exchange", "Skip halo exchange")); add_option(new SimpleOption("missing-value", "Missing value to be inserted when projection fails")); } }; int AtlasParallelInterpolation::execute(const AtlasTool::Args& args) { // Get user options std::string option; bool trigs = false; double angle = 0.; bool log_statistics = false; args.get("log-statistics", log_statistics); std::string interpolation_method = "finite-element"; args.get("method", interpolation_method); if (args.get("backend", option)) { linalg::sparse::current_backend(option); } bool with_backward = false; args.get("with-backward", with_backward); // Generate and partition source & target mesh // source mesh is partitioned on its own, the target mesh uses // (pre-partitioned) source mesh auto source_gridname = args.getString("source-gridname", "O16"); auto target_gridname = args.getString("target-gridname", "O32"); Log::info() << "atlas-parallel-interpolation from source grid " << source_gridname << " to " << target_gridname << std::endl; Grid src_grid(source_gridname); idx_t source_mesh_halo = 0; args.get("source-mesh-halo", source_mesh_halo); interpolation::PartitionedMesh src(args.get("source-mesh-partitioner", option) ? option : "default", args.get("source-mesh-generator", option) ? option : "default", args.get("source-mesh-generator-triangulate", trigs) ? trigs : false, args.get("source-mesh-generator-angle", angle) ? angle : 0., true); Log::info() << "Partitioning source grid, halo of " << eckit::Plural(source_mesh_halo, "element") << std::endl; src.partition(src_grid); if (eckit::Resource("--output-polygons", false)) { src.mesh().polygon(0).outputPythonScript("src-polygons.py"); } FunctionSpace src_functionspace; bool structured = false; for (auto& is_structured : {"structured-bicubic", "bicubic", "structured-bilinear", "bilinear"}) { if (interpolation_method == is_structured) { structured = true; break; } } if (structured) { src_functionspace = functionspace::StructuredColumns{src.mesh().grid(), option::halo(std::max(2, source_mesh_halo)) | util::Config("periodic_points", true)}; } else { src_functionspace = functionspace::NodeColumns{src.mesh(), option::halo(source_mesh_halo)}; } src.writeGmsh("src-mesh.msh"); Grid tgt_grid(target_gridname); idx_t target_mesh_halo = args.getInt("target-mesh-halo", 0); interpolation::PartitionedMesh tgt(args.get("target-mesh-partitioner", option) ? option : "spherical-polygon", args.get("target-mesh-generator", option) ? option : "default", args.get("target-mesh-generator-triangulate", trigs) ? trigs : false, args.get("target-mesh-generator-angle", angle) ? angle : 0., with_backward ? true : false); Log::info() << "Partitioning target grid, halo of " << eckit::Plural(target_mesh_halo, "element") << std::endl; tgt.partition(tgt_grid, src); functionspace::NodeColumns tgt_functionspace(tgt.mesh(), option::halo(target_mesh_halo)); tgt.writeGmsh("tgt-mesh.msh"); if (eckit::Resource("--output-polygons", false)) { tgt.mesh().polygon(0).outputPythonScript("tgt-polygons.py"); } // Setup interpolator relating source & target meshes before setting a source // FunctionSpace halo Log::info() << "Computing forward/backward interpolator" << std::endl; Interpolation interpolator_forward(option::type(interpolation_method), src_functionspace, tgt_functionspace); Interpolation interpolator_backward; if (with_backward) { std::string backward_interpolation_method = "finite-element"; args.get("method", backward_interpolation_method); Log::info() << "Computing backward interpolator" << std::endl; interpolator_backward = Interpolation(option::type(backward_interpolation_method), tgt_functionspace, src_functionspace); } if (args.getBool("forward-interpolator-output", false)) { interpolator_forward.print(Log::info()); } // Create source FunctionSpace and fields FieldSet src_fields; { src_fields.add(src_functionspace.createField(option::name("funny_scalar_1"))); src_fields.add(src_functionspace.createField(option::name("funny_scalar_2"))); // Helper constants const double deg2rad = M_PI / 180., c_lat = 0. * M_PI, c_lon = 1. * M_PI, c_rad = 2. * M_PI / 9.; array::ArrayView lonlat = [&]() { if (auto fs = functionspace::NodeColumns(src_functionspace)) { return array::make_view(fs.nodes().lonlat()); } else if (auto fs = functionspace::StructuredColumns(src_functionspace)) { return array::make_view(fs.xy()); } else { ATLAS_NOTIMPLEMENTED; } }(); array::ArrayView src_scalar_1 = array::make_view(src_fields[0]), src_scalar_2 = array::make_view(src_fields[1]); ATLAS_ASSERT(src_scalar_1.shape(0) == lonlat.shape(0)); for (idx_t j = 0; j < lonlat.shape(0); ++j) { const double lon = deg2rad * lonlat(j, 0); // (lon) const double lat = deg2rad * lonlat(j, 1); // (lat) const double c2 = std::cos(lat), s1 = std::sin((lon - c_lon) / 2.), s2 = std::sin((lat - c_lat) / 2.), dist = 2.0 * std::sqrt(c2 * s1 * c2 * s1 + s2 * s2); src_scalar_1(j) = dist < c_rad ? 0.5 * (1. + std::cos(M_PI * dist / c_rad)) : 0.; src_scalar_2(j) = -src_scalar_1(j); // double x = lonlat( j, 0 ) - 180.; // double y = lonlat( j, 1 ); // src_scalar_1( j ) = -std::tanh( y / 10. * std::cos( 50. / std::sqrt( x * x + y * y ) ) - // x / 10. * std::sin( 50. / std::sqrt( x * x + y * y ) ) ); src_scalar_1(j) = util::function::vortex_rollup(lonlat(j, 0), lonlat(j, 1), 1.); } } FieldSet tgt_fields; for (idx_t i = 0; i < src_fields.size(); ++i) { auto tgt_field = tgt_fields.add(tgt_functionspace.createField(option::name(src_fields[i].name()))); double missing_value; if (args.get("missing-value", missing_value)) { tgt_field.metadata().set("missing_value", missing_value); } } if (args.getBool("skip-halo-exchange", false)) { src_fields.set_dirty(false); } else { src_functionspace.haloExchange(src_fields); } Log::info() << "Writing input to interpolation to src.msh" << std::endl; src.writeGmsh("src.msh", src_fields); Log::info() << "Interpolate forward" << std::endl; // interpolate (matrix-field vector multiply and synchronize results) (FIXME: // necessary?) interpolator_forward.execute(src_fields, tgt_fields); if (args.getBool("skip-halo-exchange", false)) { tgt_fields.set_dirty(false); } else { tgt_functionspace.haloExchange(tgt_fields); } if (with_backward) { Log::info() << "Interpolate backward" << std::endl; interpolator_backward.execute(tgt_fields, src_fields); if (args.getBool("skip-halo-exchange", false)) { src_fields.set_dirty(false); } else { src_functionspace.haloExchange(src_fields); } } Log::info() << "Interpolations done" << std::endl; // Report simple statistics (on source & target) if (auto src_nodecolumns = functionspace::NodeColumns{src_functionspace}) { if (log_statistics) { double meanA, minA, maxA, meanB, minB, maxB; idx_t nA, nB; for (idx_t i = 0; i < src_fields.size(); ++i) { src_nodecolumns.minimum(src_fields[i], minA); src_nodecolumns.maximum(src_fields[i], maxA); src_nodecolumns.mean(src_fields[i], meanA, nA); tgt_functionspace.minimum(tgt_fields[i], minB); tgt_functionspace.maximum(tgt_fields[i], maxB); tgt_functionspace.mean(tgt_fields[i], meanB, nB); Log::info() << "Field '" << src_fields[i].name() << "' (N,min,mean,max):" << "\n\tsource:\t" << nA << ",\t" << minA << ",\t" << meanA << ",\t" << maxA << "\n\ttarget:\t" << nB << ",\t" << minB << ",\t" << meanB << ",\t" << maxB << std::endl; } } } // Output results Log::info() << "Writing forward interpolation results to tgt.msh" << std::endl; tgt.writeGmsh("tgt.msh", tgt_fields); if (with_backward) { Log::info() << "Writing backward interpolation results to src-back.msh" << std::endl; src.writeGmsh("src-back.msh", src_fields); } return success(); } int main(int argc, char* argv[]) { AtlasParallelInterpolation tool(argc, argv); return tool.start(); } atlas-0.46.0/src/sandbox/interpolation/PartitionedMesh.cc0000664000175000017500000000527415160212070023615 0ustar alastairalastair/* * (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 "PartitionedMesh.h" #include "atlas/grid/Partitioner.h" #include "atlas/output/Gmsh.h" #include "atlas/runtime/Log.h" #include "atlas/runtime/Trace.h" namespace atlas { namespace interpolation { PartitionedMesh::PartitionedMesh(const std::string& partitioner, const std::string& generator, bool generatorTriangulate, double generatorAngle, bool patchPole): optionPartitioner_(partitioner), optionGenerator_(generator) { generatorParams_.set("three_dimensional", false); generatorParams_.set("patch_pole", patchPole); generatorParams_.set("include_pole", false); generatorParams_.set("triangulate", generatorTriangulate); generatorParams_.set("angle", generatorAngle); generatorParams_.set("fixup", true); } void PartitionedMesh::writeGmsh(const std::string& fileName, const FieldSet& fields) { util::Config output_config; //output_config.set( "coordinates", std::string( "xyz" ) ); output_config.set("ghost", true); output::Gmsh out(fileName, output_config); out.write(mesh_); if (not fields.empty()) { out.write(fields); } } void PartitionedMesh::partition(const Grid& grid) { ATLAS_TRACE("PartitionedMesh::partition()"); auto meshgen_config = grid.meshgenerator(); meshgen_config.set(generatorParams_); if (optionGenerator_ != "default") { meshgen_config.set("type", optionGenerator_); } MeshGenerator meshgen(meshgen_config); auto partitioner_config = grid.partitioner(); if (optionPartitioner_ != "default") { partitioner_config.set("type", optionPartitioner_); } partitioner_ = Partitioner(partitioner_config); mesh_ = meshgen.generate(grid, partitioner_.partition(grid)); } void PartitionedMesh::partition(const Grid& grid, const PartitionedMesh& other) { ATLAS_TRACE("PartitionedMesh::partition(other)"); partitioner_ = grid::MatchingMeshPartitioner(other.mesh_, util::Config("type", optionPartitioner_)); auto meshgen_config = grid.meshgenerator(); meshgen_config.set(generatorParams_); if (optionGenerator_ != "default") { meshgen_config.set("type", optionGenerator_); } MeshGenerator meshgen(meshgen_config); mesh_ = meshgen.generate(grid, partitioner_.partition(grid)); } } // namespace interpolation } // namespace atlas atlas-0.46.0/src/sandbox/interpolation/atlas-global-matrix.cc0000664000175000017500000004353615160212070024365 0ustar alastairalastair/* * (C) Copyright 2025 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 "ScripIO.h" #include "atlas/array.h" #include "atlas/field/Field.h" #include "atlas/field/FieldSet.h" #include "atlas/functionspace/CellColumns.h" #include "atlas/functionspace/NodeColumns.h" #include "atlas/functionspace/PointCloud.h" #include "atlas/functionspace/StructuredColumns.h" #include "atlas/grid/Grid.h" #include "atlas/grid/Iterator.h" #include "atlas/interpolation.h" #include "atlas/interpolation/AssembleGlobalMatrix.h" #include "atlas/linalg/sparse.h" #include "atlas/linalg/sparse/MakeEckitSparseMatrix.h" #include "atlas/mesh/Mesh.h" #include "atlas/meshgenerator.h" #include "atlas/output/Gmsh.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/runtime/AtlasTool.h" #include "atlas/util/CoordinateEnums.h" #include "atlas/util/function/VortexRollup.h" #include "tests/AtlasTestEnvironment.h" using atlas::functionspace::PointCloud; using atlas::functionspace::NodeColumns; using atlas::functionspace::StructuredColumns; using atlas::util::Config; using Matrix = atlas::linalg::SparseMatrixStorage; namespace atlas { // USAGE: // // 1) To compute and store the global interpolation matrix to the disc: // // atlas-global-matrix --sgrid --tgrid --interpolation --format // // 2) To read in a stored global interpolation matrix from the disc: // // atlas-global-matrix --sgrid --tgrid --interpolation --format --read --matrix // // For available interpolations see AtlasGlobalMatrix::interpolation_config. // Formats can be 'eckit' (binary) or 'scrip' (netcdf). // // Using the grid API 'create_fspaces' we can hide interpolation method specific requirements // such as which functionspace needs to be set-up. class AtlasGlobalMatrix : public AtlasTool { int execute(const AtlasTool::Args& args) override; std::string briefDescription() override { return "Assemble global matrix from an Interpolation in distributed parallel run"; } std::string usage() override { return name() + " [OPTION] ... [--help]"; } int numberOfPositionalArguments() override { return -1; } int minimumPositionalArguments() override { return 0; } Config interpolation_config(std::string scheme_str); Config create_fspaces(const std::string& scheme_str, const Grid& input_grid, const Grid& output_grid, FunctionSpace& fs_in, FunctionSpace& fs_out); std::string get_matrix_name(std::string& sgrid, std::string& tgrid, std::string& interp); Matrix read_matrix(std::string matrix_name, std::string matrix_format); void write_matrix(const Matrix& mat, std::string matrix_name, std::string matrix_format); public: AtlasGlobalMatrix(int argc, char* argv[]): AtlasTool(argc, argv) { add_option(new SimpleOption("sgrid", "source grid")); add_option(new SimpleOption("tgrid", "target grid")); add_option(new SimpleOption("interpolation", "interpolation methods")); add_option(new SimpleOption("read", "read interpolation matrix")); add_option(new SimpleOption("matrix", "name of the interpolation matrix")); add_option(new SimpleOption("format", "format of the matrix output: eckit, SCRIP")); } struct Timers { using StopWatch = atlas::runtime::trace::StopWatch; StopWatch functionspace_setup; StopWatch interpolation_setup; StopWatch interpolation_exe; StopWatch global_matrix_setup; StopWatch global_matrix_exe; } timers; }; int AtlasGlobalMatrix::execute(const AtlasTool::Args& args) { std::string sgrid_name = "O8"; args.get("sgrid", sgrid_name); std::string tgrid_name = "O32"; args.get("tgrid", tgrid_name); std::string interpolation_method = "finite-element"; args.get("interpolation", interpolation_method); Log::info() << "source grid: " << sgrid_name << ", target grid: " << tgrid_name << ", interpolation: " << interpolation_method << std::endl; std::string matrix_format = "eckit"; args.get("format", matrix_format); auto sgrid = Grid{sgrid_name}; auto tgrid = Grid{tgrid_name}; if (args.has("read")) { Matrix gmatrix; std::string matrix_name(""); args.get("matrix", matrix_name); if (matrix_name == "") { matrix_name = get_matrix_name(sgrid_name, tgrid_name, interpolation_method) + '.' + matrix_format; } if (mpi::comm().rank() == 0) { gmatrix = read_matrix(matrix_name, matrix_format); auto eckit_gmatrix = atlas::linalg::make_non_owning_eckit_sparse_matrix(gmatrix); eckit_gmatrix.print(Log::info()); Log::info() << std::endl; } mpi::comm().barrier(); FunctionSpace src_fs; FunctionSpace tgt_fs; timers.functionspace_setup.start(); if (interpolation_method.find("nearest-neighbour") != std::string::npos) { interpolation_method = "finite-element"; } auto scheme = create_fspaces(interpolation_method, sgrid, tgrid, src_fs, tgt_fs); timers.functionspace_setup.stop(); timers.global_matrix_setup.start(); auto matrix = interpolation::distribute_global_matrix(src_fs, tgt_fs, gmatrix); timers.global_matrix_setup.stop(); timers.interpolation_setup.start(); interpolation::MatrixCache cache(std::move(matrix)); auto interpolator = Interpolation(scheme, src_fs, tgt_fs, cache); timers.interpolation_setup.stop(); double timer_functionspace_setup = timers.functionspace_setup.elapsed() / mpi::comm().size(); double timer_interpolation_setup = timers.interpolation_setup.elapsed() / mpi::comm().size(); double timer_global_matrix_setup = timers.global_matrix_setup.elapsed() / mpi::comm().size(); mpi::comm().allReduceInPlace(&timer_functionspace_setup, 1, eckit::mpi::sum()); mpi::comm().allReduceInPlace(&timer_interpolation_setup, 1, eckit::mpi::sum()); mpi::comm().allReduceInPlace(&timer_global_matrix_setup, 1, eckit::mpi::sum()); Log::info() << "Grid + FunctionSpace timer\t: " << timer_functionspace_setup * 1000. << " [ms]" << std::endl; Log::info() << "Interpolation setup timer\t: " << timer_interpolation_setup * 1000. << " [ms]" << std::endl; Log::info() << "Global matrix setup timer\t: " << timer_global_matrix_setup * 1000. << " [ms]" << std::endl; // Allocate and initialise own memory here to show possibilities // Note: reading a field from disc is an extra feature auto src_field = interpolator.source().createField(); auto tgt_field = interpolator.target().createField(); auto src_lonlat = array::make_view(interpolator.source().lonlat()); ATLAS_TRACE_SCOPE("initialize source") { auto src_field_v = array::make_view(src_field); for (idx_t i = 0; i < src_fs.size(); ++i) { src_field_v[i] = util::function::vortex_rollup(src_lonlat(i, 0), src_lonlat(i, 1), 1.); } } timers.interpolation_exe.start(); interpolator.execute(src_field, tgt_field); timers.interpolation_exe.stop(); double timer_interpolation_exe = timers.interpolation_exe.elapsed() / mpi::comm().size(); mpi::comm().allReduceInPlace(&timer_interpolation_exe, 1, eckit::mpi::sum()); Log::info() << "Interpolation execute timer : " << timer_interpolation_exe * 1000. << " [ms]" << std::endl; ATLAS_TRACE_SCOPE("output from the read-in matrix") { std::string tgt_name = "tfield_" + matrix_name; output::Gmsh gmsh(tgt_name + ".msh", Config("coordinates", "lonlat") | Config("ghost", "true")); if( functionspace::NodeColumns(tgt_field.functionspace())) { Log::info() << "storing distributed remapped field '" << tgt_name << "'." << std::endl; gmsh.write(functionspace::NodeColumns(tgt_field.functionspace()).mesh()); tgt_field.haloExchange(); gmsh.write(tgt_field); } } } else { FunctionSpace src_fs; FunctionSpace tgt_fs; Interpolation interpolator; timers.functionspace_setup.start(); Config scheme = create_fspaces(interpolation_method, sgrid, tgrid, src_fs, tgt_fs); timers.functionspace_setup.stop(); timers.interpolation_setup.start(); if (interpolation_method == "grid-box-average") { interpolator = Interpolation{ scheme, sgrid, tgrid }; } else { interpolator = Interpolation{ scheme, src_fs, tgt_fs }; } timers.interpolation_setup.stop(); double timer_functionspace_setup = timers.functionspace_setup.elapsed() / mpi::comm().size(); double timer_interpolation_setup = timers.interpolation_setup.elapsed() / mpi::comm().size(); mpi::comm().allReduceInPlace(&timer_functionspace_setup, 1, eckit::mpi::sum()); mpi::comm().allReduceInPlace(&timer_interpolation_setup, 1, eckit::mpi::sum()); Log::info() << "Grid + FunctionSpace timer\t: " << timer_functionspace_setup * 1000. << " [ms]" << std::endl; Log::info() << "Interpolation setup timer\t: " << timer_interpolation_setup * 1000. << " [ms]" << std::endl; ATLAS_TRACE_SCOPE("par_output") { std::string tgt_name = "par-" + scheme.getString("name"); auto tgt_field = interpolator.target().createField(); auto field_in = interpolator.source().createField(); auto lonlat_in = array::make_view(interpolator.source().lonlat()); auto view_in = array::make_view(field_in); for(idx_t j = 0; j < field_in.size(); ++j) { view_in(j) = util::function::vortex_rollup(lonlat_in(j,0), lonlat_in(j,1), 1.); } interpolator.execute(field_in, tgt_field); output::Gmsh gmsh(tgt_name + ".msh", Config("coordinates", "lonlat") | Config("ghost", "true")); if( functionspace::NodeColumns(tgt_field.functionspace())) { Log::info() << "storing distributed remapped field '" << tgt_name << "'." << std::endl; gmsh.write(functionspace::NodeColumns(tgt_field.functionspace()).mesh()); tgt_field.haloExchange(); gmsh.write(tgt_field); } } auto matrix = interpolation::assemble_global_matrix(interpolator); if (mpi::comm().rank() == 0) { ATLAS_TRACE_SCOPE("store matrix") { std::string matrix_name; args.get("matrix", matrix_name); if (matrix_name == "") { matrix_name = get_matrix_name(sgrid_name, tgrid_name, interpolation_method); } write_matrix(matrix, matrix_name, matrix_format); } // Allocate and initialise own memory here to show possibilities std::vector src_data(sgrid.size()); std::vector tgt_data(tgrid.size()); ATLAS_TRACE_SCOPE("initialize source") { idx_t n{0}; for (auto p : sgrid.lonlat()) { src_data[n++] = util::function::vortex_rollup(p.lon(), p.lat(), 1.); } } auto src = eckit::linalg::Vector(src_data.data(), src_data.size()); auto tgt = eckit::linalg::Vector(tgt_data.data(), tgt_data.size()); auto eckit_matrix = atlas::linalg::make_non_owning_eckit_sparse_matrix(matrix); timers.global_matrix_exe.start(); #if ATLAS_ECKIT_VERSION_AT_LEAST(1, 19, 0) eckit::linalg::LinearAlgebraSparse::backend().spmv(eckit_matrix, src, tgt); #else eckit::linalg::LinearAlgebra::backend().spmv(eckit_matrix, src, tgt); #endif timers.global_matrix_exe.stop(); Log::info() << "Global matrix-multiply timer \t: " << 1000.*timers.global_matrix_exe.elapsed() << " [ms]" << std::endl; Log::info() << "Global matrix non-zero entries\t: " << matrix.nnz() << std::endl; Log::info() << "Global matrix footprint \t: " << eckit_matrix.footprint() << " B" << std::endl; ATLAS_TRACE_SCOPE("output from proc 0") { mpi::Scope mpi_scope("self"); std::string tgt_name = "tfield_cache_" + get_matrix_name(sgrid_name, tgrid_name, interpolation_method); auto tgt_field = Field(tgt_name, tgt_data.data(), array::make_shape(tgt_data.size())); // wrap output::Gmsh gmsh(tgt_name + ".msh", Config("coordinates", "lonlat") | Config("ghost", "true")); gmsh.write(MeshGenerator("structured", util::Config("three_dimensional",true)).generate(tgrid)); Log::info() << "storing remapped field '" << tgt_field.name() << "'." << std::endl; gmsh.write(tgt_field, StructuredColumns(tgrid)); } } } return success(); } //----------------------------------------------------------------------------- Config AtlasGlobalMatrix::interpolation_config(std::string scheme_str) { Config scheme; scheme.set("type", scheme_str); scheme.set("halo", 1); if (scheme_str.find("cubic") != std::string::npos) { scheme.set("halo", 2); } if (scheme_str == "k-nearest-neighbours") { scheme.set("k-nearest-neighbours", 4); scheme.set("halo", 2); } if (scheme_str == "conservative-spherical-polygon") { scheme.set("type", "conservative-spherical-polygon"); scheme.set("order", 1); scheme.set("src_cell_data", false); scheme.set("tgt_cell_data", false); } if (scheme_str == "conservative-spherical-polygon-2") { scheme.set("type", "conservative-spherical-polygon"); scheme.set("order", 2); scheme.set("halo", 2); scheme.set("src_cell_data", false); scheme.set("tgt_cell_data", false); } scheme.set("name", scheme_str); return scheme; } std::string AtlasGlobalMatrix::get_matrix_name(std::string& sgrid, std::string& tgrid, std::string& interpolation_name) { return "remap_" + sgrid + "_" + tgrid + "_" + interpolation_name; } Config AtlasGlobalMatrix::create_fspaces(const std::string& scheme_str, const Grid& input_grid, const Grid& output_grid, FunctionSpace& fs_in, FunctionSpace& fs_out) { Config scheme = interpolation_config(scheme_str); auto scheme_type = scheme.getString("type"); if (scheme_type == "finite-element" || scheme_type == "unstructured-bilinear-lonlat") { auto inmesh = Mesh(input_grid); fs_in = NodeColumns(inmesh, scheme); fs_out = PointCloud(output_grid, grid::MatchingPartitioner(inmesh)); } else if (scheme_type == "conservative-spherical-polygon") { bool src_cell_data = scheme.getBool("src_cell_data"); bool tgt_cell_data = scheme.getBool("tgt_cell_data"); auto tgt_mesh_config = output_grid.meshgenerator() | option::halo(0); auto tgt_mesh = MeshGenerator(tgt_mesh_config).generate(output_grid); if (tgt_cell_data) { fs_out = functionspace::CellColumns(tgt_mesh, option::halo(0)); } else { fs_out = functionspace::NodeColumns(tgt_mesh, option::halo(1)); } auto src_mesh_config = input_grid.meshgenerator() | option::halo(2); Mesh src_mesh; if (mpi::size() > 1) { src_mesh = MeshGenerator(src_mesh_config).generate(input_grid, grid::MatchingPartitioner(tgt_mesh)); } else { src_mesh = MeshGenerator(src_mesh_config).generate(input_grid); } if (src_cell_data) { fs_in = functionspace::CellColumns(src_mesh, option::halo(2)); } else { fs_in = functionspace::NodeColumns(src_mesh, option::halo(2)); } } else if (scheme_type == "nearest-neighbour" || scheme_type == "k-nearest-neighbours" || scheme_type == "grid-box-average") { fs_in = PointCloud(input_grid); auto inmesh = Mesh(input_grid); fs_out = functionspace::PointCloud(output_grid, grid::MatchingPartitioner(inmesh)); } else { fs_in = StructuredColumns(input_grid, scheme); fs_out = functionspace::PointCloud(output_grid, grid::MatchingPartitioner(fs_in), scheme); } return scheme; } Matrix AtlasGlobalMatrix::read_matrix(std::string matrix_name, std::string format) { if (format == "eckit") { eckit::linalg::SparseMatrix eckit_matrix; Log::info() << "reading matrix '" << matrix_name << std::endl; eckit_matrix.load(matrix_name); return atlas::linalg::make_sparse_matrix_storage(std::move(eckit_matrix)); } else if (format == "scrip") { Log::info() << "reading matrix '" << matrix_name << std::endl; return ScripIO::read(matrix_name); } else { ATLAS_NOTIMPLEMENTED; } return Matrix{}; } void AtlasGlobalMatrix::write_matrix(const Matrix& matrix, std::string matrix_name, std::string format) { if (format == "eckit") { auto eckit_matrix = linalg::make_non_owning_eckit_sparse_matrix(matrix); Log::info() << "storing matrix '" << matrix_name << ".eckit'" << std::endl; eckit_matrix.save(matrix_name + ".eckit"); } else if (format == "scrip") { Log::info() << "storing matrix '" << matrix_name << ".nc'" << std::endl; ScripIO::write(matrix, matrix_name + ".nc"); } else { ATLAS_NOTIMPLEMENTED; } } } // namespace int main(int argc, char** argv) { atlas::AtlasGlobalMatrix tool(argc, argv); return tool.start(); } atlas-0.46.0/src/sandbox/interpolation/atlas-parallel-structured-interpolation.cc0000664000175000017500000001734415160212070030504 0ustar alastairalastair/* * (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 "atlas/field.h" #include "atlas/functionspace.h" #include "atlas/grid.h" #include "atlas/interpolation.h" #include "atlas/output/Gmsh.h" #include "atlas/parallel/omp/omp.h" #include "atlas/runtime/AtlasTool.h" #include "atlas/runtime/Log.h" #include "atlas/util/CoordinateEnums.h" #include "atlas/util/function/VortexRollup.h" using namespace atlas; class AtlasParallelInterpolation : public AtlasTool { int execute(const AtlasTool::Args& args) override; std::string briefDescription() override { return "Demonstration of parallel interpolation"; } std::string usage() override { return name() + " [--source=gridname] " "[--target=gridname] [OPTION]... [--help]"; } int numberOfPositionalArguments() override { return -1; } int minimumPositionalArguments() override { return 0; } public: AtlasParallelInterpolation(int argc, char* argv[]): AtlasTool(argc, argv) { add_option(new SimpleOption("source", "source gridname")); add_option(new SimpleOption("target", "target gridname")); add_option( new SimpleOption("method", "interpolation method [linear (default), cubic, quasicubic]")); add_option(new SimpleOption("with-matrix", "Store matrix for consecutive interpolations")); add_option(new SimpleOption("partitioner", "source partitioner [equal_regions (default), ...]")); add_option(new SimpleOption("output-gmsh", "Output gmsh files src_field.msh and tgt_field.msh")); add_option(new SimpleOption( "output-polygons", "Output Python scripts src-polygons.py and tgt-polygons.py that plots partitions polygons")); add_option( new SimpleOption("output-inscribed-rectangle", "Output Python scripts src-inscribed-rectangle.py and tgt-inscribed-rectangle.py " "that plots iscribed rectangular domain for each partition")); add_option( new SimpleOption("init", "Setup initial source field [ zero, vortex-rollup (default) ]")); add_option(new SimpleOption("vortex-rollup", "Value that controls vortex rollup (default = 0)")); add_option(new SimpleOption("with-backwards", "Do backwards interpolation")); } }; static Config processed_config(const eckit::Configuration& _config) { Config config; if (_config.has("partitioner")) { config.set("partitioner", option::type(_config.getString("partitioner"))); } std::string scheme_str = _config.getString("method", "linear"); if (scheme_str == "linear") { config.set("type", "structured-bilinear"); config.set("halo", 1); // The stencil does not require any halo, but we set it to 1 for pole treatment! } if (scheme_str == "cubic") { config.set("type", "structured-bicubic"); config.set("halo", 2); } if (scheme_str == "quasicubic") { config.set("type", "structured-biquasicubic"); config.set("halo", 2); } config.set("name", scheme_str); config.set("matrix_free", not _config.getBool("with-matrix", false)); return config; } int AtlasParallelInterpolation::execute(const AtlasTool::Args& args) { ATLAS_TRACE("AtlasParallelInterpolation::execute"); auto source_gridname = args.getString("source", "O32"); auto target_gridname = args.getString("target", "O64"); auto require_polygon = args.getBool("output-polygons", false) || args.getBool("output-inscribed-rectangle", false); auto config = processed_config(args); Log::info() << "atlas-parallel-interpolation from source grid " << source_gridname << " to " << target_gridname << std::endl; Grid src_grid(source_gridname); Grid tgt_grid(target_gridname); functionspace::StructuredColumns src_fs; functionspace::StructuredColumns tgt_fs; int nlev = 0; ATLAS_TRACE_SCOPE("Create source function space") { src_fs = functionspace::StructuredColumns{src_grid, config | option::levels(nlev)}; } bool output_nodes = true; if (require_polygon) { const auto& src_poly = src_fs.polygon(); if (args.getBool("output-polygons", false)) { src_poly.outputPythonScript("src-polygons.py", Config("nodes", output_nodes)); } if (args.getBool("output-inscribed-rectangle", false)) { src_poly.outputPythonScript("src-inscribed-rectangle.py", Config("nodes", output_nodes) | Config("inscribed_rectangle", true)); } } ATLAS_TRACE_SCOPE("Create target function space") { auto tgt_partitioner = grid::MatchingPartitioner(src_fs); tgt_fs = functionspace::StructuredColumns{tgt_grid, tgt_partitioner, config | option::levels(nlev)}; } if (require_polygon) { const auto& tgt_poly = tgt_fs.polygon(); if (args.getBool("output-polygons", false)) { tgt_poly.outputPythonScript("tgt-polygons.py", Config("nodes", output_nodes)); } if (args.getBool("output-inscribed-rectangle", false)) { tgt_poly.outputPythonScript("tgt-inscribed-rectangle.py", Config("nodes", output_nodes)("inscribed_rectangle", true)); } } Field src_field = src_fs.createField(option::name("source")); Field tgt_field = tgt_fs.createField(option::name("target")); // Initialize source { ATLAS_TRACE("Initialize source"); auto lonlat = array::make_view(src_fs.xy()); auto source = array::make_view(src_field); idx_t size = src_fs.size(); if (args.getString("init", "vortex-rollup") == "zero") { atlas_omp_parallel_for(idx_t n = 0; n < size; ++n) { source(n) = 0.; } } else { idx_t k = args.getInt("vortex-rollup", 0); atlas_omp_parallel_for(idx_t n = 0; n < size; ++n) { source(n) = util::function::vortex_rollup(lonlat(n, LON), lonlat(n, LAT), 0.5 + double(k) / 2); } } } src_field.haloExchange(); output::Output src_gmsh; output::Output tgt_gmsh; if (args.getBool("output-gmsh", false)) { src_gmsh = output::Gmsh("src_field.msh"); src_gmsh.write(src_field); } { ATLAS_TRACE("Interpolation: Source to Target"); Interpolation interpolation_fwd(config, src_fs, tgt_fs); interpolation_fwd.execute(src_field, tgt_field); } if (args.getBool("output-gmsh", false)) { tgt_field.haloExchange(); tgt_gmsh = output::Gmsh("tgt_field.msh", Config("ghost", true)); tgt_gmsh.write(tgt_field); } if (args.getBool("with-backwards", false)) { tgt_field.haloExchange(); { ATLAS_TRACE("Interpolation: Target to Source"); Interpolation interpolation_bwd(config, tgt_fs, src_fs); interpolation_bwd.execute(tgt_field, src_field); } if (args.getBool("output-gmsh", false)) { src_gmsh.write(src_field); } } ATLAS_TRACE_SCOPE("Load imbalance") { mpi::comm().barrier(); } return success(); } int main(int argc, char* argv[]) { AtlasParallelInterpolation tool(argc, argv); return tool.start(); } atlas-0.46.0/src/sandbox/interpolation/CMakeLists.txt0000664000175000017500000000302715160212070022741 0ustar alastairalastair# (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. ecbuild_add_executable( TARGET atlas-parallel-interpolation SOURCES atlas-parallel-interpolation.cc PartitionedMesh.h PartitionedMesh.cc LIBS atlas NOINSTALL ) ecbuild_add_executable( TARGET atlas-parallel-structured-interpolation SOURCES atlas-parallel-structured-interpolation.cc LIBS atlas NOINSTALL ) ecbuild_add_executable( TARGET atlas-conservative-interpolation SOURCES atlas-conservative-interpolation.cc LIBS atlas NOINSTALL ) # Overcome Cray linking problem where the C++ library is not linked with the C library list( APPEND NetCDF_CXX_EXTRA_LIBRARIES NetCDF::NetCDF_C ) ecbuild_add_option( FEATURE NETCDF DESCRIPTION "Compile support the SCRIP format" REQUIRED_PACKAGES "NetCDF COMPONENTS C CXX" ) ecbuild_add_executable( TARGET atlas-global-matrix SOURCES atlas-global-matrix.cc ScripIO.h ScripIO.cc LIBS atlas ) target_compile_definitions( atlas-global-matrix PRIVATE ATLAS_HAVE_NETCDF=${HAVE_NETCDF} ) if( HAVE_NETCDF ) target_link_libraries( atlas-global-matrix NetCDF::NetCDF_CXX ) endif() atlas-0.46.0/src/sandbox/fortran_submodule/0000775000175000017500000000000015160212070021042 5ustar alastairalastairatlas-0.46.0/src/sandbox/fortran_submodule/sb_module.F900000664000175000017500000000100315160212070023265 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. #include "atlas/atlas_defines_fortran.h" module sb_module end module sb_module submodule (sb_module) sb_submod1 end submodule submod1 atlas-0.46.0/src/sandbox/fortran_submodule/CMakeLists.txt0000664000175000017500000000101615160212070023600 0ustar alastairalastair# (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. if( CMAKE_Fortran_COMPILER_LOADED ) ecbuild_add_library( TARGET atlas_sandbox_fortran_submodule CONDITION OFF SOURCES sb_module.F90 ) endif() atlas-0.46.0/src/sandbox/benchmark_trans/0000775000175000017500000000000015160212070020451 5ustar alastairalastairatlas-0.46.0/src/sandbox/benchmark_trans/atlas-benchmark-trans.cc0000664000175000017500000002734715160212070025156 0ustar alastairalastair/* * (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 #include #include #include #include "eckit/exception/Exceptions.h" #include "eckit/filesystem/PathName.h" #include "eckit/log/Bytes.h" #include "atlas/functionspace/Spectral.h" #include "atlas/grid.h" #include "atlas/linalg/dense.h" #include "atlas/option.h" #include "atlas/runtime/AtlasTool.h" #include "atlas/runtime/Log.h" #include "atlas/runtime/Trace.h" #include "atlas/trans/LegendreCacheCreator.h" #include "atlas/trans/Trans.h" #include "atlas/util/Config.h" //------------------------------------------------------------------------------ using namespace atlas; using namespace atlas::grid; using atlas::util::Config; using eckit::PathName; //------------------------------------------------------------------------------ class Tool : public AtlasTool { 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"; } std::string usage() override { return name() + " --grid=name [OPTION]... OUTPUT [--help]"; } public: Tool(int argc, char** argv); private: std::string key; PathName path_in; PathName path_out; }; //----------------------------------------------------------------------------- Tool::Tool(int argc, char** argv): AtlasTool(argc, argv) { add_option(new SimpleOption( "grid", "Grid unique identifier\n" + indent() + " Example values: N80, F40, O24, L32")); add_option(new SimpleOption("truncation", "truncation (default=N-1 with N the Gaussion number)")); add_option(new SimpleOption("nscalar", "number of scalar fields (default=1)")); add_option(new SimpleOption("nvordiv", "number of vor/div fields (default=0)")); add_option( new SimpleOption("type", "Type of trans implementation (if not specified, all types are used)")); add_option(new SimpleOption("matrix_multiply", "backend to use in local trans type")); add_option(new SimpleOption("fft", "backend to use in local trans type")); add_option(new SimpleOption("caching", "caching")); add_option(new SimpleOption("niter", "number of iterations")); } //----------------------------------------------------------------------------- int Tool::execute(const Args& args) { Trace timer(Here(), displayName()); key = ""; args.get("grid", key); std::string path_in_str = ""; if (args.get("grid", path_in_str)) { path_in = path_in_str; } StructuredGrid grid; if (key.size()) { try { grid = Grid(key); } catch (eckit::Exception&) { return failed(); } } else { Log::error() << "No grid specified." << std::endl; } if (!grid) { return failed(); } int truncation; if (not args.get("truncation", truncation)) { GaussianGrid gaussian{grid}; if (gaussian) { truncation = gaussian.N() - 1; // cubic relation } else { Log::error() << "Could not determine truncation from grid" << std::endl; return failed(); } } auto domain = GlobalDomain{}; //auto domain = RectangularDomain{{0,90.},{-45.,45.}}; std::vector types; if (args.has("type")) { types.emplace_back(args.getString("type")); if (not trans::Trans::hasBackend(types[0])) { Log::error() << "Atlas was not compiled with support for '" << types[0] << "' backend." << std::endl; return failed(); } if (not domain.global() && types[0] == "ectrans") { Log::error() << "The 'ectrans' backend for Trans can only use global domains." << std::endl; return failed(); } } else { types.emplace_back("local"); if (trans::Trans::hasBackend("ectrans") && domain.global()) { types.emplace_back("ectrans"); } } bool caching = false; int nb_scalar = 1; int nb_vordiv = 0; int niter = 1; args.get("nscalar", nb_scalar); args.get("nvordiv", nb_vordiv); args.get("niter", niter); args.get("caching", caching); int nb_all = nb_scalar + 2 * nb_vordiv; Log::info() << "Configuration" << std::endl; Log::info() << "~~~~~~~~~~~~~" << std::endl; Log::info() << " Grid : " << grid.name() << std::endl; Log::info() << " OpenMP : " << atlas_omp_get_max_threads() << std::endl; Log::info() << " Trunctation : " << truncation << std::endl; Log::info() << " trans.type : " << types << std::endl; Log::info() << " scalar fields : " << nb_scalar << std::endl; Log::info() << " vor/div fields : " << nb_vordiv << std::endl; Log::info() << " niter : " << niter << std::endl; Log::info() << " caching : " << std::boolalpha << caching << std::endl; if (caching) { Log::info() << " cache path : " << atlas::Library::instance().cachePath() << std::endl; } Log::info() << std::endl; if (caching) { if (not eckit::PathName{atlas::Library::instance().cachePath()}.exists()) { Log::error() << "Cache path " << atlas::Library::instance().cachePath() << " does not exist." << std::endl; Log::error() << "Set ATLAS_CACHE_PATH environment variable to an existing path." << std::endl; return failed(); } } std::map> linalg_backends{ {"ectrans", {"lapack"}}, {"local", {"generic", "openmp", "lapack", "eigen"}}}; if (args.has("matrix_multiply")) { std::string backend = args.getString("matrix_multiply"); linalg_backends["local"] = {backend}; if (not linalg::dense::Backend{backend}.available()) { Log::error() << "Atlas does not have support for matrix_multiply backend '" << backend << "'" << std::endl; return failed(); } } std::map> fft_backends{ {"ectrans", {"FFTW"}}, {"local", {"FFTW", "pocketfft"}}}; if (args.has("fft")) { fft_backends["local"] = std::vector{args.getString("fft")}; } functionspace::Spectral spectral{truncation}; std::vector sp_scalar(spectral.nb_spectral_coefficients_global() * nb_scalar); std::vector sp_vorticity(spectral.nb_spectral_coefficients_global() * nb_vordiv); std::vector sp_divergence(spectral.nb_spectral_coefficients_global() * nb_vordiv); std::vector gp(Grid{grid, domain}.size() * nb_all); for (size_t i = 0; i < nb_scalar; ++i) { sp_scalar[i] = 1.; } for (size_t i = 0; i < nb_vordiv; ++i) { sp_vorticity[i] = 1.; sp_divergence[i] = 1.; } for (auto& type : types) { ATLAS_TRACE(type); trans::Cache cache; util::Config write_fft; if (args.getBool("caching", false)) { auto fftw_wisdom_file = eckit::PathName(atlas::Library::instance().cachePath() + "/"+type+"_fftw_wisdom"); trans::LegendreCacheCreator cache_creator(grid, truncation, option::type(type)); if (cache_creator.supported()) { auto cachefile = eckit::PathName(atlas::Library::instance().cachePath() + "/leg_" + cache_creator.uid() + ".bin"); if (not cachefile.exists()) { ATLAS_TRACE("Creating Cache"); Log::debug() << "Creating cache: " << cachefile << " estimated size: " << eckit::Bytes(cache_creator.estimate()) << std::endl; cache_creator.create(cachefile); } Log::debug() << "Reading cache " << cachefile << " size: " << eckit::Bytes(cachefile.size()) << std::endl; if (fftw_wisdom_file.exists()) { ATLAS_TRACE("Reading Legendre + FFT Cache"); cache = trans::LegendreFFTCache(cachefile, fftw_wisdom_file); } else { ATLAS_TRACE("Reading Legendre Cache"); cache = trans::LegendreCache(cachefile); } } if (not fftw_wisdom_file.exists() && type == "local") { write_fft.set("write_fft", fftw_wisdom_file); } } for( auto& fft: fft_backends.at(type) ) { ATLAS_TRACE("fft="+fft); auto cstart = std::chrono::system_clock::now(); trans::Trans trans(cache, grid, domain, truncation, option::type(type) | option::fft(fft) | write_fft); auto cend = std::chrono::system_clock::now(); std::chrono::duration celapsed_seconds = cend - cstart; Log::info() << "type=" << std::setw(8) << std::left << type; Log::info() << " fft=" << std::setw(10) << std::left << fft; Log::info() << " constructor: " << celapsed_seconds.count() << " s" << std::endl; for (auto backend : linalg_backends.at(type)) { linalg::dense::current_backend(backend); if (linalg::dense::current_backend().available()) { backend = linalg::dense::current_backend().type(); auto min = std::numeric_limits::max(); auto max = 0.; auto zeropad = [](int n) { std::stringstream s; s << std::setw(3) << std::setfill('0') << n; return s.str(); }; auto print = [&](const std::string& idx, double seconds) { Log::info() << "type=" << std::setw(8) << std::left << type; Log::info() << " backend=" << std::setw(8) << std::left << backend; Log::info() << " fft=" << std::setw(10) << std::left << fft; Log::info() << " invtrans[" << idx << "]: " << seconds << " s" << std::endl; }; for (size_t n = 0; n < niter; ++n) { ATLAS_TRACE("invtrans [backend=" + backend + ", fft="+fft+"]"); auto start = std::chrono::system_clock::now(); trans.invtrans(nb_scalar, sp_scalar.data(), nb_vordiv, sp_vorticity.data(), sp_divergence.data(), gp.data()); auto end = std::chrono::system_clock::now(); // std::chrono::duration elapsed_seconds = end - start; print(zeropad(n), elapsed_seconds.count()); min = std::min(min, elapsed_seconds.count()); max = std::max(max, elapsed_seconds.count()); } print("min", min); print("max", max); } } } } timer.stop(); Log::info() << Trace::report() << std::endl; return success(); } //------------------------------------------------------------------------------ int main(int argc, char** argv) { Tool tool(argc, argv); return tool.start(); } atlas-0.46.0/src/sandbox/benchmark_trans/CMakeLists.txt0000664000175000017500000000077515160212070023222 0ustar alastairalastair# (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. ecbuild_add_executable( TARGET atlas-benchmark-trans SOURCES atlas-benchmark-trans.cc LIBS atlas # NOINSTALL ) atlas-0.46.0/src/sandbox/benchmark_sorting/0000775000175000017500000000000015160212070021007 5ustar alastairalastairatlas-0.46.0/src/sandbox/benchmark_sorting/atlas-benchmark-sorting.cc0000664000175000017500000002076315160212070026045 0ustar alastairalastair/* * (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 #include #include #include #include "eckit/filesystem/PathName.h" #include "atlas/grid.h" #include "atlas/mesh.h" #include "atlas/mesh/actions/BuildHalo.h" #include "atlas/mesh/actions/BuildParallelFields.h" #include "atlas/mesh/actions/BuildPeriodicBoundaries.h" #include "atlas/meshgenerator.h" #include "atlas/output/detail/GmshIO.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/runtime/AtlasTool.h" #include "atlas/runtime/Log.h" #include "atlas/runtime/Trace.h" #include "atlas/util/Config.h" #include "atlas/array.h" #include "atlas/field/Field.h" #include "atlas/util/CoordinateEnums.h" #include "atlas/util/Unique.h" using Topology = atlas::mesh::Nodes::Topology; using atlas::util::UniqueLonLat; //------------------------------------------------------------------------------ using namespace atlas; using namespace atlas::grid; using atlas::util::Config; using eckit::PathName; //------------------------------------------------------------------------------ namespace atlas { struct Node { Node(gidx_t gid, int idx) { g = gid; i = idx; } gidx_t g; gidx_t i; bool operator<(const Node& other) const { return (g < other.g); } }; //------------------------------------------------------------------------------ void make_nodes_global_index_human_readable(const mesh::actions::BuildHalo& build_halo, mesh::Nodes& nodes, bool do_all) { ATLAS_TRACE(); // TODO: ATLAS-14: fix renumbering of EAST periodic boundary points // --> Those specific periodic points at the EAST boundary are not checked for // uid, // and could receive different gidx for different tasks UniqueLonLat compute_uid(nodes); // unused // int mypart = mpi::comm().rank(); int nparts = mpi::comm().size(); size_t root = 0; array::ArrayView nodes_glb_idx = array::make_view(nodes.global_index()); // nodes_glb_idx.dump( Log::info() ); Log::info() << std::endl; Log::info() << "min = " << nodes.global_index().metadata().getLong("min") << std::endl; Log::info() << "max = " << nodes.global_index().metadata().getLong("max") << std::endl; Log::info() << "human_readable = " << nodes.global_index().metadata().getBool("human_readable") << std::endl; gidx_t glb_idx_max = 0; std::vector points_to_edit; if (do_all) { points_to_edit.resize(nodes_glb_idx.size()); for (idx_t i = 0; i < nodes_glb_idx.size(); ++i) { points_to_edit[i] = i; } } else { glb_idx_max = nodes.global_index().metadata().getLong("max", 0); points_to_edit.resize(build_halo.periodic_points_local_index_.size()); for (size_t i = 0; i < points_to_edit.size(); ++i) { points_to_edit[i] = build_halo.periodic_points_local_index_[i]; } } std::vector glb_idx(points_to_edit.size()); for (size_t i = 0; i < points_to_edit.size(); ++i) { glb_idx[i] = nodes_glb_idx(i); } ATLAS_DEBUG_VAR(points_to_edit); ATLAS_DEBUG_VAR(points_to_edit.size()); ATLAS_TRACE("distributed_sort"); /* * Sorting following gidx will define global order of * gathered fields. Special care needs to be taken for * pole edges, as their centroid might coincide with * other edges */ int nb_nodes = glb_idx.size(); // 1) Gather all global indices, together with location std::vector recvcounts(mpi::comm().size()); std::vector recvdispls(mpi::comm().size()); ATLAS_TRACE_MPI(GATHER) { mpi::comm().gather(nb_nodes, recvcounts, root); } recvdispls[0] = 0; for (int jpart = 1; jpart < nparts; ++jpart) // start at 1 { recvdispls[jpart] = recvcounts[jpart - 1] + recvdispls[jpart - 1]; } int glb_nb_nodes = std::accumulate(recvcounts.begin(), recvcounts.end(), 0); std::vector glb_idx_gathered(glb_nb_nodes); ATLAS_TRACE_MPI(GATHER) { mpi::comm().gatherv(glb_idx.data(), glb_idx.size(), glb_idx_gathered.data(), recvcounts.data(), recvdispls.data(), root); } // 2) Sort all global indices, and renumber from 1 to glb_nb_edges std::vector node_sort; node_sort.reserve(glb_nb_nodes); for (size_t jnode = 0; jnode < glb_idx_gathered.size(); ++jnode) { node_sort.emplace_back(glb_idx_gathered[jnode], jnode); } ATLAS_TRACE_SCOPE("local_sort") { std::sort(node_sort.begin(), node_sort.end()); } gidx_t gid = glb_idx_max; for (size_t jnode = 0; jnode < node_sort.size(); ++jnode) { if (jnode == 0) { ++gid; } else if (node_sort[jnode].g != node_sort[jnode - 1].g) { ++gid; } int inode = node_sort[jnode].i; glb_idx_gathered[inode] = gid; } // 3) Scatter renumbered back ATLAS_TRACE_MPI(SCATTER) { mpi::comm().scatterv(glb_idx_gathered.data(), recvcounts.data(), recvdispls.data(), glb_idx.data(), glb_idx.size(), root); } for (int jnode = 0; jnode < nb_nodes; ++jnode) { nodes_glb_idx(points_to_edit[jnode]) = glb_idx[jnode]; } // nodes_glb_idx.dump( Log::info() ); // Log::info() << std::endl; } } // namespace atlas //----------------------------------------------------------------------------- class Tool : public AtlasTool { 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"; } std::string usage() override { return name() + " (--grid=name) [--help]"; } public: Tool(int argc, char** argv); private: std::string key; PathName path_in; PathName path_out; }; //----------------------------------------------------------------------------- Tool::Tool(int argc, char** argv): AtlasTool(argc, argv) { add_option(new SimpleOption( "grid", "Grid unique identifier\n" + indent() + " Example values: N80, F40, O24, L32")); add_option(new SimpleOption("halo", "size of halo")); add_option(new SimpleOption("do-all", "Renumber all points")); } //----------------------------------------------------------------------------- int Tool::execute(const Args& args) { Trace t(Here(), "main"); key = ""; args.get("grid", key); std::string path_in_str = ""; if (args.get("grid", path_in_str)) { path_in = path_in_str; } StructuredGrid grid; if (key.size()) { try { grid = Grid(key); } catch (eckit::Exception&) { } } else { Log::error() << "No grid specified." << std::endl; } if (!grid) { return failed(); } Log::debug() << "Domain: " << grid.domain() << std::endl; Log::debug() << "Periodic: " << grid.periodic() << std::endl; MeshGenerator meshgenerator("structured", Config("partitioner", "equal_regions")); size_t halo = args.getLong("halo", 0); bool do_all = args.getBool("do-all", false); ATLAS_DEBUG_VAR(do_all); size_t iterations = 1; mpi::comm().barrier(); for (size_t j = 0; j < 1; ++j) { ATLAS_TRACE("outer_iteration"); Mesh mesh = meshgenerator.generate(grid); atlas::mesh::actions::build_periodic_boundaries(mesh); Log::info() << "building halo" << std::endl; atlas::mesh::actions::BuildHalo build_halo(mesh); build_halo(halo); Trace::Barriers set_barrier(true); Trace::Tracing set_channel(Log::info()); for (size_t i = 0; i < iterations; ++i) { make_nodes_global_index_human_readable(build_halo, mesh.nodes(), do_all); } } t.stop(); Log::info() << Trace::report(Config("indent", 2)("decimals", 2) // ("depth",5) ); return success(); } //------------------------------------------------------------------------------ int main(int argc, char** argv) { Tool tool(argc, argv); return tool.start(); } atlas-0.46.0/src/sandbox/benchmark_sorting/CMakeLists.txt0000664000175000017500000000100115160212070023537 0ustar alastairalastair# (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. ecbuild_add_executable( TARGET atlas-benchmark-sorting SOURCES atlas-benchmark-sorting.cc LIBS atlas # NOINSTALL ) atlas-0.46.0/src/sandbox/apps/0000775000175000017500000000000015160212070016253 5ustar alastairalastairatlas-0.46.0/src/sandbox/apps/atlas-benchmark.cc0000664000175000017500000005200315160212070021616 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ /** * @file atlas-benchmark.cc * @author Willem Deconinck * * Benchmark testing parallel performance of gradient computation using the * Green-Gauss Theorem on an edge-based median-dual mesh. * * Configurable is * - Horizontal mesh resolution, which is unstructured and * domain-decomposed, * - Vertical resolution, which is structured, and is beneficial for caching * - Number of iterations, so caches can warm up, and timings can be averaged * - Number of OpenMP threads per MPI task * * Results should be bit-identical when changing number of OpenMP threads or MPI * tasks. * A checksum on all bits is used to verify between scaling runs. * * */ #include #include #include #include #include #include #include #include "atlas/functionspace.h" #include "atlas/grid.h" #include "atlas/library.h" #include "atlas/mesh.h" #include "atlas/mesh/IsGhostNode.h" #include "atlas/mesh/actions/BuildDualMesh.h" #include "atlas/mesh/actions/BuildEdges.h" #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" #include "atlas/parallel/HaloExchange.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/parallel/omp/omp.h" #include "atlas/runtime/AtlasTool.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Trace.h" #include "atlas/util/CoordinateEnums.h" #include "atlas/util/Earth.h" //---------------------------------------------------------------------------------------------------------------------- using std::cout; using std::endl; using std::fixed; using std::max; using std::min; using std::numeric_limits; using std::scientific; using std::setprecision; using std::setw; using std::string; using std::stringstream; using std::unique_ptr; using std::vector; using Topology = atlas::mesh::Nodes::Topology; using atlas::mesh::IsGhostNode; using namespace eckit::option; using namespace atlas; using namespace atlas::array; using namespace atlas::grid; using namespace atlas::mesh::actions; using namespace atlas::functionspace; using namespace atlas::meshgenerator; using atlas::AtlasTool; //---------------------------------------------------------------------------------------------------------------------- struct TimerStats { TimerStats(const string& _name = "timer") { max = -1; min = -1; avg = 0; cnt = 0; name = _name; } void update(Trace& timer) { double t = timer.elapsed(); if (min < 0) { min = t; } if (max < 0) { max = t; } min = std::min(min, t); max = std::max(max, t); avg = (avg * cnt + t) / (cnt + 1); ++cnt; } string str() { stringstream stream; stream << name << ": min, max, avg -- " << fixed << setprecision(5) << min << ", " << fixed << setprecision(5) << max << ", " << fixed << setprecision(5) << avg; return stream.str(); } string name; double max; double min; double avg; int cnt; }; //---------------------------------------------------------------------------------------------------------------------- class AtlasBenchmark : public AtlasTool { int execute(const Args& args) override; public: AtlasBenchmark(int argc, char** argv): AtlasTool(argc, argv) { add_option(new SimpleOption("grid", "Grid unique identifier")); add_option(new SimpleOption("nlev", "Vertical resolution: Number of levels")); add_option(new SimpleOption("niter", "Number of iterations")); add_option(new SimpleOption("omp", "Number of OpenMP threads per MPI task")); add_option(new SimpleOption("progress", "Show progress bar instead of intermediate timings")); 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(); void iteration(); double result(); int verify(const double&); void initial_condition(Field& field, const double& beta); private: Mesh mesh; functionspace::NodeColumns nodes_fs; functionspace::NodeColumns edges_fs; Field scalar_field; Field grad_field; unique_ptr avgS_arr; std::vector pole_edges; std::vector is_ghost; idx_t nnodes; idx_t nedges; idx_t nlev; idx_t niter; idx_t exclude; bool output; long omp_threads; double dz; std::string gridname; std::string reorder{"none"}; bool sort_edges{false}; TimerStats iteration_timer; TimerStats haloexchange_timer; idx_t iter; bool progress; }; //---------------------------------------------------------------------------------------------------------------------- int AtlasBenchmark::execute(const Args& args) { Trace timer(Here(), "atlas-benchmark"); // Timer::Logging set_channel( Log::info() ); nlev = 137; args.get("nlev", nlev); gridname = "N64"; args.get("grid", gridname); niter = 100; args.get("niter", niter); omp_threads = -1; args.get("omp", omp_threads); progress = false; args.get("progress", progress); exclude = niter == 1 ? 0 : 1; 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); iteration_timer = TimerStats("iteration"); haloexchange_timer = TimerStats("halo-exchange"); if (omp_threads > 0) { atlas_omp_set_num_threads(omp_threads); } Log::info() << "atlas-benchmark\n" << endl; Log::info() << Library::instance().information() << endl; Log::info() << "Configuration:" << endl; Log::info() << " grid: " << gridname << endl; Log::info() << " nlev: " << nlev << endl; Log::info() << " niter: " << niter << endl; Log::info() << endl; Log::info() << " MPI tasks: " << mpi::comm().size() << endl; Log::info() << " OpenMP threads per MPI task: " << atlas_omp_get_max_threads() << endl; Log::info() << endl; Log::info() << "Timings:" << endl; ATLAS_TRACE_SCOPE("setup", {"atlas-benchmark-setup"}) { setup(); } Log::info() << " Executing " << niter << " iterations: \n"; if (progress) { Log::info() << " 0% 10 20 30 40 50 60 70 80 90 100%\n"; Log::info() << " |----|----|----|----|----|----|----|----|----|----|\n"; Log::info() << " " << std::flush; } unsigned int tic = 0; for (iter = 0; iter < niter; ++iter) { if (progress) { unsigned int tics_needed = static_cast(static_cast(iter) / static_cast(niter - 1) * 50.0); while (tic <= tics_needed) { Log::info() << '*' << std::flush; ++tic; } if (iter == niter - 1) { if (tic < 51) { Log::info() << '*'; } Log::info() << endl; } } iteration(); } timer.stop(); Log::info() << "Iteration timer Statistics:\n" << " min: " << setprecision(5) << fixed << iteration_timer.min << " max: " << setprecision(5) << fixed << iteration_timer.max << " avg: " << setprecision(5) << fixed << iteration_timer.avg << endl; Log::info() << "Communication timer Statistics:\n" << " min: " << setprecision(5) << fixed << haloexchange_timer.min << " max: " << setprecision(5) << fixed << haloexchange_timer.max << " avg: " << setprecision(5) << fixed << haloexchange_timer.avg << " ( " << setprecision(2) << haloexchange_timer.avg / iteration_timer.avg * 100. << "% )" << endl; util::Config report_config; report_config.set("indent", 4); if (not args.getBool("details", false)) { report_config.set("exclude", std::vector{"halo-exchange", "atlas-benchmark-setup/*"}); } Log::info() << timer.report(report_config) << std::endl; Log::info() << endl; mpi::comm().barrier(); Log::info() << "Results:" << endl; double res = result(); Log::info() << endl; return verify(res); } //---------------------------------------------------------------------------------------------------------------------- void AtlasBenchmark::initial_condition(Field& field, const double& beta) { const double radius = util::Earth::radius(); const double USCAL = 20.; const double pvel = USCAL / radius; const double deg2rad = M_PI / 180.; auto lonlat_deg = array::make_view(mesh.nodes().lonlat()); auto var = array::make_view(field); idx_t nnodes = mesh.nodes().size(); for (idx_t jnode = 0; jnode < nnodes; ++jnode) { double x = lonlat_deg(jnode, LON) * deg2rad; double y = lonlat_deg(jnode, LAT) * deg2rad; double Ux = pvel * (std::cos(beta) + std::tan(y) * std::cos(x) * std::sin(beta)) * radius * std::cos(y); double Uy = -pvel * std::sin(x) * std::sin(beta) * radius; for (idx_t jlev = 0; jlev < nlev; ++jlev) { var(jnode, jlev) = std::sqrt(Ux * Ux + Uy * Uy); } } } //---------------------------------------------------------------------------------------------------------------------- void AtlasBenchmark::setup() { idx_t halo = 1; StructuredGrid grid; ATLAS_TRACE_SCOPE("Create grid") { grid = Grid(gridname); } 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) | util::Config("sort_edges", sort_edges)); } // mesh.polygon(0).outputPythonScript("plot_polygon.py"); // atlas::output::Output gmsh = atlas::output::Gmsh( "edges.msh", // util::Config("ghost",true)("edges",true)("elements",false) ); // gmsh.write( mesh ); // gmsh = atlas::output::Gmsh( "elements.msh", // util::Config("ghost",true)("edges",false)("elements",true) ); // gmsh.write( mesh ); ATLAS_TRACE_SCOPE("build_median_dual_mesh") { build_median_dual_mesh(mesh); } ATLAS_TRACE_SCOPE("build_node_to_edge_connectivity") { build_node_to_edge_connectivity(mesh); } scalar_field = nodes_fs.createField(option::name("field") | option::levels(nlev)); grad_field = nodes_fs.createField(option::name("grad") | option::levels(nlev) | option::variables(3)); nnodes = mesh.nodes().size(); nedges = mesh.edges().size(); auto lonlat = array::make_view(mesh.nodes().xy()); auto V = array::make_view(mesh.nodes().field("dual_volumes")); auto S = array::make_view(mesh.edges().field("dual_normals")); initial_condition(scalar_field, 0.); double radius = 6371.22e+03; // Earth's radius double height = 80.e+03; // Height of atmosphere double deg2rad = M_PI / 180.; atlas_omp_parallel_for(idx_t jnode = 0; jnode < nnodes; ++jnode) { lonlat(jnode, LON) = lonlat(jnode, LON) * deg2rad; lonlat(jnode, LAT) = lonlat(jnode, LAT) * deg2rad; double y = lonlat(jnode, LAT); double hx = radius * std::cos(y); double hy = radius; double G = hx * hy; V(jnode) *= std::pow(deg2rad, 2) * G; } atlas_omp_parallel_for(idx_t jedge = 0; jedge < nedges; ++jedge) { S(jedge, LON) *= deg2rad; S(jedge, LAT) *= deg2rad; } dz = height / static_cast(nlev); const mesh::Connectivity& node2edge = mesh.nodes().edge_connectivity(); const mesh::MultiBlockConnectivity& edge2node = mesh.edges().node_connectivity(); auto node2edge_sign = array::make_view(mesh.nodes().add( Field("to_edge_sign", array::make_datatype(), array::make_shape(nnodes, node2edge.maxcols())))); atlas_omp_parallel_for(idx_t jnode = 0; jnode < nnodes; ++jnode) { for (idx_t jedge = 0; jedge < node2edge.cols(jnode); ++jedge) { idx_t iedge = node2edge(jnode, jedge); idx_t ip1 = edge2node(iedge, 0); if (jnode == ip1) { node2edge_sign(jnode, jedge) = 1.; } else { node2edge_sign(jnode, jedge) = -1.; } } } auto edge_flags = array::make_view(mesh.edges().flags()); auto is_pole_edge = [&](size_t e) { return Topology::check(edge_flags(e), Topology::POLE); }; std::vector tmp(nedges); int c(0); for (idx_t jedge = 0; jedge < nedges; ++jedge) { if (is_pole_edge(jedge)) { tmp[c++] = jedge; } } pole_edges.reserve(c); for (idx_t jedge = 0; jedge < c; ++jedge) { pole_edges.push_back(tmp[jedge]); } auto flags = array::make_view(mesh.nodes().flags()); is_ghost.reserve(nnodes); for (idx_t jnode = 0; jnode < nnodes; ++jnode) { is_ghost.push_back(Topology::check(flags(jnode), Topology::GHOST)); } } //---------------------------------------------------------------------------------------------------------------------- void AtlasBenchmark::iteration() { Trace t(Here()); Trace compute(Here(), "compute"); if (!avgS_arr) { avgS_arr.reset(array::Array::create(nedges, nlev, 2ul)); } const auto& node2edge = mesh.nodes().edge_connectivity(); const auto& edge2node = mesh.edges().node_connectivity(); const auto field = array::make_view(scalar_field); const auto S = array::make_view(mesh.edges().field("dual_normals")); const auto V = array::make_view(mesh.nodes().field("dual_volumes")); const auto node2edge_sign = array::make_view(mesh.nodes().field("to_edge_sign")); auto grad = array::make_view(grad_field); auto avgS = array::make_view(*avgS_arr); atlas_omp_parallel_for(idx_t jedge = 0; jedge < nedges; ++jedge) { idx_t ip1 = edge2node(jedge, 0); idx_t ip2 = edge2node(jedge, 1); for (idx_t jlev = 0; jlev < nlev; ++jlev) { double avg = (field(ip1, jlev) + field(ip2, jlev)) * 0.5; avgS(jedge, jlev, LON) = S(jedge, LON) * avg; avgS(jedge, jlev, LAT) = S(jedge, LAT) * avg; } } atlas_omp_parallel_for(idx_t jnode = 0; jnode < nnodes; ++jnode) { for (idx_t jlev = 0; jlev < nlev; ++jlev) { grad(jnode, jlev, LON) = 0.; grad(jnode, jlev, LAT) = 0.; } for (idx_t jedge = 0; jedge < node2edge.cols(jnode); ++jedge) { idx_t iedge = node2edge(jnode, jedge); double add = node2edge_sign(jnode, jedge); for (idx_t jlev = 0; jlev < nlev; ++jlev) { grad(jnode, jlev, LON) += add * avgS(iedge, jlev, LON); grad(jnode, jlev, LAT) += add * avgS(iedge, jlev, LAT); } } for (idx_t jlev = 0; jlev < nlev; ++jlev) { grad(jnode, jlev, LON) /= V(jnode); grad(jnode, jlev, LAT) /= V(jnode); } } // special treatment for the north & south pole cell faces // Sx == 0 at pole, and Sy has same sign at both sides of pole for (size_t jedge = 0; jedge < pole_edges.size(); ++jedge) { idx_t iedge = pole_edges[jedge]; idx_t ip2 = edge2node(iedge, 1); // correct for wrong Y-derivatives in previous loop for (idx_t jlev = 0; jlev < nlev; ++jlev) { grad(ip2, jlev, LAT) += 2. * avgS(iedge, jlev, LAT) / V(ip2); } } double dzi = 1. / dz; double dzi_2 = 0.5 * dzi; atlas_omp_parallel_for(idx_t jnode = 0; jnode < nnodes; ++jnode) { if (nlev > 2) { for (idx_t jlev = 1; jlev < nlev - 1; ++jlev) { grad(jnode, jlev, ZZ) = (field(jnode, jlev + 1) - field(jnode, jlev - 1)) * dzi_2; } } if (nlev > 1) { grad(jnode, 0, ZZ) = (field(jnode, 1) - field(jnode, 0)) * dzi; grad(jnode, nlev - 1, ZZ) = (field(jnode, nlev - 2) - field(jnode, nlev - 1)) * dzi; } if (nlev == 1) { grad(jnode, 0, ZZ) = 0.; } } compute.stop(); // halo-exchange Trace halo(Here(), "halo-exchange"); nodes_fs.halo_exchange().execute(grad_field.array()); halo.stop(); t.stop(); if (iter >= exclude) { haloexchange_timer.update(halo); iteration_timer.update(t); } if (!progress) { Log::info() << setw(6) << iter + 1 << " total: " << fixed << setprecision(5) << t.elapsed() << " communication: " << setprecision(5) << halo.elapsed() << " ( " << setprecision(2) << fixed << setw(3) << halo.elapsed() / t.elapsed() * 100 << "% )" << endl; } } //---------------------------------------------------------------------------------------------------------------------- template DATA_TYPE vecnorm(const DATA_TYPE vec[], size_t size) { DATA_TYPE norm = 0; for (size_t j = 0; j < size; ++j) { norm += vec[j] * vec[j]; } return norm; } double AtlasBenchmark::result() { auto grad = array::make_view(grad_field); double maxval = -std::numeric_limits::max(); double minval = std::numeric_limits::max(); ; double norm = 0.; nodes_fs.haloExchange(grad_field); for (idx_t jnode = 0; jnode < nnodes; ++jnode) { if (!is_ghost[jnode]) { for (idx_t jlev = 0; jlev < 1; ++jlev) { const double scaling = 1.e12; grad(jnode, jlev, LON) *= scaling; grad(jnode, jlev, LAT) *= scaling; grad(jnode, jlev, ZZ) *= scaling; std::array v; v[0] = grad(jnode, jlev, LON); v[1] = grad(jnode, jlev, LAT); v[2] = grad(jnode, jlev, ZZ); maxval = std::max(maxval, v[0]); maxval = std::max(maxval, v[1]); maxval = std::max(maxval, v[2]); minval = std::min(minval, v[0]); minval = std::min(minval, v[1]); minval = std::min(minval, v[2]); // if( mpi::comm().rank() == 478 ) { // std::cout << " " << jnode << " part " << part(jnode) << " // glb_idx " << glb_idx(jnode) << " x,y,z " << v[0] << "," << v[1] // << ","<< v[2] << std::endl; //} norm += v[0] * v[0] + v[1] * v[1] + v[2] * v[2]; } } } if (output) { std::vector levels(1, 0); atlas::output::Output gmsh = atlas::output::Gmsh("benchmark.msh", util::Config("levels", levels)("ghost", true)); gmsh.write(mesh); gmsh.write(scalar_field); gmsh.write(grad_field); } ATLAS_TRACE_MPI(ALLREDUCE) { mpi::comm().allReduceInPlace(maxval, eckit::mpi::max()); mpi::comm().allReduceInPlace(minval, eckit::mpi::min()); mpi::comm().allReduceInPlace(norm, eckit::mpi::sum()); } norm = std::sqrt(norm); Log::info() << " maxval: " << setw(13) << setprecision(6) << scientific << maxval << endl; Log::info() << " minval: " << setw(13) << setprecision(6) << scientific << minval << endl; Log::info() << " norm: " << setw(13) << setprecision(6) << scientific << norm << endl; Log::info() << " checksum: " << nodes_fs.checksum().execute(grad) << endl; return norm; } int AtlasBenchmark::verify(const double& norm) { Log::warning() << "Verification is not yet implemented" << endl; return success(); } //---------------------------------------------------------------------------------------------------------------------- int main(int argc, char** argv) { AtlasBenchmark tool(argc, argv); return tool.start(); } atlas-0.46.0/src/sandbox/apps/atlas-numerics-nabla.F900000664000175000017500000002471315160212070022544 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. ! This File contains Unit Tests for testing the ! gradient computations using Edge-Based-Finite-Volume method ! A comparison between C++ built-in and a fortran version is done here ! @author Willem Deconinck ! ----------------------------------------------------------------------------- module atlas_nabla_program_module use atlas_module use, intrinsic :: iso_c_binding, only: c_double, c_int implicit none character(len=1024) :: msg type(atlas_Grid) :: grid type(atlas_Mesh) :: mesh type(atlas_mesh_Nodes) :: nodes type(atlas_functionspace_NodeColumns) :: node_columns type(atlas_MeshGenerator) :: meshgenerator type(atlas_fvm_Method) :: fvm type(atlas_Nabla) :: nabla type(atlas_Field) :: varfield type(atlas_Field) :: gradfield type(atlas_Config) :: config real(c_double), pointer :: var(:,:) real(c_double), pointer :: grad(:,:,:) integer :: nlev integer :: niter integer :: nouter integer :: startcount integer, parameter :: JPRB = c_double integer, parameter :: JPIM = c_int real(JPRB), parameter :: RPI = 2.0_JPRB*asin(1.0_JPRB) real(JPRB) :: RA type :: timer_type private integer*8 :: clck_counts_start, clck_counts_stop, clck_rate integer*8 :: counted = 0 logical :: paused = .True. contains procedure, public :: start => timer_start procedure, public :: pause => timer_pause procedure, public :: resume => timer_resume procedure, public :: elapsed => timer_elapsed end type timer_type contains function timer_elapsed(self) result(time) use, intrinsic :: iso_c_binding, only : c_double class(timer_type), intent(inout) :: self real(c_double) :: time if (.not. self%paused) then call system_clock ( self%clck_counts_stop, self%clck_rate ) time = (self%counted + self%clck_counts_stop - self%clck_counts_start)/real(self%clck_rate) else if (self%counted .ge. 0) then time = self%counted/real(self%clck_rate) else time = 0. end if end function timer_elapsed subroutine timer_start(self) class(timer_type), intent(inout) :: self call system_clock ( self%clck_counts_start, self%clck_rate ) self%paused = .False. self%counted = 0 end subroutine timer_start subroutine timer_pause(self) class(timer_type), intent(inout) :: self call system_clock ( self%clck_counts_stop, self%clck_rate ) self%counted = self%counted + self%clck_counts_stop - self%clck_counts_start self%paused = .True. end subroutine timer_pause subroutine timer_resume(self) class(timer_type), intent(inout) :: self call system_clock ( self%clck_counts_start, self%clck_rate ) self%paused = .False. end subroutine timer_resume SUBROUTINE FV_GRADIENT(PVAR,PGRAD) !**** *GP_DERIVATIVES* - COMPUTES GRID-POINT DERIVATIVES using FINITE VOLUME ! PURPOSE. !** INTERFACE. ! ---------- ! *CALL* *GP_DERIVATIVES(..)* ! EXPLICIT ARGUMENTS ! -------------------- ! IMPLICIT ARGUMENTS : ! -------------------- ! METHOD. ! ------- ! SEE DOCUMENTATION ! EXTERNALS. NONE. ! ---------- ! REFERENCE. ! ---------- ! ECMWF Research Department documentation of the IFS ! AUTHOR. ! ------- ! Mats Hamrud AND Willem Deconinck *ECMWF* ! ORIGINAL : 88-02-04 ! MODIFICATIONS. ! -------------- ! ------------------------------------------------------------------ !USE PARKIND1 , ONLY : JPIM ,JPRB !USE ATLAS_MODULE !USE YOMHOOK , ONLY : LHOOK ,DR_HOOK !USE YOMGEM , ONLY : TGEM !USE YOMATLAS , ONLY : YRATLAS !USE YOMDIM , ONLY : YRDIM !USE YOMDIMV , ONLY : YRDIMV !USE YOMCST , ONLY : RPI, RA IMPLICIT NONE REAL(KIND=JPRB),INTENT(IN) :: PVAR(:,:) REAL(KIND=JPRB),INTENT(OUT) :: PGRAD(:,:,:) TYPE(ATLAS_FIELD) :: LONLAT,DUAL_VOLUMES,DUAL_NORMALS,NODE2EDGE_SIGN type(atlas_mesh_Nodes) :: NODES type(atlas_mesh_Edges) :: EDGES TYPE(ATLAS_CONNECTIVITY) :: EDGE2NODE, NODE2EDGE REAL(KIND=JPRB), POINTER :: ZLONLAT(:,:),ZDUAL_VOLUMES(:),ZDUAL_NORMALS(:,:),& & ZNODE2EDGE_SIGN(:,:) INTEGER(KIND=ATLAS_KIND_IDX),POINTER :: IEDGE2NODE(:,:) INTEGER(KIND=ATLAS_KIND_IDX),POINTER :: INODE2EDGE(:) INTEGER(KIND=JPIM) :: INODE2EDGE_SIZE INTEGER(KIND=JPIM) :: JNODE,JEDGE,JLEV,INEDGES,IP1,IP2,IEDGE,INODES REAL(KIND=JPRB) :: ZAVG,ZSIGN,ZMETRIC_X,ZMETRIC_Y,ZSCALE REAL(KIND=JPRB), ALLOCATABLE :: ZAVG_S(:,:,:) ! ------------------------------------------------------------------ !IF (LHOOK) CALL DR_HOOK('FV_GRADIENT',0,ZHOOK_HANDLE) ASSOCIATE(NFLEVG=>nlev,& & node_columns=>fvm ) !write(0,*) 'enter fv_gradient' !write(0,*) 'shape pvar ',shape(pvar) NODES = MESH%NODES() EDGES = MESH%EDGES() LONLAT = NODES%LONLAT() EDGE2NODE = EDGES%NODE_CONNECTIVITY() NODE2EDGE = NODES%EDGE_CONNECTIVITY() DUAL_VOLUMES=NODES%FIELD('dual_volumes') DUAL_NORMALS=EDGES%FIELD('dual_normals') NODE2EDGE_SIGN=NODES%FIELD('node2edge_sign') CALL LONLAT%DATA(ZLONLAT) CALL EDGE2NODE%DATA(IEDGE2NODE) CALL DUAL_VOLUMES%DATA(ZDUAL_VOLUMES) CALL DUAL_NORMALS%DATA(ZDUAL_NORMALS) CALL NODE2EDGE_SIGN%DATA(ZNODE2EDGE_SIGN) INEDGES = SIZE(ZDUAL_NORMALS)/2 ZSCALE = RPI/180.0_JPRB * RPI/180.0_JPRB * RA ALLOCATE(ZAVG_S(2,NFLEVG,INEDGES)) !$OMP PARALLEL DO SCHEDULE(STATIC) PRIVATE(JEDGE,IP1,IP2,JLEV,ZAVG) DO JEDGE=1,INEDGES IP1 = IEDGE2NODE(1,JEDGE) IP2 = IEDGE2NODE(2,JEDGE) DO JLEV=1,NFLEVG ZAVG = (PVAR(JLEV,IP1)+PVAR(JLEV,IP2)) * 0.5_JPRB ZAVG_S(1,JLEV,JEDGE) = ZDUAL_NORMALS(1,JEDGE)*RPI/180.0_JPRB * ZAVG ZAVG_S(2,JLEV,JEDGE) = ZDUAL_NORMALS(2,JEDGE)*RPI/180.0_JPRB * ZAVG ENDDO ENDDO !$OMP END PARALLEL DO INODES = SIZE(ZLONLAT)/2 !$OMP PARALLEL DO SCHEDULE(STATIC) PRIVATE(JNODE,JLEV,JEDGE,IEDGE,ZSIGN,ZMETRIC_X,ZMETRIC_Y,INODE2EDGE,INODE2EDGE_SIZE) DO JNODE=1,INODES DO JLEV=1,NFLEVG PGRAD(1,JLEV,JNODE) = 0.0 PGRAD(2,JLEV,JNODE) = 0.0 ENDDO CALL NODE2EDGE%ROW(JNODE,INODE2EDGE,INODE2EDGE_SIZE) DO JEDGE=1,INODE2EDGE_SIZE IEDGE = INODE2EDGE(JEDGE) ZSIGN = ZNODE2EDGE_SIGN(JEDGE,JNODE) DO JLEV=1,NFLEVG PGRAD(1,JLEV,JNODE) = PGRAD(1,JLEV,JNODE)+ZSIGN*ZAVG_S(1,JLEV,IEDGE) PGRAD(2,JLEV,JNODE) = PGRAD(2,JLEV,JNODE)+ZSIGN*ZAVG_S(2,JLEV,IEDGE) ENDDO ENDDO ZMETRIC_Y = 1._JPRB/(ZDUAL_VOLUMES(JNODE)*ZSCALE) ZMETRIC_X = ZMETRIC_Y/COS(ZLONLAT(2,JNODE)*RPI/180.0_JPRB) DO JLEV=1,NFLEVG PGRAD(1,JLEV,JNODE) = PGRAD(1,JLEV,JNODE)*ZMETRIC_X PGRAD(2,JLEV,JNODE) = PGRAD(2,JLEV,JNODE)*ZMETRIC_Y ENDDO ENDDO !$OMP END PARALLEL DO END ASSOCIATE !IF (LHOOK) CALL DR_HOOK('FV_GRADIENT',1,ZHOOK_HANDLE) END SUBROUTINE FV_GRADIENT subroutine init() use fckit_resource_module character(len=:),allocatable :: grid_uid type(atlas_mesh_Nodes) :: mesh_nodes call atlas_library%initialise() call fckit_resource("--grid","N24",grid_uid) call fckit_resource("--levels",137,nlev) call fckit_resource("--iterations",100,niter) call fckit_resource("--startcount",5,startcount) call fckit_resource("--outer",1,nouter) config = atlas_Config() call config%set("radius",1.0) if( .not.config%get("radius",RA) ) RA = 1.0 ! Setup grid = atlas_Grid(grid_uid) meshgenerator = atlas_MeshGenerator() mesh = meshgenerator%generate(grid) ! second optional argument for atlas_GridDistrubution fvm = atlas_fvm_Method(mesh,config) node_columns = fvm%node_columns() nabla = atlas_Nabla(fvm) ! Create a variable field and a gradient field varfield = node_columns%create_field(name="var", kind=atlas_real(c_double), levels=nlev) gradfield = node_columns%create_field(name="grad", kind=atlas_real(c_double),levels=nlev, variables=2) ! Access to data call varfield%data(var) call gradfield%data(grad) var(:,:) = 0. mesh_nodes = mesh%nodes() write(msg,*) "Mesh has locally ",mesh_nodes%size(), " nodes" call atlas_log%info(msg) call mesh_nodes%final() end subroutine ! ----------------------------------------------------------------------------- subroutine finalize() ! Cleanup call config%final() call varfield%final() call gradfield%final() call nabla%final() call fvm%final() call node_columns%final() call nodes%final() call mesh%final() call grid%final() call meshgenerator%final() call atlas_library%finalise() end subroutine ! ----------------------------------------------------------------------------- subroutine run() use fckit_mpi_module type(timer_type) :: timer; type(fckit_mpi_comm) :: mpi integer :: jiter, jouter real(c_double) :: timing_cpp, timing_f90, timing real(c_double) :: min_timing_cpp, min_timing_f90 mpi = fckit_mpi_comm() call node_columns%halo_exchange(varfield) call mpi%barrier() timing_cpp = 1.e10 timing_f90 = 1.e10 min_timing_cpp = 1.e10 min_timing_f90 = 1.e10 do jouter=1,nouter ! Compute the gradient timing = 1.e10 do jiter = 1,niter call timer%start() call nabla%gradient(varfield,gradfield) timing = min(timing,timer%elapsed()) enddo timing_cpp = timing write(msg,*) "timing_cpp = ", timing_cpp call atlas_log%info(msg) if( nouter == 1 .or. jouter < nouter ) then min_timing_cpp = min(timing_cpp,min_timing_cpp) endif ! Compute the gradient with Fortran routine above timing = 1.e10 do jiter = 1,niter call timer%start() call FV_GRADIENT(var,grad) timing = min(timing,timer%elapsed()) enddo timing_f90 = timing write(msg,*) "timing_f90 = ", timing_f90 call atlas_log%info(msg) if( nouter == 1 .or. jouter < nouter ) then min_timing_f90 = min(timing_f90,min_timing_f90) endif write(msg,*) "|timing_f90-timing_cpp| / timing_f90 = ", & & abs(timing_f90-timing_cpp)/timing_f90 *100 , "%" call atlas_log%info(msg) enddo call atlas_log%info("==================") write(msg,*) "min_timing_cpp = ", min_timing_cpp call atlas_log%info(msg) write(msg,*) "min_timing_f90 = ", min_timing_f90 call atlas_log%info(msg) write(msg,*) "|min_timing_f90-min_timing_cpp| / min_timing_f90 = ", & & abs(min_timing_f90-min_timing_cpp)/min_timing_f90 *100 , "%" call atlas_log%info(msg) end subroutine end module atlas_nabla_program_module ! ----------------------------------------------------------------------------- program atlas_nabla_program use atlas_nabla_program_module call init() call run() call finalize() end program atlas-0.46.0/src/sandbox/apps/atlas-gmsh-extract.cc0000664000175000017500000002103415160212070022272 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ // file deepcode ignore MissingOpenCheckOnFile: False positive #include #include #include #include #include #include #include #include #include "eckit/config/Resource.h" #include "eckit/filesystem/PathName.h" #include "eckit/runtime/Main.h" #include "eckit/runtime/Tool.h" #include "atlas/library.h" #include "atlas/runtime/Exception.h" //------------------------------------------------------------------------------------------------------ using namespace eckit; using namespace atlas; //------------------------------------------------------------------------------------------------------ class gmsh_extract : public eckit::Tool { void run() override; public: gmsh_extract(int argc, char** argv): eckit::Tool(argc, argv) { do_run = true; bool help = Resource("-h", false); std::string help_str = "NAME\n" " gmsh_extract - extract fields from gmsh files\n" "\n" "SYNOPSIS\n" " gmsh_extract [OPTION]... -i SOURCE\n" "\n" "DESCRIPTION\n" " Extract fields from SOURCE depending on OPTION\n" "\n" " -i (Required) space-separated list of gmsh files to " "process\n" "\n" " -f (Optional) comma-separated list of field names\n" " If not present, all fields will be extracted\n" "\n" " -l (Optional) comma-separated list of levels\n" " If not present, all levels will be extracted\n" "\n" " -o (Optional) newly created output file\n" " If not present, the output will go to stdout\n" "\n" "EXAMPLES\n" " gmsh_extract -f theta,VelocityZ -l 0,3 -i data/fields*.msh -o " "extracted.msh\n" " # create/overwrite file extracted.msh\n" "\n" " gmsh_extract -f theta,VelocityZ -l 0,3 -i data/fields*.msh > " "extracted.msh\n" " # create/overwrite file extracted.msh\n" "\n" " gmsh_extract -f theta,VelocityZ -l 0,3 -i data/fields*.msh >> " "extracted.msh\n" " # append to file extracted.msh\n" "\n" "AUTHOR\n" " Written by Willem Deconinck.\n" "\n" "ECMWF October 2014"; if (help) { std::cout << help_str << std::endl; do_run = false; return; } fields = Resource>("-f", std::vector()); levels = Resource>("-l", std::vector()); out_filename = Resource("-o", std::string("")); // Parse in_files manually, as Resource does not allow space separated file // list, // as a wildcard would return // in_files = Resource< std::vector // >("-i",std::vector()); for (int i = 0; i < argc; ++i) { if (std::string(argv[i]) == "-i") { for (int j = i + 1; j < argc; ++j) { std::string in_file(argv[j]); if (in_file[0] == '-') { break; } in_files.push_back(in_file); } } } if (in_files.empty()) { throw_Exception("missing input filename, parameter -i\n" + help_str, Here()); } } private: std::vector fields; std::vector levels; std::vector in_files; std::string out_filename; bool do_run; }; //------------------------------------------------------------------------------------------------------ void gmsh_extract::run() { if (!do_run) { return; } atlas::initialize(argc(), argv()); Log::debug() << "Command line:" << std::endl; for (int i = 0; i < argc(); ++i) { Log::debug() << argv(i) << std::endl; } Log::debug() << Translator, std::string>()(in_files) << std::endl; std::ofstream out_file; if (!out_filename.empty()) { out_file.open(out_filename.c_str(), std::ios::out | std::ios::binary); if (!out_file.is_open()) { throw_CantOpenFile(out_filename); } } std::ostream& out = out_filename.empty() ? std::cout : out_file; for (size_t i = 0; i < in_files.size(); ++i) { PathName gmsh_file(in_files[i]); Log::debug() << "Processing " << gmsh_file << std::endl; std::set search_fields; std::set search_levels; search_fields.insert(fields.begin(), fields.end()); search_levels.insert(levels.begin(), levels.end()); std::ifstream in_file; in_file.open(gmsh_file.localPath(), std::ios::in | std::ios::binary); if (!in_file.is_open()) { throw_CantOpenFile(gmsh_file); } std::string line; std::string ctxt = ""; while (true) { std::getline(in_file, line); if (in_file.eof()) { break; } if (line == "$MeshFormat") { out << line << "\n"; std::getline(in_file, line); out << line << "\n"; std::getline(in_file, line); out << line << "\n"; std::getline(in_file, line); } if (line == "$NodeData") { ctxt = "NodeData"; } if (line == "$ElementData") { ctxt = "ElementData"; } if (line == "$ElementNodeData") { ctxt = "ElementNodeData"; } if (!ctxt.empty()) { std::string end_ctxt = "$End" + ctxt; std::getline(in_file, line); // useless line std::getline(in_file, line); // field name std::string field_name_read; std::string field_name; int lev(-1); bool extract = false; field_name_read.assign(line, 1, line.size() - 2); field_name = field_name_read; Log::debug() << "Found field " << field_name << std::endl; if (field_name[field_name.size() - 1] == ']') { std::string lev_str; lev_str.assign(field_name, field_name.size() - 4, 3); field_name.assign(field_name, 0, field_name.size() - 5); lev = Translator()(lev_str); } if (search_fields.empty() || search_fields.find(field_name) != search_fields.end()) { if (lev == -1) { Log::debug() << "Extracting field " << field_name << std::endl; extract = true; } else if (search_levels.empty() || search_levels.find(lev) != search_levels.end()) { Log::debug() << "Extracting field " << field_name << "[" << lev << "]" << std::endl; extract = true; } } if (extract) { out << "$" << ctxt << "\n1\n" << "\"" << field_name_read << "\"\n"; while (true) { std::getline(in_file, line); out << line << "\n"; if (line == end_ctxt) { out << std::flush; break; } } } ctxt.clear(); } } } if (!out_filename.empty()) { out_file.close(); } atlas::finalize(); atlas::mpi::finalize(); } //------------------------------------------------------------------------------------------------------ int main(int argc, char** argv) { gmsh_extract tool(argc, argv); return tool.start(); } atlas-0.46.0/src/sandbox/apps/atlas-loadbalance.cc0000664000175000017500000001303415160212070022112 0ustar alastairalastair/* * (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 "atlas/functionspace/NodeColumns.h" #include "atlas/grid.h" #include "atlas/mesh/Mesh.h" #include "atlas/mesh/actions/WriteLoadBalanceReport.h" #include "atlas/meshgenerator.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/runtime/AtlasTool.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Log.h" #include "atlas/util/Config.h" //------------------------------------------------------------------------------------------------------ using namespace atlas; using atlas::util::Config; //------------------------------------------------------------------------------------------------------ class AtlasLoadbalance : public AtlasTool { public: std::string briefDescription() override { return "Print load balance for meshed grid"; } std::string usage() override { return name() + " GRID [OPTION]... [--help]"; } std::string longDescription() override { return "The 'GRID' argument can be either the name of a named grid, orthe path to a" " YAML configuration file that describes the grid.\n" "Example values for grid names are: N80, F40, O24, L64x33, CS-ED-12. See the program " "'atlas-grids' for a list of named grids."; } int numberOfPositionalArguments() override { return 1; } int minimumPositionalArguments() override { return 1; } AtlasLoadbalance(int argc, char** argv): AtlasTool(argc, argv) { add_option(new SimpleOption( "meshgenerator", "Mesh generator [structured,regular,delaunay,cubedsphere] (default depends on GRID)")); add_option(new SimpleOption( "partitioner", "Grid partitioner [equal_regions,checkerboard,equal_bands,regular_bands] (default depends on GRID)")); add_option(new Separator("Advanced")); add_option(new SimpleOption("halo", "Halo size")); } int execute(const Args& args) override { std::string key = get_positional_arg(args, 0); Grid grid; if (key.size()) { eckit::PathName path_in{key}; try { if (path_in.exists()) { Log::info() << "Creating grid from file " << path_in << std::endl; Log::debug() << Config(path_in) << std::endl; grid = Grid(Config{path_in}); } else { Log::info() << "Creating grid from name " << key << std::endl; grid = Grid(key); } } catch (eckit::Exception& e) { Log::error() << e.what() << std::endl; if (path_in.exists()) { Log::error() << "Could not generate mesh for grid defined in file \"" << path_in << "\"" << std::endl; } else { Log::error() << "Could not generate mesh for grid \"" << key << "\"" << std::endl; } return failed(); } } else { Log::error() << "No grid specified." << std::endl; return failed(); } auto meshgenerator = make_meshgenerator(grid, args); auto partitioner = make_partitioner(grid, args); int halo = args.getInt("halo", 0); Log::info() << "Input:" << std::endl; Log::info() << "- grid: " << grid.name() << std::endl; Log::info() << "- meshgenerator: " << meshgenerator.type() << std::endl; Log::info() << "- partitioner: " << partitioner.type() << std::endl; Log::info() << "- halo: " << halo << std::endl; auto mesh = meshgenerator.generate(grid, partitioner); functionspace::NodeColumns nodes(mesh, option::halo(halo)); { std::stringstream s; mesh::actions::write_load_balance_report(mesh, s); if (mpi::comm().rank() == 0) { std::cout << s.str() << std::endl; } } return success(); } MeshGenerator make_meshgenerator(const Grid& grid, const Args& args) { auto config = grid.meshgenerator(); // recommended by the grid itself if (args.has("meshgenerator")) { config.set("type", args.getString("meshgenerator")); } if (args.has("halo")) { config.set("halo", args.getInt("halo")); } return MeshGenerator{config}; } grid::Partitioner make_partitioner(const Grid& grid, const Args& args) { auto config = grid.partitioner(); // recommended by the grid itself if (args.has("partitioner")) { config.set("type", args.getString("partitioner")); } if (args.has("regular")) { config.set("regular", args.getBool("regular")); } if (args.has("partitions")) { config.set("partitions", args.getInt("partitions")); } return grid::Partitioner{config}; } }; //------------------------------------------------------------------------------------------------------ int main(int argc, char** argv) { AtlasLoadbalance tool(argc, argv); return tool.start(); } atlas-0.46.0/src/sandbox/apps/CMakeLists.txt0000664000175000017500000000162715160212070021021 0ustar alastairalastair# (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. ecbuild_add_executable( TARGET atlas-loadbalance SOURCES atlas-loadbalance.cc LIBS atlas ) ecbuild_add_executable( TARGET atlas-gmsh-extract SOURCES atlas-gmsh-extract.cc LIBS atlas ) ecbuild_add_executable( TARGET atlas-benchmark SOURCES atlas-benchmark.cc LIBS atlas ${OMP_CXX} ) ecbuild_add_executable( TARGET atlas-numerics-nabla SOURCES atlas-numerics-nabla.F90 LIBS atlas_f ${OMP_Fortran} LINKER_LANGUAGE Fortran CONDITION HAVE_FORTRAN ) atlas-0.46.0/src/sandbox/example_fortran/0000775000175000017500000000000015160212070020476 5ustar alastairalastairatlas-0.46.0/src/sandbox/example_fortran/example_fortran.F900000664000175000017500000000164115160212070024146 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. #include "atlas/atlas_f.h" program example_fortran use atlas_module implicit none integer j integer :: var(2000) !$OMP PARALLEL DO SCHEDULE(STATIC) PRIVATE(j) do j=1,2000 var(j) = 1 enddo call atlas_log%info("Atlas initialized") call atlas_log%info("version = ["//atlas_version()//"]") call atlas_library%initialise() call atlas_log%set_fortran_unit(6) #if ATLAS_HAVE_OMP call atlas_log%info("OpenMP enabled") #endif call atlas_log%debug("Here is some debugging information") write(6,'(A)') "exit" call atlas_library%finalise() end program atlas-0.46.0/src/sandbox/example_fortran/CMakeLists.txt0000664000175000017500000000102515160212070023234 0ustar alastairalastair# (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. if( HAVE_FORTRAN ) ecbuild_add_executable( TARGET atlas_example_fortran LINKER_LANGUAGE Fortran SOURCES example_fortran.F90 LIBS atlas_f ) endif() atlas-0.46.0/src/sandbox/fortran_acc_fields/0000775000175000017500000000000015160212070021117 5ustar alastairalastairatlas-0.46.0/src/sandbox/fortran_acc_fields/atlas-acc-fields.F900000664000175000017500000000234715160212070024501 0ustar alastairalastair! (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. program atlas_acc_fields use atlas_module implicit none type(atlas_Field) :: field integer(4), pointer :: view(:,:,:) integer :: i,j,k integer, parameter :: Ni = 2 integer, parameter :: Nj = 3 integer, parameter :: Nk = 5 call atlas_library%initialise() field = atlas_Field(kind=atlas_integer(4),shape=[Nk,Nj,Ni]) call field%data(view) !$acc data present(view) !$acc kernels do i=1,Ni do j=1,Nj do k=1,Nk view(k,j,i) = i*100*100 + j*100 + k enddo enddo enddo !$acc end kernels !$acc end data ! We can either use the acc directive, or ! use the field's API to update the host ! !$acc update host(view) call field%update_host() ! Note that field%sync_host_device() is not working here... ! Because internal state of host_needs_update has not been changed do i=1,Ni write(0,*) " " write(0,*) "i=",i write(0,*) view(:,:,i) enddo end program atlas-0.46.0/src/sandbox/fortran_acc_fields/CMakeLists.txt0000664000175000017500000000114315160212070023656 0ustar alastairalastair# (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. if( atlas_HAVE_ACC ) ecbuild_add_executable( TARGET atlas-acc-fields SOURCES atlas-acc-fields.F90 LIBS atlas_f OpenACC::OpenACC_Fortran LINKER_LANGUAGE Fortran NOINSTALL ) endif() atlas-0.46.0/src/sandbox/interpolation-fortran/0000775000175000017500000000000015160212070021650 5ustar alastairalastairatlas-0.46.0/src/sandbox/interpolation-fortran/atlas-interpolation-fortran.F900000664000175000017500000001073615160212070027601 0ustar alastairalastair! (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. program atlas_interpolation_fortran use atlas_module implicit none type(atlas_Grid) :: grid_A type(atlas_Grid) :: grid_B type(atlas_Mesh) :: mesh_A type(atlas_Mesh) :: mesh_B type(atlas_functionspace_NodeColumns) :: fs_A type(atlas_functionspace_NodeColumns) :: fs_B type(atlas_Field) :: field_A, field_A2 type(atlas_Field) :: field_B type(atlas_MeshGenerator) :: meshgenerator type(atlas_Partitioner) :: partitioner_B type(atlas_GridDistribution) :: distribution_B type(atlas_Output) :: gmsh type(atlas_Config) :: interpolation_config type(atlas_Interpolation) :: interpolation_AB type(atlas_Interpolation) :: interpolation_BA type(atlas_Trace) :: trace call atlas_library%initialise() trace = atlas_Trace( "atlas-interpolation-fortran.F90", __LINE__, "Complete execution" ) ! Setup a meshgenerator meshgenerator = atlas_MeshGenerator() ! Generate source mesh grid_A = atlas_Grid("O32") mesh_A = meshgenerator%generate(grid_A) ! Generate target mesh based on domain decomposition of source mesh grid_B = atlas_Grid("N16") partitioner_B = atlas_MatchingMeshPartitioner(mesh_A) distribution_B = partitioner_B%partition(grid_B) mesh_B = meshgenerator%generate(grid_B,distribution_B) ! Create function spaces for each mesh fs_A = atlas_functionspace_NodeColumns(mesh_A,halo=1) fs_B = atlas_functionspace_NodeColumns(mesh_B,halo=2) ! Setup interpolators interpolation_config = atlas_Config() call interpolation_config%set("type","finite-element") interpolation_AB = atlas_Interpolation(interpolation_config,fs_A,fs_B) interpolation_BA = atlas_Interpolation(interpolation_config,fs_B,fs_A) ! Create fields and initialise source field field_A = fs_A%create_field(name="A", kind=atlas_real(atlas_kind_real64)) field_B = fs_B%create_field(name="B", kind=atlas_real(atlas_kind_real64)) field_A2 = fs_A%create_field(name="A2", kind=atlas_real(atlas_kind_real64)) call initialise_field_hill(fs_A, field_A) ! Interpolate from source to target call interpolation_AB%execute(field_A, field_B) call fs_B%halo_exchange(field_B) ! Interpolate from target back to source call interpolation_BA%execute(field_B, field_A2) call fs_A%halo_exchange(field_A2) ! Output target gmsh = atlas_output_Gmsh("B.msh",coordinates="xyz") call gmsh%write(mesh_B) call gmsh%write(field_B) ! Output source gmsh = atlas_output_Gmsh("A.msh",coordinates="xyz") call gmsh%write(mesh_A) call gmsh%write(field_A) call gmsh%write(field_A2) ! cleanup call interpolation_config%final() call interpolation_AB%final() call interpolation_BA%final() call gmsh%final() call partitioner_B%final() call distribution_B%final() call field_A%final() call field_B%final() call meshgenerator%final() call fs_A%final() call fs_B%final() call mesh_A%final() call mesh_B%final() call grid_A%final() call grid_B%final() call trace%final() call atlas_library%finalise() contains subroutine initialise_field_hill(funcspace,field) type(atlas_functionspace_NodeColumns), intent(in) :: funcspace type(atlas_Field), intent(inout) :: field real(atlas_kind_real64), parameter :: M_PI = 3.14159265358979323846 real(atlas_kind_real64), parameter :: deg2rad = M_PI/180._8 type(atlas_mesh_Nodes) :: nodes type(atlas_Field) :: field_lonlat real(atlas_kind_real64), pointer :: value(:), lonlat(:,:) integer :: jnode, nb_nodes real(atlas_kind_real64) :: lon, lat, c2, c_lon, c_lat, c_rad, dist, s1, s2 c_lat = 0. * M_PI c_lon = 1. * M_PI c_rad = 2. * M_PI / 9. nodes = funcspace%nodes() field_lonlat = nodes%lonlat() call field_lonlat%data(lonlat) call field%data(value) nb_nodes = nodes%size() do jnode=1,nb_nodes lon = deg2rad * lonlat(1,jnode) lat = deg2rad * lonlat(2,jnode) c2 = cos(lat) s1 = sin( (lon-c_lon)/2. ) s2 = sin( (lat-c_lat)/2. ) dist = 2. * sqrt( c2*s1*c2*s1 + s2*s2 ) if( dist < c_rad ) then value(jnode) = 1. + cos(M_PI*dist/c_rad) else value(jnode) = 0 endif enddo end subroutine end program atlas-0.46.0/src/sandbox/interpolation-fortran/CMakeLists.txt0000664000175000017500000000110515160212070024405 0ustar alastairalastair# (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. if( HAVE_FORTRAN ) ecbuild_add_executable( TARGET atlas-interpolation-fortran SOURCES atlas-interpolation-fortran.F90 LIBS atlas_f LINKER_LANGUAGE Fortran NOINSTALL ) endif() atlas-0.46.0/src/sandbox/CMakeLists.txt0000664000175000017500000000163215160212070020052 0ustar alastairalastair# (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. if( ECKIT_INCLUDE_DIRS ) # eckit not yet ported to CMake3 include_directories( ${ECKIT_INCLUDE_DIRS} ) endif() add_subdirectory( apps ) if( HAVE_FORTRAN ) add_subdirectory( fortran_submodule ) add_subdirectory( fortran_object ) add_subdirectory( example_fortran ) add_subdirectory( fortran_acc_fields ) endif() add_subdirectory( interpolation ) add_subdirectory( interpolation-fortran ) add_subdirectory( grid_distribution ) add_subdirectory( benchmark_ifs_setup ) add_subdirectory( benchmark_sorting ) add_subdirectory( benchmark_trans ) atlas-0.46.0/src/sandbox/benchmark_ifs_setup/0000775000175000017500000000000015160212070021323 5ustar alastairalastairatlas-0.46.0/src/sandbox/benchmark_ifs_setup/atlas-benchmark-ifs-setup.cc0000664000175000017500000000753515160212070026615 0ustar alastairalastair/* * (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 #include #include #include "eckit/exception/Exceptions.h" #include "eckit/filesystem/PathName.h" #include "atlas/grid.h" #include "atlas/mesh.h" #include "atlas/mesh/actions/BuildHalo.h" #include "atlas/meshgenerator.h" #include "atlas/numerics/fvm/Method.h" #include "atlas/output/detail/GmshIO.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/parallel/omp/omp.h" #include "atlas/runtime/AtlasTool.h" #include "atlas/runtime/Log.h" #include "atlas/runtime/Trace.h" #include "atlas/util/Config.h" //------------------------------------------------------------------------------ using namespace atlas; using namespace atlas::grid; using atlas::util::Config; using eckit::PathName; //------------------------------------------------------------------------------ class Tool : public AtlasTool { 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"; } std::string usage() override { return name() + " --grid=name [OPTION]... OUTPUT [--help]"; } public: Tool(int argc, char** argv); private: std::string key; PathName path_in; PathName path_out; }; //----------------------------------------------------------------------------- static int halo_default() { return 2; } Tool::Tool(int argc, char** argv): AtlasTool(argc, argv) { add_option(new SimpleOption( "grid", "Grid unique identifier\n" + indent() + " Example values: N80, F40, O24, L32")); add_option(new SimpleOption("halo", "Number of halos (default=" + std::to_string(halo_default()) + ")")); } //----------------------------------------------------------------------------- int Tool::execute(const Args& args) { Trace timer(Here(), displayName()); key = ""; args.get("grid", key); std::string path_in_str = ""; if (args.get("grid", path_in_str)) { path_in = path_in_str; } StructuredGrid grid; if (key.size()) { try { grid = Grid(key); } catch (eckit::Exception&) { return failed(); } } else { Log::error() << "No grid specified." << std::endl; } if (!grid) { return failed(); } size_t halo = args.getLong("halo", halo_default()); Log::info() << "Configuration" << std::endl; Log::info() << "~~~~~~~~~~~~~" << std::endl; Log::info() << " Grid : " << grid.name() << std::endl; Log::info() << " Halo : " << halo << std::endl; Log::info() << " MPI : " << mpi::comm().size() << std::endl; Log::info() << " OpenMP : " << atlas_omp_get_max_threads() << std::endl; MeshGenerator meshgenerator("structured", util::Config("partitioner", "equal_regions")); size_t iterations = 1; for (size_t i = 0; i < iterations; ++i) { ATLAS_TRACE("iteration"); Mesh mesh = meshgenerator.generate(grid); numerics::fvm::Method fvm(mesh, mesh::Halo(halo)); // mesh::actions::build_halo( mesh, halo ); mpi::comm().barrier(); } timer.stop(); Log::info() << Trace::report() << std::endl; return success(); } //------------------------------------------------------------------------------ int main(int argc, char** argv) { Tool tool(argc, argv); return tool.start(); } atlas-0.46.0/src/sandbox/benchmark_ifs_setup/CMakeLists.txt0000664000175000017500000000105515160212070024064 0ustar alastairalastair# (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. ecbuild_add_executable( TARGET atlas-benchmark-ifs-setup SOURCES atlas-benchmark-ifs-setup.cc LIBS atlas # NOINSTALL CONDITION atlas_HAVE_ATLAS_NUMERICS ) atlas-0.46.0/src/tests/0000775000175000017500000000000015160212070015014 5ustar alastairalastairatlas-0.46.0/src/tests/redistribution/0000775000175000017500000000000015160212070020062 5ustar alastairalastairatlas-0.46.0/src/tests/redistribution/test_redistribution_generic.cc0000664000175000017500000007736115160212070026210 0ustar alastairalastair/* * (C) Crown Copyright 2021 Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ #include #include "atlas/field/FieldSet.h" #include "atlas/functionspace.h" #include "atlas/functionspace/CubedSphereColumns.h" #include "atlas/grid.h" #include "atlas/grid/Partitioner.h" #include "atlas/meshgenerator.h" #include "atlas/output/Gmsh.h" #include "atlas/redistribution/Redistribution.h" #include "atlas/util/Config.h" #include "atlas/util/Constants.h" #include "atlas/util/Unique.h" #include "atlas/mesh/actions/BuildHalo.h" #include "tests/AtlasTestEnvironment.h" namespace atlas { namespace test { int mpi_color() { static int c = mpi::comm("world").rank()%2; return c; } struct Fixture { Fixture() { mpi::comm().split(mpi_color(),"split"); } ~Fixture() { if (eckit::mpi::hasComm("split")) { eckit::mpi::deleteComm("split"); } } }; // Set floating point tolerance. template Value tolerance() { return std::numeric_limits::epsilon() * 64; } // Set field config for different ranks. template util::Config fieldConfig(); // Rank 1 config. template <> util::Config fieldConfig<1>() { return util::Config(); } // Rank 2 config. template <> util::Config fieldConfig<2>() { auto config = util::Config(); config.set("levels", 10); return config; } // Rank 3 config. template <> util::Config fieldConfig<3>() { auto config = util::Config(); config.set("levels", 10); config.set("variables", 2); return config; } // Helper types to distinguish floating point and integral types. template using IsIntegral = typename std::enable_if::value>::type*; template using IsFloatingPoint = typename std::enable_if::value>::type*; // Aplitude for test function/ constexpr double testAmplitude = 5.; // Convert value to integral. template = nullptr> Value castValue(const double& inVal) { constexpr double eps = 1.e-12; Value rounded_minus_half = std::round(inVal-0.5); if (std::abs(rounded_minus_half - (inVal-0.5)) < eps) { return static_cast(rounded_minus_half); } return static_cast(std::round(inVal)); } // Cast value to different float. template = nullptr> Value castValue(const double& inVal) { return static_cast(inVal); } // Check integral types are equal. template = nullptr> bool checkValue(const Value& valA, const Value& valB) { return valA == valB; } // Check floating point types are almost equal. template = nullptr> bool checkValue(const Value& valA, const Value& valB) { const auto tol = testAmplitude * tolerance(); return std::abs(valA - valB) <= tol; } // Define test pattern for nodes. template Value testPattern(double lambda, double phi, idx_t level) { return castValue(testAmplitude * std::cos(lambda * (1 + level) * M_PI / 180.) * std::cos(phi * (1 + level) * M_PI / 180.)); } // Define test pattern for HybridElements. template Value testPattern(const mesh::Connectivity::Row& elem, const array::ArrayView& lonLatView) { Value f{}; for (idx_t i = 0; i < elem.size(); ++i) { f += testPattern(lonLatView(elem(i), LON), lonLatView(elem(i), LAT), 2); } return f / elem.size(); } // Try and get a mesh from functionspace. Mesh getMesh(const FunctionSpace& functionSpace) { // Try and create a pointer to one of the mesh based functionspaces. const auto cellColumns = functionspace::CellColumns(functionSpace); const auto edgeColumns = functionspace::EdgeColumns(functionSpace); const auto nodeColumns = functionspace::NodeColumns(functionSpace); const auto structuredColumns = functionspace::StructuredColumns(functionSpace); auto mesh = Mesh(); if (cellColumns) { mesh = cellColumns.mesh(); } else if (edgeColumns) { mesh = edgeColumns.mesh(); } else if (nodeColumns) { mesh = nodeColumns.mesh(); } else if (structuredColumns) { auto mpi_comm = util::Config("mpi_comm",functionSpace.mpi_comm()); auto grid = structuredColumns.grid(); auto partitioner = grid::Partitioner(functionSpace.distribution(),mpi_comm); mesh = MeshGenerator("structured",mpi_comm).generate(grid, partitioner); } return mesh; } // Try and get cells or edges node-connectivity from functionspace. const mesh::HybridElements::Connectivity* getConnectivity(const FunctionSpace& functionSpace) { // Try and create a pointer to CellColumns or EdgeColumns. const auto cellColumns = functionspace::CellColumns(functionSpace); const auto edgeColumns = functionspace::EdgeColumns(functionSpace); if (cellColumns) { return &(cellColumns.cells().node_connectivity()); } else if (edgeColumns) { return &(edgeColumns.edges().node_connectivity()); } else { return nullptr; } } // Class to test functionspace redistribution. template struct TestRedistribution { public: TestRedistribution(const FunctionSpace& sourceFunctionSpace, const FunctionSpace& targetFunctionSpace): sourceFunctionSpace_(sourceFunctionSpace), targetFunctionSpace_(targetFunctionSpace), redist_(sourceFunctionSpace, targetFunctionSpace), sourceFieldSet_(sourceFunctionSpace_.createField(fieldConfig())), targetFieldSet_(targetFunctionSpace_.createField(fieldConfig())), sourceView_(array::make_view(sourceFieldSet_[0])), targetView_(array::make_view(targetFieldSet_[0])) {} void outputFields(const std::string& fileStr) const { // Try and create a pointer to one of the mesh based functionspaces. const auto sourceMesh = getMesh(sourceFunctionSpace_); const auto targetMesh = getMesh(targetFunctionSpace_); // Output gmsh if both pointers are not null. if (sourceMesh && targetMesh) { // Set gmsh config. const auto gmshConfigXy = util::Config("coordinates", "xy"); // Set source gmsh object. auto sourceGmshXy = output::Gmsh(fileStr + "_source_xy.msh", gmshConfigXy); // Write out source mesh and field. sourceGmshXy.write(sourceMesh); sourceGmshXy.write(sourceFieldSet_, sourceFunctionSpace_); // Set target gmsh object. auto targetGmshXy = output::Gmsh(fileStr + "_target_xy.msh", gmshConfigXy); // Write out source mesh and field. targetGmshXy.write(targetMesh); targetGmshXy.write(targetFieldSet_, targetFunctionSpace_); } } protected: FunctionSpace sourceFunctionSpace_; FunctionSpace targetFunctionSpace_; Redistribution redist_; FieldSet sourceFieldSet_; FieldSet targetFieldSet_; array::ArrayView sourceView_; array::ArrayView targetView_; }; // Test rank 1 fields with lonlat method. template struct TestRedistributionPoints1 : public TestRedistribution { using TestRedistribution::TestRedistribution; void execute() { auto sourceLonlatView = array::make_view(this->sourceFunctionSpace_.lonlat()); auto targetLonlatView = array::make_view(this->targetFunctionSpace_.lonlat()); // Set source field. for (idx_t i = 0; i < this->sourceView_.shape(0); ++i) { this->sourceView_(i) = testPattern(sourceLonlatView(i, 0), sourceLonlatView(i, 1), 0); } // Perform redistribution. this->redist_.execute(this->sourceFieldSet_, this->targetFieldSet_); // Perform halo exchange; this->targetFunctionSpace_.haloExchange(this->targetFieldSet_); // Check target field. int nCheck{}; for (idx_t i = 0; i < this->targetView_.shape(0); ++i) { EXPECT(checkValue(this->targetView_(i), testPattern(targetLonlatView(i, LON), targetLonlatView(i, LAT), 0))); ++nCheck; } const auto& comm = mpi::comm(this->sourceFunctionSpace_.mpi_comm()); comm.allReduceInPlace(nCheck, eckit::mpi::Operation::SUM); Log::debug() << "Checked " << nCheck << " elements." << std::endl; } }; // Test rank 2 fields. template struct TestRedistributionPoints2 : public TestRedistribution { using TestRedistribution::TestRedistribution; void execute() { auto sourceLonlatView = array::make_view(this->sourceFunctionSpace_.lonlat()); auto targetLonlatView = array::make_view(this->targetFunctionSpace_.lonlat()); // Set source field. for (idx_t i = 0; i < this->sourceView_.shape(0); ++i) { for (idx_t j = 0; j < this->sourceView_.shape(1); ++j) { this->sourceView_(i, j) = testPattern(sourceLonlatView(i, LON), sourceLonlatView(i, LAT), j); } } // Perform redistribution. this->redist_.execute(this->sourceFieldSet_, this->targetFieldSet_); // Perform halo exchange; this->targetFunctionSpace_.haloExchange(this->targetFieldSet_); // Check target field. int nCheck{}; for (idx_t i = 0; i < this->targetView_.shape(0); ++i) { for (idx_t j = 0; j < this->targetView_.shape(1); ++j) { EXPECT(checkValue(this->targetView_(i, j), testPattern(targetLonlatView(i, LON), targetLonlatView(i, LAT), j))); ++nCheck; } } const auto& comm = mpi::comm(this->sourceFunctionSpace_.mpi_comm()); comm.allReduceInPlace(nCheck, eckit::mpi::Operation::SUM); Log::debug() << "Checked " << nCheck << " elements." << std::endl; } }; // Test rank 3 fields . template struct TestRedistributionPoints3 : public TestRedistribution { using TestRedistribution::TestRedistribution; void execute() { auto sourceLonlatView = array::make_view(this->sourceFunctionSpace_.lonlat()); auto targetLonlatView = array::make_view(this->targetFunctionSpace_.lonlat()); // Set source field. for (idx_t i = 0; i < this->sourceView_.shape(0); ++i) { for (idx_t j = 0; j < this->sourceView_.shape(1); ++j) { this->sourceView_(i, j, 0) = testPattern(sourceLonlatView(i, LON), sourceLonlatView(i, LAT), j); this->sourceView_(i, j, 1) = -testPattern(sourceLonlatView(i, LON), sourceLonlatView(i, LAT), j); } } // Perform redistribution. this->redist_.execute(this->sourceFieldSet_, this->targetFieldSet_); // Perform halo exchange; this->targetFunctionSpace_.haloExchange(this->targetFieldSet_); // Check target field. int nCheck{}; for (idx_t i = 0; i < this->targetView_.shape(0); ++i) { for (idx_t j = 0; j < this->targetView_.shape(1); ++j) { EXPECT(checkValue(this->targetView_(i, j, 0), testPattern(targetLonlatView(i, LON), targetLonlatView(i, LAT), j))); ++nCheck; EXPECT(checkValue(this->targetView_(i, j, 1), -testPattern(targetLonlatView(i, LON), targetLonlatView(i, LAT), j))); ++nCheck; } } const auto& comm = mpi::comm(this->sourceFunctionSpace_.mpi_comm()); comm.allReduceInPlace(nCheck, eckit::mpi::Operation::SUM); Log::debug() << "Checked " << nCheck << " elements." << std::endl; } }; // Test CellColumns or EdgeColumns fields. template struct TestRedistributionElems : public TestRedistribution { using TestRedistribution::TestRedistribution; void execute() { // Get source node connectivity and lonlat view. const auto* sourceConnectivity = getConnectivity(this->sourceFunctionSpace_); const auto sourceLonLatView = array::make_view(getMesh(this->sourceFunctionSpace_).nodes().lonlat()); // Get target node connectivity and lonlat view. const auto* targetConnectivity = getConnectivity(this->targetFunctionSpace_); const auto targetLonLatView = array::make_view(getMesh(this->targetFunctionSpace_).nodes().lonlat()); // Set source field. for (idx_t i = 0; i < this->sourceView_.shape(0); ++i) { this->sourceView_(i) = testPattern(sourceConnectivity->row(i), sourceLonLatView); } // Perform redistribution. this->redist_.execute(this->sourceFieldSet_, this->targetFieldSet_); // Perform halo exchange; this->targetFunctionSpace_.haloExchange(this->targetFieldSet_); // Check target field. int nCheck{}; for (idx_t i = 0; i < this->targetView_.shape(0); ++i) { EXPECT(checkValue(this->targetView_(i), testPattern(targetConnectivity->row(i), targetLonLatView))); ++nCheck; } const auto& comm = mpi::comm(this->sourceFunctionSpace_.mpi_comm()); comm.allReduceInPlace(nCheck, eckit::mpi::Operation::SUM); Log::debug() << "Checked " << nCheck << " elements." << std::endl; } }; CASE("Structured grid") { auto grid = atlas::Grid("L24x19"); // Set mesh config. const auto sourceMeshConfig = util::Config("partitioner", "equal_regions"); const auto targetMeshConfig = util::Config("partitioner", "equal_bands"); auto sourceMesh = MeshGenerator("structured", sourceMeshConfig).generate(grid); auto targetMesh = MeshGenerator("structured", targetMeshConfig).generate(grid); SECTION("NodeColumns") { const auto sourceFunctionSpace = functionspace::NodeColumns(sourceMesh, util::Config("halo", 2)); const auto targetFunctionSpace = functionspace::NodeColumns(targetMesh, util::Config("halo", 2)); // Test double for different ranks. auto test1 = TestRedistributionPoints1(sourceFunctionSpace, targetFunctionSpace); auto test2 = TestRedistributionPoints2(sourceFunctionSpace, targetFunctionSpace); auto test3 = TestRedistributionPoints3(sourceFunctionSpace, targetFunctionSpace); // Test float. auto test4 = TestRedistributionPoints1(sourceFunctionSpace, targetFunctionSpace); // Test int. auto test5 = TestRedistributionPoints1(sourceFunctionSpace, targetFunctionSpace); // Test long. auto test6 = TestRedistributionPoints1(sourceFunctionSpace, targetFunctionSpace); test1.execute(); test2.execute(); test3.execute(); test4.execute(); test5.execute(); test6.execute(); test2.outputFields("StructuredGrid_NodeColumns"); } SECTION("CellColumns") { // No build_cells_global_idx method implemented in mesh/actions/BuildParallelFields.cc. Log::debug() << "Structured Grid Cell Columns currently unsupported." << std::endl; //const auto sourceFunctionSpace = functionspace::CellColumns( sourceMesh, util::Config( "halo", 0 ) ); //const auto targetFunctionSpace = functionspace::CellColumns( targetMesh, util::Config( "halo", 0 ) ); //auto test = TestRedistributionElems( sourceFunctionSpace, targetFunctionSpace ); //test.execute(); //test.outputFields( "StructuredGird_CellColumns"); } SECTION("EdgeColumns") { // Note StructuredGrid EdegColumns redistribution only works for halo = 0; const auto sourceFunctionSpace = functionspace::EdgeColumns(sourceMesh, util::Config("halo", 0)); const auto targetFunctionSpace = functionspace::EdgeColumns(targetMesh, util::Config("halo", 0)); // Test long int. auto test = TestRedistributionElems(sourceFunctionSpace, targetFunctionSpace); test.execute(); // EdgeColumns not currently supported by gmsh IO. } SECTION("Structured Columns") { const auto sourceFunctionSpace = functionspace::StructuredColumns( grid, grid::Partitioner("equal_regions"), util::Config("halo", 2) | util::Config("periodic_points", true)); const auto targetFunctionSpace = functionspace::StructuredColumns( grid, grid::Partitioner("regular_bands"), util::Config("halo", 2) | util::Config("periodic_points", true)); auto test = TestRedistributionPoints2(sourceFunctionSpace, targetFunctionSpace); test.execute(); test.outputFields("StructuredGrid_StructuredColumns"); } SECTION("Point Cloud") { // Make a point cloud from NodeColumns functionspace. const auto sourceFunctionSpace = functionspace::NodeColumns(sourceMesh, util::Config("halo", 0)); const auto targetFunctionSpace = functionspace::NodeColumns(targetMesh, util::Config("halo", 0)); // Make a vector of lonlats. auto sourceLonLat = std::vector{}; auto targetLonLat = std::vector{}; const auto sourceGhostView = array::make_view(sourceFunctionSpace.ghost()); const auto sourceLonLatView = array::make_view(sourceFunctionSpace.lonlat()); const auto targetGhostView = array::make_view(targetFunctionSpace.ghost()); const auto targetLonLatView = array::make_view(targetFunctionSpace.lonlat()); // Add non-ghost lonlats to vector. sourceLonLat.reserve(sourceFunctionSpace.size()); for (idx_t i = 0; i < sourceFunctionSpace.size(); ++i) { if (!sourceGhostView(i)) { sourceLonLat.emplace_back(sourceLonLatView(i, LON), sourceLonLatView(i, LAT)); } } targetLonLat.reserve(targetFunctionSpace.size()); for (idx_t i = 0; i < targetFunctionSpace.size(); ++i) { if (!targetGhostView(i)) { targetLonLat.emplace_back(targetLonLatView(i, LON), targetLonLatView(i, LAT)); } } // Make point cloud functionspaces. const auto sourcePointCloud = functionspace::PointCloud(sourceLonLat); const auto targetPointCloud = functionspace::PointCloud(targetLonLat); auto test = TestRedistributionPoints2(sourcePointCloud, targetPointCloud); test.execute(); } } CASE("Cubed sphere grid") { auto grid = atlas::Grid("CS-LFR-C-8"); // Set mesh config. const auto sourceMeshConfig = util::Config("partitioner", "equal_regions") | util::Config("halo", "2"); const auto targetMeshConfig = util::Config("partitioner", "cubedsphere") | util::Config("halo", "2"); auto sourceMesh = MeshGenerator("cubedsphere", sourceMeshConfig).generate(grid); auto targetMesh = MeshGenerator("cubedsphere", targetMeshConfig).generate(grid); SECTION("CubedSphereNodeColumns") { const auto sourceFunctionSpace = functionspace::CubedSphereNodeColumns(sourceMesh); const auto targetFunctionSpace = functionspace::CubedSphereNodeColumns(targetMesh); auto test = TestRedistributionPoints2(sourceFunctionSpace, targetFunctionSpace); test.execute(); test.outputFields("CubedSphere_NodeColumns"); } SECTION("CubedSphereCellColumns") { const auto sourceFunctionSpace = functionspace::CubedSphereCellColumns(sourceMesh); const auto targetFunctionSpace = functionspace::CubedSphereCellColumns(targetMesh); auto test = TestRedistributionElems(sourceFunctionSpace, targetFunctionSpace); test.execute(); test.outputFields("CubedSphere_CellColumns"); } } CASE("Cubed sphere dual grid") { auto grid = atlas::Grid("CS-LFR-C-8"); // Set mesh config. const auto sourceMeshConfig = util::Config("partitioner", "equal_regions") | util::Config("halo", "0"); const auto targetMeshConfig = util::Config("partitioner", "cubedsphere") | util::Config("halo", "0"); auto sourceMesh = MeshGenerator("cubedsphere_dual", sourceMeshConfig).generate(grid); auto targetMesh = MeshGenerator("cubedsphere_dual", targetMeshConfig).generate(grid); SECTION("CubedSphereDualNodeColumns") { const auto sourceFunctionSpace = functionspace::CubedSphereNodeColumns(sourceMesh); const auto targetFunctionSpace = functionspace::CubedSphereNodeColumns(targetMesh); auto test = TestRedistributionPoints2(sourceFunctionSpace, targetFunctionSpace); test.execute(); test.outputFields("CubedSphereDual_NodeColumns"); } SECTION("CubedSphereDualCellColumns") { const auto sourceFunctionSpace = functionspace::CubedSphereCellColumns(sourceMesh); const auto targetFunctionSpace = functionspace::CubedSphereCellColumns(targetMesh); auto test = TestRedistributionElems(sourceFunctionSpace, targetFunctionSpace); test.execute(); test.outputFields("CubedSphereDual_CellColumns"); } } CASE("Gauss Grid NodeColumns to StructuredColumns") { // Target function space (Default Gauss) const auto gauss_grid = Grid("O32"); const auto gauss_functionspace = functionspace::StructuredColumns(gauss_grid); // Construct source function space (Cubespherey Gauss) const auto cubedsphere_grid = Grid("CS-LFR-14"); const auto cubedsphere_functionspace = functionspace::NodeColumns(cubedsphere_grid); atlas::StructuredMeshGenerator mesh_generator; const auto cubedsphere_partitioner = atlas::grid::MatchingPartitioner(cubedsphere_functionspace.mesh(), atlas::option::type("cubedsphere")); const auto cubedsphere_to_gauss_distribution = cubedsphere_partitioner.partition(gauss_grid); const auto cubedsphere_to_gauss_mesh = mesh_generator.generate(gauss_grid, cubedsphere_to_gauss_distribution); const auto cubedsphere_to_gauss_functionspace = atlas::functionspace::NodeColumns(cubedsphere_to_gauss_mesh); // Test redistribution on doubles (rank 1, 2, 3), floats, ints, longs auto test1 = TestRedistributionPoints1(cubedsphere_to_gauss_functionspace, gauss_functionspace); auto test2 = TestRedistributionPoints2(cubedsphere_to_gauss_functionspace, gauss_functionspace); auto test3 = TestRedistributionPoints3(cubedsphere_to_gauss_functionspace, gauss_functionspace); auto test4 = TestRedistributionPoints1(cubedsphere_to_gauss_functionspace, gauss_functionspace); auto test5 = TestRedistributionPoints1(cubedsphere_to_gauss_functionspace, gauss_functionspace); auto test6 = TestRedistributionPoints1(cubedsphere_to_gauss_functionspace, gauss_functionspace); test1.execute(); test2.execute(); test3.execute(); test4.execute(); test5.execute(); test6.execute(); } CASE("Structured grid with split comms") { Fixture fixture; auto grid = mpi_color() == 0 ? atlas::Grid("L24x13") : atlas::Grid("O16"); auto mpi_comm = util::Config("mpi_comm","split"); // auto grid = atlas::Grid("O48"); // auto mpi_comm = util::Config("mpi_comm","world"); // Set mesh config. const auto sourceMeshConfig = util::Config("partitioner", "equal_regions") | mpi_comm; const auto targetMeshConfig = util::Config("partitioner", "equal_bands") | mpi_comm; auto sourceMesh = MeshGenerator("structured", sourceMeshConfig).generate(grid); auto targetMesh = MeshGenerator("structured", targetMeshConfig).generate(grid); SECTION("NodeColumns") { const auto sourceFunctionSpace = functionspace::NodeColumns(sourceMesh, util::Config("halo", 2)); const auto targetFunctionSpace = functionspace::NodeColumns(targetMesh, util::Config("halo", 2)); // Test double for different ranks. auto test1 = TestRedistributionPoints1(sourceFunctionSpace, targetFunctionSpace); auto test2 = TestRedistributionPoints2(sourceFunctionSpace, targetFunctionSpace); auto test3 = TestRedistributionPoints3(sourceFunctionSpace, targetFunctionSpace); // Test float. auto test4 = TestRedistributionPoints1(sourceFunctionSpace, targetFunctionSpace); // Test int. auto test5 = TestRedistributionPoints1(sourceFunctionSpace, targetFunctionSpace); // Test long. auto test6 = TestRedistributionPoints1(sourceFunctionSpace, targetFunctionSpace); test1.execute(); test2.execute(); test3.execute(); test4.execute(); test5.execute(); test6.execute(); test2.outputFields("StructuredGrid_NodeColumns_"+std::to_string(mpi_color())); } SECTION("CellColumns") { // No build_cells_global_idx method implemented in mesh/actions/BuildParallelFields.cc. Log::debug() << "Structured Grid Cell Columns currently unsupported." << std::endl; //const auto sourceFunctionSpace = functionspace::CellColumns( sourceMesh, util::Config( "halo", 0 ) ); //const auto targetFunctionSpace = functionspace::CellColumns( targetMesh, util::Config( "halo", 0 ) ); //auto test = TestRedistributionElems( sourceFunctionSpace, targetFunctionSpace ); //test.execute(); //test.outputFields( "StructuredGird_CellColumns_"+std::to_string(mpi_color())); } SECTION("EdgeColumns") { // Note StructuredGrid EdegColumns redistribution only works for halo = 0; const auto sourceFunctionSpace = functionspace::EdgeColumns(sourceMesh, util::Config("halo", 0)); const auto targetFunctionSpace = functionspace::EdgeColumns(targetMesh, util::Config("halo", 0)); // Test long int. auto test = TestRedistributionElems(sourceFunctionSpace, targetFunctionSpace); test.execute(); // EdgeColumns not currently supported by gmsh IO. } SECTION("Structured Columns") { const auto sourceFunctionSpace = functionspace::StructuredColumns( grid, grid::Partitioner("equal_regions",mpi_comm), util::Config("halo", 2) | util::Config("periodic_points", true) | mpi_comm); const auto targetFunctionSpace = functionspace::StructuredColumns( grid, grid::Partitioner("regular_bands",mpi_comm), util::Config("halo", 2) | util::Config("periodic_points", true) | mpi_comm); auto test = TestRedistributionPoints2(sourceFunctionSpace, targetFunctionSpace); test.execute(); test.outputFields("StructuredGrid_StructuredColumns_"+std::to_string(mpi_color())); } SECTION("Point Cloud") { // Make a point cloud from NodeColumns functionspace. const auto sourceFunctionSpace = functionspace::NodeColumns(sourceMesh, util::Config("halo", 0)); const auto targetFunctionSpace = functionspace::NodeColumns(targetMesh, util::Config("halo", 0)); // Make a vector of lonlats. auto sourceLonLat = std::vector{}; auto targetLonLat = std::vector{}; const auto sourceGhostView = array::make_view(sourceFunctionSpace.ghost()); const auto sourceLonLatView = array::make_view(sourceFunctionSpace.lonlat()); const auto targetGhostView = array::make_view(targetFunctionSpace.ghost()); const auto targetLonLatView = array::make_view(targetFunctionSpace.lonlat()); // Add non-ghost lonlats to vector. sourceLonLat.reserve(sourceFunctionSpace.size()); for (idx_t i = 0; i < sourceFunctionSpace.size(); ++i) { if (!sourceGhostView(i)) { sourceLonLat.emplace_back(sourceLonLatView(i, LON), sourceLonLatView(i, LAT)); } } targetLonLat.reserve(targetFunctionSpace.size()); for (idx_t i = 0; i < targetFunctionSpace.size(); ++i) { if (!targetGhostView(i)) { targetLonLat.emplace_back(targetLonLatView(i, LON), targetLonLatView(i, LAT)); } } // Make point cloud functionspaces. const auto sourcePointCloud = functionspace::PointCloud(sourceLonLat); const auto targetPointCloud = functionspace::PointCloud(targetLonLat); auto test = TestRedistributionPoints2(sourcePointCloud, targetPointCloud); test.execute(); } } CASE("Cubed sphere grid with split comms") { Fixture fixture; auto grid = mpi_color() == 0 ? atlas::Grid("CS-LFR-C-8") : atlas::Grid("CS-LFR-C-16"); auto mpi_comm = util::Config("mpi_comm","split"); // Set mesh config. const auto sourceMeshConfig = util::Config("partitioner", "equal_regions") | util::Config("halo", "2") | mpi_comm; const auto targetMeshConfig = util::Config("partitioner", "cubedsphere") | util::Config("halo", "2") | mpi_comm; auto sourceMesh = MeshGenerator("cubedsphere", sourceMeshConfig).generate(grid); auto targetMesh = MeshGenerator("cubedsphere", targetMeshConfig).generate(grid); SECTION("CubedSphereNodeColumns") { const auto sourceFunctionSpace = functionspace::CubedSphereNodeColumns(sourceMesh); const auto targetFunctionSpace = functionspace::CubedSphereNodeColumns(targetMesh); EXPECT_EQ( sourceFunctionSpace.mpi_comm(), "split" ); auto test = TestRedistributionPoints2(sourceFunctionSpace, targetFunctionSpace); test.execute(); test.outputFields("CubedSphere_NodeColumns_"+std::to_string(mpi_color())); } SECTION("CubedSphereCellColumns") { const auto sourceFunctionSpace = functionspace::CubedSphereCellColumns(sourceMesh); const auto targetFunctionSpace = functionspace::CubedSphereCellColumns(targetMesh); EXPECT_EQ( sourceFunctionSpace.mpi_comm(), "split" ); auto test = TestRedistributionElems(sourceFunctionSpace, targetFunctionSpace); test.execute(); test.outputFields("CubedSphere_CellColumns_"+std::to_string(mpi_color())); } } CASE("Cubed sphere dual grid with split comms") { Fixture fixture; auto grid = mpi_color() == 0 ? atlas::Grid("CS-LFR-C-8") : atlas::Grid("CS-LFR-C-16"); auto mpi_comm = util::Config("mpi_comm","split"); // Set mesh config. const auto sourceMeshConfig = util::Config("partitioner", "equal_regions") | util::Config("halo", "0") | mpi_comm; const auto targetMeshConfig = util::Config("partitioner", "cubedsphere") | util::Config("halo", "0") | mpi_comm; auto sourceMesh = MeshGenerator("cubedsphere_dual", sourceMeshConfig).generate(grid); auto targetMesh = MeshGenerator("cubedsphere_dual", targetMeshConfig).generate(grid); EXPECT_EQ( sourceMesh.mpi_comm(), "split" ); SECTION("CubedSphereDualNodeColumns") { const auto sourceFunctionSpace = functionspace::CubedSphereNodeColumns(sourceMesh); const auto targetFunctionSpace = functionspace::CubedSphereNodeColumns(targetMesh); EXPECT_EQ( sourceFunctionSpace.mpi_comm(), "split" ); auto test = TestRedistributionPoints2(sourceFunctionSpace, targetFunctionSpace); test.execute(); test.outputFields("CubedSphereDual_NodeColumns_"+std::to_string(mpi_color())); } SECTION("CubedSphereDualCellColumns") { const auto sourceFunctionSpace = functionspace::CubedSphereCellColumns(sourceMesh); const auto targetFunctionSpace = functionspace::CubedSphereCellColumns(targetMesh); EXPECT_EQ( sourceFunctionSpace.mpi_comm(), "split" ); auto test = TestRedistributionElems(sourceFunctionSpace, targetFunctionSpace); test.execute(); test.outputFields("CubedSphereDual_CellColumns_"+std::to_string(mpi_color())); } } } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/redistribution/test_redistribution_structured.cc0000664000175000017500000003305615160212070026771 0ustar alastairalastair/* * (C) Crown Copyright 2021 Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ #include #include "atlas/field/FieldSet.h" #include "atlas/functionspace/StructuredColumns.h" #include "atlas/grid.h" #include "atlas/grid/Partitioner.h" #include "atlas/meshgenerator.h" #include "atlas/output/Gmsh.h" #include "atlas/redistribution/Redistribution.h" #include "atlas/util/Config.h" #include "tests/AtlasTestEnvironment.h" namespace atlas { namespace test { int mpi_color() { static int c = mpi::comm("world").rank()%2; return c; } struct Fixture { Fixture() { mpi::comm().split(mpi_color(),"split"); } ~Fixture() { if (eckit::mpi::hasComm("split")) { eckit::mpi::deleteComm("split"); } } }; // Define test pattern for grid. template T testPattern(const double lambda, const double phi, const idx_t field, const idx_t level) { return static_cast(100. * (1 + field) * std::cos(lambda * (1 + level) * M_PI / 180.) * std::cos(phi * (1 + level) * M_PI / 180.)); } // Define a default config for functionspaces. atlas::util::Config funcSpaceDefaultConfig(const idx_t levels = 10, const idx_t halo = 1, const bool periodicPoints = true) { // Declare result. auto funcSpaceConfig = atlas::util::Config{}; funcSpaceConfig.set("levels", levels); funcSpaceConfig.set("halo", halo); funcSpaceConfig.set("periodic_points", periodicPoints); return funcSpaceConfig; } // Test redistributer. Return true if test passed. // Output fields if gmshOutput == true. template bool testStructColsToStructCols(const atlas::Grid& grid, const idx_t nFields, const atlas::grid::Partitioner& sourcePartitioner, const atlas::grid::Partitioner& targetPartitioner, const atlas::util::Config sourceFunctionSpaceConfig, const atlas::util::Config targetFunctionSpaceConfig, const bool gmshOutput = false, const std::string& fileId = "") { const auto sourceFunctionSpace = atlas::functionspace::StructuredColumns(grid, sourcePartitioner, sourceFunctionSpaceConfig); const auto targetFunctionSpace = atlas::functionspace::StructuredColumns(grid, targetPartitioner, targetFunctionSpaceConfig); // Generate some field sets. auto sourceFieldSet = atlas::FieldSet{}; for (idx_t field = 0; field < nFields; ++field) { sourceFieldSet.add( sourceFunctionSpace.createField(atlas::option::name("source_field_" + std::to_string(field)))); } auto targetFieldSet = atlas::FieldSet{}; for (idx_t field = 0; field < nFields; ++field) { targetFieldSet.add( targetFunctionSpace.createField(atlas::option::name("target_field_" + std::to_string(field)))); } // Write some data to source fields. for (idx_t field = 0; field < sourceFieldSet.size(); ++field) { auto fieldView = atlas::array::make_view(sourceFieldSet[field]); for (idx_t j = sourceFunctionSpace.j_begin(); j < sourceFunctionSpace.j_end(); ++j) { for (idx_t i = sourceFunctionSpace.i_begin(j); i < sourceFunctionSpace.i_end(j); ++i) { for (idx_t level = 0; level < sourceFunctionSpace.levels(); ++level) { // get lon and lat. const auto xy = sourceFunctionSpace.compute_xy(i, j); const auto lonLat = grid.projection().lonlat(xy); const auto lon = lonLat.lon(); const auto lat = lonLat.lat(); const auto f = testPattern(lon, lat, field, level); // write f to field. const auto iNode = sourceFunctionSpace.index(i, j); fieldView(iNode, level) = f; } } } } // Set up redistributer. auto redist = atlas::Redistribution(sourceFunctionSpace, targetFunctionSpace, util::Config("type", "RedistributeStructuredColumns")); // Execute redistributer. redist.execute(sourceFieldSet, targetFieldSet); // Read and data from target fields. bool testPassed = true; for (idx_t field = 0; field < targetFieldSet.size(); ++field) { auto fieldView = atlas::array::make_view(targetFieldSet[field]); for (idx_t j = targetFunctionSpace.j_begin(); j < targetFunctionSpace.j_end(); ++j) { for (idx_t i = targetFunctionSpace.i_begin(j); i < targetFunctionSpace.i_end(j); ++i) { for (idx_t level = 0; level < targetFunctionSpace.levels(); ++level) { // get lon and lat. const auto xy = targetFunctionSpace.compute_xy(i, j); const auto lonLat = grid.projection().lonlat(xy); const auto lon = lonLat.lon(); const auto lat = lonLat.lat(); const auto g = testPattern(lon, lat, field, level); // read f from field. const auto iNode = targetFunctionSpace.index(i, j); auto f = fieldView(iNode, level); // check that f is *exactly* equal to g; testPassed = testPassed && (f == g); } } } } // Write mesh and fields to file. if (gmshOutput) { // Generate meshes. const auto meshGen = atlas::MeshGenerator("structured",util::Config("mpi_comm",sourceFunctionSpace.mpi_comm())); const auto sourceMesh = meshGen.generate(grid, sourcePartitioner); const auto targetMesh = meshGen.generate(grid, targetPartitioner); // Set gmsh config. auto gmshConfig = atlas::util::Config{}; gmshConfig.set("ghost", "true"); // Set source gmsh object. const auto sourceGmsh = atlas::output::Gmsh(fileId + "_source_mesh.msh", gmshConfig); // Set target gmsh object const auto targetGmsh = atlas::output::Gmsh(fileId + "_target_mesh.msh", gmshConfig); // Write gmsh sourceGmsh.write(sourceMesh); sourceGmsh.write(sourceFieldSet); targetGmsh.write(targetMesh); targetGmsh.write(targetFieldSet); } return testPassed; } CASE("Redistribute Structured Columns") { SECTION("lonlat: checkerboard to equal_regions") { // Set grid. idx_t nFields = 5; auto grid = atlas::Grid("L48x37"); // Set partitioners. auto sourcePartitioner = atlas::grid::Partitioner("checkerboard"); auto targetPartitioner = atlas::grid::Partitioner("equal_regions"); // Check redistributer. EXPECT(testStructColsToStructCols(grid, nFields, sourcePartitioner, targetPartitioner, funcSpaceDefaultConfig(), funcSpaceDefaultConfig())); return; } SECTION("lonlat: equal_regions to checkerboard") { idx_t nFields = 5; // Set grid. auto grid = atlas::Grid("L48x37"); // Set partitioners. auto sourcePartitioner = atlas::grid::Partitioner("equal_regions"); auto targetPartitioner = atlas::grid::Partitioner("checkerboard"); // Check redistributer. EXPECT(testStructColsToStructCols(grid, nFields, sourcePartitioner, targetPartitioner, funcSpaceDefaultConfig(), funcSpaceDefaultConfig())); return; } SECTION("gaussian: equal_regions to equal_bands") { idx_t nFields = 5; // Set grid. auto grid = atlas::Grid("O16"); // Set partitioners. auto sourcePartitioner = atlas::grid::Partitioner("equal_regions"); auto targetPartitioner = atlas::grid::Partitioner("equal_bands"); EXPECT(testStructColsToStructCols(grid, nFields, sourcePartitioner, targetPartitioner, funcSpaceDefaultConfig(), funcSpaceDefaultConfig())); return; } SECTION("gaussian: equal_bands to equal_regions") { idx_t nFields = 5; // Set grid. auto grid = atlas::Grid("O16"); // Set partitioners. auto sourcePartitioner = atlas::grid::Partitioner("equal_bands"); auto targetPartitioner = atlas::grid::Partitioner("equal_regions"); // Check redistributer. EXPECT(testStructColsToStructCols(grid, nFields, sourcePartitioner, targetPartitioner, funcSpaceDefaultConfig(), funcSpaceDefaultConfig())); return; } SECTION("gaussian: gmsh output") { idx_t nFields = 1; // Set up gaussian grid. auto grid = atlas::Grid("O16"); // Set partitioners. auto sourcePartitioner = atlas::grid::Partitioner("equal_bands"); auto targetPartitioner = atlas::grid::Partitioner("equal_regions"); EXPECT(testStructColsToStructCols(grid, nFields, sourcePartitioner, targetPartitioner, funcSpaceDefaultConfig(), funcSpaceDefaultConfig(), true, grid.name())); return; } SECTION("gaussian: datatype") { idx_t nFields = 5; // Set grid. auto grid = atlas::Grid("O16"); // Set partitioners. auto sourcePartitioner = atlas::grid::Partitioner("equal_bands"); auto targetPartitioner = atlas::grid::Partitioner("equal_regions"); EXPECT(testStructColsToStructCols(grid, nFields, sourcePartitioner, targetPartitioner, funcSpaceDefaultConfig(), funcSpaceDefaultConfig())); return; } SECTION("gaussian: datatype") { idx_t nFields = 5; // Set grid. auto grid = atlas::Grid("O16"); // Set partitioners. auto sourcePartitioner = atlas::grid::Partitioner("equal_bands"); auto targetPartitioner = atlas::grid::Partitioner("equal_regions"); EXPECT(testStructColsToStructCols(grid, nFields, sourcePartitioner, targetPartitioner, funcSpaceDefaultConfig(), funcSpaceDefaultConfig())); return; } SECTION("gaussian: datatype") { idx_t nFields = 5; // Set grid. auto grid = atlas::Grid("O16"); // Set partitioners. auto sourcePartitioner = atlas::grid::Partitioner("equal_bands"); auto targetPartitioner = atlas::grid::Partitioner("equal_regions"); EXPECT(testStructColsToStructCols(grid, nFields, sourcePartitioner, targetPartitioner, funcSpaceDefaultConfig(), funcSpaceDefaultConfig())); return; } SECTION("gaussian: mixed halo size I") { idx_t nFields = 5; // Set grid. auto grid = atlas::Grid("O16"); // Set partitioners. auto sourcePartitioner = atlas::grid::Partitioner("equal_bands"); auto targetPartitioner = atlas::grid::Partitioner("equal_regions"); // set function space configs auto sourceFunctionSpaceConfig = funcSpaceDefaultConfig(); sourceFunctionSpaceConfig.set("halo", 2); auto targetFunctionSpaceConfig = funcSpaceDefaultConfig(); targetFunctionSpaceConfig.set("halo", 3); EXPECT(testStructColsToStructCols(grid, nFields, sourcePartitioner, targetPartitioner, sourceFunctionSpaceConfig, targetFunctionSpaceConfig)); return; } SECTION("gaussian: mixed halo size II") { idx_t nFields = 5; // Set grid. auto grid = atlas::Grid("O16"); // Set partitioners. auto sourcePartitioner = atlas::grid::Partitioner("equal_bands"); auto targetPartitioner = atlas::grid::Partitioner("equal_regions"); // set function space configs auto sourceFunctionSpaceConfig = funcSpaceDefaultConfig(); sourceFunctionSpaceConfig.set("halo", 3); auto targetFunctionSpaceConfig = funcSpaceDefaultConfig(); targetFunctionSpaceConfig.set("halo", 2); EXPECT(testStructColsToStructCols(grid, nFields, sourcePartitioner, targetPartitioner, sourceFunctionSpaceConfig, targetFunctionSpaceConfig)); return; } } CASE("Redistribute Structured Columns with split comms") { Fixture fixture; SECTION("lonlat: checkerboard to equal_regions") { util::Config mpi_comm("mpi_comm","split"); std::string id = std::to_string(mpi_color()); // Set grid. idx_t nFields = 5; auto grid = atlas::Grid("L48x37"); // Set partitioners. auto sourcePartitioner = atlas::grid::Partitioner(option::type("checkerboard")|mpi_comm); auto targetPartitioner = atlas::grid::Partitioner(option::type("equal_regions")|mpi_comm); // Check redistributer. EXPECT(testStructColsToStructCols(grid, nFields, sourcePartitioner, targetPartitioner, funcSpaceDefaultConfig(), funcSpaceDefaultConfig(), true, id)); } } } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/redistribution/fctest_redistribution.F900000664000175000017500000000743515160212070024771 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. ! This File contains Unit Tests for testing the ! C++ / Fortran Interfaces to the State Datastructure ! ! @author Willem Deconinck ! @author Slavko Brdar #include "fckit/fctest.h" ! ----------------------------------------------------------------------------- module fcta_Redistribution_fxt use atlas_module use, intrinsic :: iso_c_binding implicit none character(len=1024) :: msg contains function do_redistribute(fspace_1, fspace_2) result(field_2) use atlas_module use atlas_redistribution_module implicit none type(atlas_FunctionSpace), intent(in) :: fspace_1, fspace_2 type(atlas_Field) :: field_2 type(atlas_FunctionSpace) :: fspace_hlp type(atlas_Redistribution) :: redist, redist_hlp type(atlas_Field) :: field_1 !, field_2 real(c_float), pointer :: field_1v(:), field_2v(:) redist = atlas_Redistribution(fspace_1, fspace_2) field_1 = fspace_1%create_field(atlas_real(c_float)) field_2 = fspace_2%create_field(atlas_real(c_float)) call field_1%data(field_1v) field_1v = 1._c_float call field_2%data(field_2v) field_2v = 2._c_float call redist%execute(field_1, field_2) call field_2%data(field_2v) call field_2%halo_exchange() ! check access to source and target function spaces redist_hlp = atlas_Redistribution(redist%c_ptr()) fspace_hlp = redist%source() fspace_hlp = redist%target() call field_1%final() call redist_hlp%final() call redist%final() end function do_redistribute end module ! ----------------------------------------------------------------------------- TESTSUITE(fcta_Redistribution) !TESTSUITE_WITH_FIXTURE(fcta_Redistribution,fcta_Redistribution_fxt) ! ----------------------------------------------------------------------------- TESTSUITE_INIT use atlas_module call atlas_library%initialise() END_TESTSUITE_INIT ! ----------------------------------------------------------------------------- TESTSUITE_FINALIZE use atlas_module call atlas_library%finalise() END_TESTSUITE_FINALIZE ! ----------------------------------------------------------------------------- TEST( test_redistribution ) use atlas_module use fcta_Redistribution_fxt use atlas_redistribution_module implicit none type(atlas_Grid) :: grid type(atlas_Mesh) :: mesh type(atlas_MeshGenerator) :: meshgenerator type(atlas_FunctionSpace) :: fspace_1, fspace_2 type(atlas_Field) :: field real(c_float) , pointer :: field_v(:) grid = atlas_Grid("O8") fspace_1 = atlas_functionspace_StructuredColumns(grid, atlas_Partitioner("equal_regions")) fspace_2 = atlas_functionspace_StructuredColumns(grid, atlas_Partitioner("regular_bands")) field = do_redistribute(fspace_1, fspace_2) call field%data(field_v) FCTEST_CHECK(maxval(field_v) == 1.) meshgenerator = atlas_MeshGenerator() mesh = meshgenerator%generate(grid, atlas_Partitioner("equal_regions")) fspace_1 = atlas_functionspace_NodeColumns(mesh) mesh = meshgenerator%generate(grid, atlas_Partitioner("regular_bands")) fspace_2 = atlas_functionspace_NodeColumns(mesh) field = do_redistribute(fspace_1, fspace_2) call field%data(field_v) FCTEST_CHECK(maxval(field_v) == 1.) fspace_2 = atlas_functionspace_EdgeColumns(mesh) mesh = meshgenerator%generate(grid, atlas_Partitioner("equal_regions")) fspace_1 = atlas_functionspace_EdgeColumns(mesh) field = do_redistribute(fspace_1, fspace_2) call field%data(field_v) FCTEST_CHECK(maxval(field_v) == 1.) call fspace_2%final() call fspace_1%final() call grid%final() END_TEST ! ----------------------------------------------------------------------------- END_TESTSUITE atlas-0.46.0/src/tests/redistribution/CMakeLists.txt0000664000175000017500000000300415160212070022617 0ustar alastairalastair# # (C) Crown Copyright 2021 Met Office # # This software is licensed under the terms of the Apache Licence Version 2.0 # which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. # if( atlas_HAVE_ATLAS_FUNCTIONSPACE ) if( HAVE_FORTRAN ) if ( CMAKE_Fortran_COMPILER_ID STREQUAL "Intel" AND CMAKE_Fortran_COMPILER_VERSION VERSION_LESS 19.1 ) ecbuild_warn( "Intel Fortran compiler version less than 19.1 has a compiler bug " "that leads to internal compiler error compiling atlas_fctest_redistribution. " "See https://github.com/ecmwf/atlas/issues/340 " "Skipping test ... " ) set( SKIP_TEST_atlas_fctest_redistribution TRUE ) endif() add_fctest( TARGET atlas_fctest_redistribution MPI 4 CONDITION eckit_HAVE_MPI AND MPI_SLOTS GREATER_EQUAL 4 AND NOT SKIP_TEST_atlas_fctest_redistribution LINKER_LANGUAGE Fortran SOURCES fctest_redistribution.F90 LIBS atlas_f ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) endif() ecbuild_add_test( TARGET atlas_test_redistribution_structured SOURCES test_redistribution_structured.cc MPI 8 LIBS atlas CONDITION eckit_HAVE_MPI AND MPI_SLOTS GREATER_EQUAL 8 ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) ecbuild_add_test( TARGET atlas_test_redistribution_generic SOURCES test_redistribution_generic.cc MPI 8 LIBS atlas CONDITION eckit_HAVE_MPI AND MPI_SLOTS GREATER_EQUAL 8 ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) endif() atlas-0.46.0/src/tests/numerics/0000775000175000017500000000000015160212070016641 5ustar alastairalastairatlas-0.46.0/src/tests/numerics/test_fvm_nabla_validation.cc0000664000175000017500000002336715160212070024361 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include #include #include #include "eckit/config/Resource.h" #include "atlas/array/MakeView.h" #include "atlas/field/Field.h" #include "atlas/field/FieldSet.h" #include "atlas/grid/Distribution.h" #include "atlas/grid/Grid.h" #include "atlas/grid/Partitioner.h" #include "atlas/mesh/Mesh.h" #include "atlas/mesh/Nodes.h" #include "atlas/meshgenerator.h" #include "atlas/numerics/Nabla.h" #include "atlas/numerics/fvm/Method.h" #include "atlas/option.h" #include "atlas/output/Gmsh.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/util/Config.h" #include "atlas/util/Constants.h" #include "atlas/util/CoordinateEnums.h" #include "atlas/util/Earth.h" #include "atlas/util/function/SolidBodyRotation.h" #include "tests/AtlasTestEnvironment.h" using namespace eckit; using namespace atlas::numerics; using namespace atlas::meshgenerator; using namespace atlas::grid; namespace atlas { namespace test { // This test relates to JIRA issues METV-2657 , MIR-459 static std::string griduid() { //return "O80"; //return "Slat80"; return "Slat720x360"; } static double radius() { return util::Earth::radius(); } static double beta_in_degrees() { return 90.; } static bool output_gmsh() { return false; } static bool mask_polar_values() { return false; } static int metric_approach() { // Experimental!!! // approach = 0 ORIGINAL, DEFAULT // metric_term cos(y) is multiplied with each wind component // --> cell-interface: avg = 0.5*( cos(y1)*u1 + cos(y2)*u2 ) // approach = 1: // metric_term cos(0.5*(y1+y2)) is used at cell interface // --> cell-interface: avg = 0.5*(u1+u2)*cos(0.5*(y1+y2)) // Results seem to indicate that approach=0 is overall better, although approach=1 // seems to handle pole slightly better (error factor 2 to 4 times lower) return 0; } FieldSet analytical_fields(const fvm::Method& fvm) { const double radius = fvm.radius(); auto lonlat = array::make_view(fvm.mesh().nodes().lonlat()); FieldSet fields; auto add_scalar_field = [&](const std::string& name) { return array::make_view(fields.add(fvm.node_columns().createField(option::name(name)))); }; auto add_vector_field = [&](const std::string& name) { return array::make_view(fields.add(fvm.node_columns().createField( option::name(name) | option::type("vector") | option::variables(2)))); }; auto f = add_scalar_field("f"); auto uv = add_vector_field("uv"); auto u = add_scalar_field("u"); auto v = add_scalar_field("v"); auto grad_f = add_vector_field("ref_grad_f"); auto dfdx = add_scalar_field("ref_dfdx"); auto dfdy = add_scalar_field("ref_dfdy"); auto div = add_scalar_field("ref_div"); auto vor = add_scalar_field("ref_vor"); auto flow = atlas::util::function::SolidBodyRotation{beta_in_degrees(), radius}; auto is_ghost = array::make_view(fvm.mesh().nodes().ghost()); const idx_t nnodes = fvm.mesh().nodes().size(); for (idx_t jnode = 0; jnode < nnodes; ++jnode) { if (is_ghost(jnode)) { continue; } double x = lonlat(jnode, LON); double y = lonlat(jnode, LAT); flow.wind(x, y, u(jnode), v(jnode)); flow.vordiv(x, y, vor(jnode), div(jnode)); f(jnode) = flow.windMagnitudeSquared(x, y); flow.windMagnitudeSquaredGradient(x, y, dfdx(jnode), dfdy(jnode)); uv(jnode, XX) = u(jnode); uv(jnode, YY) = v(jnode); grad_f(jnode, XX) = dfdx(jnode); grad_f(jnode, YY) = dfdy(jnode); } fields.set_dirty(); fields.haloExchange(); return fields; } //----------------------------------------------------------------------------- CASE("test_analytical") { Grid grid(griduid(), GlobalDomain(-180.)); Mesh mesh = MeshGenerator{"structured"}.generate(grid); fvm::Method fvm(mesh, option::radius(radius())); Nabla nabla(fvm, util::Config("metric_approach", metric_approach())); FieldSet fields = analytical_fields(fvm); Field div = fields.add(fvm.node_columns().createField(option::name("div"))); Field vor = fields.add(fvm.node_columns().createField(option::name("vor"))); Field grad_f = fields.add(fvm.node_columns().createField(option::name("grad_f") | option::variables(2))); Field dfdx = fields.add(fvm.node_columns().createField(option::name("dfdx"))); Field dfdy = fields.add(fvm.node_columns().createField(option::name("dfdy"))); auto split = [](const Field& vector, Field& component_x, Field& component_y) { auto v = array::make_view(vector); auto x = array::make_view(component_x); auto y = array::make_view(component_y); for (idx_t j = 0; j < v.shape(0); ++j) { x(j) = v(j, XX); y(j) = v(j, YY); } }; ATLAS_TRACE_SCOPE("gradient") nabla.gradient(fields["f"], grad_f); split(grad_f, dfdx, dfdy); ATLAS_TRACE_SCOPE("divergence") nabla.divergence(fields["uv"], div); ATLAS_TRACE_SCOPE("vorticity") nabla.curl(fields["uv"], vor); auto do_mask_polar_values = [&](Field& field, double mask) { using Topology = atlas::mesh::Nodes::Topology; using Range = atlas::array::Range; auto node_flags = array::make_view(fvm.node_columns().nodes().flags()); auto is_polar = [&](idx_t j) { return Topology::check(node_flags(j), Topology::BC | Topology::NORTH) || Topology::check(node_flags(j), Topology::BC | Topology::SOUTH); }; auto apply = [&](array::LocalView&& view) { for (idx_t j = 0; j < view.shape(0); ++j) { if (is_polar(j)) { for (idx_t v = 0; v < view.shape(1); ++v) { view(j, v) = mask; } } } }; if (field.rank() == 1) { apply(array::make_view(field).slice(Range::all(), Range::dummy())); } else if (field.rank() == 2) { apply(array::make_view(field).slice(Range::all(), Range::all())); } }; for (auto fieldname : std::vector{"dfdx", "dfdy", "div", "vor"}) { auto err_field = fields.add(fvm.node_columns().createField(option::name("err_" + fieldname))); auto err2_field = fields.add(fvm.node_columns().createField(option::name("err2_" + fieldname))); auto fld = array::make_view(fields[fieldname]); auto ref = array::make_view(fields["ref_" + fieldname]); auto err = array::make_view(fields["err_" + fieldname]); auto err2 = array::make_view(fields["err2_" + fieldname]); for (idx_t j = 0; j < fld.shape(0); ++j) { err(j) = fld(j) - ref(j); err2(j) = err(j) * err(j); } if (mask_polar_values()) { do_mask_polar_values(fields["err_" + fieldname], 0.); do_mask_polar_values(fields["err2_" + fieldname], 0.); } } fields.haloExchange(); // output to gmsh if (output_gmsh()) { output::Gmsh{"mesh_2d.msh", util::Config("coordinates", "lonlat")}.write(mesh); output::Gmsh{"mesh_3d.msh", util::Config("coordinates", "xyz")}.write(mesh); output::Gmsh{"fields.msh"}.write(fields); } auto minmax_within_error = [&](const std::string& name, double error) { Field field = fields["err_" + name]; error = std::abs(error); double min, max; fvm.node_columns().minimum(field, min); fvm.node_columns().maximum(field, max); bool success = true; if (min < -error) { Log::warning() << "minumum " << min << " smaller than error " << -error << std::endl; success = false; } if (max > error) { Log::warning() << "maximum " << max << " greater than error " << error << std::endl; success = false; } Log::info() << name << "\t: minmax error between { " << min << " , " << max << " }" << std::endl; return success; }; EXPECT(minmax_within_error("dfdx", 1.e-11)); EXPECT(minmax_within_error("dfdy", 1.e-11)); EXPECT(minmax_within_error("div", 1.e-16)); EXPECT(minmax_within_error("vor", 1.5e-9)); auto rms_within_error = [&](const std::string& name, double error) { Field field = fields["err2_" + name]; double mean; idx_t N; fvm.node_columns().mean(field, mean, N); double rms = std::sqrt(mean / double(N)); bool success = true; if (rms > error) { Log::warning() << "rms " << rms << " greater than error " << error << std::endl; success = false; } Log::info() << name << "\t: rms error = " << rms << std::endl; return success; }; EXPECT(rms_within_error("dfdx", 1.e-14)); EXPECT(rms_within_error("dfdy", 1.e-14)); EXPECT(rms_within_error("div", 5.e-20)); EXPECT(rms_within_error("vor", 5.e-13)); // error for vorticity seems too high ? } //----------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/numerics/fctest_fvm_nabla.F900000664000175000017500000002755115160212070022430 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. ! This File contains Unit Tests for testing the ! gradient computations using Edge-Based-Finite-Volume method ! A comparison between C++ built-in and a fortran version is done here ! @author Willem Deconinck #include "fckit/fctest.h" ! ----------------------------------------------------------------------------- module fctest_atlas_fvm_nabla_Fxt use atlas_module use, intrinsic :: iso_c_binding implicit none type(atlas_StructuredGrid) :: grid type(atlas_Mesh) :: mesh type(atlas_mesh_Nodes) :: nodes type(atlas_MeshGenerator) :: meshgenerator type(atlas_fvm_Method) :: fvm type(atlas_Nabla) :: nabla type(atlas_functionspace_NodeColumns) :: node_columns type(atlas_Field) :: varfield type(atlas_Field) :: gradfield real(c_double), pointer :: var(:,:) real(c_double), pointer :: grad(:,:,:) integer, parameter :: nlev = 137 type(atlas_Config) :: config integer, parameter :: JPRB = c_double integer, parameter :: JPIM = c_int real(JPRB), parameter :: RPI = 2.0_JPRB*asin(1.0_JPRB) real(JPRB) :: RA type :: timer_type private integer(8) :: clck_counts_start, clck_counts_stop, clck_rate integer(8) :: counted = 0 logical :: paused = .True. contains procedure, public :: start => timer_start procedure, public :: pause => timer_pause procedure, public :: resume => timer_resume procedure, public :: elapsed => timer_elapsed end type timer_type contains function timer_elapsed(self) result(time) use, intrinsic :: iso_c_binding, only : c_double class(timer_type), intent(inout) :: self real(c_double) :: time if (.not. self%paused) then call system_clock ( self%clck_counts_stop, self%clck_rate ) time = real(self%counted + self%clck_counts_stop - self%clck_counts_start,c_double) & & /real(self%clck_rate,c_double) else if (self%counted .ge. 0) then time = real(self%counted,c_double)/real(self%clck_rate,c_double) else time = 0. end if end function timer_elapsed subroutine timer_start(self) class(timer_type), intent(inout) :: self call system_clock ( self%clck_counts_start, self%clck_rate ) self%paused = .False. self%counted = 0 end subroutine timer_start subroutine timer_pause(self) class(timer_type), intent(inout) :: self call system_clock ( self%clck_counts_stop, self%clck_rate ) self%counted = self%counted + self%clck_counts_stop - self%clck_counts_start self%paused = .True. end subroutine timer_pause subroutine timer_resume(self) class(timer_type), intent(inout) :: self call system_clock ( self%clck_counts_start, self%clck_rate ) self%paused = .False. end subroutine timer_resume subroutine rotated_flow_magnitude( fvm, field, beta, radius ) type(atlas_fvm_method), intent(in) :: fvm type(atlas_field), intent(inout) :: field real(c_double), intent(in) :: beta real(c_double), intent(in) :: radius !real(c_double) :: radius real(c_double) :: USCAL real(c_double) :: pvel real(c_double) :: deg2rad type(atlas_functionspace_NodeColumns) :: fs_nodecolumns type(atlas_mesh_Nodes) :: nodes type(atlas_field) :: lonlat real(c_double), pointer :: lonlat_deg(:,:) real(c_double), pointer :: var(:,:) integer :: nnodes, jnode, nlev, jlev real(c_double) :: x, y, Ux, Uy integer :: LON=1 integer :: LAT=2 !radius = fvm%radius() USCAL = 20. pvel = USCAL/radius deg2rad = RPI/180. fs_nodecolumns = fvm%node_columns() mesh = fs_nodecolumns%mesh() nodes = fs_nodecolumns%nodes() lonlat = nodes%lonlat() call lonlat%data(lonlat_deg) call field%data(var) nnodes = nodes%size() nlev = field%levels() do jnode=1,nnodes x = lonlat_deg(LON,jnode) * deg2rad y = lonlat_deg(LAT,jnode) * deg2rad Ux = pvel*(cos(beta)+tan(y)*cos(x)*sin(beta))*radius*cos(y) Uy = -pvel*sin(x)*sin(beta)*radius do jlev=1,nlev var(jlev,jnode) = sqrt(Ux*Ux+Uy*Uy) enddo enddo end subroutine SUBROUTINE FV_GRADIENT(PVAR,PGRAD) !**** *GP_DERIVATIVES* - COMPUTES GRID-POINT DERIVATIVES using FINITE VOLUME ! PURPOSE. !** INTERFACE. ! ---------- ! *CALL* *GP_DERIVATIVES(..)* ! EXPLICIT ARGUMENTS ! -------------------- ! IMPLICIT ARGUMENTS : ! -------------------- ! METHOD. ! ------- ! SEE DOCUMENTATION ! EXTERNALS. NONE. ! ---------- ! REFERENCE. ! ---------- ! ECMWF Research Department documentation of the IFS ! AUTHOR. ! ------- ! Mats Hamrud AND Willem Deconinck *ECMWF* ! ORIGINAL : 88-02-04 ! MODIFICATIONS. ! -------------- ! ------------------------------------------------------------------ !USE PARKIND1 , ONLY : JPIM ,JPRB !USE ATLAS_MODULE !USE YOMHOOK , ONLY : LHOOK ,DR_HOOK !USE YOMGEM , ONLY : TGEM !USE YOMATLAS , ONLY : YRATLAS !USE YOMDIM , ONLY : YRDIM !USE YOMDIMV , ONLY : YRDIMV !USE YOMCST , ONLY : RPI, RA IMPLICIT NONE REAL(KIND=JPRB),INTENT(IN) :: PVAR(:,:) REAL(KIND=JPRB),INTENT(OUT) :: PGRAD(:,:,:) TYPE(ATLAS_FIELD) :: LONLAT,DUAL_VOLUMES,DUAL_NORMALS,NODE2EDGE_SIGN type(atlas_mesh_Nodes) :: NODES type(atlas_mesh_Edges) :: EDGES TYPE(ATLAS_CONNECTIVITY) :: EDGE2NODE, NODE2EDGE REAL(KIND=JPRB), POINTER :: ZLONLAT(:,:),ZDUAL_VOLUMES(:),ZDUAL_NORMALS(:,:),& & ZNODE2EDGE_SIGN(:,:) INTEGER(KIND=ATLAS_KIND_IDX),POINTER :: IEDGE2NODE(:,:) INTEGER(KIND=ATLAS_KIND_IDX),POINTER :: INODE2EDGE(:) INTEGER(KIND=JPIM) :: INODE2EDGE_SIZE INTEGER(KIND=JPIM) :: JNODE,JEDGE,JLEV,INEDGES,IP1,IP2,IEDGE,INODES REAL(KIND=JPRB) :: ZAVG,ZSIGN,ZMETRIC_X,ZMETRIC_Y REAL(KIND=JPRB), ALLOCATABLE :: ZAVG_S(:,:,:) INTEGER(KIND=JPIM) :: NFLEVG REAL(KIND=JPRB) :: SCALE, DEG2RAD DEG2RAD = RPI/180. SCALE = DEG2RAD*DEG2RAD*RA ! ------------------------------------------------------------------ !IF (LHOOK) CALL DR_HOOK('FV_GRADIENT',0,ZHOOK_HANDLE) NFLEVG=nlev !write(0,*) 'enter fv_gradient' !write(0,*) 'shape pvar ',shape(pvar) NODES = MESH%NODES() EDGES = MESH%EDGES() LONLAT = NODES%LONLAT() EDGE2NODE = EDGES%NODE_CONNECTIVITY() NODE2EDGE = NODES%EDGE_CONNECTIVITY() DUAL_VOLUMES=NODES%FIELD('dual_volumes') DUAL_NORMALS=EDGES%FIELD('dual_normals') NODE2EDGE_SIGN=NODES%FIELD('node2edge_sign') CALL LONLAT%DATA(ZLONLAT) CALL EDGE2NODE%DATA(IEDGE2NODE) CALL DUAL_VOLUMES%DATA(ZDUAL_VOLUMES) CALL DUAL_NORMALS%DATA(ZDUAL_NORMALS) CALL NODE2EDGE_SIGN%DATA(ZNODE2EDGE_SIGN) INEDGES = SIZE(ZDUAL_NORMALS)/2 ALLOCATE(ZAVG_S(2,NFLEVG,INEDGES)) !$OMP PARALLEL DO SCHEDULE(STATIC) PRIVATE(JEDGE,IP1,IP2,JLEV,ZAVG) DO JEDGE=1,INEDGES IP1 = IEDGE2NODE(1,JEDGE) IP2 = IEDGE2NODE(2,JEDGE) DO JLEV=1,NFLEVG ZAVG = (PVAR(JLEV,IP1)+PVAR(JLEV,IP2)) * 0.5_JPRB ZAVG_S(1,JLEV,JEDGE) = ZDUAL_NORMALS(1,JEDGE)*DEG2RAD*ZAVG ZAVG_S(2,JLEV,JEDGE) = ZDUAL_NORMALS(2,JEDGE)*DEG2RAD*ZAVG ENDDO ENDDO !$OMP END PARALLEL DO INODES = SIZE(ZLONLAT)/2 !$OMP PARALLEL DO SCHEDULE(STATIC) & !$OMP& PRIVATE(JNODE,JLEV,JEDGE,IEDGE,ZSIGN,ZMETRIC_X,ZMETRIC_Y,INODE2EDGE,INODE2EDGE_SIZE) DO JNODE=1,INODES DO JLEV=1,NFLEVG PGRAD(1,JLEV,JNODE) = 0.0 PGRAD(2,JLEV,JNODE) = 0.0 ENDDO CALL NODE2EDGE%ROW(JNODE,INODE2EDGE,INODE2EDGE_SIZE) DO JEDGE=1,INODE2EDGE_SIZE IEDGE = INODE2EDGE(JEDGE) ZSIGN = ZNODE2EDGE_SIGN(JEDGE,JNODE) DO JLEV=1,NFLEVG PGRAD(1,JLEV,JNODE) = PGRAD(1,JLEV,JNODE)+ZSIGN*ZAVG_S(1,JLEV,IEDGE) PGRAD(2,JLEV,JNODE) = PGRAD(2,JLEV,JNODE)+ZSIGN*ZAVG_S(2,JLEV,IEDGE) ENDDO ENDDO ZMETRIC_Y = 1./(ZDUAL_VOLUMES(JNODE)*SCALE) ZMETRIC_X = ZMETRIC_Y/COS(ZLONLAT(2,JNODE)*DEG2RAD) DO JLEV=1,NFLEVG PGRAD(1,JLEV,JNODE) = PGRAD(1,JLEV,JNODE)*ZMETRIC_X PGRAD(2,JLEV,JNODE) = PGRAD(2,JLEV,JNODE)*ZMETRIC_Y ENDDO ENDDO !$OMP END PARALLEL DO !IF (LHOOK) CALL DR_HOOK('FV_GRADIENT',1,ZHOOK_HANDLE) END SUBROUTINE FV_GRADIENT end module fctest_atlas_fvm_nabla_Fxt ! ----------------------------------------------------------------------------- TESTSUITE_WITH_FIXTURE(fctest_atlas_fvm_nabla,fctest_atlas_fvm_nabla_Fxt) ! ----------------------------------------------------------------------------- TESTSUITE_INIT call atlas_library%initialise() RA = 10. config = atlas_Config() call config%set("radius",RA) call config%set("levels",nlev) ! Setup grid = atlas_StructuredGrid("N24") meshgenerator = atlas_MeshGenerator() mesh = meshgenerator%generate(grid) ! second optional argument for atlas_GridDistrubution fvm = atlas_fvm_Method(mesh,config) node_columns = fvm%node_columns() nabla = atlas_Nabla(fvm) ! Create a variable field and a gradient field varfield = node_columns%create_field(name="var",kind=atlas_real(c_double),levels=nlev) gradfield = node_columns%create_field(name="grad",kind=atlas_real(c_double),levels=nlev,variables=2) ! Access to data call varfield%data(var) call gradfield%data(grad) var(:,:) = 0. call rotated_flow_magnitude(fvm,varfield,beta=0.5*RPI*0.75,radius=RA) END_TESTSUITE_INIT ! ----------------------------------------------------------------------------- TESTSUITE_FINALIZE ! Cleanup call config%final() call varfield%final() call gradfield%final() call nabla%final() call fvm%final() call node_columns%final() call nodes%final() call mesh%final() call grid%final() call meshgenerator%final() call atlas_library%finalise() END_TESTSUITE_FINALIZE ! ----------------------------------------------------------------------------- TEST( test_fvm ) type(atlas_StructuredGrid) :: grid type(atlas_MeshGenerator) :: meshgenerator type(atlas_Mesh) :: mesh type(atlas_fvm_Method) :: fvm grid = atlas_StructuredGrid("N24") meshgenerator = atlas_MeshGenerator() mesh = meshgenerator%generate(grid) fvm = atlas_fvm_Method(mesh) call fvm%final() call mesh%final() call grid%final() END_TEST ! ----------------------------------------------------------------------------- TEST( test_nabla ) type(timer_type) :: timer; integer :: jiter, niter real(c_double), allocatable :: norm_native(:) real(c_double), allocatable :: norm_fortran(:) real(c_double) :: checked_value_X = 1.7893197319163668E-016 real(c_double) :: checked_value_Y = -1.0547670904632068E-006 type(atlas_Output) :: gmsh call node_columns%halo_exchange(varfield) niter = 5 ! Compute the gradient call timer%start() do jiter = 1,niter call nabla%gradient(varfield,gradfield) enddo write(0,*) "time elapsed: ", timer%elapsed() call node_columns%halo_exchange(gradfield) call node_columns%mean(gradfield,norm_native) write(0,*) "mean : ", norm_native FCTEST_CHECK_CLOSE( norm_native(1), checked_value_X, 1.e-12_c_double ) FCTEST_CHECK_CLOSE( norm_native(2), checked_value_Y, 1.e-12_c_double ) write(0,*) "" ! Compute the gradient with Fortran routine above call timer%start() do jiter = 1,niter CALL FV_GRADIENT(var,grad) enddo write(0,*) "time elapsed: ", timer%elapsed() call node_columns%halo_exchange(gradfield) call node_columns%mean(gradfield,norm_fortran) write(0,*) "mean : ", norm_fortran FCTEST_CHECK_CLOSE( norm_native(1), checked_value_X, 1.e-12_c_double ) FCTEST_CHECK_CLOSE( norm_native(2), checked_value_Y, 1.e-12_c_double ) gmsh = atlas_output_Gmsh("out_atlas_fctest_fvm_nabla.msh", levels=[10]) call gmsh%write( mesh ) call gmsh%write( varfield ) call gmsh%write( gradfield ) END_TEST ! ----------------------------------------------------------------------------- END_TESTSUITE atlas-0.46.0/src/tests/numerics/test_fvm_nabla.cc0000664000175000017500000004371515160212070022146 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include #include #include #include "eckit/config/Resource.h" #include "atlas/array/MakeView.h" #include "atlas/field/Field.h" #include "atlas/field/FieldSet.h" #include "atlas/grid/Distribution.h" #include "atlas/grid/Grid.h" #include "atlas/grid/Partitioner.h" #include "atlas/mesh/Mesh.h" #include "atlas/mesh/Nodes.h" #include "atlas/meshgenerator.h" #include "atlas/numerics/Nabla.h" #include "atlas/numerics/fvm/Method.h" #include "atlas/option.h" #include "atlas/output/Gmsh.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/util/Config.h" #include "atlas/util/Constants.h" #include "atlas/util/CoordinateEnums.h" #include "atlas/util/Earth.h" #include "tests/AtlasTestEnvironment.h" using namespace eckit; using namespace atlas::numerics; using namespace atlas::meshgenerator; using namespace atlas::grid; namespace atlas { namespace test { int test_levels() { static int levels = []() { int levels = eckit::Resource("--levels", 0); Log::info() << "levels = " << levels << std::endl; return levels; }(); return levels; } static std::string griduid() { return "Slat20"; } template array::LocalView make_vectorview(Field& field) { using array::Range; return field.levels() ? array::make_view(field).slice(Range::all(), Range::all(), Range::all()) : array::make_view(field).slice(Range::all(), Range::dummy(), Range::all()); } template array::LocalView make_scalarview(Field& field) { using array::Range; return field.levels() ? array::make_view(field).slice(Range::all(), Range::all()) : array::make_view(field).slice(Range::all(), Range::dummy()); } #define print_min_max_mean(name) \ do { \ Log::info() << #name << std::endl; \ Log::info() << std::setprecision(18) << " min " << min << std::endl; \ Log::info() << std::setprecision(18) << " max " << max << std::endl; \ Log::info() << std::setprecision(18) << " mean " << mean << std::endl; \ } while (0) //----------------------------------------------------------------------------- double dual_volume(const Mesh& mesh) { const mesh::Nodes& nodes = mesh.nodes(); int nb_nodes = nodes.size(); auto dual_volumes = array::make_view(nodes.field("dual_volumes")); auto is_ghost = array::make_view(nodes.ghost()); double area = 0; for (int node = 0; node < nb_nodes; ++node) { if (!is_ghost(node)) { area += dual_volumes(node); } } mpi::comm().allReduceInPlace(area, eckit::mpi::sum()); return area; } /// @brief Compute magnitude of flow with rotation-angle beta /// (beta=0 --> zonal, beta=pi/2 --> meridional) template void rotated_flow(const fvm::Method& fvm, Field& field, const double& beta) { const double radius = fvm.radius(); const double USCAL = 20.; const double pvel = USCAL / radius; const double deg2rad = M_PI / 180.; auto lonlat_deg = array::make_view(fvm.mesh().nodes().lonlat()); auto var = make_vectorview(field); const idx_t nnodes = fvm.mesh().nodes().size(); const idx_t nlev = var.shape(1); for (idx_t jnode = 0; jnode < nnodes; ++jnode) { double x = lonlat_deg(jnode, LON) * deg2rad; double y = lonlat_deg(jnode, LAT) * deg2rad; double Ux = pvel * (std::cos(beta) + std::tan(y) * std::cos(x) * std::sin(beta)) * radius * std::cos(y); double Uy = -pvel * std::sin(x) * std::sin(beta) * radius; for (idx_t jlev = 0; jlev < nlev; ++jlev) { var(jnode, jlev, LON) = Ux; var(jnode, jlev, LAT) = Uy; } } } /// @brief Compute magnitude of flow with rotation-angle beta /// (beta=0 --> zonal, beta=pi/2 --> meridional) template void rotated_flow_magnitude(const fvm::Method& fvm, Field& field, const double& beta) { const double radius = fvm.radius(); const double USCAL = 20.; const double pvel = USCAL / radius; const double deg2rad = M_PI / 180.; auto lonlat_deg = array::make_view(fvm.mesh().nodes().lonlat()); auto var = make_scalarview(field); const idx_t nnodes = fvm.mesh().nodes().size(); const idx_t nlev = var.shape(1); for (idx_t jnode = 0; jnode < nnodes; ++jnode) { double x = lonlat_deg(jnode, LON) * deg2rad; double y = lonlat_deg(jnode, LAT) * deg2rad; double Ux = pvel * (std::cos(beta) + std::tan(y) * std::cos(x) * std::sin(beta)) * radius * std::cos(y); double Uy = -pvel * std::sin(x) * std::sin(beta) * radius; for (idx_t jlev = 0; jlev < nlev; ++jlev) { var(jnode, jlev) = std::sqrt(Ux * Ux + Uy * Uy); } } } //----------------------------------------------------------------------------- CASE("test_factory") { EXPECT(NablaFactory::has("fvm")); } CASE("test_build") { Log::info() << "test_build" << std::endl; MeshGenerator meshgenerator("structured"); Mesh mesh = meshgenerator.generate(Grid("O16")); const double R = util::Earth::radius(); fvm::Method fvm(mesh, util::Config("radius", R)); Nabla nabla(fvm); double spherical_area = 360. * 180.; EXPECT(eckit::types::is_approximately_equal(dual_volume(mesh), spherical_area, 5.0)); } CASE("test_grad") { auto radius = option::radius("Earth"); Grid grid(griduid()); MeshGenerator meshgenerator("structured"); Mesh mesh = meshgenerator.generate(grid, Distribution(grid, Partitioner("equal_regions"))); fvm::Method fvm(mesh, radius | option::levels(test_levels())); Nabla nabla(fvm); idx_t nnodes = mesh.nodes().size(); idx_t nlev = std::max(1, test_levels()); auto do_test = [&](auto value, double tolerance) { using Value = std::decay_t; FieldSet fields; fields.add(fvm.node_columns().createField(option::name("scalar"))); fields.add(fvm.node_columns().createField(option::name("rscalar"))); fields.add(fvm.node_columns().createField(option::name("grad") | option::variables(2))); fields.add(fvm.node_columns().createField(option::name("rgrad") | option::variables(2))); fields.add(fvm.node_columns().createField(option::name("xder"))); fields.add(fvm.node_columns().createField(option::name("yder"))); fields.add(fvm.node_columns().createField(option::name("rxder"))); fields.add(fvm.node_columns().createField(option::name("ryder"))); EXPECT(fields["scalar"].rank() == (test_levels() ? 2 : 1)); EXPECT(fields["grad"].rank() == fields["scalar"].rank() + 1); EXPECT(fields["scalar"].levels() == test_levels()); EXPECT(fields["grad"].levels() == test_levels()); rotated_flow_magnitude(fvm, fields["scalar"], 0.); rotated_flow_magnitude(fvm, fields["rscalar"], M_PI_2 * 0.75); nabla.gradient(fields["scalar"], fields["grad"]); nabla.gradient(fields["rscalar"], fields["rgrad"]); auto xder = make_scalarview(fields["xder"]); auto yder = make_scalarview(fields["yder"]); auto rxder = make_scalarview(fields["rxder"]); auto ryder = make_scalarview(fields["ryder"]); const auto grad = make_vectorview(fields["grad"]); const auto rgrad = make_vectorview(fields["rgrad"]); for (idx_t jnode = 0; jnode < nnodes; ++jnode) { for (idx_t jlev = 0; jlev < nlev; ++jlev) { xder(jnode, jlev) = grad(jnode, jlev, LON); yder(jnode, jlev) = grad(jnode, jlev, LAT); rxder(jnode, jlev) = rgrad(jnode, jlev, LON); ryder(jnode, jlev) = rgrad(jnode, jlev, LAT); } } // output to gmsh { fvm.node_columns().haloExchange(fields); output::Gmsh(grid.name() + ".msh").write(mesh); output::Gmsh gmsh_fields(grid.name() + "_fields.msh"); gmsh_fields.write(fields["scalar"]); gmsh_fields.write(fields["xder"]); gmsh_fields.write(fields["yder"]); gmsh_fields.write(fields["rscalar"]); gmsh_fields.write(fields["rxder"]); gmsh_fields.write(fields["ryder"]); } double min, max, mean; idx_t N; fvm.node_columns().minimum(fields["xder"], min); fvm.node_columns().maximum(fields["xder"], max); fvm.node_columns().mean(fields["xder"], mean, N); print_min_max_mean("xder"); EXPECT_APPROX_EQ(min, 0., tolerance); EXPECT_APPROX_EQ(max, 0., tolerance); EXPECT_APPROX_EQ(mean, 0., tolerance); fvm.node_columns().minimum(fields["yder"], min); fvm.node_columns().maximum(fields["yder"], max); fvm.node_columns().mean(fields["yder"], mean, N); print_min_max_mean("yder"); EXPECT_APPROX_EQ(min, -3.1141489788326316614e-06, tolerance); EXPECT_APPROX_EQ(max, 3.1141489788326316614e-06, tolerance); EXPECT_APPROX_EQ(mean, 0., tolerance); fvm.node_columns().minimum(fields["rxder"], min); fvm.node_columns().maximum(fields["rxder"], max); fvm.node_columns().mean(fields["rxder"], mean, N); print_min_max_mean("rxder"); EXPECT_APPROX_EQ(min, -3.02863817262107e-06, tolerance); EXPECT_APPROX_EQ(max, +3.02863817262107e-06, tolerance); EXPECT_APPROX_EQ(mean, 0., tolerance); fvm.node_columns().minimum(fields["ryder"], min); fvm.node_columns().maximum(fields["ryder"], max); fvm.node_columns().mean(fields["ryder"], mean, N); print_min_max_mean("ryder"); EXPECT_APPROX_EQ(min, -3.114148978832633e-06, tolerance); EXPECT_APPROX_EQ(max, +3.114148978832633e-06, tolerance); EXPECT_APPROX_EQ(mean, 0., tolerance); }; SECTION("double precision") { do_test(double{}, 1.e-20); } SECTION("single precision") { do_test(float{}, 1.e-10); } } CASE("test_div") { const double radius = util::Earth::radius(); // const double radius = 1.; Grid grid(griduid()); MeshGenerator meshgenerator("structured"); Mesh mesh = meshgenerator.generate(grid, Distribution(grid, Partitioner("equal_regions"))); fvm::Method fvm(mesh, util::Config("radius", radius) | option::levels(test_levels())); Nabla nabla(fvm); auto do_test = [&](auto value, double tolerance) { using Value = std::decay_t; FieldSet fields; fields.add(fvm.node_columns().createField(option::name("wind") | option::variables(2))); fields.add(fvm.node_columns().createField(option::name("div"))); rotated_flow(fvm, fields["wind"], M_PI_2 * 0.75); nabla.divergence(fields["wind"], fields["div"]); // output to gmsh { fvm.node_columns().haloExchange(fields); output::Gmsh gmsh(grid.name() + "_fields.msh", "a"); gmsh.write(fields["wind"]); gmsh.write(fields["div"]); } double min, max, mean; idx_t N; fvm.node_columns().minimum(fields["div"], min); fvm.node_columns().maximum(fields["div"], max); fvm.node_columns().mean(fields["div"], mean, N); // Divergence free flow! print_min_max_mean("div"); EXPECT_APPROX_EQ(min, 0.,tolerance); EXPECT_APPROX_EQ(max, 0., tolerance); EXPECT_APPROX_EQ(mean, 0., tolerance); }; SECTION("double precision") { do_test(double{}, 1.e-18); } SECTION("single precision") { do_test(float{}, 1.e-10); } } CASE("test_curl") { Log::info() << "test_curl" << std::endl; const double radius = util::Earth::radius(); // const double radius = 1.; Grid grid(griduid()); MeshGenerator meshgenerator("structured"); Mesh mesh = meshgenerator.generate(grid, Distribution(grid, Partitioner("equal_regions"))); fvm::Method fvm(mesh, util::Config("radius", radius) | option::levels(test_levels())); Nabla nabla(fvm); auto do_test = [&](auto value, double tolerance) { using Value = std::decay_t; FieldSet fields; fields.add(fvm.node_columns().createField(option::name("wind") | option::variables(2))); fields.add(fvm.node_columns().createField(option::name("vor"))); rotated_flow(fvm, fields["wind"], M_PI_2 * 0.75); nabla.curl(fields["wind"], fields["vor"]); fields.add(fvm.node_columns().createField(option::name("windgrad") | option::variables(2 * 2))); nabla.gradient(fields["wind"], fields["windgrad"]); fields.add(fvm.node_columns().createField(option::name("windX") | option::levels(false))); fields.add(fvm.node_columns().createField(option::name("windY") | option::levels(false))); fields.add(fvm.node_columns().createField(option::name("windXgradX"))); fields.add(fvm.node_columns().createField(option::name("windXgradY"))); fields.add(fvm.node_columns().createField(option::name("windYgradX"))); fields.add(fvm.node_columns().createField(option::name("windYgradY"))); auto wind = make_vectorview(fields["wind"]); auto windgrad = make_vectorview(fields["windgrad"]); auto windX = array::make_view(fields["windX"]); auto windY = array::make_view(fields["windY"]); auto windXgradX = make_scalarview(fields["windXgradX"]); auto windXgradY = make_scalarview(fields["windXgradY"]); auto windYgradX = make_scalarview(fields["windYgradX"]); auto windYgradY = make_scalarview(fields["windYgradY"]); for (idx_t j = 0; j < windX.size(); ++j) { static const idx_t lev0 = 0; static const idx_t XdX = XX * 2 + XX; static const idx_t XdY = XX * 2 + YY; static const idx_t YdX = YY * 2 + XX; static const idx_t YdY = YY * 2 + YY; windX(j) = wind(j, lev0, XX); windY(j) = wind(j, lev0, YY); windXgradX(j, lev0) = windgrad(j, lev0, XdX); windXgradY(j, lev0) = windgrad(j, lev0, XdY); windYgradX(j, lev0) = windgrad(j, lev0, YdX); windYgradY(j, lev0) = windgrad(j, lev0, YdY); } // output to gmsh { fvm.node_columns().haloExchange(fields); output::Gmsh gmsh(grid.name() + "_fields.msh", "a"); gmsh.write(fields["vor"]); gmsh.write(fields["windX"]); gmsh.write(fields["windXgradX"]); gmsh.write(fields["windXgradY"]); gmsh.write(fields["windY"]); gmsh.write(fields["windYgradX"]); gmsh.write(fields["windYgradY"]); gmsh.write(fields["windgrad"]); } double min, max, mean; idx_t N; // Vorticity! fvm.node_columns().minimum(fields["vor"], min); fvm.node_columns().maximum(fields["vor"], max); fvm.node_columns().mean(fields["vor"], mean, N); print_min_max_mean("vor"); EXPECT_APPROX_EQ(min, -6.257451225821150e-06, tolerance); EXPECT_APPROX_EQ(max, 6.257451225821150e-06, tolerance); EXPECT_APPROX_EQ(mean, 0., tolerance); }; SECTION("double precision") { do_test(double{}, 1.e-18); } SECTION("single precision") { do_test(float{}, 1.e-10); } } CASE("test_lapl") { Log::info() << "test_lapl" << std::endl; const double radius = util::Earth::radius(); // const double radius = 1.; Grid grid(griduid()); MeshGenerator meshgenerator("structured"); Mesh mesh = meshgenerator.generate(grid, Distribution(grid, Partitioner("equal_regions"))); fvm::Method fvm(mesh, util::Config("radius", radius) | option::levels(test_levels())); Nabla nabla(fvm); auto do_test = [&](auto value, double tolerance) { using Value = std::decay_t; FieldSet fields; fields.add(fvm.node_columns().createField(option::name("scal"))); fields.add(fvm.node_columns().createField(option::name("lapl"))); rotated_flow_magnitude(fvm, fields["scal"], M_PI_2 * 0.75); nabla.laplacian(fields["scal"], fields["lapl"]); // output to gmsh { fvm.node_columns().haloExchange(fields); output::Gmsh gmsh(grid.name() + "_fields.msh", "a"); gmsh.write(fields["lapl"]); } double min, max, mean; idx_t N; fvm.node_columns().minimum(fields["lapl"], min); fvm.node_columns().maximum(fields["lapl"], max); fvm.node_columns().mean(fields["lapl"], mean, N); print_min_max_mean("lapl"); EXPECT_APPROX_EQ(min, -6.4088005677811607095e-13, tolerance); EXPECT_APPROX_EQ(max, 9.8984499569639476135e-12, tolerance); EXPECT_APPROX_EQ(mean, -1.03409e-13, tolerance); }; SECTION("double precision") { do_test(double{}, 1.e-16); } SECTION("single precision") { do_test(float{}, 1.e-10); } } //----------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/numerics/fctest_ifs_setup.F900000664000175000017500000000712615160212070022500 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. ! This File contains Unit Tests for testing the ! C++ / Fortran Interfaces to the Mesh Datastructure ! @author Willem Deconinck #include "fckit/fctest.h" ! ----------------------------------------------------------------------------- TESTSUITE(fctest_ifs_setup) ! ----------------------------------------------------------------------------- TESTSUITE_INIT use atlas_module call atlas_library%initialise() END_TESTSUITE_INIT ! ----------------------------------------------------------------------------- TESTSUITE_FINALIZE use atlas_module call atlas_library%finalise() END_TESTSUITE_FINALIZE ! ----------------------------------------------------------------------------- TEST( test_fv ) use atlas_module implicit none type(atlas_StructuredGrid) :: grid type(atlas_Mesh) :: mesh type(atlas_MeshGenerator) :: meshgenerator type(atlas_GridDistribution) :: griddistribution type(atlas_functionspace_NodeColumns) :: nodes_fs type(atlas_mesh_Edges) :: edges type(atlas_mesh_Nodes) :: nodes integer, allocatable :: nloen(:) integer, allocatable :: part(:) integer :: halo_size = 1 type(atlas_Connectivity) :: node_to_node type(atlas_Connectivity) :: node_to_edge allocate(nloen(36)) nloen(1:32) = 64 write(0,*) "test_fv" ! Create a new Reduced Gaussian Grid based on a nloen array call atlas_log%info("Creating grid") grid = atlas_ReducedGaussianGrid( nloen(1:32) ) FCTEST_CHECK_EQUAL( grid%owners(), 1 ) ! Grid distribution: all points belong to partition 1 allocate( part(grid%size()) ) part(:) = 1 griddistribution = atlas_GridDistribution(part, part0=1) FCTEST_CHECK_EQUAL( griddistribution%owners(), 1 ) ! Generate mesh with given grid and distribution meshgenerator = atlas_MeshGenerator() FCTEST_CHECK_EQUAL( meshgenerator%owners(), 1 ) write(0,*) " + mesh = meshgenerator%generate(grid,griddistribution)" write(0,*) "mesh%is_null()",mesh%is_null() mesh = meshgenerator%generate(grid,griddistribution) write(0,*) " + call griddistribution%final()" call griddistribution%final() ! Generate nodes function-space, with a given halo_size write(0,*) " + nodes_fs = atlas_functionspace_NodeColumns(mesh,halo_size)" nodes_fs = atlas_functionspace_NodeColumns(mesh,halo_size) ! Create edge elements from surface elements call atlas_build_edges(mesh) call atlas_build_pole_edges(mesh) ! Generate edges function-space (This api will change soon) edges = mesh%edges() nodes = mesh%nodes() call atlas_build_edges_parallel_fields(mesh) ! Build node to edge connectivity call atlas_build_node_to_edge_connectivity(mesh) ! Generate median-dual mesh, (dual_normals, dual_volumes) call atlas_build_median_dual_mesh(mesh) node_to_node = atlas_Connectivity("node") call nodes%add(node_to_node) call node_to_node%final() node_to_node = nodes%connectivity("node") FCTEST_CHECK_EQUAL( node_to_node%rows(), 0 ) FCTEST_CHECK_EQUAL( node_to_node%name(),"node") node_to_node = nodes%connectivity("node") node_to_edge = nodes%connectivity("edge") call node_to_node%final() call mesh%final() call grid%final() call nodes_fs%final() write(0,*) "end test_fv" END_TEST ! ----------------------------------------------------------------------------- END_TESTSUITE atlas-0.46.0/src/tests/numerics/CMakeLists.txt0000664000175000017500000000304615160212070021404 0ustar alastairalastair# (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. ecbuild_add_executable( TARGET atlas_test_fvm_nabla_exe SOURCES test_fvm_nabla.cc LIBS atlas ${OMP_CXX} NOINSTALL ) ecbuild_add_test( TARGET atlas_test_fvm_nabla_L0 COMMAND atlas_test_fvm_nabla_exe ARGS "--levels 0" ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) ecbuild_add_test( TARGET atlas_test_fvm_nabla_L5 COMMAND atlas_test_fvm_nabla_exe ARGS "--levels 5" ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) ecbuild_add_test( TARGET atlas_test_fvm_nabla_validation SOURCES test_fvm_nabla_validation.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) if( TEST atlas_test_fvm_nabla_validation ) set_tests_properties( atlas_test_fvm_nabla_validation PROPERTIES TIMEOUT 450 ) # For certain debug builds (e.g. with address-sanitizer) endif() if( HAVE_FCTEST) add_fctest( TARGET atlas_fctest_fvm_nabla LINKER_LANGUAGE Fortran SOURCES fctest_fvm_nabla.F90 LIBS atlas_f ${OMP_Fortran} ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) add_fctest( TARGET atlas_fctest_ifs_setup LINKER_LANGUAGE Fortran SOURCES fctest_ifs_setup.F90 LIBS atlas_f ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) endif() atlas-0.46.0/src/tests/projection/0000775000175000017500000000000015160212070017170 5ustar alastairalastairatlas-0.46.0/src/tests/projection/test_projection_LAEA.cc0000664000175000017500000001556015160212070023503 0ustar alastairalastair/* * (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 "atlas/grid.h" #include "atlas/projection/Projection.h" #include "atlas/util/Config.h" #include "atlas/util/Constants.h" #include "atlas/util/Point.h" #include "tests/AtlasTestEnvironment.h" using atlas::util::Config; using ListPointXY = std::vector; namespace atlas { namespace test { static const double km = 1000.; //----------------------------------------------------------------------------- CASE("test_projection_LAEA") { auto points_xy = ListPointXY{// in Meters!!! {0., 0.}, {100. * km, 0.}, {0., 100. * km}}; auto projection = Projection{Config("type", "lambert_azimuthal_equal_area")("central_longitude", -67.)("standard_parallel", 50.)}; EXPECT(projection.type() == "lambert_azimuthal_equal_area"); EXPECT(projection.units() == "meters"); EXPECT(projection.strictlyRegional()); for (auto pxy : points_xy) { double tolerance_micrometers = 1.e-6; auto p_identity = projection.xy(projection.lonlat(pxy)); EXPECT(is_approximately_equal(p_identity.x(), pxy.x(), tolerance_micrometers)); EXPECT(is_approximately_equal(p_identity.y(), pxy.y(), tolerance_micrometers)); } } //----------------------------------------------------------------------------- CASE("test_grid_creation_from_GRIB") { auto mir_parametrisation = []() -> Config { Config parametrisation; parametrisation.set("gridType", "lambert_azimuthal_equal_area"); parametrisation.set("standardParallelInDegrees", 50.); parametrisation.set("centralLongitudeInDegrees", -67.); parametrisation.set("latitudeOfFirstGridPointInDegrees", 75.); parametrisation.set("longitudeOfFirstGridPointInDegrees", -90.); parametrisation.set("xDirectionGridLengthInMetres", 100. * km); parametrisation.set("yDirectionGridLengthInMetres", 10. * km); parametrisation.set("numberOfPointsAlongXAxis", 100); parametrisation.set("numberOfPointsAlongYAxis", 50); parametrisation.set("jScansPositively", 0L); parametrisation.set("radius", util::Earth::radius()); return parametrisation; }; auto from_GRIB = [](const eckit::Parametrisation& parametrisation) -> Grid::Spec { // Convert parametrisation to Grid::Spec std::vector firstPointLonLat(2); double standard_parallel, central_longitude, dx, dy; long nx, ny; double radius = util::Earth::radius(); long jScansPositively = 0; parametrisation.get("standardParallelInDegrees", standard_parallel); parametrisation.get("centralLongitudeInDegrees", central_longitude); parametrisation.get("radius", radius); parametrisation.get("longitudeOfFirstGridPointInDegrees", firstPointLonLat[0]); parametrisation.get("latitudeOfFirstGridPointInDegrees", firstPointLonLat[1]); parametrisation.get("numberOfPointsAlongXAxis", nx); parametrisation.get("numberOfPointsAlongYAxis", ny); parametrisation.get("xDirectionGridLengthInMetres", dx); parametrisation.get("yDirectionGridLengthInMetres", dy); parametrisation.get("jScansPositively", jScansPositively); std::string firstPointLoc = jScansPositively ? "lonlat(xmin,ymin)" : "lonlat(xmin,ymax)"; Grid::Spec gridspec; gridspec.set("type", "regional"); // --> indicates following values will be parsed gridspec.set("nx", nx); gridspec.set("ny", ny); gridspec.set("dx", dx); gridspec.set("dy", dy); gridspec.set(firstPointLoc, firstPointLonLat); gridspec.set("y_numbering", -1); // Always decrease y for MIR gridspec.set("projection", [&]() { Grid::Spec projection; projection.set("type", "lambert_azimuthal_equal_area"); projection.set("standard_parallel", standard_parallel); projection.set("central_longitude", central_longitude); projection.set("radius", radius); return projection; }()); return gridspec; }; // Create the grid from mir_parametrisation auto g = Grid(from_GRIB(mir_parametrisation())); // Compute first point and compare with mir_parametrisation { auto domain = RectangularDomain(g.domain()); auto firstPointXY = mir_parametrisation().getLong("jScansPositively", 0L) ? PointXY{domain.xmin(), domain.ymin()} : PointXY{domain.xmin(), domain.ymax()}; auto firstPointLonLat = g.projection().lonlat(firstPointXY); EXPECT(is_approximately_equal(firstPointLonLat.lon(), mir_parametrisation().getDouble("longitudeOfFirstGridPointInDegrees"))); EXPECT(is_approximately_equal(firstPointLonLat.lat(), mir_parametrisation().getDouble("latitudeOfFirstGridPointInDegrees"))); } // Check if iteration is correct { auto domain = RectangularDomain(g.domain()); auto firstPointLonLat = g.projection().lonlat({domain.xmin(), domain.ymax()}); auto lastPointLonLat = g.projection().lonlat({domain.xmax(), domain.ymin()}); long n = 0; for (PointLonLat p : g.lonlat()) { if (n == 0) { EXPECT(is_approximately_equal(p.lon(), firstPointLonLat.lon())); EXPECT(is_approximately_equal(p.lat(), firstPointLonLat.lat())); } if (n == g.size() - 1) { EXPECT(is_approximately_equal(p.lon(), lastPointLonLat.lon())); EXPECT(is_approximately_equal(p.lat(), lastPointLonLat.lat())); } ++n; } EXPECT(n == g.size()); } // Check if bounding box is correct { RectangularLonLatDomain bb{g.lonlatBoundingBox()}; const double tolerance = 1.e-6; EXPECT_APPROX_EQ(bb.west(), -90., tolerance); EXPECT_APPROX_EQ(bb.east(), 41.882046, tolerance); EXPECT_APPROX_EQ(bb.south(), 3.818356, tolerance); EXPECT_APPROX_EQ(bb.north(), 76.040387, tolerance); for (PointLonLat p : g.lonlat()) { EXPECT(bb.contains(p)); } } } //----------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/projection/test_projection_variable_resolution.cc0000664000175000017500000006176115160212070027055 0ustar alastairalastair/** * (C) Crown copyright 2021, Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ #include "atlas/grid.h" #include "atlas/mesh/Mesh.h" #include "atlas/option.h" #include "atlas/output/Gmsh.h" #include "atlas/projection.h" #include "atlas/util/Config.h" #include "atlas/util/Point.h" #include "atlas/util/Rotation.h" #include "tests/AtlasTestEnvironment.h" using namespace atlas::util; using namespace atlas::grid; using atlas::util::Rotation; namespace { ///< use vectors of double for testing const std::vector lon_LAM_str{ 346.9838795150958504, 347.9838795150958504, 348.9838795150958504, 349.8677242998691668, 350.6659835640296592, 351.3869448275862055, 352.0380931034482614, 352.6892413793103174, 353.3403896551723733, 353.9915379310344861, 354.642686206896542, 355.2938344827585979, 355.9449827586206538, 356.5961310344827666, 357.2472793103448225, 357.8984275862068785, 358.5495758620689344, 359.2007241379310472, 359.8518724137931031, 360.503020689655159, 361.1541689655172149, 361.8053172413793277, 362.4564655172413836, 363.1076137931034395, 363.7587620689654955, 364.4099103448276082, 365.0610586206896642, 365.7122068965517201, 366.363355172413776, 367.0843164359702087, 367.8825757001305305, 368.7664204849036764, 369.7664204849036764, 370.7664204849036764}; const std::vector lat_LAM_str{ -9.41181948490414122, -8.41181948490414122, -7.41181948490414122, -6.527974700130756425, -5.729715435970231141, -5.00875417241366172, -4.357605896551548952, -3.706457620689436183, -3.055309344827323415, -2.404161068965210646, -1.753012793103097877, -1.101864517240985109, -0.4507162413788723399, 0.2004320344832404288, 0.8515803103453531975, 1.502728586207465966, 2.153876862069578735, 2.805025137931691503, 3.456173413793804272, 4.107321689655917041, 4.758469965518029809, 5.409618241380142578, 6.060766517242255347, 6.711914793104368115, 7.363063068966480884, 8.014211344828593653, 8.665359620690706421, 9.386320884247275842, 10.18458014840780024, 11.06842493318118414, 12.06842493318118414, 13.06842493318118414}; const std::vector lon_LAM_reg = { 348.13120344827576, 348.78235172413787, 349.4334999999999809, 350.0846482758620368, 350.7357965517240928, 351.3869448275862055, 352.0380931034482614, 352.6892413793103174, 353.3403896551723733, 353.9915379310344861, 354.642686206896542, 355.2938344827585979, 355.9449827586206538, 356.5961310344827666, 357.2472793103448225, 357.8984275862068785, 358.5495758620689344, 359.2007241379310472, 359.8518724137931031, 360.503020689655159, 361.1541689655172149, 361.8053172413793277, 362.4564655172413836, 363.1076137931034395, 363.7587620689654955, 364.4099103448276082, 365.0610586206896642, 365.7122068965517201, 366.363355172413776, 367.0145034482758888, 367.6656517241379447, 368.3168000000000006, 368.9679482758621, 369.6190965517242}; const std::vector lat_LAM_reg = {-8.264495551724226, -7.613347275862113, -6.962199, -6.311050724137887258, -5.659902448275774489, -5.00875417241366172, -4.357605896551548952, -3.706457620689436183, -3.055309344827323415, -2.404161068965210646, -1.753012793103097877, -1.101864517240985109, -0.4507162413788723399, 0.2004320344832404288, 0.8515803103453531975, 1.502728586207465966, 2.153876862069578735, 2.805025137931691503, 3.456173413793804272, 4.107321689655917041, 4.758469965518029809, 5.409618241380142578, 6.060766517242255347, 6.711914793104368115, 7.363063068966480884, 8.014211344828593653, 8.665359620690706421, 9.31650789655281919, 9.967656172414931959, 10.61880444827704473, 11.269952724139157, 11.92110100000127}; const int nx = 34; // These number are here hardcoded, but there is a test using x/yrange_outer and delta_inner const int ny = 32; const double xrange_outer[2] = {348.13120344827576, 369.6190965517242}; const double yrange_outer[2] = {-8.264495551724226, 11.92110100000127}; const double xrange_inner[2] = {351.386944827586319, 366.363355172413776}; const double yrange_inner[2] = {-5.008754172413662, 8.665359620690706}; const double rim_width = 4.; const double delta_outer = 1.; const double delta_inner = 0.6511482758621128; ///< correction used constexpr float epstest = std::numeric_limits::epsilon(); auto make_var_ratio_projection = [](double var_ratio) { Config conf; conf.set("type", "variable_resolution"); conf.set("outer.dx", delta_outer); ///< resolution of the external regular grid (rim) conf.set("inner.dx", delta_inner); ///< resolution of the regional model (regular grid) conf.set("progression", var_ratio); ///< power used for the stretching conf.set("inner.xmin", xrange_inner[0]); ///< xstart of the internal regional grid conf.set("inner.ymin", yrange_inner[0]); ///< ystart of the internal regional grid conf.set("inner.xend", xrange_inner[1]); ///< xend of the regular part of stretched internal grid conf.set("inner.yend", yrange_inner[1]); ///< yend of the regular part of stretched internal grid conf.set("outer.xmin", xrange_outer[0]); ///< original domain startx conf.set("outer.xend", xrange_outer[1]); ///< original domain endx conf.set("outer.ymin", yrange_outer[0]); ///< original domain starty conf.set("outer.yend", yrange_outer[1]); ///< original domain endy conf.set("outer.width", rim_width); return atlas::Projection(conf); }; auto make_var_ratio_projection_rot = [](double var_ratio, std::vector north_pole) { Config conf; conf.set("type", "rotated_variable_resolution"); conf.set("outer.dx", delta_outer); ///< resolution of the external regular grid (rim) conf.set("inner.dx", delta_inner); ///< resolution of the regional model (regular grid) conf.set("progression", var_ratio); ///< power used for the stretching conf.set("inner.xmin", xrange_inner[0]); ///< xstart of the internal regional grid conf.set("inner.ymin", yrange_inner[0]); ///< ystart of the internal regional grid conf.set("inner.xend", xrange_inner[1]); ///< xend of the regular part of stretched internal grid conf.set("inner.yend", yrange_inner[1]); ///< yend of the regular part of stretched internal grid conf.set("outer.xmin", xrange_outer[0]); ///< original domain startx conf.set("outer.xend", xrange_outer[1]); ///< original domain endx conf.set("outer.ymin", yrange_outer[0]); ///< original domain starty conf.set("outer.yend", yrange_outer[1]); ///< original domain endy conf.set("outer.width", rim_width); conf.set("north_pole", north_pole); return atlas::Projection(conf); }; auto not_equal = [](double a, double b) { return std::abs(b - a) > 1.e-5; }; static double new_ratio(int n_stretched, double var_ratio) { /** * compute ratio, * change stretching factor so that high and low grids * retain original sizes */ ///< number of variable (stretched) grid points in one side double var_ints_f = n_stretched * 1.; double logr = std::log(var_ratio); double log_ratio = (var_ints_f - 0.5) * logr; return std::exp(log_ratio / n_stretched); }; /** * n_stretched and n_rim. Outside the regular grid, only one side, * number of grid points in the stretched grid and in the rim region */ auto create_stretched_grid = [](const int& n_points_, const int& n_stretched_, const double& var_ratio_, const int& n_rim, double lamphi, const bool& L_long) { double new_ratio_{}; double range_outer[2]{}; auto normalised = [L_long](double p) { if (L_long) { p = (p < 180) ? p + 360.0 : p; } return p; }; lamphi = normalised(lamphi); if (L_long) { range_outer[0] = xrange_outer[0]; range_outer[1] = xrange_outer[1]; } else { range_outer[0] = yrange_outer[0]; range_outer[1] = yrange_outer[1]; } if (var_ratio_ > 1.) { //< compute number of points in the original regular grid // THIS IS TO NORMALIZE int n_idx_ = ((lamphi + epstest - range_outer[0]) / delta_inner) + 1; // first point in the internal regular grid int nstart_int = n_rim + n_stretched_ + 1; // last point in the internal regular grid int nend_int = n_points_ - (n_rim + n_stretched_); double delta = delta_inner; double delta_last = delta_inner; double delta_add{}; double delta_tot{}; new_ratio_ = new_ratio(n_stretched_, var_ratio_); //< stretched area //< n_idx_ start from 1 if (((n_idx_ < nstart_int) && (n_idx_ > n_rim)) || ((n_idx_ > nend_int) && (n_idx_ < n_points_ - n_rim + 1))) { //< number of stretched points for the loop int n_st{}; if (n_idx_ < nstart_int) { n_st = nstart_int - n_idx_; } else { n_st = n_idx_ - nend_int; } delta_tot = 0.; for (int ix = 0; ix < n_st; ix += 1) { delta_last = delta * new_ratio_; delta_add = delta_last - delta_inner; delta = delta_last; delta_tot += delta_add; } if (n_idx_ < nstart_int) { lamphi -= delta_tot; } else { lamphi += delta_tot; } } //< rim region if (((n_idx_ < n_rim + 1)) || (n_idx_ > n_points_ - n_rim)) { delta_tot = 0.; //compute total stretched for (int ix = 0; ix < n_stretched_; ix += 1) { delta_last = delta * new_ratio_; delta_add = delta_last - delta_inner; delta = delta_last; delta_tot += delta_add; } double drim_size = ((rim_width / 2.) / n_rim) - delta_inner; int ndrim{}; if (n_idx_ < nstart_int) { ndrim = nstart_int - n_stretched_ - n_idx_; lamphi = lamphi - delta_tot - (ndrim * drim_size); } else { ndrim = n_idx_ - (n_points_ - n_rim); lamphi = lamphi + delta_tot + ndrim * drim_size; } } } //< return the new point in the stretched grid return normalised(lamphi); }; }; // namespace namespace atlas { namespace test { CASE("Understanding of the above data") { const std::vector& v = lon_LAM_str; std::vector delta(v.size() - 1); for (size_t i = 0; i < delta.size(); ++i) { delta[i] = v[i + 1] - v[i]; } Log::info() << "delta = " << delta << std::endl; // Outputs: // delta = [1,1,0.883845,0.798259,0.720961,0.651148,0.651148,0.651148,0.651148, // 0.651148,0.651148,0.651148,0.651148,0.651148,0.651148,0.651148,0.651148, // 0.651148,0.651148,0.651148,0.651148,0.651148,0.651148,0.651148,0.651148, // 0.651148,0.651148,0.651148,0.720961,0.798259,0.883845,1,1] std::vector delta_half(delta.begin() + delta.size() / 2, delta.end()); Log::info() << "delta_half = " << delta_half << std::endl; // Outputs: // delta_half = [0.651148,0.651148,0.651148,0.651148,0.651148,0.651148,0.651148, // 0.651148,0.651148,0.651148,0.651148,0.651148,0.720961,0.798259, // 0.883845,1,1] std::vector progression(delta_half.size() - 1); for (size_t i = 0; i < progression.size(); ++i) { progression[i] = delta_half[i + 1] / delta_half[i]; } Log::info() << "progression = " << progression << std::endl; // Outputs: // progression = [1,1,1,1,1,1,1,1,1,1,1,1.10722,1.10722,1.10722,1.13142,1] int nx = 34; int nx_var = 6; int nx_outer = 4; double var_ratio = 1.13; int inner_i_begin = -1; int inner_i_end = -1; for (int i = 0; i < nx; ++i) { if (std::abs(v[i] - xrange_inner[0]) < 1.e-10) { inner_i_begin = i; } if (std::abs(v[i] - xrange_inner[1]) < 1.e-10) { inner_i_end = i + 1; } } double inner_size = ((nx - 1) - nx_var - nx_outer) * delta_inner; ///< number of regular internal grid points, integer int nx_inner = (inner_size + epstest) / delta_inner + 1; EXPECT_EQ(nx_inner, inner_i_end - inner_i_begin); EXPECT_EQ(nx_inner, inner_i_end - inner_i_begin); EXPECT_EQ(inner_i_begin, nx_outer / 2 + nx_var / 2); EXPECT_EQ(inner_i_end, nx - nx_outer / 2 - nx_var / 2); for (int i = 0; i < nx_outer / 2; ++i) { EXPECT_APPROX_EQ(v[i + 1] - v[i], delta_outer, 1.e-10); } for (int i = nx_outer / 2; i < inner_i_begin; ++i) { double d = v[i + 1] - v[i]; EXPECT(not_equal(d, delta_inner)); EXPECT(not_equal(d, delta_outer)); } for (int i = inner_i_begin; i < inner_i_end - 1; ++i) { EXPECT_APPROX_EQ(v[i + 1] - v[i], delta_inner, 1.e-10); } for (int i = inner_i_end - 1; i < nx - nx_outer / 2 - 1; ++i) { double d = v[i + 1] - v[i]; EXPECT(not_equal(d, delta_inner)); EXPECT(not_equal(d, delta_outer)); } for (int i = nx - nx_outer / 2 - 1; i < nx - 1; ++i) { EXPECT_APPROX_EQ(v[i + 1] - v[i], delta_outer, 1.e-10); } double var_ints_f = ((nx - 1) - nx_outer - (nx_inner - 1)) / 2.; double logr = std::log(var_ratio); double log_ratio = (var_ints_f - 0.5) * logr; double new_ratio = std::exp(log_ratio / std::floor(var_ints_f)); EXPECT_APPROX_EQ(new_ratio, 1.10722, 1.e-5); } //< create stretched grid from regular grid CASE("var_ratio_create = 1.13") { ///< definition of grid constexpr float epstest = std::numeric_limits::epsilon(); int nx_reg = ((xrange_outer[1] - xrange_outer[0] + epstest) / delta_inner) + 1; int ny_reg = ((yrange_outer[1] - yrange_outer[0] + epstest) / delta_inner) + 1; auto grid_st = RegularGrid{grid::LinearSpacing{xrange_outer[0], xrange_outer[1], nx_reg}, grid::LinearSpacing{yrange_outer[0], yrange_outer[1], ny_reg}, make_var_ratio_projection(1.13)}; ///< check over regular grid points stretched using new atlas object and check using look-up table for (idx_t j = 0; j < grid_st.ny(); ++j) { for (idx_t i = 0; i < grid_st.nx(); ++i) { auto ll = grid_st.lonlat(i, j); EXPECT_APPROX_EQ(ll.lon(), lon_LAM_str[i], 1.e-10); EXPECT_APPROX_EQ(ll.lat(), lat_LAM_str[j], 1.e-10); } } ///< check over regular grid points stretched using new atlas object and check using function above for (idx_t j = 0; j < grid_st.ny(); ++j) { for (idx_t i = 0; i < grid_st.nx(); ++i) { auto ll_st_lon = create_stretched_grid(nx_reg, 3, 1.13, 2, lon_LAM_reg[i], true); auto ll_st_lat = create_stretched_grid(ny_reg, 3, 1.13, 2, lat_LAM_reg[j], false); EXPECT_APPROX_EQ(ll_st_lon, lon_LAM_str[i], 1.e-10); EXPECT_APPROX_EQ(ll_st_lat, lat_LAM_str[j], 1.e-10); } } ///< check internal regular grid idx_t ymid = grid_st.ny() / 2; idx_t xmid = grid_st.nx() / 2; auto expect_equal_dlon = [&](int i, double dlon) { EXPECT_APPROX_EQ(grid_st.lonlat(i + 1, ymid).lon() - grid_st.lonlat(i, ymid).lon(), dlon, 1.e-10); }; auto expect_equal_dlat = [&](int j, double dlat) { EXPECT_APPROX_EQ(grid_st.lonlat(xmid, j + 1).lat() - grid_st.lonlat(xmid, j).lat(), dlat, 1.e-10); }; expect_equal_dlon(0, delta_outer); expect_equal_dlon(xmid, delta_inner); expect_equal_dlat(0, delta_outer); expect_equal_dlat(ymid, delta_inner); //< Check that the spacing in xy coordinates matches "delta_inner" for (int i = 0; i < grid_st.nx() - 1; ++i) { EXPECT_APPROX_EQ(grid_st.xy(i + 1, ymid).x() - grid_st.xy(i, ymid).x(), delta_inner, 1.e-10); } for (int j = 0; j < grid_st.ny() - 1; ++j) { EXPECT_APPROX_EQ(grid_st.xy(xmid, j + 1).y() - grid_st.xy(xmid, j).y(), delta_inner, 1.e-10); } } CASE("var_ratio = 1.13") { ///< definition of grid auto grid = RegularGrid{grid::LinearSpacing{xrange_outer[0], xrange_outer[1], nx}, grid::LinearSpacing{yrange_outer[0], yrange_outer[1], ny}, make_var_ratio_projection(1.13)}; ///< check over regular grid points stretched using new atlas object and check using look-up table for (idx_t j = 0; j < grid.ny(); ++j) { for (idx_t i = 0; i < grid.nx(); ++i) { auto ll = grid.lonlat(i, j); EXPECT_APPROX_EQ(ll.lon(), lon_LAM_str[i], 1.e-10); EXPECT_APPROX_EQ(ll.lat(), lat_LAM_str[j], 1.e-10); } } idx_t ymid = grid.ny() / 2; idx_t xmid = grid.nx() / 2; auto expect_equal_dlon = [&](int i, double dlon) { EXPECT_APPROX_EQ(grid.lonlat(i + 1, ymid).lon() - grid.lonlat(i, ymid).lon(), dlon, 1.e-10); }; auto expect_equal_dlat = [&](int j, double dlat) { EXPECT_APPROX_EQ(grid.lonlat(xmid, j + 1).lat() - grid.lonlat(xmid, j).lat(), dlat, 1.e-10); }; expect_equal_dlon(0, delta_outer); expect_equal_dlon(xmid, delta_inner); expect_equal_dlat(0, delta_outer); expect_equal_dlat(ymid, delta_inner); //< Check that the spacing in xy coordinates matches "delta_inner" for (int i = 0; i < grid.nx() - 1; ++i) { EXPECT_APPROX_EQ(grid.xy(i + 1, ymid).x() - grid.xy(i, ymid).x(), delta_inner, 1.e-10); } for (int j = 0; j < grid.ny() - 1; ++j) { EXPECT_APPROX_EQ(grid.xy(xmid, j + 1).y() - grid.xy(xmid, j).y(), delta_inner, 1.e-10); } ///< Set meshes ///< define mesh to write in file auto mesh = Mesh{grid}; /** * Write mesh in gmsh object. * output under: * /atlas/src/tests/grid/ */ output::Gmsh{"stretch_mesh_lonlat.msh", Config("coordinates", "lonlat")("info", true)}.write(mesh); output::Gmsh{"stretch_mesh_xy.msh", Config("coordinates", "xy")("info", true)}.write(mesh); /** Create additional regular grid with same delta_hi * for additional .msh file with lon lat in approximately the same range * as the stretched one. */ int nx_new = 37; int ny_new = 35; ///< 37 the exact range would have 36.52 points double alpha = ((delta_inner * nx_new) - (xrange_outer[1] - xrange_outer[0])) / 2.; double startx_n = xrange_outer[0] - alpha; double endx_n = xrange_outer[1] + alpha; double starty_n = yrange_outer[0] - alpha; double endy_n = yrange_outer[1] + alpha; ///< create regular grid auto grid_reg_approx = RegularGrid{LinearSpacing{startx_n, endx_n, nx_new}, LinearSpacing{starty_n, endy_n, ny_new}}; /** * Write mesh in gmsh object. * output under: * /atlas/src/tests/grid/ */ output::Gmsh("grid_reg_approx_lonlat.msh", Config("coordinates", "lonlat")("info", true)) .write(Mesh{grid_reg_approx}); } CASE("var_ratio = 1.0") { ///< TEST of var_ratio = 1.0 configuration ///< definition of stretched grid auto grid = RegularGrid{grid::LinearSpacing{xrange_outer[0], xrange_outer[1], nx}, grid::LinearSpacing{yrange_outer[0], yrange_outer[1], ny}, make_var_ratio_projection(1.0)}; ///< Check if bounding box is correct { auto bb = grid.lonlatBoundingBox(); EXPECT(RectangularLonLatDomain(bb)); const double tolerance = 1.e-6; EXPECT_APPROX_EQ(bb.west(), xrange_outer[0], tolerance); EXPECT_APPROX_EQ(bb.east(), xrange_outer[1], tolerance); EXPECT_APPROX_EQ(bb.south(), yrange_outer[0], tolerance); EXPECT_APPROX_EQ(bb.north(), yrange_outer[1], tolerance); for (PointLonLat p : grid.lonlat()) { EXPECT(bb.contains(p)); } } ///< check over regular grid points stretched using new atlas object and check using look-up table for (idx_t j = 0; j < grid.ny(); ++j) { for (idx_t i = 0; i < grid.nx(); ++i) { auto ll = grid.lonlat(i, j); EXPECT_APPROX_EQ(ll.lon(), lon_LAM_reg[i], 1.e-10); EXPECT_APPROX_EQ(ll.lat(), lat_LAM_reg[j], 1.e-10); } } auto mesh = Mesh{grid}; output::Gmsh{"reg_mesh_lonlat.msh", Config("coordinates", "lonlat")("info", true)}.write(mesh); output::Gmsh{"reg_mesh_xy.msh", Config("coordinates", "xy")("info", true)}.write(mesh); } CASE("var_ratio_rot = 1.13") { ///< check over regular grid points stretched using new atlas object and check using look-up table ///< in this case add a rotation Config config; std::vector north_pole = {-176., 40.}; config.set("north_pole", north_pole); ///< definition of grid I have to rotate this auto proj_st = make_var_ratio_projection_rot(1.13, north_pole); auto grid = RegularGrid{grid::LinearSpacing{xrange_outer[0], xrange_outer[1], nx}, grid::LinearSpacing{yrange_outer[0], yrange_outer[1], ny}, proj_st}; Rotation rotation(config); for (idx_t j = 0; j < grid.ny(); ++j) { for (idx_t i = 0; i < grid.nx(); ++i) { ////atlas/src/tests/grid/ */ output::Gmsh{"stretch_mesh_lonlat_rot.msh", Config("coordinates", "lonlat")("info", true)}.write(mesh); output::Gmsh{"stretch_mesh_xy_rot.msh", Config("coordinates", "xy")("info", true)}.write(mesh); /** Create additional regular grid with same delta_hi * for additional .msh file with lon lat in approximately the same range * as the stretched one. */ int nx_new = 37; int ny_new = 35; ///< 37 the exact range would have 36.52 points double alpha = ((delta_inner * nx_new) - (xrange_outer[1] - xrange_outer[0])) / 2.; double startx_n = xrange_outer[0] - alpha; double endx_n = xrange_outer[1] + alpha; double starty_n = yrange_outer[0] - alpha; double endy_n = yrange_outer[1] + alpha; /// ///< create regular grid auto grid_reg_approx = RegularGrid{LinearSpacing{startx_n, endx_n, nx_new}, LinearSpacing{starty_n, endy_n, ny_new}}; /** * Write mesh in gmsh object. * output under: * /atlas/src/tests/grid/ */ output::Gmsh("grid_regrot_approx_lonlat.msh", Config("coordinates", "lonlat")("info", true)) .write(Mesh{grid_reg_approx}); } CASE("var_ratio_rot_inv = 1.13") { ///< check over regular grid points stretched using new atlas object and check using look-up table ///< in this case add a rotation Config config; ///< definition of grid I have to rotate this auto proj_st_nr = make_var_ratio_projection(1.13); auto grid_nr = RegularGrid{grid::LinearSpacing{xrange_outer[0], xrange_outer[1], nx}, grid::LinearSpacing{yrange_outer[0], yrange_outer[1], ny}, proj_st_nr}; ///< definition of grid I have to rotate this auto proj_st = make_var_ratio_projection_rot(1.13, {-176., 40.}); auto grid = RegularGrid{grid::LinearSpacing{xrange_outer[0], xrange_outer[1], nx}, grid::LinearSpacing{yrange_outer[0], yrange_outer[1], ny}, proj_st}; ///< Test the inverse for (idx_t j = 0; j < grid.ny(); ++j) { for (idx_t i = 0; i < grid.nx(); ++i) { /// void doTaylorTest(const StructuredGrid& grid, double dmax, SKIP skip) { const auto& proj = grid.projection(); eckit::MD5 hash; for (int j = 0, jglo = 0; j < grid.ny(); j++) { for (int i = 0; i < grid.nx(j); i++, jglo++) { if (!skip(grid, i, j)) { double lonlat[2]; grid.lonlat(i, j, lonlat); auto jacA = proj.jacobian(PointLonLat(lonlat)); auto jacB = getJacobian(proj, PointLonLat(lonlat)); auto jacC = jacB * jacA.inverse() - Projection::Jacobian::identity(); hash.add(jacA[0][0]); hash.add(jacA[0][1]); hash.add(jacA[1][0]); hash.add(jacA[1][1]); double diff = jacC.norm(); if (diff > dmax) { printf("------------------\n"); printf(" i, j = %8d, %8d\n", i, j); printJacobian(jacA, "jacA"); printf("\n"); printJacobian(jacB, "jacB"); printf("\n"); printf("%12.4e\n", diff); printf("\n"); } EXPECT(diff < dmax); } } } Log::info() << "Jacobian checksum (not crossplatform) : " << hash.digest() << std::endl; } void doTaylorTest(const StructuredGrid& grid, double dmax) { auto noskip = [](const StructuredGrid& grid, int i, int j) { return false; }; doTaylorTest(grid, dmax, noskip); } CASE("test_rotated_schmidt") { auto projection = [] { util::Config config; config.set("type", "rotated_schmidt"); config.set("stretching_factor", 2.4); config.set("rotation_angle", 0.0); config.set("north_pole", {2.0, 46.7}); return Projection{config}; }(); auto jacobian = projection.jacobian(PointLonLat{0., 0.}); double tol = 1.e-10; EXPECT_APPROX_EQ(jacobian[0][0], 1.372570713035, tol); EXPECT_APPROX_EQ(jacobian[0][1], -0.045140586761, tol); EXPECT_APPROX_EQ(jacobian[1][0], 0.045110961287, tol); EXPECT_APPROX_EQ(jacobian[1][1], 1.371669903783, tol); doTaylorTest(Grid{"N16", projection}, 1e-3, // Avoid large jumps of pseudo-longitude (ie 359.5 -> 0) [](const StructuredGrid& grid, int i, int j) { return (i == 0) || (i == grid.nx(j) - 1) || (i == grid.nx(j) / 2); }); } CASE("test_lambert_conformal_conic") { const int Nx = 64, Ny = 64, Nux = 53, Nuy = 53; const double DxInMetres = 50000., DyInMetres = 50000.; const double LaDInDegrees = 46.2, Latin1InDegrees = 46.2, Latin2InDegrees = 46.2, LoVInDegrees = 2.0; const double XMinInMetres = -Nux / 2 * DxInMetres, YMinInMetres = -Nuy / 2 * DyInMetres; auto projection = [&] { util::Config config; config.set("type", "lambert_conformal_conic"); config.set("longitude0", LoVInDegrees); config.set("latitude0", LaDInDegrees); config.set("latitude1", Latin1InDegrees); config.set("latitude2", Latin2InDegrees); return Projection{config}; }(); auto jacobian = projection.jacobian(PointLonLat{0., 0.}); double tol = 1.e-5; EXPECT_APPROX_EQ(jacobian[0][0], 148530.135993682081, tol); EXPECT_APPROX_EQ(jacobian[0][1], 3742.887654051571, tol); EXPECT_APPROX_EQ(jacobian[1][0], -3742.887654051572, tol); EXPECT_APPROX_EQ(jacobian[1][1], 148530.135993682081, tol); util::Config grid; grid.set("type", "regional"); grid.set("dx", DxInMetres); grid.set("dy", DyInMetres); grid.set("xmin", XMinInMetres); grid.set("ymin", YMinInMetres); grid.set("nx", Nx); grid.set("ny", Ny); grid.set("projection", projection.spec()); doTaylorTest(Grid(grid), 1e-6); } CASE("test_lonlat") { doTaylorTest(StructuredGrid("N16"), 1e-9); } CASE("test_constructors") { Projection::Jacobian a = {}; Projection::Jacobian b = {1., 2., 3., 4.}; Projection::Jacobian c = {{5., 6.}, {7., 8.}}; Log::info() << "a =" << std::endl; Log::info() << a << std::endl; Log::info() << "b =" << std::endl; Log::info() << b << std::endl; Log::info() << "c =" << std::endl; Log::info() << c << std::endl; EXPECT_EQ(a[0][0], 0.); EXPECT_EQ(a[0][1], 0.); EXPECT_EQ(a[1][0], 0.); EXPECT_EQ(a[1][1], 0.); EXPECT_EQ(b[0][0], 1.); EXPECT_EQ(b[0][1], 2.); EXPECT_EQ(b[1][0], 3.); EXPECT_EQ(b[1][1], 4.); EXPECT_EQ(c[0][0], 5.); EXPECT_EQ(c[0][1], 6.); EXPECT_EQ(c[1][0], 7.); EXPECT_EQ(c[1][1], 8.); } CASE("test_multiplication") { const Projection::Jacobian A = {{0., -1.}, {1., 0.}}; const Point2 x = {2., 1.}; const auto Ax = A * x; const auto AinvAx = A.inverse() * Ax; const auto Atimes2 = A * 2.; Log::info() << "A =" << std::endl; Log::info() << A << std::endl; Log::info() << "2A =" << std::endl; Log::info() << Atimes2 << std::endl; Log::info() << "x =" << std::endl; Log::info() << x << std::endl; Log::info() << "Ax =" << std::endl; Log::info() << Ax << std::endl; Log::info() << "A^-1 Ax =" << std::endl; Log::info() << AinvAx << std::endl; EXPECT_EQ(Atimes2[0][0], 0.); EXPECT_EQ(Atimes2[0][1], -2.); EXPECT_EQ(Atimes2[1][0], 2.); EXPECT_EQ(Atimes2[1][1], 0.); EXPECT_EQ(Ax, Point2(-1., 2.)); EXPECT_EQ(AinvAx, x); } //----------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/projection/test_proj.cc0000664000175000017500000000316715160212070021517 0ustar alastairalastair/* * (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); } atlas-0.46.0/src/tests/projection/test_bounding_box.cc0000664000175000017500000002223415160212070023216 0ustar alastairalastair/* * (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 #include #include #include #include #include "eckit/geometry/Point2.h" #include "eckit/log/Plural.h" #include "eckit/types/FloatCompare.h" #include "atlas/domain.h" #include "atlas/domain/detail/GlobalDomain.h" // FIXME not included by atlas/domain.h #include "atlas/grid.h" #include "atlas/runtime/Log.h" #include "atlas/util/Point.h" #include "atlas/util/Rotation.h" #include "tests/AtlasTestEnvironment.h" namespace atlas { namespace test { //----------------------------------------------------------------------------- double normalise(double weird, double minimum) { double lon = weird; double GLOBE = 360.; while (lon < minimum) { lon += GLOBE; } while (lon >= minimum + GLOBE) { lon -= GLOBE; } return lon; } struct BoundingBox : public std::array { BoundingBox(double n, double w, double s, double e): std::array({n, w, s, e}) {} BoundingBox(): BoundingBox(90, 0, -90, 360) {} double north() const { return operator[](0); } double south() const { return operator[](2); } double west() const { return operator[](1); } double east() const { return operator[](3); } operator RectangularDomain() const { return {{{west(), east()}}, {{south(), north()}}}; } bool contains(const double& lat, const double& lon) const { return (lat <= north()) && (lat >= south()) && (normalise(lon, west()) <= east()); } bool contains(const BoundingBox& other) const { bool otherEmpty = !eckit::types::is_strictly_greater(other.north(), other.south()) || !eckit::types::is_strictly_greater(other.east(), other.west()); if (otherEmpty) { return contains(other.south(), other.west()); } // check for West/East range (if non-periodic), then other's corners if (east() - west() < other.east() - other.west() || east() < normalise(other.east(), west())) { return false; } return contains(other.north(), other.west()) && contains(other.north(), other.east()) && contains(other.south(), other.west()) && contains(other.south(), other.east()); } }; struct Rotation : std::array { Rotation(double lat, double lon): std::array({lon, lat}) {} double south_pole_latitude() const { return operator[](1); } double south_pole_longitude() const { return normalise(operator[](0), 0.); } BoundingBox rotate(const BoundingBox& box) const { util::Config config; config.set("type", "rotated_lonlat"); config.set("south_pole", std::vector({south_pole_longitude(), south_pole_latitude()})); atlas::Projection p(config); RectangularDomain before({box.west(), box.east()}, {box.south(), box.north()}); Domain after = p.lonlatBoundingBox(before); ATLAS_ASSERT(after); RectangularLonLatDomain r(after); ATLAS_ASSERT(r); return {r.north(), r.west(), r.south(), r.east()}; } }; using NiNj = std::array; struct test_poles_t { public: test_poles_t(const NiNj& ninj, const Rotation& rotation, const BoundingBox& bbox, bool includesNorthPole = false, bool includesSouthPole = false): ninj_(ninj), rotation_(rotation), bbox_(bbox), includesNorthPole_(includesNorthPole), includesSouthPole_(includesSouthPole) {} const NiNj ninj_; const Rotation rotation_; const BoundingBox bbox_; const bool includesNorthPole_; const bool includesSouthPole_; private: friend std::ostream& operator<<(std::ostream& out, const test_poles_t& test) { return (out << "test:" << "\n\t" << "NiNj = " << test.ninj_ << "\n\t" << "Rotation = " << test.rotation_ << "\n\t" << "BoundingBox = " << test.bbox_ << "\n\t" << "includesNorthPole? " << test.includesNorthPole_ << "\n\t" << "includesSouthPole? " << test.includesSouthPole_); } }; //----------------------------------------------------------------------------- CASE("MIR-282") { auto old = Log::info().precision(16); std::vector test_poles{ {NiNj{124, 118}, Rotation(-35., 0.), BoundingBox{12, -14.5, -17.25, 16.25}}, {NiNj{360, 181}, Rotation(-90., 0.), BoundingBox(), true, true}, {NiNj{81, 56}, Rotation(-75., 15.), BoundingBox{75.1, -35., 20., 45.}, true}, {NiNj{111, 86}, Rotation(-35., 15.), BoundingBox{40., -55., -45., 55.}, true}, {NiNj{91, 76}, Rotation(-30., -15.), BoundingBox{35., -40., -40., 50.}, true}, {NiNj{101, 81}, Rotation(-25., 0.), BoundingBox{40., -50., -40., 50.}, true}, {NiNj{56, 61}, Rotation(-15., 45.), BoundingBox{30., -50., -30., 5.}, true}, {NiNj{96, 91}, Rotation(0., 80.), BoundingBox{50., -65., -40., 30.}, true}, {NiNj{178, 143}, Rotation(-40., 10.), BoundingBox{22.7, -13.6, -5.9, 21.8}}, {NiNj{117, 79}, Rotation(-43., 10.), BoundingBox{3.4, -6.8, -4.4, 4.8}}, {NiNj{776, 492}, Rotation(-30., 0.), BoundingBox{18.1, -37.6, -31., 39.9}}, {NiNj{149, 105}, Rotation(-76., 14.), BoundingBox{72., -32., 20., 42.}}, {NiNj{240, 240}, Rotation(-30., -5.), BoundingBox{9.875, -15., -20., 14.875}}, {NiNj{13, 12}, Rotation(-15., 45.), BoundingBox{27.5, -46.5, -28, 1.5}, true}, {NiNj{321, 370}, Rotation(-15., 45.), BoundingBox{27.5, -46.5, -28, 1.5}, true}, {NiNj{202, 235}, Rotation(0., 130.), BoundingBox{32.75, -86.75, -37.75, -26.15}, false}, {NiNj{409, 309}, Rotation(-35., 15.), BoundingBox{36., -51., -41., 51.}, true}, {NiNj{171, 6}, Rotation(-35., 160.), BoundingBox{80., 30., 75., 200.}}, {NiNj{81, 11}, Rotation(30., -30.), BoundingBox{70., 120., 60., 200.}}, {NiNj{261, 66}, Rotation(45., -120.), BoundingBox{55., -120., -10., 140.}}, {NiNj{4, 4}, Rotation(50., 100.), BoundingBox{10., 70., -20., 100.}}, }; SECTION("MIR-282: rotated_ll covering North/South poles") { for (auto& test : test_poles) { Log::info() << '\n' << test << std::endl; const PointLonLat southPole(test.rotation_.south_pole_longitude(), test.rotation_.south_pole_latitude()); const util::Rotation r(southPole); // check bbox including poles (in the unrotated frame) PointLonLat NP{r.unrotate({0., 90.})}; PointLonLat SP{r.unrotate({0., -90.})}; bool includesNorthPole = test.bbox_.contains(NP.lat(), NP.lon()); bool includesSouthPole = test.bbox_.contains(SP.lat(), SP.lon()); Log::info() << "check:" << "\n\t" << "NP = " << NP << "\n\t" << "SP = " << SP << "\n\t" << "includesNorthPole? " << includesNorthPole << "\n\t" << "includesSouthPole? " << includesSouthPole << std::endl; EXPECT(includesNorthPole == test.includesNorthPole_); EXPECT(includesSouthPole == test.includesSouthPole_); } } SECTION("MIR-282: rotated_ll contained by cropping") { for (auto& test : test_poles) { Log::info() << '\n' << test << std::endl; double n = test.bbox_.north(); double s = test.bbox_.south(); double w = test.bbox_.west(); double e = test.bbox_.east(); RectangularDomain dom(test.bbox_); using grid::LinearSpacing; StructuredGrid::XSpace xspace(LinearSpacing(w, e, test.ninj_[0], !ZonalBandDomain(dom))); StructuredGrid::YSpace yspace(LinearSpacing(n, s, test.ninj_[1])); util::Config config; config.set("type", "rotated_lonlat"); config.set("south_pole", std::vector({test.rotation_.south_pole_longitude(), test.rotation_.south_pole_latitude()})); Projection rotation(config); Grid g = StructuredGrid(xspace, yspace, rotation, dom); ATLAS_ASSERT(g); BoundingBox crop(test.rotation_.rotate(test.bbox_)); Log::info() << "contained by cropping" << "\n\t " << test.bbox_ << "\n\t + " << test.rotation_ << "\n\t = " << crop << std::endl; for (PointLonLat p : g.lonlat()) { EXPECT(crop.contains(p.lat(), p.lon())); } Log::info() << "checked " << eckit::Plural(g.size(), "point") << std::endl; } } Log::info().precision(old); } //----------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/projection/test_rotation.cc0000664000175000017500000002543415160212070022405 0ustar alastairalastair/* * (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 "atlas/runtime/Log.h" #include "atlas/util/Config.h" #include "atlas/util/Constants.h" #include "atlas/util/Rotation.h" #include "tests/AtlasTestEnvironment.h" using atlas::util::Config; using atlas::util::Rotation; namespace atlas { namespace test { //----------------------------------------------------------------------------- constexpr double eps = 1.e-5; constexpr double d2r = atlas::util::Constants::degreesToRadians(); constexpr double r2d = atlas::util::Constants::radiansToDegrees(); bool equivalent(const PointLonLat& p1, const PointLonLat& p2) { using eckit::types::is_approximately_equal; auto f = [=](double lon) { return 10. + std::cos(lon * d2r); }; return is_approximately_equal(p1.lat(), p2.lat(), eps) && (is_approximately_equal(std::abs(p2.lat()), 90., eps) || is_approximately_equal(f(p1.lon()), f(p2.lon()), eps)); } #define EXPECT_EQUIVALENT(p1, p2) EXPECT(equivalent(p1, p2)) //----------------------------------------------------------------------------- class MagicsRotation { // For reference, this what magics uses, it appears as if it originated from // fortran code // Strangely the definition of rotate and unrotate are switched. public: MagicsRotation(const PointLonLat& south_pole): south_pole_(south_pole) {} PointLonLat rotate(const PointLonLat& point) const { return magics_unrotate(point); /// Switch meaning !!! } PointLonLat unrotate(const PointLonLat& point) const { return magics_rotate(point); /// Swich meaning !!! } private: PointLonLat south_pole_; PointLonLat magics_rotate(const PointLonLat& point) const { double lat_y = point.lat(); double lon_x = point.lon(); double sin_south_pole_lat = std::sin(d2r * (south_pole_.lat() + 90.)); double cos_south_pole_lat = std::cos(d2r * (south_pole_.lat() + 90.)); double ZXMXC = d2r * (lon_x - south_pole_.lon()); double sin_lon_decr_sp = std::sin(ZXMXC); double cos_lon_decr_sp = std::cos(ZXMXC); double sin_lat = std::sin(d2r * lat_y); double cos_lat = std::cos(d2r * lat_y); double ZSYROT = cos_south_pole_lat * sin_lat - sin_south_pole_lat * cos_lat * cos_lon_decr_sp; ZSYROT = std::max(std::min(ZSYROT, +1.0), -1.0); double PYROT = std::asin(ZSYROT) * r2d; double ZCYROT = std::cos(PYROT * d2r); double ZCXROT = (cos_south_pole_lat * cos_lat * cos_lon_decr_sp + sin_south_pole_lat * sin_lat) / ZCYROT; ZCXROT = std::max(std::min(ZCXROT, +1.0), -1.0); double ZSXROT = cos_lat * sin_lon_decr_sp / ZCYROT; double PXROT = std::acos(ZCXROT) * r2d; if (ZSXROT < 0.0) { PXROT = -PXROT; } return PointLonLat(PXROT, PYROT); } PointLonLat magics_unrotate(const PointLonLat& point) const { double lat_y = point.lat(); double lon_x = point.lon(); double sin_south_pole_lat = std::sin(d2r * (south_pole_.lat() + 90.)); double cos_south_pole_lat = std::cos(d2r * (south_pole_.lat() + 90.)); double cos_lon = std::cos(d2r * lon_x); double sin_lat = std::sin(d2r * lat_y); double cos_lat = std::cos(d2r * lat_y); double ZSYREG = cos_south_pole_lat * sin_lat + sin_south_pole_lat * cos_lat * cos_lon; ZSYREG = std::max(std::min(ZSYREG, +1.0), -1.0); double PYREG = std::asin(ZSYREG) * r2d; double ZCYREG = std::cos(PYREG * d2r); double ZCXMXC = (cos_south_pole_lat * cos_lat * cos_lon - sin_south_pole_lat * sin_lat) / ZCYREG; ZCXMXC = std::max(std::min(ZCXMXC, +1.0), -1.0); double ZSXMXC = cos_lat * sin_lat / ZCYREG; double ZXMXC = std::acos(ZCXMXC) * r2d; if (ZSXMXC < 0.0) { ZXMXC = -ZXMXC; } double PXREG = ZXMXC + south_pole_.lon(); return PointLonLat(PXREG, PYREG); } }; //----------------------------------------------------------------------------- CASE("test_rotation_angle") { const int nx = 12, ny = 6; Rotation rot(Config("rotation_angle", 180.) | Config("north_pole", std::vector{2.0, 46.7})); const PointLonLat ref[] = { {-178.00000,-46.70000}, {-178.00000,-76.70000}, {2.00000,-73.30000}, {2.00000,-43.30000}, {2.00000,-13.30000}, {2.00000,16.70000}, {2.00000,46.70000}, {-178.00000,-46.70000}, {140.11755,-68.00825}, {66.89106,-61.43199}, {40.42536,-36.43683}, {27.97634,-8.65459}, {17.37657,19.46929}, {2.00000,46.70000}, {-178.00000,-46.70000}, {135.57504,-53.29508}, {94.12083,-41.36507}, {69.20884,-20.05422}, {50.73654,3.83700}, {31.16557,27.31067}, {2.00000,46.70000}, {-178.00000,-46.70000}, {141.90794,-39.07002}, {113.60146,-21.33906}, {92.00000,0.00000}, {70.39854,21.33906}, {42.09206,39.07002}, {2.00000,46.70000}, {-178.00000,-46.70000}, {152.83443,-27.31067}, {133.26346,-3.83700}, {114.79116,20.05422}, {89.87917,41.36507}, {48.42496,53.29508}, {2.00000,46.70000}, {-178.00000,-46.70000}, {166.62343,-19.46929}, {156.02366,8.65459}, {143.57464,36.43683}, {117.10894,61.43199}, {43.88245,68.00825}, {2.00000,46.70000}, {-178.00000,-46.70000}, {-178.00000,-16.70000}, {-178.00000,13.30000}, {-178.00000,43.30000}, {-178.00000,73.30000}, {2.00000,76.70000}, {2.00000,46.70000}, {-178.00000,-46.70000}, {-162.62343,-19.46929}, {-152.02366,8.65459}, {-139.57464,36.43683}, {-113.10894,61.43199}, {-39.88245,68.00825}, {2.00000,46.70000}, {-178.00000,-46.70000}, {-148.83443,-27.31067}, {-129.26346,-3.83700}, {-110.79116,20.05422}, {-85.87917,41.36507}, {-44.42496,53.29508}, {2.00000,46.70000}, {-178.00000,-46.70000}, {-137.90794,-39.07002}, {-109.60146,-21.33906}, {-88.00000,0.00000}, {-66.39854,21.33906}, {-38.09206,39.07002}, {2.00000,46.70000}, {-178.00000,-46.70000}, {-131.57504,-53.29508}, {-90.12083,-41.36507}, {-65.20884,-20.05422}, {-46.73654,3.83700}, {-27.16557,27.31067}, {2.00000,46.70000}, {-178.00000,-46.70000}, {-136.11755,-68.00825}, {-62.89106,-61.43199}, {-36.42536,-36.43683}, {-23.97634,-8.65459}, {-13.37657,19.46929}, {2.00000,46.70000}, }; for (int i = 0, jglo = 0; i < nx; i++) { for (int j = 0; j < ny + 1; j++, jglo++) { double lon = static_cast(i) * 360. / static_cast(nx); double lat = static_cast(j - ny / 2) * 90. / static_cast(ny / 2); PointLonLat p0(lon, lat); PointLonLat p1 = rot.rotate(p0); PointLonLat p2 = rot.unrotate(p1); EXPECT(equivalent(p0, p2)); EXPECT(equivalent(p1, ref[jglo])); } } } CASE("test_rotation_construction") { static const PointLonLat SP{0., -90.}; static const PointLonLat NP{180., 90.}; std::vector rotation_poles = {SP, NP, {0., -90.1}, {0., 90.1}}; for (auto& p : rotation_poles) { Rotation s(Config("south_pole", std::vector{p.lon(), p.lat()})); Log::info() << "rotate_south_pole=" << s << std::endl; EXPECT(s.rotated() == (p != SP)); Rotation n(Config("north_pole", std::vector{p.lon(), p.lat()})); Log::info() << "rotate_north_pole=" << n << std::endl; EXPECT(n.rotated() == (p != NP)); } } CASE("test_rotation") { Config config; config.set("north_pole", std::vector{-176, 40}); Rotation rotation(config); MagicsRotation magics(rotation.southPole()); Log::info() << rotation << std::endl; EXPECT(rotation.rotated()); PointLonLat p, r; p = {0., 90.}; r = {-176., 40.}; EXPECT_EQUIVALENT(rotation.rotate(p), r); EXPECT_EQUIVALENT(magics.rotate(p), r); EXPECT_EQUIVALENT(rotation.unrotate(r), p); EXPECT_EQUIVALENT(magics.unrotate(r), p); p = {0., 0.}; r = {4., 50.}; EXPECT_EQUIVALENT(rotation.rotate(p), r); EXPECT_EQUIVALENT(magics.rotate(p), r); EXPECT_EQUIVALENT(rotation.unrotate(r), p); EXPECT_EQUIVALENT(magics.unrotate(r), p); p = {-180., 45.}; r = {-176., -5.}; EXPECT_EQUIVALENT(rotation.rotate(p), r); EXPECT_EQUIVALENT(magics.rotate(p), r); EXPECT_EQUIVALENT(rotation.unrotate(r), p); EXPECT_EQUIVALENT(magics.unrotate(r), p); } CASE("test_no_rotation") { Config config; Rotation rotation(config); MagicsRotation magics(rotation.southPole()); Log::info() << rotation << std::endl; EXPECT(not rotation.rotated()); PointLonLat p, r; p = {0., 90.}; r = p; EXPECT_EQUIVALENT(rotation.rotate(p), r); EXPECT_EQUIVALENT(magics.rotate(p), r); EXPECT_EQUIVALENT(rotation.unrotate(r), p); EXPECT_EQUIVALENT(magics.unrotate(r), p); p = {0., 0.}; r = p; EXPECT_EQUIVALENT(rotation.rotate(p), r); EXPECT_EQUIVALENT(magics.rotate(p), r); EXPECT_EQUIVALENT(rotation.unrotate(r), p); EXPECT_EQUIVALENT(magics.unrotate(r), p); p = {-180., 45.}; r = p; EXPECT_EQUIVALENT(rotation.rotate(p), r); EXPECT_EQUIVALENT(magics.rotate(p), r); EXPECT_EQUIVALENT(rotation.unrotate(r), p); EXPECT_EQUIVALENT(magics.unrotate(r), p); } CASE("test_rotation_angle_only") { Config config; config.set("rotation_angle", -180.); Rotation rotation(config); MagicsRotation magics(rotation.southPole()); Log::info() << rotation << std::endl; EXPECT(rotation.rotated()); PointLonLat p, r; p = {0., 90.}; r = {-180., 90}; EXPECT_EQUIVALENT(rotation.rotate(p), r); EXPECT_EQUIVALENT(rotation.unrotate(r), p); p = {0., 0.}; r = {-180., 0.}; EXPECT_EQUIVALENT(rotation.rotate(p), r); EXPECT_EQUIVALENT(rotation.unrotate(r), p); p = {270., 25.}; r = {90., 25.}; EXPECT_EQUIVALENT(rotation.rotate(p), r); EXPECT_EQUIVALENT(rotation.unrotate(r), p); p = {-180., 45.}; r = {-360., 45.}; EXPECT_EQUIVALENT(rotation.rotate(p), r); EXPECT_EQUIVALENT(rotation.unrotate(r), p); } //----------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/projection/CMakeLists.txt0000664000175000017500000000277315160212070021741 0ustar alastairalastair# (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. foreach(test test_bounding_box test_projection_LAEA test_rotation ) ecbuild_add_test( TARGET atlas_${test} SOURCES ${test}.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) endforeach() ecbuild_add_test( TARGET atlas_test_jacobian SOURCES test_jacobian.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) if(atlas_HAVE_PROJ) ecbuild_add_test( TARGET atlas_test_proj SOURCES test_proj.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) endif() if( HAVE_FCTEST ) add_fctest( TARGET atlas_fctest_projection SOURCES fctest_projection.F90 LINKER_LANGUAGE Fortran LIBS atlas_f ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) endif() ecbuild_add_test( TARGET atlas_test_projection_variable_resolution SOURCES test_projection_variable_resolution.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} CONDITION atlas_HAVE_ATLAS_FUNCTIONSPACE ) ecbuild_add_test( TARGET atlas_test_cubedsphere_projection MPI 6 CONDITION eckit_HAVE_MPI AND MPI_SLOTS GREATER_EQUAL 6 AND atlas_HAVE_ATLAS_FUNCTIONSPACE SOURCES test_cubedsphere_projection.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) atlas-0.46.0/src/tests/projection/fctest_projection.F900000664000175000017500000001167215160212070023203 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. ! This File contains Unit Tests for testing the ! C++ / Fortran Interfaces to the State Datastructure ! @author Willem Deconinck #include "fckit/fctest.h" ! ----------------------------------------------------------------------------- module projection_fixture use atlas_module end module ! ----------------------------------------------------------------------------- TESTSUITE_WITH_FIXTURE(fctest_projection,projection_fixture) ! ----------------------------------------------------------------------------- TESTSUITE_INIT call atlas_library%initialise() END_TESTSUITE_INIT ! ----------------------------------------------------------------------------- TESTSUITE_FINALIZE call atlas_library%finalise() END_TESTSUITE_FINALIZE ! ----------------------------------------------------------------------------- TEST( test_xy2lonlat ) type(atlas_Projection) :: projection type(atlas_Config) :: config real(dp) :: x, y, lon, lat config = atlas_Config() call config%set("type","mercator") call config%set("latitude1",45.0_dp) projection = atlas_Projection(config) x = 5000.0e3_dp y = 5000.0e3_dp call projection%xy2lonlat(x, y, lon, lat) FCTEST_CHECK_CLOSE( lon, 63.589_dp, 1.0e-3_dp ) FCTEST_CHECK_CLOSE( lat, 53.514_dp, 1.0e-3_dp ) call config%final() call projection%final() END_TEST TEST( test_lonlat2xy ) type(atlas_Projection) :: projection type(atlas_Config) :: config real(dp) :: lon, lat, x, y config = atlas_Config() call config%set("type","mercator") call config%set("latitude1",45.0_dp) projection = atlas_Projection(config) lon = 12.0_dp lat = 38.0_dp call projection%lonlat2xy(lon, lat, x, y) FCTEST_CHECK_CLOSE( x, 943554.154_dp, 1.0e-3_dp ) FCTEST_CHECK_CLOSE( y, 3234635.895_dp, 1.0e-3_dp ) call config%final() call projection%final() END_TEST ! ----------------------------------------------------------------------------- TEST( test_rotated_schmidt_config ) type(atlas_Projection) :: projection type(atlas_Config) :: config config = atlas_Config() call config%set("type","rotated_schmidt") call config%set("stretching_factor",2.0_dp) call config%set("rotation_angle", 180.0_dp) call config%set("north_pole", [2.0_dp,46.7_dp] ) projection = atlas_Projection(config) FCTEST_CHECK_EQUAL( projection%type(), "rotated_schmidt" ) FCTEST_CHECK_EQUAL( projection%hash(), "3a39e0635b7d0a45f684696ca89825e6" ) call config%final() call projection%final() END_TEST TEST( test_rotated_schmidt_specific_constructor ) type(atlas_Projection) :: projection projection = atlas_RotatedSchmidtProjection( stretching_factor=2.0_dp, & north_pole=[2.0_dp,46.7_dp], rotation_angle=180.0_dp) FCTEST_CHECK_EQUAL( projection%type(), "rotated_schmidt" ) FCTEST_CHECK_EQUAL( projection%hash(), "3a39e0635b7d0a45f684696ca89825e6" ) call projection%final() END_TEST ! ----------------------------------------------------------------------------- TEST( test_lambert_conformal_conic_config ) type(atlas_Projection) :: projection type(atlas_Config) :: config config = atlas_Config() call config%set("type","lambert_conformal_conic") call config%set("longitude0",4.0_dp) call config%set("latitude0", 50.0_dp) projection = atlas_Projection(config) FCTEST_CHECK_EQUAL( projection%type(), "lambert_conformal_conic" ) FCTEST_CHECK_EQUAL( projection%hash(), "47244ee950cf8357763a9b5b871b52ab" ) call config%final() call projection%final() END_TEST TEST( test_lambert_conformal_conic_specific_constructor ) type(atlas_Projection) :: projection projection = atlas_LambertConformalConicProjection(4.0_dp,50._dp) FCTEST_CHECK_EQUAL( projection%type(), "lambert_conformal_conic" ) FCTEST_CHECK_EQUAL( projection%hash(), "47244ee950cf8357763a9b5b871b52ab" ) call projection%final() END_TEST ! ----------------------------------------------------------------------------- TEST( test_rotated_lonlat_config ) type(atlas_Projection) :: projection type(atlas_Config) :: config config = atlas_Config() call config%set("type","rotated_lonlat") call config%set("north_pole", [2.0_dp,46.7_dp] ) call config%set("rotation_angle", 180.0_dp) projection = atlas_Projection(config) FCTEST_CHECK_EQUAL( projection%type(), "rotated_lonlat" ) FCTEST_CHECK_EQUAL( projection%hash(), "79586cfbc8145cdef1a25d075a9ae07e" ) call config%final() call projection%final() END_TEST TEST( test_rotated_lonlat_specific_constructor ) type(atlas_Projection) :: projection projection = atlas_RotatedLonLatProjection([2.0_dp,46.7_dp],180._dp) FCTEST_CHECK_EQUAL( projection%type(), "rotated_lonlat" ) FCTEST_CHECK_EQUAL( projection%hash(), "79586cfbc8145cdef1a25d075a9ae07e" ) call projection%final() END_TEST ! ----------------------------------------------------------------------------- END_TESTSUITE atlas-0.46.0/src/tests/projection/test_cubedsphere_projection.cc0000664000175000017500000002500415160212070025264 0ustar alastairalastair/* * (C) Crown Copyright 2022 Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ #include "atlas/array/MakeView.h" #include "atlas/field/FieldSet.h" #include "atlas/functionspace/NodeColumns.h" #include "atlas/grid.h" #include "atlas/grid/CubedSphereGrid.h" #include "atlas/mesh.h" #include "atlas/meshgenerator.h" #include "atlas/output/Gmsh.h" #include "atlas/projection/detail/CubedSphereProjectionBase.h" #include "atlas/util/Constants.h" #include "tests/AtlasTestEnvironment.h" namespace atlas { namespace test { // Small number relative to 360. constexpr double epsilon = 2. * std::numeric_limits::epsilon() * 360.; void testProjection(const std::string& gridType, const std::string& meshType, const std::string& outputID) { // Create grid. const auto grid = CubedSphereGrid(gridType); // Get projection. const auto& csProjection = grid.cubedSphereProjection(); // Output tile centres and Jacobains. for (size_t i = 0; i < 6; ++i) { Log::info() << "Tile " << i << std::endl << "Centre:" << std::endl << csProjection.getCubedSphereTiles().tileCentre(i) << std::endl << "Jacobian:" << std::endl << csProjection.getCubedSphereTiles().tileJacobian(i) << std::endl << std::endl; } // Create mesh. auto mesh = MeshGenerator(meshType, util::Config("halo", 3)).generate(grid); // Output mesh. const auto gmshConfig = util::Config("coordinates", "xy") | util::Config("ghost", true) | util::Config("info", true); auto gmsh = output::Gmsh(outputID + "_before.msh", gmshConfig); gmsh.write(mesh); // "Hack" mesh xy coordinates. auto xyView = array::make_view(mesh.nodes().xy()); const auto tijView = array::make_view(mesh.nodes().field("tij")); const auto lonlatView = array::make_view(mesh.nodes().lonlat()); for (idx_t i = 0; i < mesh.nodes().size(); ++i) { const auto t = tijView(i, 0); const auto xy = PointXY(xyView(i, XX), xyView(i, YY)); const auto alphabeta = csProjection.xy2alphabeta(xy, t); // Inverse function is degenerate when abs(alpha) == abs(beta) and // abs(alpha) > 45. const bool degenerate = std::abs(alphabeta[0]) > 45. && approx_eq(std::abs(alphabeta[0]), std::abs(alphabeta[1])); // Check inverse function. if (!degenerate) { const auto newXy = csProjection.alphabeta2xy(alphabeta, t); EXPECT_APPROX_EQ(xy[XX], newXy[XX], epsilon); EXPECT_APPROX_EQ(xy[YY], newXy[YY], epsilon); } // overwrite mesh xy field. xyView(i, XX) = alphabeta[0]; xyView(i, YY) = alphabeta[1]; // Check lonlat2alphabeta methods. const auto lonlat = csProjection.alphabeta2lonlat(alphabeta, t); const auto newAlphabeta = csProjection.lonlat2alphabeta(lonlat, t); EXPECT_APPROX_EQ(alphabeta[0], newAlphabeta[0], epsilon); EXPECT_APPROX_EQ(alphabeta[1], newAlphabeta[1], epsilon); EXPECT_APPROX_EQ(lonlat[0], lonlatView(i, 0), epsilon); EXPECT_APPROX_EQ(lonlat[1], lonlatView(i, 1), epsilon); } // Output mesh updated mesh. gmsh = output::Gmsh(outputID + "_after.msh", gmshConfig); gmsh.write(mesh); } void testJacobian(const std::string& gridType, const std::string& meshType, const std::string& outputID) { // Create grid. const auto grid = CubedSphereGrid(gridType); // Get projection. const auto& csProjection = grid.cubedSphereProjection(); // Set xy0 values on different tiles. const auto xy0 = std::array{PointXY{10., -35}, PointXY{100., -35}, PointXY{190., -35}, PointXY{280., -35}, PointXY{10., 55}, PointXY{10., -125}}; // Get equivalent lonlat values. const auto lonlat0 = std::array{csProjection.lonlat(xy0[0]), csProjection.lonlat(xy0[1]), csProjection.lonlat(xy0[2]), csProjection.lonlat(xy0[3]), csProjection.lonlat(xy0[4]), csProjection.lonlat(xy0[5])}; // Set lonlat step size. const auto hLonlat0 = PointLonLat{5., 10.}; // Perform first order extrapolation test on each tile. for (size_t t = 0; t < 6; ++t) { const auto jac = csProjection.jacobian(lonlat0[t]); double dxyOld{}; for (int i = 0; i < 5; ++i) { // Sequentially halve lonlat increment. const auto hLonlat = hLonlat0 * std::pow(0.5, i); // Make a first order estimate of xy using Jacobian. const auto xy = xy0[t] + jac * hLonlat; // Get true xy values. const auto xyTarget = csProjection.xy(lonlat0[t] + hLonlat); double dxy = Point2::distance(xyTarget, xy); // Error should reduce roughly by a factor of four every iteration. if (i > 0) { const auto tol = 0.02; EXPECT(dxy / dxyOld < 0.25 + tol); } dxyOld = dxy; } } // Create mesh. auto mesh = MeshGenerator(meshType).generate(grid); // Create functionspace. auto functionspace = functionspace::NodeColumns(mesh); // Create fields for each component of Jacobian. auto fieldSet = FieldSet{}; fieldSet.add(functionspace.createField(option::name("dx_by_dlambda"))); fieldSet.add(functionspace.createField(option::name("dx_by_dphi"))); fieldSet.add(functionspace.createField(option::name("dy_by_dlambda"))); fieldSet.add(functionspace.createField(option::name("dy_by_dphi"))); auto dx_dlambda = array::make_view(fieldSet["dx_by_dlambda"]); auto dx_dphi = array::make_view(fieldSet["dx_by_dphi"]); auto dy_dlambda = array::make_view(fieldSet["dy_by_dlambda"]); auto dy_dphi = array::make_view(fieldSet["dy_by_dphi"]); const auto lonlat = array::make_view(functionspace.lonlat()); for (idx_t i = 0; i < functionspace.size(); ++i) { const auto ll = PointLonLat(lonlat(i, LON), lonlat(i, LAT)); const auto jac = csProjection.jacobian(ll); dx_dlambda(i) = jac.dx_dlon(); dx_dphi(i) = jac.dx_dlat(); dy_dlambda(i) = jac.dy_dlon(); dy_dphi(i) = jac.dy_dlat(); } // Output mesh. const auto gmshConfig = util::Config("coordinates", "xy") | util::Config("ghost", true) | util::Config("info", true); auto gmsh = output::Gmsh(outputID + "_jacobian.msh", gmshConfig); gmsh.write(mesh); gmsh.write(fieldSet, functionspace); } CASE("cubedsphere_xy_to_alphabeta_test") { testProjection("CS-LFR-12", "cubedsphere", "cs_primal"); testProjection("CS-LFR-12", "cubedsphere_dual", "cs_dual"); } CASE("cubedsphere_jacobian_test") { testJacobian("CS-LFR-24", "cubedsphere", "cs_primal"); testJacobian("CS-LFR-24", "cubedsphere_dual", "cs_dual"); } CASE("test_tiles") { int resolution(2); Grid gEA{"CS-EA-L-" + std::to_string(resolution)}; Grid gLFR{"CS-LFR-L-" + std::to_string(resolution)}; using util::Constants; util::Config params; grid::CubedSphereTiles f("cubedsphere_fv3"); grid::CubedSphereTiles l("cubedsphere_lfric"); double cd[2]; idx_t jn(0); std::array EAOffset{0, resolution * resolution + 1, 2 * resolution * resolution + 2, 3 * resolution * resolution + 2, 4 * resolution * resolution + 2, 5 * resolution * resolution + 2, 6 * resolution * resolution + 2}; for (auto crd : gEA.lonlat()) { atlas::PointLonLat pointLonLat = crd; cd[LON] = pointLonLat.lon(); cd[LAT] = pointLonLat.lat(); int t = f.indexFromLonLat(cd); gEA.projection().lonlat2xy(crd); cd[LON] = crd.lon(); cd[LAT] = crd.lat(); int t2 = f.indexFromXY(cd); for (std::size_t i = 0; i < 6; ++i) { if (jn >= EAOffset[i] && jn < EAOffset[i + 1]) { EXPECT(t == static_cast(i)); EXPECT(t2 == static_cast(i)); } } ++jn; } std::array LFRicOffset{0, resolution * resolution, 2 * resolution * resolution, 3 * resolution * resolution, 4 * resolution * resolution, 4 * resolution * resolution + (resolution + 1) * (resolution + 1), 6 * resolution * resolution + 2}; jn = 0; for (auto crd : gLFR.lonlat()) { atlas::PointLonLat pointLonLat = crd; cd[LON] = pointLonLat.lon(); cd[LAT] = pointLonLat.lat(); int t3 = l.indexFromLonLat(cd); gLFR.projection().lonlat2xy(crd); cd[LON] = crd.lon(); cd[LAT] = crd.lat(); int t4 = l.indexFromXY(cd); for (std::size_t i = 0; i < 6; ++i) { if (jn >= LFRicOffset[i] && jn < LFRicOffset[i + 1]) { EXPECT(t3 == static_cast(i)); EXPECT(t4 == static_cast(i)); } } ++jn; } } CASE("test_projection_cubedsphere_xy_latlon") { int resolution(12); std::vector grid_names{"CS-EA-L-" + std::to_string(resolution), "CS-ED-L-" + std::to_string(resolution)}; for (std::string& s : grid_names) { Grid g{s}; for (auto crd : g.lonlat()) { Point2 lonlat{crd}; g->projection().lonlat2xy(crd); g->projection().xy2lonlat(crd); // except for point lonlat (90,82.5) on compiler pgc++ // we have a maximum error tolerance of 1e-11 EXPECT_APPROX_EQ(lonlat, crd, 1e-6); } for (auto crd : g.xy()) { Point2 xy{crd}; g->projection().xy2lonlat(crd); g->projection().lonlat2xy(crd); EXPECT_APPROX_EQ(xy, crd, 1e-6); } } } } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/util/0000775000175000017500000000000015160212070015771 5ustar alastairalastairatlas-0.46.0/src/tests/util/test_flags.cc0000664000175000017500000000253015160212070020433 0ustar alastairalastair/* * (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/util/Bitflags.h" #include "tests/AtlasTestEnvironment.h" using atlas::util::Bitflags; namespace atlas { namespace test { //----------------------------------------------------------------------------- CASE("test_Flags") { int b1 = (1 << 0); int b2 = (1 << 1); int b3 = (1 << 2); int b4 = (1 << 3); int bits = b1 | b2; std::cout << Bitflags::bitstr(bits) << std::endl; EXPECT(bits == 3); EXPECT(Bitflags::check(bits, b1)); EXPECT(Bitflags::check(bits, b2)); EXPECT(!Bitflags::check(bits, b3)); EXPECT(Bitflags::check_all(bits, b1 | b2)); EXPECT(!Bitflags::check_all(bits, b1 | b3)); EXPECT(Bitflags::check_any(bits, b1 | b3)); EXPECT(!Bitflags::check_any(bits, b3 | b4)); } //----------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/util/test_util.cc0000664000175000017500000000262715160212070020323 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "atlas/util/MicroDeg.h" #include "tests/AtlasTestEnvironment.h" using namespace atlas::util; namespace atlas { namespace test { //----------------------------------------------------------------------------- CASE("convert to microdegrees") { EXPECT_EQ(microdeg(-179.999999999999), -180000000); EXPECT_EQ(microdeg(+179.999999999999), +180000000); EXPECT_EQ(microdeg(-180.000000000001), -180000000); EXPECT_EQ(microdeg(+180.000000000001), +180000000); EXPECT_EQ(microdeg(-0.000000000001), 0); EXPECT_EQ(microdeg(+0.000000000001), 0); EXPECT_EQ(microdeg(-0.000001), -1); EXPECT_EQ(microdeg(+0.000001), +1); EXPECT_EQ(microdeg(-0.000001499999), -1); EXPECT_EQ(microdeg(+0.000001499999), +1); EXPECT_EQ(microdeg(-0.000001500001), -2); EXPECT_EQ(microdeg(+0.000001500001), +2); } //----------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/util/test_point.cc0000664000175000017500000000377215160212070020501 0ustar alastairalastair/* * (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/util/Point.h" #include "tests/AtlasTestEnvironment.h" using atlas::util::Earth; namespace atlas { namespace test { CASE("test PointLonLat normalisation") { // latitude at Valparaíso-Shanghai mid-point PointLonLat p; p = PointLonLat(-71.6, -33.); p.normalise(); EXPECT(is_approximately_equal(p.lon(), -71.6 + 360.)); p.normalise(-180., 180.); EXPECT(is_approximately_equal(p.lon(), -71.6)); p = PointLonLat(121.8, 31.4); p.normalise(-180., 180.); EXPECT(is_approximately_equal(p.lon(), 121.8)); p = PointLonLat(181., 31.4); p.normalise(-180., 180.); EXPECT(is_approximately_equal(p.lon(), 181. - 360.)); p = PointLonLat(180., 31.4); p.normalise(-180., 180.); EXPECT(is_approximately_equal(p.lon(), 180.)); p = PointLonLat(180., 31.4); p.normalise(-180); EXPECT(is_approximately_equal(p.lon(), -180.)); p = PointLonLat(-180., 31.4); p.normalise(-180., 180.); EXPECT(is_approximately_equal(p.lon(), -180.)); p = PointLonLat(-180., 31.4); p.normalise(-180.); EXPECT(is_approximately_equal(p.lon(), -180.)); } CASE("test output vector") { // clang-format off std::vector points = { {0.,1.}, {0.,2.}, {0.,3.}, {1.,1.}, {1.,2.}, {1.,3.}, {2.,1.}, {3.,2.}, {4.,3.}, }; // clang-format on Log::info() << "points = " << points << std::endl; } } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/util/test_pack_vector_fields.cc0000664000175000017500000002636615160212070023202 0ustar alastairalastair/* * (C) Crown Copyright 2024 Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ #include #include #include "atlas/array.h" #include "atlas/array_fwd.h" #include "atlas/field.h" #include "atlas/functionspace.h" #include "atlas/functionspace/NodeColumns.h" #include "atlas/functionspace/PointCloud.h" #include "atlas/grid.h" #include "atlas/meshgenerator.h" #include "atlas/option.h" #include "atlas/option/Options.h" #include "atlas/util/Config.h" #include "atlas/util/PackVectorFields.h" #include "eckit/testing/Test.h" #include "tests/AtlasTestEnvironment.h" namespace atlas { namespace test { FieldSet setFields(const FunctionSpace& functionSpace, const std::vector& fieldConfigs) { auto fields = FieldSet{}; // Set unique values to all field elements. auto value = 0; for (const auto& fieldConfig : fieldConfigs) { auto field = fields.add(functionSpace.createField(fieldConfig)); for (auto arrayIdx = size_t{0}; arrayIdx < field.size(); arrayIdx++) { field->data()[arrayIdx] = value++; } field.metadata().set("comment", "This field is made with love."); } return fields; } FieldSet createOrderedTestFields() { const auto grid = Grid("O16"); const auto functionSpace = functionspace::StructuredColumns(grid); // Note: vector components 0 and 1 are contiguous in field set. auto fieldConfigs = std::vector{}; fieldConfigs.push_back(option::name("scalar") | option::levels(1) | option::datatype(DataType::kind())); fieldConfigs.push_back(option::name("vector_component_0") | option::levels(1) | option::datatype(DataType::kind()) | option::vector_component("vector", 0)); fieldConfigs.push_back(option::name("vector_component_1") | option::levels(1) | option::datatype(DataType::kind()) | option::vector_component("vector", 1)); return setFields(functionSpace, fieldConfigs); } FieldSet createUnorderedTestFields() { const auto grid = Grid("O16"); const auto functionSpace = functionspace::StructuredColumns(grid); // Note: vector components 0 and 1 are not contiguous in field set. auto fieldConfigs = std::vector{}; fieldConfigs.push_back(option::name("vector_component_0") | option::datatype(DataType::kind()) | option::vector_component("vector", 0)); fieldConfigs.push_back(option::name("scalar") | option::datatype(DataType::kind())); fieldConfigs.push_back(option::name("vector_component_1") | option::datatype(DataType::kind()) | option::vector_component("vector", 1)); return setFields(functionSpace, fieldConfigs); } FieldSet createInterleavedVectorFields() { const auto grid = Grid("O16"); const auto functionSpace = functionspace::PointCloud(grid); // Note: vector components 0 and 1 are not contiguous in field set. auto fieldConfigs = std::vector{}; fieldConfigs.push_back(option::name("vector_0_component_0") | option::datatype(DataType::kind()) | option::vector_component("vector_0", 0)); fieldConfigs.push_back(option::name("vector_1_component_1") | option::datatype(DataType::kind()) | option::vector_component("vector_1", 1)); fieldConfigs.push_back(option::name("vector_0_component_1") | option::datatype(DataType::kind()) | option::vector_component("vector_0", 1)); fieldConfigs.push_back(option::name("vector_1_component_0") | option::datatype(DataType::kind()) | option::vector_component("vector_1", 0)); auto fields = setFields(functionSpace, fieldConfigs); array::make_view(fields["vector_0_component_0"]).assign(0.); array::make_view(fields["vector_0_component_1"]).assign(1.); array::make_view(fields["vector_1_component_0"]).assign(2.); array::make_view(fields["vector_1_component_1"]).assign(3.); return fields; } FieldSet createInconsistentRankFields() { const auto grid = Grid("O16"); const auto functionSpace = functionspace::PointCloud(grid); // Note: vector components 0 and 1 have different ranks. auto fieldConfigs = std::vector{}; fieldConfigs.push_back(option::name("vector_component_0") | option::levels(10) | option::datatype(DataType::kind()) | option::vector_component("vector", 0)); fieldConfigs.push_back(option::name("scalar") | option::datatype(DataType::kind())); fieldConfigs.push_back(option::name("vector_component_1") | option::datatype(DataType::kind()) | option::vector_component("vector", 1)); return setFields(functionSpace, fieldConfigs); } FieldSet createInconsistentDatatypeFields() { const auto grid = Grid("O16"); const auto mesh = MeshGenerator("structured").generate(grid); const auto functionSpace = functionspace::NodeColumns(mesh); // Note: vector components 0 and 1 have different datatypes. auto fieldConfigs = std::vector{}; fieldConfigs.push_back(option::name("vector_component_0") | option::datatype(DataType::kind()) | option::vector_component("vector", 0)); fieldConfigs.push_back(option::name("scalar") | option::datatype(DataType::kind())); fieldConfigs.push_back(option::name("vector_component_1") | option::datatype(DataType::kind()) | option::vector_component("vector", 1)); return setFields(functionSpace, fieldConfigs); } FieldSet createInconsistentLevelsFields() { const auto grid = Grid("O16"); const auto functionSpace = functionspace::StructuredColumns(grid); // Note: vector components 0 and 1 have different number of levels. auto fieldConfigs = std::vector{}; fieldConfigs.push_back(option::name("vector_component_0") | option::levels(10) | option::datatype(DataType::kind()) | option::vector_component("vector", 0)); fieldConfigs.push_back(option::name("scalar") | option::datatype(DataType::kind())); fieldConfigs.push_back(option::name("vector_component_1") | option::levels(20) | option::datatype(DataType::kind()) | option::vector_component("vector", 1)); return setFields(functionSpace, fieldConfigs); } FieldSet createInconsistentVariablesFields() { const auto grid = Grid("O16"); const auto functionSpace = functionspace::StructuredColumns(grid); // Note: vector components 0 and 1 have different number of variables. auto fieldConfigs = std::vector{}; fieldConfigs.push_back(option::name("vector_component_0") | option::datatype(DataType::kind()) | option::variables(2) | option::vector_component("vector", 0)); fieldConfigs.push_back(option::name("scalar") | option::datatype(DataType::kind())); fieldConfigs.push_back(option::name("vector_component_1") | option::datatype(DataType::kind()) | option::vector_component("vector", 1)); return setFields(functionSpace, fieldConfigs); } void checkTestFields(const FieldSet& fields) { auto value = 0; for (const auto& field : fields) { for (auto arrayIdx = size_t{0}; arrayIdx < field.size(); arrayIdx++) { EXPECT(field->data()[arrayIdx] == value++); } EXPECT(field.metadata().get("comment") == "This field is made with love."); } } CASE("Basic pack and unpack") { const auto fields = createOrderedTestFields(); const auto packedFields = util::pack_vector_fields(fields); EXPECT(!packedFields.has("vector_component_0")); EXPECT(!packedFields.has("vector_component_1")); EXPECT(packedFields.has("vector")); EXPECT(packedFields.has("scalar")); const auto unpackedFields = util::unpack_vector_fields(packedFields); EXPECT(unpackedFields.has("vector_component_0")); EXPECT(unpackedFields.has("vector_component_1")); EXPECT(!unpackedFields.has("vector")); EXPECT(unpackedFields.has("scalar")); checkTestFields(unpackedFields); } CASE("unpack into existing field set") { auto fields = createUnorderedTestFields(); const auto packedFields = util::pack_vector_fields(fields); EXPECT(!packedFields.has("vector_component_0")); EXPECT(!packedFields.has("vector_component_1")); EXPECT(packedFields.has("vector")); EXPECT(packedFields.has("scalar")); // Need to unpack into existing field to guarantee field order is preserved. array::make_view(fields["vector_component_0"]).assign(0.); array::make_view(fields["vector_component_1"]).assign(0.); util::unpack_vector_fields(packedFields, fields); EXPECT(fields.has("vector_component_0")); EXPECT(fields.has("vector_component_1")); EXPECT(!fields.has("vector")); EXPECT(fields.has("scalar")); checkTestFields(fields); } CASE("check interleaved fields") { const auto fields = createInterleavedVectorFields(); const auto packedFields = util::pack_vector_fields(fields); EXPECT(!packedFields.has("vector_0_component_0")); EXPECT(!packedFields.has("vector_1_component_0")); EXPECT(!packedFields.has("vector_0_component_1")); EXPECT(!packedFields.has("vector_1_component_1")); EXPECT(packedFields.has("vector_0")); EXPECT(packedFields.has("vector_1")); EXPECT_EQUAL((array::make_view(packedFields["vector_0"])(0, 0)), 0.); EXPECT_EQUAL((array::make_view(packedFields["vector_0"])(0, 1)), 1.); EXPECT_EQUAL((array::make_view(packedFields["vector_1"])(0, 0)), 2.); EXPECT_EQUAL((array::make_view(packedFields["vector_1"])(0, 1)), 3.); const auto unpackedFields = util::unpack_vector_fields(packedFields); EXPECT(unpackedFields.has("vector_0_component_0")); EXPECT(unpackedFields.has("vector_1_component_0")); EXPECT(unpackedFields.has("vector_0_component_1")); EXPECT(unpackedFields.has("vector_1_component_1")); EXPECT(!unpackedFields.has("vector_0")); EXPECT(!unpackedFields.has("vector_1")); EXPECT_EQUAL((array::make_view(unpackedFields["vector_0_component_0"])(0)), 0.); EXPECT_EQUAL((array::make_view(unpackedFields["vector_0_component_1"])(0)), 1.); EXPECT_EQUAL((array::make_view(unpackedFields["vector_1_component_0"])(0)), 2.); EXPECT_EQUAL((array::make_view(unpackedFields["vector_1_component_1"])(0)), 3.); } CASE("check that bad inputs throw") { // Try to apply pack to inconsistent field sets. EXPECT_THROWS(util::pack_vector_fields(createInconsistentRankFields())); EXPECT_THROWS(util::pack_vector_fields(createInconsistentDatatypeFields())); EXPECT_THROWS(util::pack_vector_fields(createInconsistentLevelsFields())); EXPECT_THROWS(util::pack_vector_fields(createInconsistentVariablesFields())); } } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/util/fctest_kdtree.F900000664000175000017500000002612115160212070021101 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. ! This File contains Unit Tests for testing the ! C++ / Fortran Interfaces to the KDTree Datastructure ! @author Benjamin Menetrier #include "fckit/fctest.h" #include "atlas/atlas_f.h" ! ----------------------------------------------------------------------------- module fcta_KDTree_fixture use atlas_module use, intrinsic :: iso_c_binding implicit none end module fcta_KDTree_fixture ! ----------------------------------------------------------------------------- TESTSUITE_WITH_FIXTURE(fctest_atlas_KDTree,fcta_KDTree_fixture) ! ----------------------------------------------------------------------------- TESTSUITE_INIT call atlas_library%initialise() END_TESTSUITE_INIT ! ----------------------------------------------------------------------------- TESTSUITE_FINALIZE use fckit_main_module call atlas_library%finalise() END_TESTSUITE_FINALIZE ! ----------------------------------------------------------------------------- TEST( test_kdtree ) use fckit_log_module use fckit_c_interop_module implicit none type(atlas_Geometry) :: geometry type(atlas_IndexKDTree) :: kdtree type(atlas_StructuredGrid) :: grid integer(ATLAS_KIND_IDX) :: n, i, ix, iy integer(c_int) :: k, kk integer(ATLAS_KIND_IDX), allocatable :: tree_indices(:), indices(:), indices_rad(:), result_indices(:) real(c_double) :: lonlat(2), xyz(3), plonlat(2), pxyz(3) real(c_double), allocatable :: tree_lonlats(:,:), tree_distances(:) real(c_double), allocatable :: lonlats(:,:), distances(:) real(c_double), allocatable :: lons_rad(:), lats_rad(:), lonlats_rad(:,:), distances_rad(:) real(c_double), allocatable :: result_lonlats(:,:), result_distances(:) write(*,*) "test_kdtree for UnitSphere starting" ! Define grid grid = atlas_StructuredGrid("O32") ! Allocation n = grid%size() allocate(tree_lonlats(n,2)) allocate(tree_indices(n)) allocate(tree_distances(n)) k = 6 allocate(result_lonlats(k,2)) allocate(result_indices(k)) allocate(result_distances(k)) allocate(lonlats(k,2)) allocate(indices(k)) allocate(distances(k)) ! Define tree points i = 0 do iy = 1, int(grid%ny(), c_int) do ix = 1, int(grid%nx(iy), c_int) i = i+1 tree_indices(i) = i tree_lonlats(i,:) = grid%lonlat(ix, iy) end do end do ! Define test point plonlat = (/-76.1_c_double, -33._c_double/) ! Build geometry geometry = atlas_Geometry("UnitSphere") ! Define result points call geometry%lonlat2xyz(plonlat, pxyz) tree_distances = 0.0 do i = 1, n call geometry%lonlat2xyz(tree_lonlats(i,:), xyz) tree_distances(i) = sqrt(sum((xyz-pxyz)**2)) end do do i = 1, k result_indices(i:i) = minloc(tree_distances) result_distances(i) = tree_distances(result_indices(i)) tree_distances(result_indices(i)) = huge(c_double) result_lonlats(i,:) = tree_lonlats(result_indices(i),:) end do ! Check interfaces with separate coordinates ! Check constructor with specific geometry kdtree = atlas_IndexKDTree(geometry) write(0,*) "kdtree%c_ptr() = ", c_ptr_to_loc(kdtree%CPTR_PGIBUG_A) ! Check reserve call kdtree%reserve(n) ! Check insert do i = 1, n call kdtree%insert(tree_lonlats(i,1), tree_lonlats(i,2), tree_indices(i)) end do ! Check build only call kdtree%build() ! Check closestPoints call kdtree%closestPoints(plonlat(1), plonlat(2), k, indices, distances, lonlats(:,1), lonlats(:,2)) do i = 1, k FCTEST_CHECK_EQUAL( indices(i) , result_indices(i) ) FCTEST_CHECK_CLOSE( lonlats(i,1) , result_lonlats(i,1) , 1.e-12_c_double ) FCTEST_CHECK_CLOSE( lonlats(i,2) , result_lonlats(i,2) , 1.e-12_c_double ) FCTEST_CHECK_CLOSE( distances(i) , result_distances(i) , 1.e-12_c_double ) end do ! Check closestPoint call kdtree%closestPoint(plonlat(1), plonlat(2), indices(1), distances(1), lonlats(1,1), lonlats(1,2)) FCTEST_CHECK_EQUAL( indices(1) , result_indices(1) ) FCTEST_CHECK_CLOSE( lonlats(1,1) , result_lonlats(1,1) , 1.e-12_c_double ) FCTEST_CHECK_CLOSE( lonlats(1,2) , result_lonlats(1,2) , 1.e-12_c_double ) FCTEST_CHECK_CLOSE( distances(1) , result_distances(1) , 1.e-12_c_double ) ! Check closestPoints call kdtree%closestPointsWithinRadius(plonlat(1), plonlat(2), 5.e-2_c_double, kk, indices_rad, distances_rad, lons_rad, lats_rad) FCTEST_CHECK_EQUAL( kk , 3 ) do i = 1, kk FCTEST_CHECK_EQUAL( indices_rad(i) , result_indices(i) ) FCTEST_CHECK_CLOSE( lons_rad(i), result_lonlats(i,1) , 1.e-12_c_double ) FCTEST_CHECK_CLOSE( lats_rad(i) , result_lonlats(i,2) , 1.e-12_c_double ) FCTEST_CHECK_CLOSE( distances_rad(i) , result_distances(i) , 1.e-12_c_double ) end do ! Finalization call kdtree%final() ! Check interfaces with vectorized coordinates ! Check constructor with specific geometry kdtree = atlas_IndexKDTree(geometry) write(0,*) "kdtree%c_ptr() = ", c_ptr_to_loc(kdtree%CPTR_PGIBUG_A) ! Check reserve call kdtree%reserve(n) ! Check insert do i = 1, n call kdtree%insert(tree_lonlats(i,:), tree_indices(i)) end do ! Check build only call kdtree%build() ! Check closestPoints call kdtree%closestPoints(plonlat, k, indices, distances, lonlats) do i = 1, k FCTEST_CHECK_EQUAL( indices(i) , result_indices(i) ) FCTEST_CHECK_CLOSE( lonlats(i,1) , result_lonlats(i,1) , 1.e-12_c_double ) FCTEST_CHECK_CLOSE( lonlats(i,2) , result_lonlats(i,2) , 1.e-12_c_double ) FCTEST_CHECK_CLOSE( distances(i) , result_distances(i) , 1.e-12_c_double ) end do ! Check closestPoint call kdtree%closestPoint(plonlat, indices(1), distances(1), lonlats(1,:)) FCTEST_CHECK_EQUAL( indices(1) , result_indices(1) ) FCTEST_CHECK_CLOSE( lonlats(1,1) , result_lonlats(1,1) , 1.e-12_c_double ) FCTEST_CHECK_CLOSE( lonlats(1,2) , result_lonlats(1,2) , 1.e-12_c_double ) FCTEST_CHECK_CLOSE( distances(1) , result_distances(1) , 1.e-12_c_double ) ! Check closestPoints call kdtree%closestPointsWithinRadius(plonlat, 5.e-2_c_double, kk, indices_rad, distances_rad, lonlats_rad) FCTEST_CHECK_EQUAL( kk , 3 ) do i = 1, kk FCTEST_CHECK_EQUAL( indices_rad(i) , result_indices(i) ) FCTEST_CHECK_CLOSE( lonlats_rad(i,1) , result_lonlats(i,1) , 1.e-12_c_double ) FCTEST_CHECK_CLOSE( lonlats_rad(i,2) , result_lonlats(i,2) , 1.e-12_c_double ) FCTEST_CHECK_CLOSE( distances_rad(i) , result_distances(i) , 1.e-12_c_double ) end do ! Finalization call kdtree%final() call geometry%final() ! Check constructor without geometry (Earth) kdtree = atlas_IndexKDTree() write(0,*) "kdtree%c_ptr() = ", c_ptr_to_loc(kdtree%CPTR_PGIBUG_A) ! Check geometry accessor geometry = kdtree%geometry() FCTEST_CHECK_EQUAL( geometry%radius() , 6371229._c_double ) ! Define result points call geometry%lonlat2xyz(plonlat(1), plonlat(2), pxyz(1), pxyz(2), pxyz(3)) tree_distances = 0.0 do i = 1, n call geometry%lonlat2xyz(tree_lonlats(i,1), tree_lonlats(i,2), xyz(1), xyz(2), xyz(3)) tree_distances(i) = sqrt(sum((xyz-pxyz)**2)) end do do i = 1, k result_indices(i:i) = minloc(tree_distances) result_distances(i) = tree_distances(result_indices(i)) tree_distances(result_indices(i)) = huge(c_double) result_lonlats(i,:) = tree_lonlats(result_indices(i),:) end do ! Check interfaces with separate coordinates ! Check build with list call kdtree%build(n, tree_lonlats(:,1), tree_lonlats(:,2), tree_indices) ! Check closestPoints call kdtree%closestPoints(plonlat(1), plonlat(2), k, indices, distances, lonlats(:,1), lonlats(:,2)) do i = 1, k FCTEST_CHECK_EQUAL( indices(i) , result_indices(i) ) FCTEST_CHECK_CLOSE( lonlats(i,1) , result_lonlats(i,1) , 1.e-10_c_double ) FCTEST_CHECK_CLOSE( lonlats(i,2) , result_lonlats(i,2) , 1.e-10_c_double ) FCTEST_CHECK_CLOSE( distances(i) , result_distances(i) , 1.e-10_c_double ) end do ! Check closestPoint call kdtree%closestPoint(plonlat(1), plonlat(2), indices(1), distances(1), lonlats(1,1), lonlats(1,2)) FCTEST_CHECK_EQUAL( indices(1) , result_indices(1) ) FCTEST_CHECK_CLOSE( lonlats(1,1) , result_lonlats(1,1) , 1.e-10_c_double ) FCTEST_CHECK_CLOSE( lonlats(1,2) , result_lonlats(1,2) , 1.e-10_c_double ) FCTEST_CHECK_CLOSE( distances(1) , result_distances(1) , 1.e-10_c_double ) ! Check closestPoints call kdtree%closestPointsWithinRadius(plonlat(1), plonlat(2), 3.5e5_c_double, kk, indices_rad, distances_rad, lons_rad, lats_rad) FCTEST_CHECK_EQUAL( kk , 4 ) do i = 1, kk FCTEST_CHECK_EQUAL( indices_rad(i) , result_indices(i) ) FCTEST_CHECK_CLOSE( lons_rad(i) , result_lonlats(i,1) , 1.e-10_c_double ) FCTEST_CHECK_CLOSE( lats_rad(i) , result_lonlats(i,2) , 1.e-10_c_double ) FCTEST_CHECK_CLOSE( distances_rad(i) , result_distances(i) , 1.e-10_c_double ) end do ! Finalization call kdtree%final() call geometry%final() ! Check interfaces with vectorized coordinates ! Check constructor without geometry (Earth) kdtree = atlas_IndexKDTree() write(0,*) "kdtree%c_ptr() = ", c_ptr_to_loc(kdtree%CPTR_PGIBUG_A) ! Check build with list call kdtree%build(n, tree_lonlats, tree_indices) ! Check closestPoints call kdtree%closestPoints(plonlat, k, indices, distances, lonlats) do i = 1, k FCTEST_CHECK_EQUAL( indices(i) , result_indices(i) ) FCTEST_CHECK_CLOSE( lonlats(i,1) , result_lonlats(i,1) , 1.e-10_c_double ) FCTEST_CHECK_CLOSE( lonlats(i,2) , result_lonlats(i,2) , 1.e-10_c_double ) FCTEST_CHECK_CLOSE( distances(i) , result_distances(i) , 1.e-10_c_double ) end do ! Check closestPoint call kdtree%closestPoint(plonlat, indices(1), distances(1), lonlats(1,:)) FCTEST_CHECK_EQUAL( indices(1) , result_indices(1) ) FCTEST_CHECK_CLOSE( lonlats(1,1) , result_lonlats(1,1) , 1.e-10_c_double ) FCTEST_CHECK_CLOSE( lonlats(1,2) , result_lonlats(1,2) , 1.e-10_c_double ) FCTEST_CHECK_CLOSE( distances(1) , result_distances(1) , 1.e-10_c_double ) ! Check closestPoints call kdtree%closestPointsWithinRadius(plonlat, 3.5e5_c_double, kk, indices_rad, distances_rad, lonlats_rad) FCTEST_CHECK_EQUAL( kk , 4 ) do i = 1, kk FCTEST_CHECK_EQUAL( indices_rad(i) , result_indices(i) ) FCTEST_CHECK_CLOSE( lonlats_rad(i,1) , result_lonlats(i,1) , 1.e-10_c_double ) FCTEST_CHECK_CLOSE( lonlats_rad(i,2) , result_lonlats(i,2) , 1.e-10_c_double ) FCTEST_CHECK_CLOSE( distances_rad(i) , result_distances(i) , 1.e-10_c_double ) end do ! Finalization call kdtree%final() ! Release memory call grid%final() deallocate(tree_lonlats) deallocate(tree_indices) deallocate(tree_distances) deallocate(lonlats) deallocate(indices) deallocate(distances) deallocate(lons_rad) deallocate(lats_rad) deallocate(lonlats_rad) deallocate(indices_rad) deallocate(distances_rad) deallocate(result_lonlats) deallocate(result_indices) deallocate(result_distances) END_TEST ! ----------------------------------------------------------------------------- END_TESTSUITE atlas-0.46.0/src/tests/util/test_kdtree.cc0000664000175000017500000002616715160212070020631 0ustar alastairalastair/* * (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 "atlas/grid.h" #include "atlas/util/KDTree.h" #include "tests/AtlasTestEnvironment.h" using namespace atlas::util; namespace atlas { namespace test { //------------------------------------------------------------------------------------------------ namespace { // helpers template std::string to_string(const std::vector& vector) { std::stringstream s; s << vector; return s.str(); } template std::string to_string(const T& v) { return std::to_string(v); } class PayloadGenerator { public: class iterator { friend class PayloadGenerator; public: long int operator*() const { return i_; } const iterator& operator++() { ++i_; return *this; } /* // unused iterator operator++( int ) { iterator copy( *this ); ++i_; return copy; }*/ bool operator==(const iterator& other) const { return i_ == other.i_; } bool operator!=(const iterator& other) const { return i_ != other.i_; } protected: iterator(long int start): i_(start) {} private: unsigned long i_; }; iterator begin() const { return begin_; } iterator end() const { return end_; } PayloadGenerator(long int end): begin_(0), end_(end) {} template void fill(Container& container) { std::iota(container.begin(), container.end(), Value(*begin_)); } template std::array make_array() { std::array array; fill(array); return array; } private: iterator begin_; iterator end_; }; static double radius() { return util::Earth::radius(); } static Geometry& geometry() { static Geometry _geometry(radius()); return _geometry; } static PointXYZ make_xyz(const PointLonLat& lonlat) { return geometry().xyz(lonlat); } static std::array& test_lon() { static std::array lon = {0., 30., 60., 90., 120., 150., 180.}; return lon; } static std::array& test_lat() { static std::array lat = {90., 60., 30., 0., -30., -60., -90.}; return lat; } static std::array& test_lonlat() { static std::array lonlat{PointLonLat{0., 90.}, PointLonLat{30., 60.}, PointLonLat{60., 30.}, PointLonLat{90., 0.}, PointLonLat{120., -30.}, PointLonLat{150., -60.}, PointLonLat{180., -90.}}; return lonlat; } static std::array& test_xyz() { static std::array xyz{make_xyz(PointLonLat{0., 90.}), make_xyz(PointLonLat{30., 60.}), make_xyz(PointLonLat{60., 30.}), make_xyz(PointLonLat{90., 0.}), make_xyz(PointLonLat{120., -30.}), make_xyz(PointLonLat{150., -60.}), make_xyz(PointLonLat{180., -90.})}; return xyz; } static std::array& test_payloads() { static auto payloads = PayloadGenerator(7).make_array(); EXPECT_EQ(std::distance(payloads.begin(), payloads.end()), 7); return payloads; } void validate(const KDTree& tree) { EXPECT_NO_THROW(tree.closestPoint(PointLonLat{180., 45.})); // Search 4 nearest neighbours (k=4), sorted by shortest distance auto neighbours = tree.closestPoints(PointLonLat{89.9, 44.9}, 4); auto expected_payloads = std::vector{2, 1, 3, 0}; EXPECT_EQ(neighbours.payloads(), expected_payloads); } static const IndexKDTree& search() { static IndexKDTree kdtree = []() { IndexKDTree kdtree(geometry()); auto grid = Grid{"O32"}; kdtree.build(grid.lonlat(), PayloadGenerator(grid.size())); return kdtree; }(); return kdtree; } } // namespace //------------------------------------------------------------------------------------------------ static bool ECKIT_515_implemented = ATLAS_ECKIT_VERSION_AT_LEAST(1, 13, 2); // --> implements eckit::KDTree::size() and eckit::KDTree::empty() CASE("test kdtree") { auto grid = Grid{"O32"}; IndexKDTree search(geometry()); EXPECT(search.empty()); search.reserve(grid.size()); idx_t n{0}; for (auto& point : grid.lonlat()) { search.insert(point, n++); if (ECKIT_515_implemented) { EXPECT(search.empty()); } } search.build(); EXPECT_EQ(search.size(), grid.size()); EXPECT_NO_THROW(search.closestPoint(PointLonLat{180., 45.})); // ... // Search 4 nearest neighbours (k=4), sorted by shortest distance auto neighbours = search.closestPoints(PointLonLat{180., 45.}, 4).payloads(); auto expected_neighbours = std::vector{760, 842, 759, 761}; EXPECT_EQ(neighbours, expected_neighbours); } CASE("test assertion") { auto grid = Grid{"O32"}; IndexKDTree search(geometry()); search.reserve(grid.size()); idx_t n{0}; for (auto& point : grid.lonlat()) { search.insert(point, n++); } // Forgot to call search.build() --> assertion thrown when trying to access EXPECT_THROWS_AS(search.closestPoint(PointLonLat{180., 45.}), eckit::AssertionFailed); } CASE("test no assertion") { // Like case "test assertion", but without reserving size auto grid = Grid{"O32"}; IndexKDTree search(geometry()); // No search.reserve() --> build() will not be necessary. EXPECT(search.empty()); idx_t n{0}; for (auto& point : grid.lonlat()) { search.insert(point, n++); if (ECKIT_515_implemented) { EXPECT_EQ(search.size(), n); } } EXPECT_EQ(search.size(), grid.size()); // search.build() Not required EXPECT_NO_THROW(search.closestPoint(PointLonLat{180., 45.})); } CASE("test kdtree building with separate lon and lat and payload arrays") { IndexKDTree search(geometry()); search.build(test_lon(), test_lat(), test_payloads()); validate(search); } CASE("test kdtree building with separate lon and lat and raw payload iterators") { IndexKDTree search(geometry()); auto lon = test_lon(); auto lat = test_lat(); auto payloads_ = test_payloads(); search.build(lon.begin(), lon.end(), lat.begin(), lat.end(), payloads_.begin(), payloads_.end()); validate(search); } CASE("test kdtree building with separate PointLonLat and payload containers") { IndexKDTree search(geometry()); search.build(test_lonlat(), test_payloads()); validate(search); } CASE("test kdtree building with separate PointXYZ and payload containers") { IndexKDTree search(geometry()); search.build(test_xyz(), test_payloads()); validate(search); } CASE("test assignment") { IndexKDTree search; search = IndexKDTree(); search.build(test_lonlat(), test_payloads()); validate(search); } CASE("test closestPoint") { auto neighbour = search().closestPoint(PointLonLat{180., 45.}).payload(); auto expected_neighbour = 760; EXPECT_EQ(neighbour, expected_neighbour); } CASE("test closestPoints") { auto neighbours = search().closestPoints(PointLonLat{180., 45.}, 4).payloads(); auto expected_neighbours = std::vector{760, 842, 759, 761}; EXPECT_EQ(neighbours, expected_neighbours); } CASE("test closestPointsWithinRadius") { double km = 1000. * radius() / util::Earth::radius(); auto neighbours = search().closestPointsWithinRadius(PointLonLat{180., 45.}, 500 * km).payloads(); auto expected_neighbours = std::vector{760, 842, 759, 761, 841, 843, 682}; EXPECT_EQ(neighbours, expected_neighbours); } CASE("test compatibility with external eckit KDTree") { // External world struct ExternalKDTreeTraits { using Point = Point3; using Payload = size_t; }; using ExternalKDTree = typename eckit::KDTreeMemory; auto external_kdtree = std::make_shared(); // Atlas world IndexKDTree search(external_kdtree, geometry()); // Construction of tree; could happen in Atlas world or External world separately auto grid = Grid{"O32"}; search.build(grid.lonlat(), PayloadGenerator(grid.size())); // Usage in External world { auto neighbours = external_kdtree->kNearestNeighbours(make_xyz({180., 45.}), 4); auto payloads = std::vector{}; for (auto& neighbour : neighbours) { payloads.push_back(neighbour.payload()); } auto expected_payloads = std::vector{760, 842, 759, 761}; EXPECT_EQ(payloads, expected_payloads); } // Usage in Atlas world { auto payloads = search.closestPoints(PointLonLat{180., 45.}, 4).payloads(); auto expected_payloads = std::vector{760, 842, 759, 761}; EXPECT_EQ(payloads, expected_payloads); } } CASE("test IndexKDTree 2D vs 3D") { IndexKDTree2D search2d(geometry()); IndexKDTree3D search3d(geometry()); search2d.build(test_lonlat(), test_payloads()); search3d.build(test_lonlat(), test_payloads()); auto payloads2d = search2d.closestPoints(PointLonLat{89.9, 44.9}, 4).payloads(); auto payloads3d = search3d.closestPoints(PointLonLat{89.9, 44.9}, 4).payloads(); EXPECT_EQ(payloads2d, (std::vector{2, 3, 1, 4})); EXPECT_EQ(payloads3d, (std::vector{2, 1, 3, 0})); // Note that the expected values are different whether 2D search or 3D search is used } CASE("test kdtree with configured geometry") { auto grid = Grid{"O32"}; IndexKDTree search_unit(util::Config("geometry","UnitSphere")); IndexKDTree search_earth(util::Config("geometry","Earth")); search_unit.build(grid.lonlat(),PayloadGenerator(grid.size())); search_earth.build(grid.lonlat(),PayloadGenerator(grid.size())); double km_unit = 1000. / util::Earth::radius(); double km_earth = 1000.; auto neighbours_unit = search_unit. closestPointsWithinRadius(PointLonLat{180., 45.}, 500 * km_unit ).payloads(); auto neighbours_earth = search_earth.closestPointsWithinRadius(PointLonLat{180., 45.}, 500 * km_earth).payloads(); auto expected_neighbours = std::vector{760, 842, 759, 761, 841, 843, 682}; EXPECT_EQ(neighbours_unit, expected_neighbours); EXPECT_EQ(neighbours_earth, expected_neighbours); } //------------------------------------------------------------------------------------------------ } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/util/test_convexsphericalmeanvalue.cc0000664000175000017500000002422415160212070024436 0ustar alastairalastair/* * (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 "atlas/util/ConvexSphericalPolygon.h" #include "atlas/util/Point.h" #include "eckit/types/FloatCompare.h" #include "tests/AtlasTestEnvironment.h" namespace atlas { namespace test { //---------------------------------------------------------------------------------------------------------------------- using ConvexSphericalPolygon = util::ConvexSphericalPolygon; const double relative_error = 0.00005; const double recipRoot2 = 1 / sqrt(2); const double recipRoot3 = 1 / sqrt(3); CASE("test_convex_spherical_polygon_triag") { std::array testTriangleVertices{PointXYZ{0, 1, 0}, PointXYZ{recipRoot2, 0, recipRoot2}, PointXYZ{1, 0, 0}}; util::ConvexSphericalPolygon testTriangle(testTriangleVertices.data(), testTriangleVertices.size()); SECTION("test_edge_normals") { std::array expectedEdgeNormals = {PointXYZ{recipRoot2, 0, -1 * recipRoot2}, PointXYZ{0, recipRoot2, 0}, PointXYZ{0, 0, 1}}; for (int i = 0; i < 3; ++i) { for (int j = 0; j < 3; ++j) { EXPECT(eckit::types::is_approximately_equal(expectedEdgeNormals[i][j], testTriangle.edge_normals()[i][j])); } } } SECTION("test_weight_computation") { const size_t numberTestPoints = 10; std::array candidatePoints = { PointXYZ{0, 1, 0}, // Vertex 0 PointXYZ{recipRoot2, 0, recipRoot2}, // Vertex 1 PointXYZ{1, 0, 0}, // Vertex 2 PointXYZ{recipRoot3, recipRoot3, recipRoot3}, // Inside PointXYZ{recipRoot2, recipRoot2, 0}, // On edge PointXYZ{recipRoot2, recipRoot2, 0.000001}, // Just inside edge PointXYZ{0.01, 0.9999, 0.01}, // Just inside vertex PointXYZ{0, recipRoot2, recipRoot2}, // Outside PointXYZ{-1 * recipRoot3, -1 * recipRoot3, -1 * recipRoot3}, // Opposite Side PointXYZ{recipRoot2, recipRoot2, -0.000001} // Just outside edge }; std::array isPointInside = {1, 1, 1, 1, 1, 1, 1, 0, 0, 0}; size_t expectedInside = std::accumulate(std::begin(isPointInside), std::end(isPointInside), 0); size_t expectedOutside = numberTestPoints - expectedInside; std::array, numberTestPoints> candidateWeights = { std::vector{1, 0, 0}, // Vertex 0 std::vector{0, 1, 0}, // Vertex 1 std::vector{0, 0, 1}, // Vertex 2 std::vector{0.5774, 0.8165, 0}, // Inside std::vector{0.7071, 0, 0.7071}, // On edge std::vector{0.7071, 0, 0.7071}, // Just inside edge std::vector{0.9999, 0.0141, 0}, // Just inside vertex std::vector{0, 0, 0}, // Outside std::vector{0, 0, 0}, // Opposite Side std::vector{0, 0, 0} // Just outside edge }; size_t pointsInside = 0; size_t pointsOutside = 0; std::vector polygonWeights(testTriangle.size()); for (size_t i = 0; i < numberTestPoints; ++i) { if (testTriangle.compute_vertex_weights(candidatePoints[i], polygonWeights.data(), polygonWeights.size()) == 0) { EXPECT((isPointInside[i] == 0)); pointsOutside += 1; } else { EXPECT((isPointInside[i] == 1)); pointsInside += 1; for (size_t j = 0; j < 3; ++j) { EXPECT(eckit::types::is_approximately_equal(polygonWeights[j], candidateWeights[i][j], relative_error)); } } } Log::info() << "Points in/out: " << pointsInside << "/" << pointsOutside << std::endl; EXPECT(pointsOutside == expectedOutside); EXPECT(pointsInside == expectedInside); } } CASE("test_spherical_polygon_nonplanar_quad") { std::array testQuadVertices{PointXYZ{1,0,0},PointXYZ{recipRoot2,recipRoot2,0},{recipRoot3,recipRoot3,recipRoot3},{recipRoot2,0,recipRoot2}}; util::ConvexSphericalPolygon testQuad(testQuadVertices.data(), testQuadVertices.size()); const size_t numberTestPoints = 11; std::array candidatePoints = { PointXYZ{1, 0, 0}, // Vertex 0 PointXYZ{recipRoot2, recipRoot2, 0}, // Vertex 1 PointXYZ{recipRoot3, recipRoot3, recipRoot3}, // Vertex2 PointXYZ{recipRoot2, 0, recipRoot2}, // Vertex 3 PointXYZ{0.8246, 0.4, 0.4}, // Inside PointXYZ{0.5 * sqrt(3), 0.5, 0}, // On edge PointXYZ{0.5 * sqrt(3), 0.5, 0.000001}, // Just inside edge PointXYZ{0.5918, 0.57, 0.57}, // Just inside vertex PointXYZ{recipRoot2, -1 * recipRoot2, 0}, // Outside PointXYZ{-0.8246, -0.4, -0.4}, // Opposite side PointXYZ{0.5 * sqrt(3), 0.5, -0.000001}, // Just outside edge }; std::array isPointInside = {1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0}; const size_t expectedInside = 8; const size_t expectedOutside = numberTestPoints - expectedInside; SECTION("test_edge_normals") { double recipRoot6 = recipRoot2 * recipRoot3; std::array expectedEdgeNormals = { PointXYZ{0, 0, recipRoot2}, PointXYZ{recipRoot6, -1 * recipRoot6, 0}, PointXYZ{recipRoot6, 0, -1 * recipRoot6}, PointXYZ{0, recipRoot2, 0}}; for (int i = 0; i < 4; ++i) { for (int j = 0; j < 3; ++j) { EXPECT( eckit::types::is_approximately_equal(expectedEdgeNormals[i][j], testQuad.edge_normals()[i][j])); } } } SECTION("test_weight_computation") { std::array, numberTestPoints> candidateWeights = { std::vector{1, 0, 0, 0}, std::vector{0, 1, 0, 0}, std::vector{0, 0, 1, 0}, std::vector{0, 0, 0, 1}, std::vector{0.23847, 0.26325, 0.37043, 0.26325}, std::vector{0.3660, 0.7071, 0, 0}, std::vector{0.3660, 0.7071, 0, 0}, std::vector{0.0075, 0.0203, 0.9624, 0.0203}, std::vector{0, 0, 0, 0}, std::vector{0, 0, 0, 0}, std::vector{0, 0, 0, 0} // }; size_t pointsInside = 0; size_t pointsOutside = 0; std::vector polygonWeights(testQuad.size()); for (size_t i = 0; i < numberTestPoints; ++i) { if (testQuad.compute_vertex_weights(candidatePoints[i], polygonWeights.data(), polygonWeights.size()) == 0) { EXPECT((isPointInside[i] == 0)); pointsOutside += 1; } else { EXPECT((isPointInside[i] == 1)); pointsInside += 1; for (size_t j = 0; j < 4; ++j) { EXPECT(eckit::types::is_approximately_equal(polygonWeights[j], candidateWeights[i][j], relative_error)); } } } Log::info() << "Points in/out: " << pointsInside << "/" << pointsOutside << std::endl; EXPECT(pointsOutside == expectedOutside); EXPECT(pointsInside == expectedInside); // Test whether rotated polygon gives the same weights const std::array testQuadRotatedVertices = {PointXYZ{recipRoot2, 0, recipRoot2}, PointXYZ{1, 0, 0}, PointXYZ{recipRoot2, recipRoot2, 0}, PointXYZ{recipRoot3, recipRoot3, recipRoot3}}; util::ConvexSphericalPolygon testQuadRotated(testQuadRotatedVertices.data(), testQuadRotatedVertices.size()); size_t pointsInsideRotated = 0; size_t pointsOutsideRotated = 0; std::vector polygonWeightsRotated(testQuadRotated.size()); for (size_t i = 0; i < numberTestPoints; ++i) { if (testQuadRotated.compute_vertex_weights(candidatePoints[i], polygonWeightsRotated.data(), polygonWeightsRotated.size()) == 0) { EXPECT((isPointInside[i] == 0)); pointsOutsideRotated += 1; } else { EXPECT((isPointInside[i] == 1)); pointsInsideRotated += 1; for (size_t j = 0; j < 4; ++j) { EXPECT(eckit::types::is_approximately_equal( polygonWeightsRotated[j], candidateWeights[i][testQuadRotated.previous(j)], relative_error)); } } } Log::info() << "Points in/out: " << pointsInsideRotated << "/" << pointsOutsideRotated << std::endl; EXPECT(pointsOutsideRotated == expectedOutside); EXPECT(pointsInsideRotated == expectedInside); } } //----------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); }atlas-0.46.0/src/tests/util/fctest_functions.F900000664000175000017500000000675515160212070021646 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. ! This File contains Unit Tests for testing the ! C++ / Fortran Interfaces to the logging facilities ! @author Willem Deconinck #include "fckit/fctest.h" ! ----------------------------------------------------------------------------- module fcta_functions_fxt use atlas_module use, intrinsic :: iso_c_binding implicit none character(len=1024) :: msg end module fcta_functions_fxt ! ----------------------------------------------------------------------------- TESTSUITE_WITH_FIXTURE(fctest_atlas_functions,fcta_functions_fxt) ! ----------------------------------------------------------------------------- TESTSUITE_INIT call atlas_library%initialise() END_TESTSUITE_INIT ! ----------------------------------------------------------------------------- TESTSUITE_FINALIZE call atlas_library%finalise() END_TESTSUITE_FINALIZE ! ----------------------------------------------------------------------------- TEST( test_atlas_Functions ) real(c_double) :: val val = MDPI_sinusoid(1._c_double, 1._c_double) FCTEST_CHECK_CLOSE(val, 1.0002115216773033_c_double, 1e-12_c_double) val = MDPI_harmonic(1._c_double, 1._c_double) FCTEST_CHECK_CLOSE(val, 2.0000000000000000_c_double, 1e-12_c_double) val = MDPI_vortex(1._c_double, 1._c_double) FCTEST_CHECK_CLOSE(val, 2.7267489215500755_c_double, 1e-12_c_double) val = MDPI_gulfstream(1._c_double, 1._c_double) FCTEST_CHECK_CLOSE(val, 1.0002115216773033_c_double, 1e-12_c_double) END_TEST TEST( test_atlas_Functions_vector ) real(c_double), dimension(3) :: val, lon, lat, val_ref lon = [ 1._c_double, 2._c_double, 3._c_double ] lat = [ 1._c_double, 2._c_double, 3._c_double ] val = MDPI_sinusoid(lon, lat) val_ref = [1.0002115216773033_c_double, 1.0008458683590891_c_double, 1.0019023851484181_c_double] FCTEST_CHECK_CLOSE(val, val_ref, 1e-12_c_double) val = MDPI_harmonic(lon, lat) val_ref = [2.0000000000000000_c_double, 2.0000000000000000_c_double, 2.0000000000000000_c_double] FCTEST_CHECK_CLOSE(val, val_ref, 1e-12_c_double) val = MDPI_vortex(lon, lat) val_ref = [2.7267489215500755_c_double, 2.7520839004022091_c_double, 2.7755506683886928_c_double] FCTEST_CHECK_CLOSE(val, val_ref, 1e-12_c_double) val = MDPI_gulfstream(lon, lat) val_ref = [1.0002115216773033_c_double, 1.0008458683590891_c_double, 1.0019023851484181_c_double] FCTEST_CHECK_CLOSE(val, val_ref, 1e-12_c_double) END_TEST TEST( test_initialise_field ) type(atlas_Field) :: field_xy, field_val real(c_double), dimension(:,:), pointer :: field_xy_v real(c_double), dimension(:), pointer :: field_val_v real(c_double), dimension(3) :: field_val_ref field_xy = atlas_Field(kind=atlas_real(c_double), shape=[2,3]) field_val = atlas_Field(kind=atlas_real(c_double), shape=[3]) call field_xy%data(field_xy_v) field_xy_v = 1._c_double call field_val%data(field_val_v) field_val_v = MDPI_sinusoid(field_xy_v(1,:), field_xy_v(2,:)) field_val_ref = [1.0002115216773033_c_double, 1.0002115216773033_c_double, 1.0002115216773033_c_double] FCTEST_CHECK_CLOSE(field_val_v, field_val_ref, 1e-12_c_double) END_TEST ! ----------------------------------------------------------------------------- END_TESTSUITE atlas-0.46.0/src/tests/util/test_convexsphericalpolygon.cc0000664000175000017500000007204115160212070024150 0ustar alastairalastair#include #include #include #include #include #include #include "atlas/util/ConvexSphericalPolygon.h" #include "atlas/util/Geometry.h" #include "atlas/util/Point.h" #include "tests/AtlasTestEnvironment.h" namespace atlas { namespace test { using ConvexSphericalPolygon = util::ConvexSphericalPolygon; const double EPS = std::numeric_limits::epsilon(); util::ConvexSphericalPolygon make_polygon(const std::initializer_list& list) { return util::ConvexSphericalPolygon{std::vector(list)}; } template util::ConvexSphericalPolygon make_polygon(Ts&&... ts) { std::array arr{ts...}; return util::ConvexSphericalPolygon{arr}; } util::ConvexSphericalPolygon make_polygon() { return util::ConvexSphericalPolygon{}; } template std::string to_json(const It& begin, const It& end, int precision = 0) { std::stringstream ss; ss << "[\n"; size_t size = std::distance(begin,end); size_t c=0; for( auto it = begin; it != end; ++it, ++c ) { ss << " " << it->json(precision); if( c < size-1 ) { ss << ",\n"; } } ss << "\n]"; return ss.str(); } template std::string to_json(const ConvexSphericalPolygonContainer& polygons, int precision = 0) { return to_json(polygons.begin(),polygons.end(),precision); } std::string to_json(std::initializer_list&& polygons, int precision = 0) { return to_json(polygons.begin(),polygons.end(),precision); } void check_intersection(const ConvexSphericalPolygon& plg1, const ConvexSphericalPolygon& plg2, const ConvexSphericalPolygon& iplg_sol, double pointsSameEPS = 5.e6 * EPS, std::ostream* out = nullptr) { auto iplg = plg1.intersect(plg2, out, pointsSameEPS); Log::info().indent(); Log::info() << "plg1 area : " << plg1.area() << "\n"; Log::info() << "plg2 area : " << plg2.area() << "\n"; Log::info() << "iplg area : " << iplg.area() << "\n"; Log::info() << "json: \n" << to_json({plg1,plg2,iplg},20) << "\n"; EXPECT(std::min(plg1.area(), plg2.area()) >= iplg.area()); EXPECT(iplg.equals(iplg_sol, 1.e-8)); Log::info().unindent(); } CASE("test default constructor") { ConvexSphericalPolygon p; EXPECT(bool(p) == false); } CASE("Size of ConvexSphericalPolygon") { // This test illustrates that ConvexSphericalPolygon is allocated on the stack completely, // as sizeof(ConvexSphericalPolygon) includes space for MAX_SIZE coordinates of type PointXYZ EXPECT(sizeof(PointXYZ) == sizeof(double) * 3); size_t expected_size = 0; expected_size += (1 + ConvexSphericalPolygon::MAX_SIZE) * sizeof(PointXYZ); expected_size += sizeof(size_t); expected_size += sizeof(bool); expected_size += 2 * sizeof(double); EXPECT(sizeof(ConvexSphericalPolygon) >= expected_size); // greater because compiler may add some padding } CASE("analyse intersect") { const double du = 0.5; const double dv = 1.1 * EPS; const double duc = 0.5 * du; const double sduc = std::sqrt(1. - 0.25 * du * du); const double dvc = 1. - 0.5 * dv * dv; const double sdvc = dv * std::sqrt(1. - 0.25 * dv * dv); PointXYZ s0p0{sduc, -duc, 0.}; PointXYZ s0p1{sduc, duc, 0.}; PointXYZ s1p0{dvc * sduc, -dvc * duc, -sdvc}; PointXYZ s1p1{dvc * sduc, dvc * duc, sdvc}; EXPECT_APPROX_EQ(dv, PointXYZ::norm(s0p0 - s1p0), EPS); EXPECT_APPROX_EQ(du, PointXYZ::norm(s0p0 - s0p1), EPS); EXPECT_APPROX_EQ(dv, PointXYZ::norm(s0p1 - s1p1), EPS); ConvexSphericalPolygon::GreatCircleSegment s1(s0p0, s0p1); ConvexSphericalPolygon::GreatCircleSegment s2(s1p0, s1p1); // analytical solution PointXYZ Isol{1., 0., 0.}; // test "intersection" PointXYZ I12 = s1.intersect(s2); PointXYZ I21 = s2.intersect(s1); EXPECT_APPROX_EQ(std::abs(PointXYZ::norm(I12) - 1.), 0., EPS); EXPECT_APPROX_EQ(PointXYZ::norm(I12 - Isol), 0., EPS); EXPECT_APPROX_EQ(std::abs(PointXYZ::norm(I21) - 1.), 0., EPS); EXPECT_APPROX_EQ(PointXYZ::norm(I21 - Isol), 0., EPS); } CASE("test_json_format") { auto plg = make_polygon({{0., 45.}, {0., 0.}, {90., 0.}, {90., 45.}}); EXPECT_EQ(plg.json(), "[[0,45],[0,0],[90,0],[90,45]]"); std::vector plg_g = { make_polygon({{0, 60}, {0, 50}, {40, 60}}), //0 make_polygon({{0, 60}, {0, 50}, {20, 60}}), make_polygon({{10, 60}, {10, 50}, {30, 60}}), //3 }; Log::info() << to_json(plg_g) << std::endl; } CASE("test_spherical_polygon_area") { auto plg1 = make_polygon({{0., 90.}, {0., 0.}, {90., 0.}}); EXPECT_APPROX_EQ(plg1.area(), M_PI_2); auto plg2 = make_polygon({{0., 45.}, {0., 0.}, {90., 0.}, {90., 45.}}); auto plg3 = make_polygon({{0., 90.}, {0., 45.}, {90., 45.}}); Log::info().indent(); EXPECT(plg1.area() - plg2.area() - plg3.area() <= EPS); Log::info().unindent(); EXPECT_APPROX_EQ(std::abs(plg1.area() - plg2.area() - plg3.area()), 0, 1e-15); } CASE("test_spherical_polygon_intersection") { constexpr int nplg_f = 2; constexpr int nplg_g = 17; constexpr int nplg_i = nplg_f * nplg_g; std::array plg_f = {make_polygon({{0, 70}, {0, 60}, {40, 60}, {40, 70}}), make_polygon({{0, 90}, {0, 0}, {40, 0}})}; std::array plg_g = { make_polygon({{0, 60}, {0, 50}, {40, 60}}), // 0 make_polygon({{0, 60}, {0, 50}, {20, 60}}), // 1 make_polygon({{10, 60}, {10, 50}, {30, 60}}), // 2 make_polygon({{40, 80}, {0, 60}, {40, 60}}), // 3 make_polygon({{0, 80}, {0, 60}, {40, 60}}), // 4 make_polygon({{20, 80}, {0, 60}, {40, 60}}), // 5 make_polygon({{20, 70}, {0, 50}, {40, 50}}), // 6 make_polygon({{0, 90}, {0, 60}, {40, 60}}), // 7 make_polygon({{-10, 80}, {-10, 50}, {50, 80}}), // 8 make_polygon({{0, 80}, {0, 50}, {40, 50}, {40, 80}}), // 9 make_polygon({{0, 65}, {20, 55}, {40, 60}, {20, 65}}), // 10 make_polygon({{20, 65}, {0, 60}, {20, 55}, {40, 60}}), // 11 make_polygon({{10, 63}, {20, 55}, {30, 63}, {20, 65}}), // 12 make_polygon({{20, 75}, {0, 70}, {5, 5}, {10, 0}, {20, 0}, {40, 70}}), // 13 make_polygon({{0, 50}, {0, 40}, {5, 45}}), // 14 make_polygon({{0, 90}, {0, 80}, {20, 0}, {40, 80}}), // 15 make_polygon({{0, 65}, {0, 55}, {40, 65}, {40, 75}}), // 16 }; std::array plg_i = { make_polygon(), //0 make_polygon(), make_polygon(), //2 make_polygon({{0, 60}, {40, 60}, {40, 70}, {10, 70.8}}), make_polygon({{0, 70}, {0, 60}, {40, 60}, {30, 70.8}}), //4 make_polygon({{0, 60}, {40, 60}, {34.6, 70.5}, {5.3, 70.5}}), make_polygon({{7.5, 60.9}, {32.5, 60.9}, {20, 70}}), //6 make_polygon({{0, 70}, {0, 60}, {40, 60}, {40, 70}}), make_polygon({{0, 65.5}, {6.9, 70.6}, {0, 70}}), //8 make_polygon({{0, 70}, {0, 60}, {40, 60}, {40, 70}}), make_polygon({{0, 65}, {10, 61.1}, {40, 60}, {20, 65}}), //10 make_polygon({{0, 60}, {40, 60}, {20, 65}}), make_polygon({{12.6, 61.3}, {27.4, 61.3}, {30, 63}, {20, 65}, {10, 63}}), //12 make_polygon({{0, 70}, {1.9, 60.3}, {32.9, 60.9}, {40, 70}}), make_polygon(), //14 make_polygon({{13.7, 61.4}, {26.3, 61.4}, {30, 70.8}, {10, 70.8}}), make_polygon({{0, 65}, {0, 60}, {16.8, 61.5}, {40, 65}, {40, 70}, {15, 71.1}}), //16 make_polygon({{0, 60}, {0, 50}, {40, 60}}), make_polygon({{0, 60}, {0, 50}, {20, 60}}), //18 make_polygon({{10, 60}, {10, 50}, {30, 60}}), make_polygon({{0, 60}, {40, 60}, {40, 80}}), //20 make_polygon({{0, 80}, {0, 60}, {40, 60}}), make_polygon({{0, 60}, {40, 60}, {20, 80}}), //22 make_polygon({{0, 50}, {40, 50}, {20, 70}}), make_polygon({{0, 90}, {0, 60}, {40, 60}}), //24 make_polygon({{0, 65.5}, {40, 79.2}, {40, 80.8}, {0, 80.8}}), make_polygon({{0, 80}, {0, 50}, {40, 50}, {40, 80}}), //26 make_polygon({{0, 65}, {20, 55}, {40, 60}, {20, 65}}), make_polygon({{0, 60}, {20, 55}, {40, 60}, {20, 65}}), //28 make_polygon({{10, 63}, {20, 55}, {30, 63}, {20, 65}}), make_polygon({{0, 70}, {5, 5}, {10, 0}, {20, 0}, {40, 70}, {20, 75}}), //30 make_polygon({{0, 50}, {0, 40}, {5, 45}}), make_polygon({{0, 90}, {0, 80}, {20, 0}, {40, 80}}), //32 make_polygon({{0, 65}, {0, 55}, {40, 65}, {40, 75}})}; Log::info().indent(); for (int i = 0; i < nplg_f; i++) { for (int j = 0; j < nplg_g; j++) { auto plg_fg = plg_f[i].intersect(plg_g[j], nullptr, 5.e6 * std::numeric_limits::epsilon()); bool polygons_equal = plg_i[i * nplg_g + j].equals(plg_fg, 0.1); EXPECT(polygons_equal); if( not polygons_equal or (i==0 && j==10)) { Log::info() << "\nIntersected the polygon plg_f["< plg2_points = { PointXYZ{u, v, 0.}, PointXYZ{a, 0., w }, PointXYZ{u, -v, 0.}, PointXYZ{0., 0., -1.}}; auto plg2 = util::ConvexSphericalPolygon(plg2_points.data(), plg2_points.size()); EXPECT(plg2.size() == 4); auto plg11 = plg1.intersect(plg1); auto plg12 = plg1.intersect(plg2); auto plg22 = plg2.intersect(plg2); EXPECT(plg11.size() == 3); EXPECT(plg12.size() == 3); EXPECT(plg22.size() == 4); // plg3 is the analytical solution std::vector plg3_points = { PointXYZ{u, v, 0.}, PointXYZ{a, 0, w}, PointXYZ{1, 0, 0.}}; auto plg3 = util::ConvexSphericalPolygon(plg3_points.data(), plg3_points.size()); EXPECT(plg3.size() == 3); EXPECT(plg12.size() == 3); auto plg_sol = make_polygon({{1.2074e-06, 0.}, {0., 1.3994e-14}, {0., 0.}}); EXPECT(plg12.equals(plg_sol, 1e-10)); Log::info().indent(); Log::info() << "delta : " << delta << std::endl; Log::info() << "plg12.area : " << plg12.area() << std::endl; Log::info() << "exact intersection area : " << plg3.area() << std::endl; double error_area = plg12.area() - plg3.area(); EXPECT(error_area < EPS and error_area >= 0.); Log::info().unindent(); Log::info().precision(-1); } CASE("test_lonlat_pole_problem") { const auto north_octant = make_polygon({{0, 90}, {0, 0}, {90, 0}}); const double first_lat = 90. - 1.e+12 * EPS; const int m = 10000; const double dlon = 90. / m; std::vector csp(m); Log::info().indent(); ATLAS_TRACE_SCOPE("create polygons") for (int j = 0; j < m; j++) { csp[j] = make_polygon(PointLonLat{0, 90}, PointLonLat{dlon * j, first_lat}, PointLonLat{dlon * (j + 1), first_lat}); } double max_area_overshoot = 0.; int false_zero = 0; for (int j = 0; j < m; j++) { auto csp_i = csp[j].intersect(north_octant); // intersection area should not be larger than its father polygon's max_area_overshoot = std::max(max_area_overshoot, csp_i.area() - csp[j].area()); if (csp_i.area() < EPS) { false_zero++; } } Log::info() << "False zero area intersection: " << false_zero << std::endl; Log::info() << "Max area overshoot: " << max_area_overshoot << std::endl; EXPECT(max_area_overshoot <= m * EPS); EXPECT(false_zero == 0); Log::info().unindent(); } CASE("test_thin_elements_area") { const auto north_octant = make_polygon({{0, 90}, {0, 0}, {90, 0}}); const auto south_octant = make_polygon({{0,0}, {0, -90},{90, 0}}); const int n = 3; const int m = 2500; const double dlat = 180. / n; const double dlon = 90. / m; ATLAS_ASSERT(n > 1); std::vector csp(n * m); ATLAS_TRACE_SCOPE("create polygons") for (int j = 0; j < m; j++) { csp[j] = make_polygon(PointLonLat{0, 90}, PointLonLat{dlon * j, 90 - dlat}, PointLonLat{dlon * (j + 1), 90 - dlat}); } for (int i = 1; i < n - 1; i++) { for (int j = 0; j < m; j++) { csp[i * m + j] = make_polygon( PointLonLat{dlon * j, 90 - dlat * i}, PointLonLat{dlon * j, 90 - dlat * (i + 1)}, PointLonLat{dlon * (j + 1), 90 - dlat * (i + 1)}, PointLonLat{dlon * (j + 1), 90 - dlat * i}); } } for (int j = 0; j < m; j++) { csp[(n - 1) * m + j] = make_polygon(PointLonLat{dlon * j, 90 - dlat * (n-1)}, PointLonLat{dlon * j, -90.}, PointLonLat{dlon * (j + 1), 90 - dlat * (n-1)}); } double coverage_north = 0.; // north octant coverage by intersections with "csp"s double coverage_south = 0.; // south octant coverage by intersections with "csp"s double coverage_norm = 0.; // area sum of intersections in the north octant normalised to sum up to the area of the north octant double coverage_csp = 0.; // area sum of all "csp"s std::vector i_north_area(n * m); ATLAS_TRACE_SCOPE("intersect polygons") for (int i = 0; i < n; i++) { for (int j = 0; j < m; j++) { auto ipoly = i * m + j; double a_north_csp_i = csp[ipoly].intersect(north_octant).area(); // intersect narrow csp with the north octant double a_south_csp_i = csp[ipoly].intersect(south_octant).area(); // intersect narrow csp with the south octant if (i == 0) { if (n == 2) { EXPECT_APPROX_EQ(a_north_csp_i, csp[ipoly].area(), EPS); } else { if (a_north_csp_i > csp[ipoly].area()) { Log::info() << " error: " << a_north_csp_i - csp[ipoly].area() << std::endl; } } } coverage_north += a_north_csp_i; coverage_csp += csp[ipoly].area(); coverage_south += a_south_csp_i; i_north_area[ipoly] = a_north_csp_i; } } ATLAS_DEBUG_VAR(coverage_csp); // normalise weights of the intersection polygons to sum up to the area of the north octant double norm_fac = north_octant.area() / coverage_north; for (int i = 0; i < n; i++) { for (int j = 0; j < m; j++) { coverage_norm += i_north_area[i * m + j] * norm_fac; } } EXPECT(north_octant.area() - M_PI_2 < EPS); // spherical area error for the north octant EXPECT(south_octant.area() - M_PI_2 < EPS); // spherical area error for the south octant EXPECT(north_octant.area() - coverage_north < m * EPS); // error polygon intersec. north EXPECT(south_octant.area() - coverage_south < m * EPS); // error polygon intersec. north EXPECT(north_octant.area() - coverage_norm < m * EPS); // error polygon intersec. north auto err = std::max(north_octant.area() - coverage_north, south_octant.area() - coverage_south); auto err_normed = north_octant.area() - coverage_norm; Log::info() << "Octant coverting error : " << err << std::endl; Log::info() << "Octant coverting error normed : " << err_normed << std::endl; } CASE("intesection of epsilon-distorted polygons") { const double eps = 1e-4; // degrees const auto plg0 = make_polygon({{42.45283019, 50.48004426}, {43.33333333, 49.70239033}, {44.1509434, 50.48004426}, {43.26923077, 51.2558069}}); const auto plg1 = make_polygon({{42.45283019, 50.48004426 - eps}, {43.33333333 + eps, 49.70239033}, {44.1509434, 50.48004426 + eps}, {43.26923077 - eps, 51.2558069}}); const auto iplg_sol = make_polygon({{44.15088878324276, 50.48009332686897}, {43.68455392823953, 50.89443301919586}, {43.26918271448949, 51.25576215711414}, {42.86876285000331, 50.87921047438197}, {42.45288468219661, 50.47999711267543}, {42.92307395320301, 50.06869923211562}, {43.33338148668725, 49.70243705225555}, {43.72937844034824, 50.08295071539503}}); check_intersection(plg0, plg1, iplg_sol); } CASE("Edge cases in polygon intersections") { Log::info().precision(20); SECTION("CS-LFR-256 -> H1280") { const auto plg0 = make_polygon({{-23.55468749999994, -41.11286269132660}, {-23.20312500000000, -41.18816845938357}, {-23.20312500000000, -40.83947225425061}, {-23.55468749999994, -40.76429594967151}}); const auto plg1 = make_polygon({{-23.30859375000000, -40.81704944888558}, {-23.27343750000000, -40.85649237345376}, {-23.23828125000000, -40.81704944888558}, {-23.27343750000000, -40.77762996221442}}); auto iplg = plg0.intersect(plg1); Log::info().indent(); Log::info() << "source area: " << plg0.area() << "\n"; Log::info() << "target area: " << plg1.area() << "\n"; Log::info() << "inters area: " << iplg.area() << "\n"; Log::info() << "json: \n" << to_json({plg0,plg1,iplg},16) << "\n"; EXPECT(std::min(plg0.area(), plg1.area()) >= iplg.area()); Log::info().unindent(); } SECTION("CS-LFR-16 -> O32") { const auto plg0 = make_polygon({{174.3750000000001, -16.79832945594544}, {174.3750000000001, -11.19720014633353}, {168.7500000000000, -11.03919441545243}, {168.7500000000000, -16.56868711281520}}); const auto plg1 = make_polygon({{174.3750000000000, -12.55775611523100}, {174.1935483870968, -15.34836475949100}, {177.0967741935484, -15.34836475949100}}); auto iplg = plg0.intersect(plg1); Log::info().indent(); Log::info() << "source area: " << plg0.area() << "\n"; Log::info() << "target area: " << plg1.area() << "\n"; Log::info() << "inters area: " << iplg.area() << "\n"; Log::info() << "json: \n" << to_json({plg0,plg1,iplg},16) << "\n"; EXPECT(std::min(plg0.area(), plg1.area()) >= iplg.area()); Log::info().unindent(); } SECTION("CS-LFR-2 -> O48") { const auto plg0 = make_polygon({{180, -45}, {180, 0}, {135, 0}, {135, -35.26438968}}); const auto plg1 = make_polygon({{180, -23.31573073}, {180, -25.18098558}, {-177.75, -23.31573073}}); auto iplg = plg0.intersect(plg1); EXPECT_EQ(iplg.size(), 0); Log::info().indent(); Log::info() << "source area: " << plg0.area() << "\n"; Log::info() << "target area: " << plg1.area() << "\n"; Log::info() << "inters area: " << iplg.area() << "\n"; Log::info() << "json: \n" << to_json({plg0,plg1,iplg},10) << "\n"; EXPECT(std::min(plg0.area(), plg1.area()) >= iplg.area()); EXPECT_APPROX_EQ(iplg.area(), 0.); Log::info().unindent(); } SECTION("H128->H256") { const auto plg0 = make_polygon({{42.45283019, 50.48004426}, {43.33333333, 49.70239033}, {44.15094340, 50.48004426}, {43.26923077, 51.25580690}}); const auto plg1 = make_polygon({{43.30188679, 50.48004426}, {43.73831776, 50.09145680}, {44.15094340, 50.48004426}, {43.71428571, 50.86815893}}); auto iplg = plg0.intersect(plg1); Log::info().indent(); Log::info() << "source area: " << plg0.area() << "\n"; Log::info() << "target area: " << plg1.area() << "\n"; Log::info() << "inters area: " << iplg.area() << "\n"; Log::info() << "json: \n" << to_json({plg0,plg1,iplg},10) << "\n"; EXPECT(std::min(plg0.area(), plg1.area()) >= iplg.area()); Log::info().unindent(); } SECTION("intesection of epsilon-distorted polygons") { const double eps = 1e-4; // degrees const auto plg0 = make_polygon({{42.45283019, 50.48004426}, {43.33333333, 49.70239033}, {44.1509434, 50.48004426}, {43.26923077, 51.2558069}}); const auto plg1 = make_polygon({{42.45283019, 50.48004426 - eps}, {43.33333333 + eps, 49.70239033}, {44.1509434, 50.48004426 + eps}, {43.26923077 - eps, 51.2558069}}); auto iplg = plg0.intersect(plg1); EXPECT_EQ(iplg.size(), 8); Log::info().indent(); Log::info() << "source area: " << plg0.area() << "\n"; Log::info() << "target area: " << plg1.area() << "\n"; Log::info() << "inters area: " << iplg.area() << "\n"; Log::info() << "json: \n" << to_json({plg0,plg1,iplg},10) << "\n"; EXPECT(std::min(plg0.area(), plg1.area()) >= iplg.area()); Log::info().unindent(); } SECTION("one") { // TODO: remove, do not know what example auto plg0 = make_polygon({{0,90},{67.463999999999998636,89.999899999085130275},{67.5,89.999899999085130275}}); auto plg1 = make_polygon({{72,85.760587120443801723},{90,85.760587120443801723},{-90,85.760587120443801723}}); auto iplg = plg0.intersect(plg1); Log::info().indent(); Log::info() << "source area: " << plg0.area() << "\n"; Log::info() << "target area: " << plg1.area() << "\n"; Log::info() << "inters area: " << iplg.area() << "\n"; Log::info() << "json: \n" << to_json({plg0,plg1,iplg},20) << "\n"; EXPECT(std::min(plg0.area(), plg1.area()) >= iplg.area()); Log::info().unindent(); } SECTION("debug") { // TODO: remove, do not know what example auto plg0 = make_polygon({{168.599999999999994,-6.88524379355500038},{168.561872909699019,-7.16627415158899961},{168.862876254180634,-7.16627415158899961}}); auto plg1 = make_polygon({{168.84,-6.84},{168.84,-7.2},{169.2,-7.2},{169.2,-6.84}}); auto plg2 = make_polygon({{168.48,-6.84},{168.48,-7.2},{168.84,-7.2},{168.84,-6.84}}); auto iplg = plg0.intersect(plg1); auto iplg2 = plg0.intersect(plg2); Log::info().indent(); Log::info() << "source area: " << plg0.area() << "\n"; Log::info() << "target area: " << plg1.area() << "\n"; Log::info() << "inters area: " << iplg.area() << "\n"; Log::info() << "json: \n" << to_json({plg0,plg1,iplg2,iplg,iplg2},20) << "\n"; EXPECT(std::min(plg0.area(), plg1.area()) >= iplg.area()); Log::info().unindent(); } SECTION("debug2") { // TODO: remove, do not know what example auto plg0 = make_polygon({{168.599999999999994, -6.88524379355500038}, {168.561872909699019, -7.16627415158899961}, {168.862876254180634, -7.16627415158899961}}); auto plg1 = make_polygon({{168.48, -6.84}, {168.48, -7.20}, {168.84, -7.20}, {168.84, -6.84}}); const auto iplg_sol = make_polygon({{168.600000000000, -6.885243793555000}, {168.561872909699, -7.166274151589000}, {168.840000000000, -7.166281023991750}, {168.840000000000, -7.141837487873564}}); check_intersection(plg0, plg1, iplg_sol); } SECTION("O320c_O320n_tcell-2524029") { auto plg0 = make_polygon({{ 16.497973615369710, 89.85157892074884}, { 0.000000000000000, 89.87355342974176}, {-54.000000000000010, 89.78487690721863}, { -9.000000000000002, 89.84788464490568}}); auto plg1 = make_polygon({{ 36.000000000000000, 89.78487690721863}, {-54.000000000000010, 89.78487690721863}, {-36.000000000000000, 89.78487690721863}}); const auto iplg_sol = make_polygon(); check_intersection(plg0, plg1, iplg_sol); } SECTION("O128c_F128c_tcell-77536") { auto plg0 = make_polygon({{157.5,-16.49119584364},{157.5,-17.192948774143},{158.203125,-17.192948774143},{158.203125,-16.49119584364}}); auto plg1 = make_polygon({{157.7064220183486,-16.49119584364},{157.5,-17.192948774143},{158.3333333333333,-17.192948774143}}); const auto iplg_sol = make_polygon({{157.7066386314724, -16.49143953797217}, {157.7063506671565, -16.49143933951490}, {157.5000000000000, -17.19294877414300}, {158.2031250000000, -17.19294877414292}, {158.2031250000000, -17.04778221576889}}); check_intersection(plg0, plg1, iplg_sol); } SECTION("O128c_F128c_tcell") { auto plg0 = make_polygon({{135,-10.505756145244},{135,-11.906523334954},{136.40625,-11.906523334954},{136.40625,-10.505756145244}}); auto plg1 = make_polygon({{135.7377049180328,-10.505756145244},{135,-11.906523334954},{136.5,-11.906523334954}}); const auto iplg_sol = make_polygon({{135.7381225488238, -10.50652773728132}, {135.7373006953013, -10.50652782622945}, {135.0000000000000, -11.90652333495400}, {136.4062500000000, -11.90652333495396}, {136.4062500000000, -11.73509894855489}}); check_intersection(plg0, plg1, iplg_sol); } SECTION("O128c_F128c_tcell-2") { auto plg0 = make_polygon({{134.296875,-10.877172064989},{134.296875,-11.578925065131},{135,-11.578925065131},{135,-10.877172064989}}); auto plg1 = make_polygon({{134.6153846153846,-10.877172064989},{134.2241379310345,-11.578925065131},{135,-11.578925065131}}); const auto iplg_sol = make_polygon({{134.6154929040914, -10.87737018871372}, {134.6152744739456, -10.87737016536156}, {134.2968750000000, -11.44875997313061}, {134.2968749999999, -11.57892506513095}, {135.0000000000000, -11.57892506513100}}); check_intersection(plg0, plg1, iplg_sol); } } //----------------------------------------------------------------------------- } // end namespace test } // end namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/util/fctest_metadata.F900000664000175000017500000000646715160212070021416 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. ! This File contains Unit Tests for testing the ! C++ / Fortran Interfaces to the Mesh Datastructure ! @author Willem Deconinck #include "fckit/fctest.h" #include "atlas/atlas_f.h" ! ----------------------------------------------------------------------------- module fcta_Metadata_fixture use atlas_module use, intrinsic :: iso_c_binding implicit none end module fcta_Metadata_fixture ! ----------------------------------------------------------------------------- TESTSUITE_WITH_FIXTURE(fctest_atlas_Metadata,fcta_Metadata_fixture) ! ----------------------------------------------------------------------------- TESTSUITE_INIT call atlas_library%initialise() END_TESTSUITE_INIT ! ----------------------------------------------------------------------------- TESTSUITE_FINALIZE use fckit_main_module call atlas_library%finalise() END_TESTSUITE_FINALIZE ! ----------------------------------------------------------------------------- TEST( test_metadata ) use fckit_log_module use fckit_c_interop_module implicit none integer(c_int) :: intval logical :: true, false real(c_float) :: real32 real(c_double) :: real64 character(len=:), allocatable :: string integer(c_int), allocatable :: arr_int32(:) real(c_float), allocatable :: arr_real32(:) type(atlas_Metadata) :: metadata type(fckit_logchannel) :: info write(*,*) "test_metadata starting" metadata = atlas_Metadata() write(0,*) "metadata%c_ptr() = ", c_ptr_to_loc(metadata%CPTR_PGIBUG_A) call metadata%set("true",.True.) call metadata%set("false",.False.) call metadata%set("int",20) call metadata%set("real32", real(0.1,kind=c_float) ) call metadata%set("real64", real(0.2,kind=c_double) ) call metadata%set("string", "hello world") call metadata%set("arr_int32", (/1,2,3/)) call metadata%set("arr_int64", (/1_c_long,2_c_long,3_c_long/)) call metadata%set("arr_real32", (/1.1_c_float,2.1_c_float,3.7_c_float/)) call metadata%set("arr_real64", (/1.1_c_double,2.1_c_double,3.7_c_double/)) call metadata%get("true",true) call metadata%get("false",false) call metadata%get("int",intval) call metadata%get("real32",real32) call metadata%get("real64",real64) call metadata%get("string",string) call metadata%get("arr_int64",arr_int32) call metadata%get("arr_real64",arr_real32) info = fckit_log%info_channel() call metadata%print(info) call fckit_log%info("",newl=.true.) call fckit_log%info(metadata%json()) CHECK( true .eqv. .True. ) CHECK( false .eqv. .False. ) FCTEST_CHECK_EQUAL( intval, 20 ) FCTEST_CHECK_CLOSE( real32, real(0.1,kind=c_float), real(0.,kind=c_float) ) FCTEST_CHECK_CLOSE( real64, real(0.2,kind=c_double), real(0.,kind=c_double) ) FCTEST_CHECK_EQUAL( string, "hello world" ) FCTEST_CHECK_EQUAL( arr_int32, (/1,2,3/) ) FCTEST_CHECK_EQUAL( arr_real32, (/1.1_c_float,2.1_c_float,3.7_c_float/) ) call metadata%final() END_TEST ! ----------------------------------------------------------------------------- END_TESTSUITE atlas-0.46.0/src/tests/util/test_metadata.cc0000664000175000017500000000331515160212070021121 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "atlas/array/ArrayView.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/util/Metadata.h" #include "tests/AtlasTestEnvironment.h" using namespace eckit; using namespace atlas::util; namespace atlas { namespace test { //----------------------------------------------------------------------------- CASE("test_broadcast_to_self") { Metadata metadata; if (mpi::comm().rank() == 0) { metadata.set("paramID", 128); } // broadcast metadata.broadcast(); EXPECT(metadata.has("paramID")); if (metadata.has("paramID")) { EXPECT(metadata.get("paramID") == 128); } } // ----------------------------------------------------------------------------- CASE("test_broadcast_to_other") { size_t root = 0; Metadata global; if (mpi::comm().rank() == root) { global.set("paramID", 128); } Metadata local; // broadcast global.broadcast(local); EXPECT(local.has("paramID")); if (local.has("paramID")) { EXPECT(local.get("paramID") == 128); } if (mpi::comm().rank() != root) { EXPECT(!global.has("paramID")); } } //----------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/util/fctest_error.F900000664000175000017500000000524015160212070020753 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. ! This File contains Unit Tests for testing the ! C++ / Fortran Interfaces to the Mesh Datastructure ! @author Willem Deconinck #include "fckit/fctest.h" ! ----------------------------------------------------------------------------- module fcta_error_fxt use atlas_module use, intrinsic :: iso_c_binding implicit none character(len=*), parameter :: filename = "fctest_error.F90" end module fcta_error_fxt ! ----------------------------------------------------------------------------- TESTSUITE_WITH_FIXTURE(fcta_error,fcta_error_fxt) ! ----------------------------------------------------------------------------- TESTSUITE_INIT call atlas_library%initialise() END_TESTSUITE_INIT ! ----------------------------------------------------------------------------- TESTSUITE_FINALIZE call atlas_library%finalise() END_TESTSUITE_FINALIZE ! ----------------------------------------------------------------------------- TEST( test_error ) call atlas_err_set_throws(.False.) call atlas_err_set_aborts(.False.) call atlas_err_set_backtrace(.True.) CHECK_EQUAL(atlas_err_code(),atlas_err_cleared) call atlas_err_success() CHECK(atlas_noerr()) CHECK_EQUAL(atlas_err_code(),atlas_err_noerr) CHECK_EQUAL(atlas_err_msg(),"") call atlas_throw_exception("reason",atlas_code_location(filename,__LINE__)) CHECK( atlas_err() ) CHECK_EQUAL( atlas_err_code(), atlas_err_exception ) call atlas_throw_seriousbug("reason",atlas_code_location(filename,__LINE__)) CHECK_EQUAL( atlas_err_code(), atlas_err_seriousbug ) call atlas_throw_usererror("reason",atlas_code_location(filename,__LINE__)) CHECK_EQUAL( atlas_err_code(), atlas_err_usererror ) call atlas_throw_outofrange("myarray",128,100) call atlas_throw_outofrange("myarray",128,100,atlas_code_location(filename,__LINE__)) CHECK_EQUAL( atlas_err_code(), atlas_err_outofrange ) ! call atlas_log%warning(atlas_err_msg()) call atlas_err_clear() CHECK_EQUAL(atlas_err_code(),atlas_err_cleared) ! call atlas_abort("I give up!!!", atlas_code_location(filename,__LINE__,"test_error")) ! call atlas_abort() ! if( atlas_err() ) then ! write(0,'(A)') atlas_err_msg() ! call atlas_err_clear() ! endif ! call atlas_throw_exception("exception from fortran",atlas_code_location(filename,__LINE__)) ! call atlas_throw_notimplemented(atlas_code_location(filename,__LINE__)) END_TEST END_TESTSUITE atlas-0.46.0/src/tests/util/test_earth.cc0000664000175000017500000001220715160212070020444 0ustar alastairalastair/* * (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/eckit_version.h" #include "atlas/util/Earth.h" #include "atlas/util/Point.h" #include "atlas/util/Geometry.h" #include "tests/AtlasTestEnvironment.h" using atlas::util::Earth; namespace atlas { namespace test { const double R = Earth::radius(); // ----------------------------------------------------------------------------- // test_earth_poles CASE("test_earth_north_pole") { const PointLonLat p1(0., 90.); PointXYZ p2; Earth::convertSphericalToCartesian(p1, p2); EXPECT(p2.x() == 0); EXPECT(p2.y() == 0); EXPECT(p2.z() == R); } CASE("test_earth_south_pole") { const PointLonLat p1(0., -90.); PointXYZ p2; Earth::convertSphericalToCartesian(p1, p2); EXPECT(p2.x() == 0); EXPECT(p2.y() == 0); EXPECT(p2.z() == -R); } // ----------------------------------------------------------------------------- // test_earth_quadrants CASE("test_earth_lon_0") { const PointLonLat p1[2] = {{0., 0.}, {-360., 0.}}; PointXYZ p2[2]; Earth::convertSphericalToCartesian(p1[0], p2[0]); Earth::convertSphericalToCartesian(p1[1], p2[1]); EXPECT(p2[0].x() == R); EXPECT(p2[0].y() == 0); EXPECT(p2[0].z() == 0); EXPECT(PointXYZ::equal(p2[0], p2[1])); } CASE("test_earth_lon_90") { const PointLonLat p1[2] = {{90., 0.}, {-270., 0.}}; PointXYZ p2[2]; Earth::convertSphericalToCartesian(p1[0], p2[0]); Earth::convertSphericalToCartesian(p1[1], p2[1]); EXPECT(p2[0].x() == 0); EXPECT(p2[0].y() == R); EXPECT(p2[0].z() == 0); EXPECT(PointXYZ::equal(p2[0], p2[1])); } CASE("test_earth_lon_180") { const PointLonLat p1[2] = {{180., 0.}, {-180., 0.}}; PointXYZ p2[2]; Earth::convertSphericalToCartesian(p1[0], p2[0]); Earth::convertSphericalToCartesian(p1[1], p2[1]); EXPECT(p2[0].x() == -R); EXPECT(p2[0].y() == 0); EXPECT(p2[0].z() == 0); EXPECT(PointXYZ::equal(p2[0], p2[1])); } CASE("test_earth_lon_270") { const PointLonLat p1[2] = {{270., 0.}, {-90., 0.}}; PointXYZ p2[2]; Earth::convertSphericalToCartesian(p1[0], p2[0]); Earth::convertSphericalToCartesian(p1[1], p2[1]); EXPECT(p2[0].x() == 0); EXPECT(p2[0].y() == -R); EXPECT(p2[0].z() == 0); EXPECT(PointXYZ::equal(p2[0], p2[1])); } // ----------------------------------------------------------------------------- // test_earth_octants const double L = R * std::sqrt(2) / 2.; CASE("test_earth_lon_45") { const PointLonLat p1[2] = {{45., 0.}, {-315., 0.}}; PointXYZ p2[2]; Earth::convertSphericalToCartesian(p1[0], p2[0]); Earth::convertSphericalToCartesian(p1[1], p2[1]); EXPECT(eckit::types::is_approximately_equal(p2[0].x(), L)); EXPECT(eckit::types::is_approximately_equal(p2[0].y(), L)); EXPECT(p2[0].z() == 0); EXPECT(PointXYZ::equal(p2[0], p2[1])); } CASE("test_earth_lon_135") { const PointLonLat p1[2] = {{135., 0.}, {-225., 0.}}; PointXYZ p2[2]; Earth::convertSphericalToCartesian(p1[0], p2[0]); Earth::convertSphericalToCartesian(p1[1], p2[1]); EXPECT(eckit::types::is_approximately_equal(p2[0].x(), -L)); EXPECT(eckit::types::is_approximately_equal(p2[0].y(), L)); EXPECT(p2[0].z() == 0); EXPECT(PointXYZ::equal(p2[0], p2[1])); } CASE("test_earth_lon_225") { const PointLonLat p1[2] = {{225., 0.}, {-135., 0.}}; PointXYZ p2[2]; Earth::convertSphericalToCartesian(p1[0], p2[0]); Earth::convertSphericalToCartesian(p1[1], p2[1]); EXPECT(eckit::types::is_approximately_equal(p2[0].x(), -L)); EXPECT(eckit::types::is_approximately_equal(p2[0].y(), -L)); EXPECT(p2[0].z() == 0); EXPECT(PointXYZ::equal(p2[0], p2[1])); } CASE("test_earth_lon_315") { const PointLonLat p1[2] = {{315., 0.}, {-45., 0.}}; PointXYZ p2[2]; Earth::convertSphericalToCartesian(p1[0], p2[0]); Earth::convertSphericalToCartesian(p1[1], p2[1]); EXPECT(eckit::types::is_approximately_equal(p2[0].x(), L)); EXPECT(eckit::types::is_approximately_equal(p2[0].y(), -L)); EXPECT(p2[0].z() == 0); EXPECT(PointXYZ::equal(p2[0], p2[1])); } CASE("test_earth_great_circle_latitude_given_longitude") { // latitude at Valparaíso-Shanghai mid-point const PointLonLat P1(-71.6, -33.); const PointLonLat P2(121.8, 31.4); double lon = -159.18; double lat = Earth::greatCircleLatitudeGivenLongitude(P1, P2, lon); EXPECT(eckit::types::is_approximately_equal(lat, -6.81, 0.01)); } CASE("test_across_pole") { if (eckit_version_int() < 12400) { return; } const PointLonLat P1{-16.875,-105.255}; atlas::geometry::Earth geo; EXPECT_APPROX_EQ(geo.xyz(P1), PointXYZ(-1.60418e+06,486624,-6.14673e+06), 50.); } } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/util/test_unitsphere.cc0000664000175000017500000000357015160212070021532 0ustar alastairalastair/* * (C) Crown Copyright 2023 Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ #include "atlas/util/Geometry.h" #include "atlas/util/Point.h" #include "tests/AtlasTestEnvironment.h" namespace atlas { namespace test { using namespace atlas::util; CASE("great-circle course") { geometry::UnitSphere g; const auto point1 = PointLonLat(-71.6, -33.0); // Valparaiso const auto point2 = PointLonLat(121.8, 31.4); // Shanghai const auto point3 = PointLonLat(0., 89.); const auto point4 = PointLonLat(180., 89.); const auto point5 = PointLonLat(0., 90.); const auto point6 = PointLonLat(180., 90.); const auto targetCourse1 = -94.41; const auto targetCourse2 = -78.42; const auto targetCourse3 = 0.; const auto targetCourse4 = 180.; const auto targetCourse5 = 0.; const auto targetCourse6 = 180.; const auto targetCourse7 = 0.; const auto targetCourse8 = 0.; const auto [course1, course2] = g.greatCircleCourse(point1, point2); const auto [course3, course4] = g.greatCircleCourse(point3, point4); // Colocated points on pole. const auto [course5, course6] = g.greatCircleCourse(point5, point6); const auto [course7, course8] = g.greatCircleCourse(point5, point5); EXPECT_APPROX_EQ(course1, targetCourse1, 0.01); EXPECT_APPROX_EQ(course2, targetCourse2, 0.01); EXPECT_APPROX_EQ(course3, targetCourse3, 1.e-14); EXPECT_APPROX_EQ(std::abs(course4), targetCourse4, 1.e-14); EXPECT_APPROX_EQ(std::abs(course5), targetCourse5, 1.e-14); EXPECT_APPROX_EQ(std::abs(course6), targetCourse6, 1.e-14); EXPECT_APPROX_EQ(std::abs(course7), targetCourse7, 1.e-14); EXPECT_APPROX_EQ(std::abs(course8), targetCourse8, 1.e-14); } } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/util/fctest_parametrisation.F900000664000175000017500000001543215160212070023030 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. ! This File contains Unit Tests for testing the ! C++ / Fortran Interfaces to the Mesh Datastructure ! @author Willem Deconinck #include "fckit/fctest.h" ! ----------------------------------------------------------------------------- module fctest_atlas_Config_fixture use atlas_module use fckit_module, only : fckit_exception implicit none end module fctest_atlas_Config_fixture ! ----------------------------------------------------------------------------- TESTSUITE_WITH_FIXTURE(fctest_atlas_Config,fctest_atlas_Config_fixture) ! ----------------------------------------------------------------------------- TESTSUITE_INIT call atlas_library%initialise() END_TESTSUITE_INIT ! ----------------------------------------------------------------------------- TESTSUITE_FINALIZE call atlas_library%finalise() END_TESTSUITE_FINALIZE ! ----------------------------------------------------------------------------- TEST( test_parametrisation ) type(atlas_Config) :: params type(atlas_Config) :: nested type(atlas_Config), allocatable :: list(:) logical :: found integer :: intval integer :: j type(atlas_Config) :: anested type(atlas_Config), allocatable :: alist(:) ! --------------------- SET ------------------ ! Corresponding YAML: ! { ! nested: ! list: ! - ! l1: 21 ! l2: 22 ! - ! l1: 21 ! l2: 22 ! n1: 11 ! n2: 12 ! p1: 1 ! p2: 2 ! } params = atlas_Config() call params%set("p1",1) call params%set("p2",2) nested = atlas_Config() call nested%set("n1",11) call nested%set("n2",12) allocate( list(2) ) do j=1,2 list(j) = atlas_Config() call list(j)%set("l1",21) call list(j)%set("l2",22) enddo call nested%set("list",list) do j=1,2 call list(j)%final() enddo call params%set("nested",nested) call nested%final() ! --------------------- JSON ------------------ call atlas_log%info("params = "//params%json()) ! --------------------- GET ------------------ found = params%get("p1",intval) FCTEST_CHECK( found ) FCTEST_CHECK_EQUAL( intval , 1 ) found = params%get("nested",anested) FCTEST_CHECK( found ) found = anested%get("n1",intval) FCTEST_CHECK( found ) FCTEST_CHECK_EQUAL(intval, 11) found = anested%get("list",alist) FCTEST_CHECK( found ) FCTEST_CHECK_EQUAL( size(alist), 2 ) found = alist(1)%get("l1",intval) FCTEST_CHECK( found ) FCTEST_CHECK_EQUAL(intval, 21) found = alist(1)%get("l2",intval) FCTEST_CHECK( found ) FCTEST_CHECK_EQUAL(intval, 22) found = alist(2)%get("l1",intval) FCTEST_CHECK( found ) FCTEST_CHECK_EQUAL(intval, 21) found = alist(2)%get("l2",intval) FCTEST_CHECK( found ) FCTEST_CHECK_EQUAL(intval, 22) do j=1,size(alist) call alist(j)%final() enddo call anested%final() ! --------------------------------------------- call params%final() END_TEST ! ----------------------------------------------------------------------------- TEST(test_parametrisation_json_string) type(atlas_Config) :: params type(atlas_Config), allocatable :: records(:) type(atlas_JSON) :: json character (len=:), allocatable :: name character(len=1024) :: msg integer :: age integer :: jrec json=atlas_JSON('{"records":['//& & '{"name":"Joe", "age":30},'//& & '{"name":"Alison","age":43}' //& & ']}') params = atlas_Config(json) call atlas_log%info(params%json()) if( params%get("records",records) ) then do jrec=1,size(records) if( .not. records(jrec)%get("name",name) ) call fckit_exception%abort("name not found") if( .not. records(jrec)%get("age",age) ) call fckit_exception%abort("age not found") write(msg,'(2A,I0,A)') name," is ",age," years old"; call atlas_log%info(msg) enddo do jrec=1,size(records) call records(jrec)%final() enddo endif FCTEST_CHECK_EQUAL( params%owners() , 1 ) call params%final() END_TEST TEST(test_parametrisation_json_file) type(atlas_Config) :: params type(atlas_Config), allocatable :: records(:) type(atlas_Config) :: location character (len=:), allocatable :: name, company, street, city integer :: age integer :: jrec character(len=1024) :: msg type(atlas_PathName) :: json_file ! Write a json file OPEN (UNIT=9 , FILE="fctest_parametrisation.json", STATUS='REPLACE') write(9,'(A)') '{"location":{"city":"Reading","company":"ECMWF","street":"Shinfield Road"},'//& &'"records":[{"age":42,"name":"Anne"},{"age":36,"name":"Bob"}]}' CLOSE(9) json_file = atlas_PathName("fctest_parametrisation.json") params = atlas_Config( json_file ) ! params = atlas_Config( atlas_PathName("fctest_parametrisation.json") ) ! --> Does not work for XL compiler TODO: make reproducer call atlas_log%info("params = "//params%json()) if( params%get("records",records) ) then do jrec=1,size(records) if( .not. records(jrec)%get("name",name) ) call fckit_exception%abort("name not found") if( .not. records(jrec)%get("age",age) ) call fckit_exception%abort("age not found") write(msg,'(2A,I0,A)') name," is ",age," years old"; call atlas_log%info(msg) enddo do jrec=1,size(records) call records(jrec)%final() enddo endif if( params%get("location",location) ) then call atlas_log%info("location = "//location%json()) if( location%get("company",company) ) then write(0,*) "company = ",company endif if( location%get("street",street) ) then write(0,*) "street = ",street endif if( location%get("city",city) ) then write(0,*) "city = ",city endif call location%final() endif call params%final() END_TEST ! ----------------------------------------------------------------------------- TEST(test_json_file) type(atlas_JSON) :: json type(atlas_Config) :: config type(atlas_PathName) :: json_file ! Write a json file OPEN (UNIT=9 , FILE="fctest_parametrisation.json", STATUS='REPLACE') write(9,'(A)') '{"location":{"city":"Reading","company":"ECMWF","street":"Shinfield Road"},'//& &'"records":[{"age":42,"name":"Anne"},{"age":36,"name":"Bob"}]}' CLOSE(9) json_file = atlas_PathName( "fctest_parametrisation.json" ) json = atlas_JSON( json_file ) ! json = atlas_JSON( atlas_PathName("fctest_parametrisation.json") ) ! --> Does not work with XL compiler TODO: make reproducer call atlas_log%info("json = "//json%str()) config = atlas_Config( json ) call atlas_log%info("config = "//config%json()) END_TEST ! ----------------------------------------------------------------------------- END_TESTSUITE atlas-0.46.0/src/tests/util/fctest_logging.F900000664000175000017500000000501015160212070021243 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. ! This File contains Unit Tests for testing the ! C++ / Fortran Interfaces to the logging facilities ! @author Willem Deconinck #include "fckit/fctest.h" ! ----------------------------------------------------------------------------- module fcta_logging_fxt use atlas_module use, intrinsic :: iso_c_binding implicit none character(len=1024) :: msg end module fcta_logging_fxt ! ----------------------------------------------------------------------------- TESTSUITE_WITH_FIXTURE(fctest_atlas_logging,fcta_logging_fxt) ! ----------------------------------------------------------------------------- TESTSUITE_INIT call atlas_library%initialise() END_TESTSUITE_INIT ! ----------------------------------------------------------------------------- TESTSUITE_FINALIZE call atlas_library%finalise() END_TESTSUITE_FINALIZE ! ----------------------------------------------------------------------------- TEST( test_logging ) call atlas_log%info("test_logging begin") call atlas_log%debug("hello world") call atlas_log%warning("World is ending") write(msg,'(A)') "goodbye" call atlas_log%debug(msg,newl=.False.) call atlas_log%debug(" world") call atlas_log%error(" oops ") call atlas_log%info("test_logging end") call atlas_log%panic("AAAAAGRRG") END_TEST ! ----------------------------------------------------------------------------- !TEST( test_channel ) !type( atlas_logchannel ) :: info, debug, warning, error !call atlas_log%channel_info%log("atlas_log%channel_info%log() called") !info = atlas_log%channel(ATLAS_LOG_CATEGORY_INFO) !error = atlas_log%channel(ATLAS_LOG_CATEGORY_ERROR) !call info%log("info%log() called") !call error%log("error%log() called") !END_TEST ! ----------------------------------------------------------------------------- TEST( test_log_fortran_unit ) integer :: NULLOUT = 9 OPEN( NULLOUT, FILE='null.out' ) call atlas_log%set_fortran_unit(NULLOUT) call atlas_log%error("error to fortran") call atlas_log%info("info to fortran") call atlas_log%debug("debug to fortran") !call atlas_log%stats("stats to fortran") CLOSE( NULLOUT ) END_TEST ! ----------------------------------------------------------------------------- END_TESTSUITE atlas-0.46.0/src/tests/util/test_vector.cc0000664000175000017500000000351415160212070020644 0ustar alastairalastair/* * (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/library/config.h" #include "atlas/util/vector.h" #include "tests/AtlasTestEnvironment.h" namespace atlas { namespace test { //----------------------------------------------------------------------------- // template atlas::vector square(const int n) { atlas::vector x(n); for (int i = 0; i < n; i++) { x[i] = static_cast(i) * static_cast(i); } return x; } template void pp(const std::string& t, T& v) { Log::info() << t << " = " << std::endl; Log::info() << v.size() << std::endl; for (int i = 0; i < v.size(); i++) { Log::info() << i << " > " << v[i] << std::endl; } } CASE("test_vector") { const int N = 100; // Ctor atlas::vector x(N); std::iota(std::begin(x), std::end(x), 0); // Copy ctor atlas::vector y = x; EXPECT(x.size() == y.size()); for (int i = 0; i < x.size(); i++) { EXPECT(x[i] == y[i]); } // Assignment operator auto z = square(20); for (int i = 0; i < z.size(); i++) { EXPECT(z[i] == i * i); } // Assignment operator x = y; for (int i = 0; i < x.size(); i++) { EXPECT(static_cast(x[i]) == i); } } //----------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/util/test_polygon.cc0000664000175000017500000000654615160212070021041 0ustar alastairalastair/* * (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/util/Point.h" #include "atlas/util/SphericalPolygon.h" #include "tests/AtlasTestEnvironment.h" namespace atlas { namespace test { CASE("test_polygon_something") { using util::SphericalPolygon; using p = PointLonLat; using point_inside_t = std::pair; SphericalPolygon poly(std::vector{ p(122.143, 35.9951), p(120, 30.4576), p(118.125, 24.9199), p(116.471, 19.3822), p(121.765, 19.3822), p(120, 13.8445), p(118.421, 8.3067), p(117, 2.7689), p(112.5, 2.7689), p(112.5, -2.7689), p(117, -2.7689), p(113.684, -8.3067), p(118.421, -8.3067), p(115, -13.8445), p(120, -13.8445), p(116.471, -19.3822), p(112.5, -24.9199), p(118.125, -24.9199), p(114, -30.4576), p(120, -30.4576), p(115.714, -35.9951), p(122.143, -35.9951), p(128.571, -35.9951), p(135, -35.9951), p(141.429, -35.9951), p(147.857, -35.9951), p(154.286, -35.9951), p(160.714, -35.9951), p(167.143, -35.9951), p(173.571, -35.9951), p(180, -35.9951), p(186.429, -35.9951), p(192.857, -35.9951), p(199.286, -35.9951), p(205.714, -35.9951), p(212.143, -35.9951), p(218.571, -35.9951), p(225, -35.9951), p(231.429, -35.9951), p(237.857, -35.9951), p(240, -30.4576), p(234, -30.4576), p(236.25, -24.9199), p(238.235, -19.3822), p(232.941, -19.3822), p(235, -13.8445), p(236.842, -8.3067), p(238.5, -2.7689), p(234, -2.7689), p(234, 2.7689), p(238.5, 2.7689), p(241.579, 8.3067), p(236.842, 8.3067), p(240, 13.8445), p(235, 13.8445), p(238.235, 19.3822), p(241.875, 24.9199), p(236.25, 24.9199), p(240, 30.4576), p(244.286, 35.9951), p(237.857, 35.9951), p(231.429, 35.9951), p(225, 35.9951), p(218.571, 35.9951), p(212.143, 35.9951), p(205.714, 35.9951), p(199.286, 35.9951), p(192.857, 35.9951), p(186.429, 35.9951), p(180, 35.9951), p(173.571, 35.9951), p(167.143, 35.9951), p(160.714, 35.9951), p(154.286, 35.9951), p(147.857, 35.9951), p(141.429, 35.9951), p(135, 35.9951), p(128.571, 35.9951), p(122.143, 35.9951)}); // test some partitioning points that (approximately) exist in lon-lat // polygon, but not in a spherical polygon for (auto P : std::vector{ point_inside_t(p(118.8, 26.9135), true), point_inside_t(p(118.8, 19.3822), false), point_inside_t(p(118.8, 9.6359), true), point_inside_t(p(118.8, -13.8445), true), point_inside_t(p(118.8, -15.7275), false), point_inside_t(p(118.8, -30.4576), true), point_inside_t(p(118.8, -32.0080), false), point_inside_t(p(118.8, -35.9951), true)}) { // 'contains' uses spherical geometry EXPECT(poly.contains(P.first) == P.second); } } } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/util/fctest_geometry.F900000664000175000017500000001767415160212070021473 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. ! This File contains Unit Tests for testing the ! C++ / Fortran Interfaces to the Geometry Datastructure ! @author Benjamin Menetrier #include "fckit/fctest.h" #include "atlas/atlas_f.h" ! ----------------------------------------------------------------------------- module fcta_Geometry_fixture use atlas_module use, intrinsic :: iso_c_binding implicit none end module fcta_Geometry_fixture ! ----------------------------------------------------------------------------- TESTSUITE_WITH_FIXTURE(fctest_atlas_Geometry,fcta_Geometry_fixture) ! ----------------------------------------------------------------------------- TESTSUITE_INIT call atlas_library%initialise() END_TESTSUITE_INIT ! ----------------------------------------------------------------------------- TESTSUITE_FINALIZE use fckit_main_module call atlas_library%finalise() END_TESTSUITE_FINALIZE ! ----------------------------------------------------------------------------- TEST( test_geometry ) use fckit_log_module use fckit_c_interop_module implicit none type(atlas_Geometry) :: geometry real(c_double) :: lonlat1(2), lonlat2(2), lonlat(2) real(c_double) :: xyz1(3), xyz2(3), xyz(3) real(c_double) :: distance write(*,*) "test_geometry for UnitSphere starting" ! Check constructor geometry = atlas_Geometry("UnitSphere") write(0,*) "geometry%c_ptr() = ", c_ptr_to_loc(geometry%CPTR_PGIBUG_A) ! Define points lonlat1 = (/-71.6_c_double, -33._c_double/) lonlat2 = (/121.8_c_double, 31.4_c_double/) xyz1 = (/2.647e-1_c_double, -7.958e-1_c_double, -5.446e-1_c_double/) xyz2 = (/-4.498e-1_c_double, 7.254e-1_c_double, 5.210e-1_c_double/) ! Check conversion from cartesian to spherical call geometry%xyz2lonlat(xyz1(1), xyz1(2), xyz1(3), lonlat(1), lonlat(2)) FCTEST_CHECK_CLOSE( lonlat(1) , lonlat1(1), 1.e-2_c_double ) FCTEST_CHECK_CLOSE( lonlat(2) , lonlat1(2), 1.e-2_c_double ) call geometry%xyz2lonlat(xyz1, lonlat) FCTEST_CHECK_CLOSE( lonlat(1) , lonlat1(1), 1.e-2_c_double ) FCTEST_CHECK_CLOSE( lonlat(2) , lonlat1(2), 1.e-2_c_double ) ! Check conversion from spherical to cartesian call geometry%lonlat2xyz(lonlat1(1), lonlat1(2), xyz(1), xyz(2), xyz(3)) FCTEST_CHECK_CLOSE( xyz(1) , xyz1(1) , 1.e3_c_double ) FCTEST_CHECK_CLOSE( xyz(2) , xyz1(2) , 1.e3_c_double ) FCTEST_CHECK_CLOSE( xyz(3) , xyz1(3) , 1.e3_c_double ) call geometry%lonlat2xyz(lonlat1, xyz) FCTEST_CHECK_CLOSE( xyz(1) , xyz1(1) , 1.e3_c_double ) FCTEST_CHECK_CLOSE( xyz(2) , xyz1(2) , 1.e3_c_double ) FCTEST_CHECK_CLOSE( xyz(3) , xyz1(3) , 1.e3_c_double ) ! Check distance distance = geometry%distance(lonlat1(1), lonlat1(2), lonlat2(1), lonlat2(2)) FCTEST_CHECK_CLOSE( distance , 2.942_c_double , 1.e-3_c_double ) distance = geometry%distance(lonlat1, lonlat2) FCTEST_CHECK_CLOSE( distance , 2.942_c_double , 1.e-3_c_double ) distance = geometry%distance(xyz1(1), xyz1(2), xyz1(3), xyz2(1), xyz2(2), xyz2(3)) FCTEST_CHECK_CLOSE( distance , 2.942_c_double , 1.e-3_c_double ) distance = geometry%distance(xyz1, xyz2) FCTEST_CHECK_CLOSE( distance , 2.942_c_double , 1.e-3_c_double ) ! Check radius FCTEST_CHECK_EQUAL( geometry%radius() , 1.0_c_double ) ! Check area FCTEST_CHECK_CLOSE( geometry%area() , 1.257e1_c_double , 1.e-2_c_double ) ! Finalization call geometry%final() write(*,*) "test_geometry for Earth starting" ! Check constructor for Earth geometry = atlas_Geometry("Earth") write(0,*) "geometry%c_ptr() = ", c_ptr_to_loc(geometry%CPTR_PGIBUG_A) ! Define points lonlat1 = (/-71.6_c_double, -33._c_double/) lonlat2 = (/121.8_c_double, 31.4_c_double/) xyz1 = (/1.687e6_c_double, -5.070e6_c_double, -3.470e6_c_double/) xyz2 = (/-2.866e6_c_double, 4.622e6_c_double, 3.319e6_c_double/) ! Check conversion from cartesian to spherical call geometry%xyz2lonlat(xyz1(1), xyz1(2), xyz1(3), lonlat(1), lonlat(2)) FCTEST_CHECK_CLOSE( lonlat(1) , lonlat1(1), 1.e-2_c_double ) FCTEST_CHECK_CLOSE( lonlat(2) , lonlat1(2), 1.e-2_c_double ) call geometry%xyz2lonlat(xyz1, lonlat) FCTEST_CHECK_CLOSE( lonlat(1) , lonlat1(1), 1.e-2_c_double ) FCTEST_CHECK_CLOSE( lonlat(2) , lonlat1(2), 1.e-2_c_double ) ! Check conversion from spherical to cartesian call geometry%lonlat2xyz(lonlat1(1), lonlat1(2), xyz(1), xyz(2), xyz(3)) FCTEST_CHECK_CLOSE( xyz(1) , xyz1(1) , 1.e3_c_double ) FCTEST_CHECK_CLOSE( xyz(2) , xyz1(2) , 1.e3_c_double ) FCTEST_CHECK_CLOSE( xyz(3) , xyz1(3) , 1.e3_c_double ) call geometry%lonlat2xyz(lonlat1, xyz) FCTEST_CHECK_CLOSE( xyz(1) , xyz1(1) , 1.e3_c_double ) FCTEST_CHECK_CLOSE( xyz(2) , xyz1(2) , 1.e3_c_double ) FCTEST_CHECK_CLOSE( xyz(3) , xyz1(3) , 1.e3_c_double ) ! Check distance distance = geometry%distance(lonlat1(1), lonlat1(2), lonlat2(1), lonlat2(2)) FCTEST_CHECK_CLOSE( distance , 1.874e7_c_double , 1.e4_c_double ) distance = geometry%distance(lonlat1, lonlat2) FCTEST_CHECK_CLOSE( distance , 1.874e7_c_double , 1.e4_c_double ) distance = geometry%distance(xyz1(1), xyz1(2), xyz1(3), xyz2(1), xyz2(2), xyz2(3)) FCTEST_CHECK_CLOSE( distance , 1.874e7_c_double , 1.e4_c_double ) distance = geometry%distance(xyz1, xyz2) FCTEST_CHECK_CLOSE( distance , 1.874e7_c_double , 1.e4_c_double ) ! Check radius FCTEST_CHECK_EQUAL( geometry%radius() , 6371229.0_c_double ) ! Check area FCTEST_CHECK_CLOSE( geometry%area() , 5.101e14_c_double , 1.e11_c_double ) ! Finalization call geometry%final() write(*,*) "test_geometry for another planet (Mars here) starting" ! Check constructor for another planet (Mars here) geometry = atlas_Geometry( 3389500.0_c_double ) write(0,*) "geometry%c_ptr() = ", c_ptr_to_loc(geometry%CPTR_PGIBUG_A) ! Define points lonlat1 = (/-71.6_c_double, -33._c_double/) lonlat2 = (/121.8_c_double, 31.4_c_double/) xyz1 = (/8.973e5_c_double, -2.697e6_c_double, -1.846e6_c_double/) xyz2 = (/-1.525e6_c_double, 2.459e6_c_double, 1.766e6_c_double/) ! Check conversion from cartesian to spherical call geometry%xyz2lonlat(xyz1(1), xyz1(2), xyz1(3), lonlat(1), lonlat(2)) FCTEST_CHECK_CLOSE( lonlat(1) , lonlat1(1), 1.e-2_c_double ) FCTEST_CHECK_CLOSE( lonlat(2) , lonlat1(2), 1.e-2_c_double ) call geometry%xyz2lonlat(xyz1, lonlat) FCTEST_CHECK_CLOSE( lonlat(1) , lonlat1(1), 1.e-2_c_double ) FCTEST_CHECK_CLOSE( lonlat(2) , lonlat1(2), 1.e-2_c_double ) ! Check conversion from spherical to cartesian call geometry%lonlat2xyz(lonlat1(1), lonlat1(2), xyz(1), xyz(2), xyz(3)) FCTEST_CHECK_CLOSE( xyz(1) , xyz1(1) , 1.e3_c_double ) FCTEST_CHECK_CLOSE( xyz(2) , xyz1(2) , 1.e3_c_double ) FCTEST_CHECK_CLOSE( xyz(3) , xyz1(3) , 1.e3_c_double ) call geometry%lonlat2xyz(lonlat1, xyz) FCTEST_CHECK_CLOSE( xyz(1) , xyz1(1) , 1.e3_c_double ) FCTEST_CHECK_CLOSE( xyz(2) , xyz1(2) , 1.e3_c_double ) FCTEST_CHECK_CLOSE( xyz(3) , xyz1(3) , 1.e3_c_double ) ! Check distance distance = geometry%distance(lonlat1(1), lonlat1(2), lonlat2(1), lonlat2(2)) FCTEST_CHECK_CLOSE( distance , 9.971e6_c_double , 1.e3_c_double ) distance = geometry%distance(lonlat1, lonlat2) FCTEST_CHECK_CLOSE( distance , 9.971e6_c_double , 1.e3_c_double ) distance = geometry%distance(xyz1(1), xyz1(2), xyz1(3), xyz2(1), xyz2(2), xyz2(3)) FCTEST_CHECK_CLOSE( distance , 9.971e6_c_double , 1.e3_c_double ) distance = geometry%distance(xyz1, xyz2) FCTEST_CHECK_CLOSE( distance , 9.971e6_c_double , 1.e3_c_double ) ! Check radius FCTEST_CHECK_EQUAL( geometry%radius() , 3389500.0_c_double ) ! Check area FCTEST_CHECK_CLOSE( geometry%area() , 1.444e14_c_double , 1.e11_c_double ) ! Finalization call geometry%final() END_TEST ! ----------------------------------------------------------------------------- END_TESTSUITE atlas-0.46.0/src/tests/util/CMakeLists.txt0000664000175000017500000000551315160212070020535 0ustar alastairalastair# (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. if( HAVE_FCTEST ) add_fctest( TARGET atlas_fctest_functions LINKER_LANGUAGE Fortran SOURCES fctest_functions.F90 LIBS atlas_f ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) add_fctest( TARGET atlas_fctest_logging LINKER_LANGUAGE Fortran SOURCES fctest_logging.F90 LIBS atlas_f ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) add_fctest( TARGET atlas_fctest_parametrisation CONDITION NOT atlas_fctest_parametrisation_DISABLED LINKER_LANGUAGE Fortran SOURCES fctest_parametrisation.F90 LIBS atlas_f ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) add_fctest( TARGET atlas_fctest_metadata LINKER_LANGUAGE Fortran SOURCES fctest_metadata.F90 LIBS atlas_f ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) add_fctest( TARGET atlas_fctest_geometry LINKER_LANGUAGE Fortran SOURCES fctest_geometry.F90 LIBS atlas_f ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) add_fctest( TARGET atlas_fctest_kdtree LINKER_LANGUAGE Fortran SOURCES fctest_kdtree.F90 LIBS atlas_f ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) endif() foreach( test util earth flags polygon point ) ecbuild_add_test( TARGET atlas_test_${test} SOURCES test_${test}.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) endforeach() ecbuild_add_test( TARGET atlas_test_vector SOURCES test_vector.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) ecbuild_add_test( TARGET atlas_test_metadata MPI 4 CONDITION eckit_HAVE_MPI AND MPI_SLOTS GREATER_EQUAL 4 SOURCES test_metadata.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) ecbuild_add_test( TARGET atlas_test_kdtree SOURCES test_kdtree.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} CONDITION atlas_HAVE_ATLAS_GRID ) ecbuild_add_test( TARGET atlas_test_convexsphericalpolygon SOURCES test_convexsphericalpolygon.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) ecbuild_add_test( TARGET atlas_test_convexsphericalmeanvalue SOURCES test_convexsphericalmeanvalue.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) ecbuild_add_test( TARGET atlas_test_unitsphere SOURCES test_unitsphere.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) ecbuild_add_test( TARGET atlas_test_pack_vector_fields SOURCES test_pack_vector_fields.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) atlas-0.46.0/src/tests/runtime/0000775000175000017500000000000015160212070016477 5ustar alastairalastairatlas-0.46.0/src/tests/runtime/test_library.cc0000664000175000017500000000105315160212070021510 0ustar alastairalastair/* * (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 "atlas/library.h" int main(int argc, char** argv) { atlas::initialise(argc,argv); atlas::finalise(); } atlas-0.46.0/src/tests/runtime/test_library_noinit_final.cc0000664000175000017500000000176315160212070024251 0ustar alastairalastair/* * (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/runtime/Main.h" #include "atlas/library.h" int main(int argc, char** argv) { try { atlas::Library::instance().registerDataPath("bogus"); } catch (std::exception& e) { // Attempting to access a non-existent instance of eckit::Main() eckit::Main::initialise(argc,argv); atlas::Library::instance().registerDataPath("bogus"); std::cout << "atlas::Library::instance().dataPath() : " << atlas::Library::instance().dataPath() << std::endl; atlas::finalise(); return 0; } return 1; } atlas-0.46.0/src/tests/runtime/test_library_noargs.cc0000664000175000017500000000152115160212070023061 0ustar alastairalastair/* * (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/runtime/Main.h" #include "atlas/library.h" int main(int argc, char** argv) { try { atlas::initialise(); } catch (std::exception& e) { // Attempting to access a non-existent instance of eckit::Main() eckit::Main::initialise(argc,argv); atlas::initialise(); atlas::finalise(); return 0; // SUCCESS } return 1; // FAILED } atlas-0.46.0/src/tests/runtime/test_trace.cc0000664000175000017500000000577215160212070021156 0ustar alastairalastair/* * (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 "atlas/parallel/omp/omp.h" #include "atlas/runtime/Trace.h" #include "tests/AtlasTestEnvironment.h" namespace atlas { namespace test { static void work() { std::this_thread::sleep_for(std::chrono::milliseconds(10)); } CASE("test elapsed") { auto trace = Trace(Here()); EXPECT(trace.running()); EXPECT(trace.elapsed() == 0.); work(); trace.pause(); EXPECT(trace.running()); double elapsed = trace.elapsed(); EXPECT(elapsed != 0.); EXPECT(trace.elapsed() == elapsed); trace.resume(); work(); EXPECT(trace.running()); trace.stop(); EXPECT(trace.elapsed() != elapsed); } CASE("test trace OpenMP") { atlas_omp_parallel_for(int i = 0; i < 10; ++i) { auto trace = Trace(Here(), "loop"); if (ATLAS_HAVE_OMP) { work(); trace.stop(); if (atlas_omp_get_thread_num() > 0) { EXPECT(trace.elapsed() == 0.); } else { EXPECT(trace.elapsed() != 0.); } } } } CASE("test barrier") { EXPECT(runtime::trace::Barriers::state() == Library::instance().traceBarriers()); { runtime::trace::Barriers set_barriers(true); EXPECT(runtime::trace::Barriers::state() == true); { runtime::trace::Barriers set_barriers(false); EXPECT(runtime::trace::Barriers::state() == false); } EXPECT(runtime::trace::Barriers::state() == true); } EXPECT(runtime::trace::Barriers::state() == Library::instance().traceBarriers()); } // -------------------------------------------------------------------------- void haloexchange() { ATLAS_TRACE(); } void wind_const() { ATLAS_TRACE(); } void wind_next() { ATLAS_TRACE(); haloexchange(); } static int count = 0; void dp_meth() { ATLAS_TRACE(); if (count == 0) { wind_const(); } else { wind_next(); } ++count; } void tracer_interpolation() { ATLAS_TRACE(); haloexchange(); } void execute_sladv() { ATLAS_TRACE(); dp_meth(); tracer_interpolation(); } CASE("test report") { SECTION("1") { for (int i = 0; i < 3; ++i) { execute_sladv(); } } SECTION("2") { count = 0; for (int i = 0; i < 3; ++i) { execute_sladv(); } } Log::info() << atlas::Trace::report() << std::endl; } // -------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/runtime/fctest_trace.fypp0000664000175000017500000000402715160212070022050 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. ! This File contains Unit Tests for testing the ! C++ / Fortran Interfaces to the Mesh Datastructure ! @author Willem Deconinck #include "fckit/fctest.h" #:include "atlas/atlas_f.fypp" ! ----------------------------------------------------------------------------- module fcta_trace_fixture use atlas_module use, intrinsic :: iso_c_binding @:ENABLE_ATLAS_MACROS() implicit none contains end module ! ----------------------------------------------------------------------------- TESTSUITE_WITH_FIXTURE(fctest_atlas_trace,fcta_trace_fixture) ! ----------------------------------------------------------------------------- subroutine sub2() type(atlas_Trace) :: trace @:ATLAS_TRACE_BEGIN( trace, "sub2" ) FCTEST_CHECK( trace%running() ) @:ATLAS_TRACE_END( trace ) end subroutine subroutine sub1() integer :: i type(atlas_Trace) :: trace trace = @{ ATLAS_TRACE( "sub1" ) }@ FCTEST_CHECK( trace%running() ) do i=1,10 call sub2() enddo call trace%final() end subroutine TESTSUITE_INIT call atlas_library%initialise() END_TESTSUITE_INIT ! ----------------------------------------------------------------------------- TESTSUITE_FINALIZE use fckit_main_module call atlas_library%finalise() END_TESTSUITE_FINALIZE ! ----------------------------------------------------------------------------- TEST( test_trace ) implicit none type(atlas_Trace) :: trace trace = atlas_Trace("${_FILE_}$",${_LINE_}$,"test_trace") FCTEST_CHECK( trace%running() ) call sub1() call trace%stop() FCTEST_CHECK( .not. trace%running() ) call trace%final() END_TEST ! ----------------------------------------------------------------------------- END_TESTSUITE atlas-0.46.0/src/tests/runtime/test_library_init_nofinal.cc0000664000175000017500000000127615160212070024250 0ustar alastairalastair/* * (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/library.h" int main(int argc, char** argv) { atlas::initialise(argc,argv); atlas::Library::instance().registerDataPath("bogus"); std::cout << "atlas::Library::instance().dataPath() : " << atlas::Library::instance().dataPath() << std::endl; return 0; } atlas-0.46.0/src/tests/runtime/CMakeLists.txt0000664000175000017500000000202415160212070021235 0ustar alastairalastair# (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. ecbuild_add_test( TARGET atlas_test_trace SOURCES test_trace.cc LIBS atlas ${OMP_CXX} ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ATLAS_TRACE_REPORT=1 OMP 2 ) foreach( test test_library test_library_noargs test_library_init_nofinal test_library_noinit_final ) ecbuild_add_test( TARGET atlas_${test} SOURCES ${test}.cc LIBS atlas ENVIRONMENT ATLAS_TRACE_REPORT=1 ATLAS_DEBUG=1 ) endforeach() if( HAVE_FCTEST ) add_fctest( TARGET atlas_fctest_trace SOURCES fctest_trace.fypp LINKER_LANGUAGE Fortran LIBS atlas_f ${OMP_Fortran} ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ATLAS_TRACE_REPORT=1 ) endif() atlas-0.46.0/src/tests/interpolation/0000775000175000017500000000000015160212070017703 5ustar alastairalastairatlas-0.46.0/src/tests/interpolation/test_interpolation_global_matrix.cc0000664000175000017500000002675315160212070027061 0ustar alastairalastair/* * (C) Copyright 2025- ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "atlas/array.h" #include "atlas/field/Field.h" #include "atlas/field/FieldSet.h" #include "atlas/functionspace/NodeColumns.h" #include "atlas/functionspace/PointCloud.h" #include "atlas/functionspace/StructuredColumns.h" #include "atlas/grid/Grid.h" #include "atlas/grid/Iterator.h" #include "atlas/interpolation.h" #include "atlas/interpolation/AssembleGlobalMatrix.h" #include "atlas/mesh/Mesh.h" #include "atlas/meshgenerator.h" #include "atlas/util/CoordinateEnums.h" #include "atlas/util/function/VortexRollup.h" #include "atlas/linalg/sparse.h" #include "atlas/functionspace/CellColumns.h" #include "tests/AtlasTestEnvironment.h" using atlas::functionspace::PointCloud; using atlas::functionspace::NodeColumns; using atlas::functionspace::StructuredColumns; using atlas::util::Config; namespace atlas::test { //----------------------------------------------------------------------------- Config interpolation_config(std::string scheme_str) { Config scheme; scheme.set("matrix_free", false); scheme.set("type", scheme_str); scheme.set("halo", 1); if (scheme_str.find("cubic") != std::string::npos) { scheme.set("halo", 2); } if (scheme_str == "k-nearest-neighbours") { scheme.set("k-nearest-neighbours", 4); scheme.set("halo", 2); } if (scheme_str == "conservative-spherical-polygon") { scheme.set("halo", 2); scheme.set("order", 2); scheme.set("src_cell_data", false); scheme.set("tgt_cell_data", false); } return scheme; } Config create_fspaces(const std::string& scheme_str, const Grid& input_grid, const Grid& output_grid, FunctionSpace& fs_in, FunctionSpace& fs_out) { Config scheme = interpolation_config(scheme_str); auto scheme_type = scheme.getString("type"); if (scheme_type == "finite-element") { auto inmesh = Mesh(input_grid); auto outmesh = Mesh(output_grid, grid::MatchingPartitioner(inmesh)); fs_in = NodeColumns(inmesh, scheme); fs_out = NodeColumns(outmesh); } else if (scheme_type == "unstructured-bilinear-lonlat") { auto inmesh = Mesh(input_grid); fs_in = NodeColumns(input_grid, scheme); fs_out = PointCloud(output_grid, grid::MatchingPartitioner(inmesh)); } else if (scheme_type == "conservative-spherical-polygon") { bool src_cell_data = scheme.getBool("src_cell_data"); bool tgt_cell_data = scheme.getBool("tgt_cell_data"); auto tgt_mesh_config = output_grid.meshgenerator() | option::halo(0); auto tgt_mesh = MeshGenerator(tgt_mesh_config).generate(output_grid); if (tgt_cell_data) { fs_out = functionspace::CellColumns(tgt_mesh, option::halo(0)); } else { fs_out = functionspace::NodeColumns(tgt_mesh, option::halo(1)); } auto src_mesh_config = input_grid.meshgenerator() | option::halo(2); Mesh src_mesh; if (mpi::size() > 1) { src_mesh = MeshGenerator(src_mesh_config).generate(input_grid, grid::MatchingPartitioner(tgt_mesh)); } else { src_mesh = MeshGenerator(src_mesh_config).generate(input_grid); } if (src_cell_data) { fs_in = functionspace::CellColumns(src_mesh, option::halo(2)); } else { fs_in = functionspace::NodeColumns(src_mesh, option::halo(2)); } } else { fs_in = StructuredColumns(input_grid, scheme); fs_out = StructuredColumns(output_grid, grid::MatchingPartitioner(fs_in), scheme); } return scheme; } CASE("test_interpolation_global_matrix_vs_distributed_interpolation") { using SparseMatrixStorage = atlas::linalg::SparseMatrixStorage; auto assemble_global_matrix = [&](const std::string scheme_str, const Grid& input_grid, const Grid& output_grid, const int mpi_root) { Interpolation interpolator; FunctionSpace fs_in; FunctionSpace fs_out; ATLAS_TRACE_SCOPE("Create interpolation in assembling") { auto scheme = create_fspaces(scheme_str, input_grid, output_grid, fs_in, fs_out); if (scheme_str == "conservative") { interpolator = Interpolation{ scheme, input_grid, output_grid }; fs_in = interpolator.source(); fs_out = interpolator.target(); } else { interpolator = Interpolation{ scheme, fs_in, fs_out }; } } auto tgt_field = interpolator.target().createField(); ATLAS_TRACE_SCOPE("parallel interpolation [reference]") { auto field_in = interpolator.source().createField(); auto lonlat_in = array::make_view(interpolator.source().lonlat()); auto view_in = array::make_view(field_in); for(idx_t j=0; j src_data(input_grid.size()); std::vector tgt_data(output_grid.size()); ATLAS_TRACE_SCOPE("initialize source") { idx_t n{0}; for (auto p : input_grid.lonlat()) { src_data[n++] = util::function::vortex_rollup(p.lon(), p.lat(), 1.); } } // gather the global field from the distributed one auto tgt_field_global = interpolator.target().createField(option::global(mpi_root)); tgt_field.haloExchange(); interpolator.target().gather(tgt_field, tgt_field_global); if (mpi::comm().rank() == mpi_root) { ATLAS_TRACE_SCOPE("spmv") { auto src = eckit::linalg::Vector(src_data.data(), src_data.size()); auto tgt = eckit::linalg::Vector(tgt_data.data(), tgt_data.size()); auto eckit_matrix = atlas::linalg::make_non_owning_eckit_sparse_matrix(matrix); #if ATLAS_ECKIT_VERSION_AT_LEAST(1, 19, 0) eckit::linalg::LinearAlgebraSparse::backend().spmv(eckit_matrix, src, tgt); #else eckit::linalg::LinearAlgebra::backend().spmv(eckit_matrix, src, tgt); #endif } auto tfield_global_v = array::make_view(tgt_field_global); for (gidx_t i = 0; i < tgt_data.size(); ++i) { EXPECT_APPROX_EQ(tgt_data[i], tfield_global_v(i), 1.e-14); } } // avoid deadlocks whilst waiting for proc mpi_root mpi::comm().barrier(); return std::make_tuple(fs_in, fs_out, std::move(matrix)); }; auto do_assemble_distribute_matrix = [&](const std::string scheme_str, const Grid& input_grid, const Grid& output_grid, const int mpi_root) { Log::info() << "[TEST] assemble / distribute from " << scheme_str << ", " << input_grid.name() << " - " << output_grid.name() << std::endl; FunctionSpace fs_in; FunctionSpace fs_out; SparseMatrixStorage gmatrix; std::tie(fs_in, fs_out, gmatrix) = assemble_global_matrix(scheme_str, input_grid, output_grid, mpi_root); Interpolation interpolator; ATLAS_TRACE_SCOPE("Create distributed interpolation from the assembled global matrix") { SparseMatrixStorage matrix = interpolation::distribute_global_matrix(fs_in, fs_out, gmatrix, mpi_root); interpolation::MatrixCache cache(std::move(matrix)); Config scheme = interpolation_config(scheme_str); interpolator = Interpolation{ scheme, fs_in, fs_out, cache }; } std::vector tgt_data(output_grid.size()); ATLAS_TRACE_SCOPE("TEST Compute the global field from the global matrix") { if (mpi::comm().rank() == mpi_root) { std::vector src_data(input_grid.size()); auto src = eckit::linalg::Vector(src_data.data(), src_data.size()); idx_t n{0}; for (auto p : input_grid.lonlat()) { src_data[n++] = util::function::vortex_rollup(p.lon(), p.lat(), 1.); } auto tgt = eckit::linalg::Vector(tgt_data.data(), tgt_data.size()); auto eckit_matrix = atlas::linalg::make_non_owning_eckit_sparse_matrix(gmatrix); #if ATLAS_ECKIT_VERSION_AT_LEAST(1, 19, 0) eckit::linalg::LinearAlgebraSparse::backend().spmv(eckit_matrix, src, tgt); #else eckit::linalg::LinearAlgebra::backend().spmv(eckit_matrix, src, tgt); #endif } } auto tgt_field_global = interpolator.target().createField(option::global(mpi_root)); ATLAS_TRACE_SCOPE("TEST Compute the global field from the distributed interpolation") { auto tgt_field = interpolator.target().createField(); auto field_in = interpolator.source().createField(); auto lonlat_in = array::make_view(interpolator.source().lonlat()); auto field_in_v = array::make_view(field_in); for(idx_t j = 0; j < field_in.size(); ++j) { field_in_v(j) = util::function::vortex_rollup(lonlat_in(j,0), lonlat_in(j,1), 1.); } interpolator.execute(field_in, tgt_field); tgt_field.haloExchange(); interpolator.target().gather(tgt_field, tgt_field_global); } ATLAS_TRACE_SCOPE("TEST Compare the two global fields") { if (mpi::comm().rank() == mpi_root) { auto tfield_global_v = array::make_view(tgt_field_global); for (gidx_t i = 0; i < tgt_data.size(); ++i) { EXPECT_APPROX_EQ(tgt_data[i], tfield_global_v(i), 1.e-14); } Field fdiff = interpolator.target().createField(option::global(mpi_root)); auto fdiff_v = array::make_view(fdiff); for (gidx_t p = 0; p < fdiff_v.size(); ++p) { fdiff_v(p) = tfield_global_v(p) - tgt_data[p]; } } } }; auto test_matrix_assemble_distribute = [&](const Grid& input_grid, const Grid& output_grid) { int mpi_root = 0; do_assemble_distribute_matrix("structured-bilinear", input_grid, output_grid, mpi_root); mpi_root = mpi::size() - 1; std::vector interpolators; interpolators.insert(interpolators.end(), {"structured-bilinear", "structured-bicubic", "structured-biquasicubic"}); interpolators.insert(interpolators.end(), {"nearest-neighbour", "k-nearest-neighbours", "conservative-spherical-polygon", "finite-element", "unstructured-bilinear-lonlat"}); for (auto scheme_str : interpolators) { do_assemble_distribute_matrix(scheme_str, input_grid, output_grid, mpi_root); } }; test_matrix_assemble_distribute(Grid("O48"), Grid("F48")); } } // namespace int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/interpolation/test_interpolation_spherical_vector.cc0000664000175000017500000006501715160212070027565 0ustar alastairalastair/* * (C) Crown Copyright 2024 Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ #include #include #include #include "atlas/array.h" #include "atlas/array/helpers/ArrayForEach.h" #include "atlas/field.h" #include "atlas/functionspace.h" #include "atlas/grid.h" #include "atlas/interpolation.h" #include "atlas/mesh.h" #include "atlas/meshgenerator.h" #include "atlas/output/Gmsh.h" #include "atlas/util/Config.h" #include "atlas/util/Constants.h" #include "atlas/util/Point.h" #include "atlas/util/function/VortexRollup.h" #include "tests/AtlasTestEnvironment.h" namespace atlas { namespace test { using namespace atlas::util; using namespace atlas::array::helpers; constexpr auto Rank2dField = 2; constexpr auto Rank3dField = 3; // Return (u, v) field with vortex_rollup as the streamfunction. // This has no physical significance, but it makes a nice swirly field. std::pair vortexHorizontal(double lon, double lat) { // set hLon and hLat step size. const double hLon = 0.0001; const double hLat = 0.0001; // Get finite differences. const double u = (function::vortex_rollup(lon, lat + 0.5 * hLat, 0.1) - function::vortex_rollup(lon, lat - 0.5 * hLat, 0.1)) / hLat; const double v = -(function::vortex_rollup(lon + 0.5 * hLon, lat, 0.1) - function::vortex_rollup(lon - 0.5 * hLon, lat, 0.1)) / (hLon * std::cos(lat * util::Constants::degreesToRadians())); return std::make_pair(u, v); } double vortexVertical(double lon, double lat) { return function::vortex_rollup(lon, lat, 0.1); } void gmshOutput(const std::string& fileName, const FieldSet& fieldSet) { const auto functionSpace = fieldSet[0].functionspace(); const auto structuredColumns = functionspace::StructuredColumns(functionSpace); const auto nodeColumns = functionspace::NodeColumns(functionSpace); const auto mesh = structuredColumns ? Mesh(structuredColumns.grid()) : nodeColumns.mesh(); const auto gmshConfig = Config("coordinates", "xyz") | Config("ghost", true) | Config("info", true); const auto gmsh = output::Gmsh(fileName, gmshConfig); gmsh.write(mesh); gmsh.write(fieldSet, functionSpace); } // Helper function to generate a NodeColumns functionspace auto generateNodeColums(const std::string& gridName, const std::string& meshName) { const auto grid = Grid(gridName); const auto mesh = MeshGenerator(meshName).generate(grid); return functionspace::NodeColumns(mesh); } // Helper function to create part-empty PointCloud auto generateEmptyPointCloud() { const auto functionSpace = functionspace::PointCloud(std::vector{}); return functionSpace; } // Helper struct to key different Functionspaces to strings struct FunctionSpaceFixtures { static const FunctionSpace& get(const std::string& fixture) { static auto functionSpaces = std::map{ {"cubedsphere_mesh", generateNodeColums("CS-LFR-48", "cubedsphere_dual")}, {"gaussian_mesh", generateNodeColums("O48", "structured")}, {"structured_columns", functionspace::StructuredColumns(Grid("O48"), option::halo(1))}, {"structured_columns_classic", functionspace::StructuredColumns(Grid("F48"), option::halo(1))}, {"structured_columns_classic_halo2", functionspace::StructuredColumns(Grid("F48"), option::halo(2))}, {"structured_columns_classic_highres_halo2", functionspace::StructuredColumns(Grid("F96"), option::halo(2))}, {"structured_columns_halo2", functionspace::StructuredColumns(Grid("O48"), option::halo(2))}, {"structured_columns_lowres", functionspace::StructuredColumns(Grid("O24"), option::halo(1))}, {"structured_columns_hires", functionspace::StructuredColumns(Grid("O96"), option::halo(1))}, {"empty_point_cloud", generateEmptyPointCloud()}}; return functionSpaces.at(fixture); } }; // Helper struct to key different grid configs to strings struct FieldSpecFixtures { static const Config& get(const std::string& fixture) { static const auto fieldSpecs = std::map{ {"scalar", option::name("test field") | option::variables(1) | option::type("scalar")}, {"2vector", option::name("test field") | option::variables(2) | option::type("vector")}, {"3vector", option::name("test field") | option::variables(3) | option::type("vector")}}; return fieldSpecs.at(fixture); } }; // Helper struct to key different interpolation schemes to strings struct InterpSchemeFixtures { static const Config& get(const std::string& fixture) { static const auto cubedsphereBilinear = option::type("cubedsphere-bilinear") | Config("adjoint", true); static const auto sphericalMeanValue = option::type("spherical-mean-value") | Config("normalisation", false); static const auto sphericalMeanValueNormalised = option::type("spherical-mean-value") | Config("normalisation", true); static const auto finiteElement = option::type("finite-element") | Config("adjoint", true); static const auto structuredLinear = option::type("structured-bilinear") | option::halo(1) | Config("adjoint", true); static const auto structuredCubic = option::type("structured-bicubic") | option::halo(2) | Config("adjoint", true); static const auto sphericalVector = option::type("spherical-vector") | Config("adjoint", true); static const auto interpSchemes = std::map{ {"cubedsphere_bilinear", cubedsphereBilinear}, {"finite_element", finiteElement}, {"structured_linear", structuredLinear}, {"structured_cubic", structuredCubic}, {"cubedsphere_bilinear_spherical", sphericalVector | Config("scheme", cubedsphereBilinear)}, {"finite_element_spherical", sphericalVector | Config("scheme", finiteElement)}, {"structured_linear_spherical", sphericalVector | Config("scheme", structuredLinear)}, {"structured_cubic_spherical", sphericalVector | Config("scheme", structuredCubic)}, {"spherical_mean_value", sphericalVector | Config("scheme", sphericalMeanValue)}, {"spherical_mean_value_normalised", sphericalVector | Config("scheme", sphericalMeanValueNormalised)}}; return interpSchemes.at(fixture); } }; template double dotProduct(const array::ArrayView& a, const array::ArrayView& b) { auto dotProd = 0.; arrayForEachDim(std::make_integer_sequence{}, std::tie(a, b), [&](const double& aElem, const double& bElem) { dotProd += aElem * bElem; }); return dotProd; } template int countNans(const array::ArrayView& view) { auto nNans = 0; arrayForEachDim(std::make_integer_sequence{}, std::tie(view), [&](const double& viewElem) { if (!std::isfinite(viewElem)) { ++nNans; } }); return nNans; } template void testInterpolation(const Config& config) { const auto sourceFunctionSpace = FunctionSpaceFixtures::get(config.getString("source_fixture")); const auto targetFunctionSpace = FunctionSpaceFixtures::get(config.getString("target_fixture")); auto sourceFieldSet = FieldSet{}; auto targetFieldSet = FieldSet{}; const auto sourceLonLat = array::make_view(sourceFunctionSpace.lonlat()); const auto targetLonLat = array::make_view(targetFunctionSpace.lonlat()); auto fieldSpec = FieldSpecFixtures::get(config.getString("field_spec_fixture")); if constexpr (Rank == 3) { fieldSpec.set("levels", 2); } auto sourceField = sourceFieldSet.add(sourceFunctionSpace.createField(fieldSpec)); auto targetField = targetFieldSet.add(targetFunctionSpace.createField(fieldSpec)); auto sourceView = array::make_view(sourceField); auto targetView = array::make_view(targetField); ArrayForEach<0>::apply( std::tie(sourceLonLat, sourceView), [](auto&& lonLat, auto&& sourceColumn) { const auto setElems = [&](auto&& sourceElem) { if (sourceElem.size() == 1) { sourceElem(0) = vortexVertical(lonLat(0), lonLat(1)); } else { std::tie(sourceElem(0), sourceElem(1)) = vortexHorizontal(lonLat(0), lonLat(1)); if (sourceElem.size() == 3) { sourceElem(2) = vortexVertical(lonLat(0), lonLat(1)); } } }; if constexpr (Rank == 2) { setElems(sourceColumn); } else if constexpr (Rank == 3) { ArrayForEach<0>::apply(std::tie(sourceColumn), setElems); } }); const auto interp = Interpolation( InterpSchemeFixtures::get(config.getString("interp_fixture")), sourceFunctionSpace, targetFunctionSpace); interp.execute(sourceFieldSet, targetFieldSet); targetFieldSet.haloExchange(); auto errorFieldSpec = fieldSpec; errorFieldSpec.remove("variables").set("name", "error field"); auto errorView = array::make_view(targetFieldSet.add( targetFunctionSpace.createField(errorFieldSpec))); errorView.assign(0.); if (config.has("tol")) { auto maxError = 0.; ArrayForEach<0>::apply( std::tie(targetLonLat, targetView, errorView), [&](auto&& lonLat, auto&& targetColumn, auto&& errorColumn) { const auto calcError = [&](auto&& targetElem, auto&& errorElem) { auto trueValue = std::vector(targetElem.size()); if (targetElem.size() == 1) { trueValue[0] = vortexVertical(lonLat(0), lonLat(1)); } else { std::tie(trueValue[0], trueValue[1]) = vortexHorizontal(lonLat(0), lonLat(1)); if (targetElem.size() == 3) { trueValue[2] = vortexVertical(lonLat(0), lonLat(1)); } } auto errorSqrd = 0.; for (auto k = 0; k < targetElem.size(); ++k) { errorSqrd += (targetElem(k) - trueValue[k]) * (targetElem(k) - trueValue[k]); } errorElem = std::sqrt(errorSqrd); maxError = std::max(maxError, static_cast(errorElem)); }; if constexpr (Rank == 2) { calcError(targetColumn, errorColumn); } else if constexpr (Rank == 3) { ArrayForEach<0>::apply(std::tie(targetColumn, errorColumn), calcError); } }); EXPECT_APPROX_EQ(maxError, 0., config.getDouble("tol")); } if (config.has("file_id")) { gmshOutput(config.getString("file_id") + "_source.msh", sourceFieldSet); gmshOutput(config.getString("file_id") + "_target.msh", targetFieldSet); } // Adjoint test auto targetAdjoint = targetFunctionSpace.createField(fieldSpec); auto targetAdjointView = array::make_view(targetAdjoint); targetAdjoint.array().copy(targetField); targetAdjoint.adjointHaloExchange(); auto sourceAdjoint = sourceFunctionSpace.createField(fieldSpec); auto sourceAdjointView = array::make_view(sourceAdjoint); sourceAdjointView.assign(0.); interp.execute_adjoint(sourceAdjoint, targetAdjoint); // Check fields for nans or +/-inf EXPECT_EQ(countNans(targetView), 0); EXPECT_EQ(countNans(sourceView), 0); EXPECT_EQ(countNans(targetAdjointView), 0); EXPECT_EQ(countNans(sourceAdjointView), 0); constexpr auto tinyNum = 1e-13; const auto targetDotTarget = dotProduct(targetView, targetView); const auto sourceDotSourceAdjoint = dotProduct(sourceView, sourceAdjointView); if (targetFunctionSpace.size() > 0) { const auto dotProdRatio = targetDotTarget / sourceDotSourceAdjoint; EXPECT_APPROX_EQ(dotProdRatio, 1., tinyNum); } } CASE("cubed sphere CS-LFR-48 scalar interpolation (3d-field, scalar)") { const auto config = Config("source_fixture", "cubedsphere_mesh") .set("target_fixture", "gaussian_mesh") .set("field_spec_fixture", "scalar") .set("interp_fixture", "cubedsphere_bilinear_spherical") .set("file_id", "spherical_vector_cs2") .set("tol", 0.00096); testInterpolation((config)); } CASE("cubed sphere CS-LFR-48 vector interpolation (3d-field, 2-vector)") { const auto config = Config("source_fixture", "cubedsphere_mesh") .set("target_fixture", "gaussian_mesh") .set("field_spec_fixture", "2vector") .set("interp_fixture", "cubedsphere_bilinear_spherical") .set("file_id", "spherical_vector_cs2") .set("tol", 0.00018); testInterpolation((config)); } CASE("cubed sphere CS-LFR-48 vector interpolation (3d-field, 3-vector)") { const auto config = Config("source_fixture", "cubedsphere_mesh") .set("target_fixture", "gaussian_mesh") .set("field_spec_fixture", "3vector") .set("interp_fixture", "cubedsphere_bilinear_spherical") .set("file_id", "spherical_vector_cs3") .set("tol", 0.00096); testInterpolation((config)); } CASE("cubed sphere CS-LFR-48 (spherical vector) to empty point cloud") { const auto config = Config("source_fixture", "cubedsphere_mesh") .set("target_fixture", "empty_point_cloud") .set("field_spec_fixture", "2vector") .set("interp_fixture", "cubedsphere_bilinear_spherical"); testInterpolation((config)); } CASE("cubed sphere CS-LFR-48 to empty point cloud") { const auto config = Config("source_fixture", "cubedsphere_mesh") .set("target_fixture", "empty_point_cloud") .set("field_spec_fixture", "2vector") .set("interp_fixture", "cubedsphere_bilinear"); testInterpolation((config)); } CASE("finite element to empty point cloud") { const auto config = Config("source_fixture", "gaussian_mesh") .set("target_fixture", "cubedsphere_mesh") .set("field_spec_fixture", "2vector") .set("interp_fixture", "finite_element"); testInterpolation((config)); } CASE("finite element vector interpolation (2d-field, 2-vector)") { const auto config = Config("source_fixture", "gaussian_mesh") .set("target_fixture", "cubedsphere_mesh") .set("field_spec_fixture", "2vector") .set("interp_fixture", "finite_element_spherical") .set("file_id", "spherical_vector_fe") .set("tol", 0.00015); testInterpolation((config)); } CASE("structured columns F48 cubic vector spherical interpolation (3d-field, 2-vector)") { const auto config = Config("source_fixture", "structured_columns_classic_halo2") .set("target_fixture", "cubedsphere_mesh") .set("field_spec_fixture", "2vector") .set("interp_fixture", "structured_cubic_spherical") .set("file_id", "spherical_cubic_vector_classic_sc") .set("tol", 0.0000082); testInterpolation((config)); } CASE("structured columns F96 cubic vector spherical interpolation (2d-field, 2-vector)") { const auto config = Config("source_fixture", "structured_columns_classic_highres_halo2") .set("target_fixture", "cubedsphere_mesh") .set("field_spec_fixture", "2vector") .set("interp_fixture", "structured_cubic_spherical") .set("file_id", "spherical_2D_cubic_vector_highres_classic_sc") .set("tol", 0.000000425); testInterpolation((config)); } CASE("structured columns F96 cubic vector spherical interpolation (3d-field, 2-vector)") { const auto config = Config("source_fixture", "structured_columns_classic_highres_halo2") .set("target_fixture", "cubedsphere_mesh") .set("field_spec_fixture", "2vector") .set("interp_fixture", "structured_cubic_spherical") .set("file_id", "spherical_3D_cubic_vector_highres_classic_sc") .set("tol", 0.00000085); testInterpolation((config)); } CASE("structured columns O24 linear vector interpolation (2d-field, 2-vector)") { const auto config = Config("source_fixture", "structured_columns_lowres") .set("target_fixture", "gaussian_mesh") .set("field_spec_fixture", "2vector") .set("interp_fixture", "structured_linear_spherical") .set("file_id", "spherical_vector_sc_lr") .set("tol", 0.00056); testInterpolation((config)); } CASE("structured columns O48 cubic vector spherical interpolation (3d-field, 2-vector)") { const auto config = Config("source_fixture", "structured_columns_halo2") .set("target_fixture", "cubedsphere_mesh") .set("field_spec_fixture", "2vector") .set("interp_fixture", "structured_cubic_spherical") .set("file_id", "spherical_cubic_vector_sc3") .set("tol", 0.000007); testInterpolation((config)); } CASE("structured columns O48 linear vector interpolation (2d-field, 2-vector)") { const auto config = Config("source_fixture", "structured_columns") .set("target_fixture", "cubedsphere_mesh") .set("field_spec_fixture", "2vector") .set("interp_fixture", "structured_linear_spherical") .set("file_id", "spherical_vector_sc") .set("tol", 0.00017); testInterpolation((config)); } CASE("structured columns O48 to empty point cloud") { const auto config = Config("source_fixture", "structured_columns") .set("target_fixture", "empty_point_cloud") .set("field_spec_fixture", "2vector") .set("interp_fixture", "structured_linear"); testInterpolation((config)); } CASE("structured columns O96 vector interpolation (2d-field, 2-vector, hi-res)") { const auto config = Config("source_fixture", "structured_columns_hires") .set("target_fixture", "gaussian_mesh") .set("field_spec_fixture", "2vector") .set("interp_fixture", "structured_linear_spherical") .set("file_id", "spherical_vector_sc_hr") .set("tol", 0.000044); testInterpolation((config)); } CASE("cubed sphere CS-LFR-48 scalar spherical mean value interpolation (3d-field, scalar)") { const auto config = Config("source_fixture", "cubedsphere_mesh") .set("target_fixture", "gaussian_mesh") .set("field_spec_fixture", "scalar") .set("interp_fixture", "spherical_mean_value") .set("file_id", "spherical_vector_cs2") .set("tol", 0.00096); testInterpolation((config)); } CASE("cubed sphere CS-LFR-48 scalar normalised spherical mean value interpolation (3d-field, scalar)") { const auto config = Config("source_fixture", "cubedsphere_mesh") .set("target_fixture", "gaussian_mesh") .set("field_spec_fixture", "scalar") .set("interp_fixture", "spherical_mean_value_normalised") .set("file_id", "spherical_vector_cs2") .set("tol", 0.00096); testInterpolation((config)); } CASE("cubed sphere CS-LFR-48 vector spherical mean value interpolation (3d-field, 2-vector)") { const auto config = Config("source_fixture", "cubedsphere_mesh") .set("target_fixture", "gaussian_mesh") .set("field_spec_fixture", "2vector") .set("interp_fixture", "spherical_mean_value") .set("file_id", "spherical_vector_cs2") .set("tol", 0.00018); testInterpolation((config)); } CASE("cubed sphere CS-LFR-48 vector normalised spherical mean value interpolation (3d-field, 2-vector)") { const auto config = Config("source_fixture", "cubedsphere_mesh") .set("target_fixture", "gaussian_mesh") .set("field_spec_fixture", "2vector") .set("interp_fixture", "spherical_mean_value_normalised") .set("file_id", "spherical_vector_cs2") .set("tol", 0.00018); testInterpolation((config)); } CASE("cubed sphere CS-LFR-48 vector spherical mean value interpolation (3d-field, 3-vector)") { const auto config = Config("source_fixture", "cubedsphere_mesh") .set("target_fixture", "gaussian_mesh") .set("field_spec_fixture", "3vector") .set("interp_fixture", "spherical_mean_value") .set("file_id", "spherical_vector_cs3") .set("tol", 0.00096); testInterpolation((config)); } CASE("cubed sphere CS-LFR-48 vector normalised spherical mean value interpolation (3d-field, 3-vector)") { const auto config = Config("source_fixture", "cubedsphere_mesh") .set("target_fixture", "gaussian_mesh") .set("field_spec_fixture", "3vector") .set("interp_fixture", "spherical_mean_value_normalised") .set("file_id", "spherical_vector_cs3") .set("tol", 0.00096); testInterpolation((config)); } CASE("cubed sphere CS-LFR-48 (spherical mean value) to empty point cloud") { const auto config = Config("source_fixture", "cubedsphere_mesh") .set("target_fixture", "empty_point_cloud") .set("field_spec_fixture", "2vector") .set("interp_fixture", "spherical_mean_value"); testInterpolation((config)); } CASE("cubed sphere CS-LFR-48 (normalised spherical mean value) to empty point cloud") { const auto config = Config("source_fixture", "cubedsphere_mesh") .set("target_fixture", "empty_point_cloud") .set("field_spec_fixture", "2vector") .set("interp_fixture", "spherical_mean_value_normalised"); testInterpolation((config)); } CASE("separate vector field components") { const auto sourceFunctionSpace = FunctionSpaceFixtures::get("structured_columns"); const auto targetFunctionSpace = FunctionSpaceFixtures::get("cubedsphere_mesh"); auto sourceFieldSet = FieldSet{}; auto targetFieldSet = FieldSet{}; const auto sourceLonLatView = array::make_view(sourceFunctionSpace.lonlat()); const auto targetLonLatView = array::make_view(targetFunctionSpace.lonlat()); const auto createFieldView = [&](const FunctionSpace& functionSpace, const std::string& name, idx_t index, FieldSet& fieldSet) { // Note: Vector field name can be anything that uniquely identifies field. auto field = functionSpace.createField(option::name(name) | option::vector_component("vector", index++)); return array::make_view(fieldSet.add(field)); }; auto uSourceView = createFieldView(sourceFunctionSpace, "u", 0, sourceFieldSet); auto vSourceView = createFieldView(sourceFunctionSpace, "v", 1, sourceFieldSet); const auto uTargetView = createFieldView(targetFunctionSpace, "u", 0, targetFieldSet); const auto vTargetView = createFieldView(targetFunctionSpace, "v", 1, targetFieldSet); uSourceView.assign(0.); vSourceView.assign(0.); for (auto idx = idx_t{0}; idx < sourceFunctionSpace.size(); idx++) { std::tie(uSourceView(idx), vSourceView(idx)) = vortexHorizontal(sourceLonLatView(idx, 0), sourceLonLatView(idx, 1)); } const auto interpScheme = InterpSchemeFixtures::get("structured_linear_spherical"); const auto interp = Interpolation(interpScheme, sourceFunctionSpace, targetFunctionSpace); interp.execute(sourceFieldSet, targetFieldSet); targetFieldSet.haloExchange(); auto errorView = createFieldView(targetFunctionSpace, "error", 2, targetFieldSet); auto maxError = 0.; for (auto idx = idx_t{0}; idx < targetFunctionSpace.size(); idx++) { auto [uTrue, vTrue] = vortexHorizontal(targetLonLatView(idx, 0), targetLonLatView(idx, 1)); errorView(idx) = std::hypot(uTrue - uTargetView(idx), vTrue - vTargetView(idx)); maxError = std::max(maxError, errorView(idx)); } EXPECT_APPROX_EQ(maxError, 0., 0.00017); gmshOutput("vector_components_source.msh", sourceFieldSet); gmshOutput("vector_components_target.msh", targetFieldSet); auto sourceAdjointFieldSet = FieldSet{}; auto targetAdjointFieldSet = FieldSet{}; targetAdjointFieldSet.add(targetFieldSet["u"].clone()); targetAdjointFieldSet.add(targetFieldSet["v"].clone()); targetAdjointFieldSet.adjointHaloExchange(); auto uSourceAdjointView = createFieldView(sourceFunctionSpace, "u", 0, sourceAdjointFieldSet); auto vSourceAdjointView = createFieldView(sourceFunctionSpace, "v", 1, sourceAdjointFieldSet); uSourceAdjointView.assign(0.); vSourceAdjointView.assign(0.); // sourceAdjointFieldSet.set_dirty(false); interp.execute_adjoint(sourceAdjointFieldSet, targetAdjointFieldSet); constexpr auto tinyNum = 1e-13; const auto targetDotTarget = dotProduct(uTargetView, uTargetView) + dotProduct(vTargetView, vTargetView); const auto sourceDotSourceAdjoint = dotProduct(uSourceView, uSourceAdjointView) + dotProduct(vSourceView, vSourceAdjointView); const auto dotProdRatio = targetDotTarget / sourceDotSourceAdjoint; EXPECT_APPROX_EQ(dotProdRatio, 1., tinyNum); } } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/interpolation/CubicInterpolationPrototype.h0000664000175000017500000004053115160212070025602 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "eckit/linalg/SparseMatrix.h" #include "eckit/types/Types.h" #include "atlas/array/ArrayView.h" #include "atlas/array/MakeView.h" #include "atlas/functionspace/StructuredColumns.h" #include "atlas/grid/Grid.h" #include "atlas/grid/Stencil.h" #include "atlas/grid/StencilComputer.h" #include "atlas/grid/Vertical.h" #include "atlas/runtime/Exception.h" using namespace eckit; using namespace atlas::functionspace; using namespace atlas::util; namespace atlas { namespace test { //----------------------------------------------------------------------------- class CubicVerticalInterpolation { grid::ComputeVerticalStencil compute_vertical_stencil_; Vertical vertical_; static constexpr idx_t stencil_width() { return 4; } static constexpr idx_t stencil_size() { return stencil_width() * stencil_width(); } idx_t first_level_; idx_t last_level_; bool limiter_; public: CubicVerticalInterpolation(const Vertical& vertical, const eckit::Configuration& config = util::NoConfig()): compute_vertical_stencil_(vertical, stencil_width()), vertical_(vertical), first_level_(vertical_.k_begin()), last_level_(vertical_.k_end() - 1) { limiter_ = config.getBool("limiter", false); } struct Weights { std::array weights_k; }; using Stencil = grid::VerticalStencil<4>; template void compute_stencil(const double z, stencil_t& stencil) const { compute_vertical_stencil_(z, stencil); } template void compute_weights(const double z, const stencil_t& stencil, weights_t& weights) const { auto& w = weights.weights_k; std::array zvec; for (idx_t k = 0; k < 4; ++k) { zvec[k] = vertical_(stencil.k(k)); } // auto quadratic_interpolation = [z]( const double zvec[], double w[] ) { // double d01 = zvec[0] - zvec[1]; // double d02 = zvec[0] - zvec[2]; // double d12 = zvec[1] - zvec[2]; // double dc0 = d01 * d02; // double dc1 = -d01 * d12; // double d0 = z - zvec[0]; // double d1 = z - zvec[1]; // double d2 = z - zvec[2]; // w[0] = ( d1 * d2 ) / dc0; // w[1] = ( d0 * d2 ) / dc1; // w[2] = 1. - w[0] - w[1]; // }; if (stencil.k_interval() == -1) { // constant extrapolation // lev0 lev1 lev2 lev3 // + |------X------X------X // w=1 w=0 w=0 w=0 w[0] = 1.; w[1] = 0.; w[2] = 0.; w[3] = 0.; return; } else if (stencil.k_interval() == 3) { // constant extrapolation // lev(n-4) lev(n-3) lev(n-2) lev(n-1) // X---------X---------X---------| + // w=0 w=0 w=0 w=1 w[0] = 0.; w[1] = 0.; w[2] = 0.; w[3] = 1.; return; } // else if ( stencil.k_interval() == 0 ) { // // quadratic interpolation // // lev0 lev1 lev2 lev3 // // | + | | | // // w=0 // quadratic_interpolation( zvec.data(), w.data() ); // w[3] = 0.; // return; // } // else if ( stencil.k_interval() == 2 ) { // // quadratic interpolation // // lev(n-4) lev(n-3) lev(n-2) lev(n-1) // // | | | + | // // w=0 // quadratic_interpolation( zvec.data() + 1, w.data() + 1 ); // w[0] = 0.; // return; // } // cubic interpolation // lev(k+0) lev(k+1) lev(k+2) lev(k+3) // | | x | | double d01 = zvec[0] - zvec[1]; double d02 = zvec[0] - zvec[2]; double d03 = zvec[0] - zvec[3]; double d12 = zvec[1] - zvec[2]; double d13 = zvec[1] - zvec[3]; double d23 = zvec[2] - zvec[3]; double dc0 = d01 * d02 * d03; double dc1 = -d01 * d12 * d13; double dc2 = d02 * d12 * d23; double d0 = z - zvec[0]; double d1 = z - zvec[1]; double d2 = z - zvec[2]; double d3 = z - zvec[3]; w[0] = (d1 * d2 * d3) / dc0; #if defined(_CRAYC) || defined(__NVCOMPILER) && ATLAS_BUILD_TYPE_RELEASE // prevents FE_INVALID somehow (tested with Cray 8.7) ATLAS_ASSERT(!std::isnan(w[0])); #endif w[1] = (d0 * d2 * d3) / dc1; w[2] = (d0 * d1 * d3) / dc2; w[3] = 1. - w[0] - w[1] - w[2]; } template void interpolate(const stencil_t& stencil, const weights_t& weights, const array_t& input, double& output) const { output = 0.; const auto& w = weights.weights_k; for (idx_t k = 0; k < stencil_width(); ++k) { output += w[k] * input[stencil.k(k)]; } if (limiter_) { idx_t k = stencil.k_interval(); idx_t k1, k2; if (k < 0) { k1 = k2 = 0; } else if (k > 2) { k1 = k2 = 3; } else { k1 = k; k2 = k + 1; } double f1 = input[stencil.k(k1)]; double f2 = input[stencil.k(k2)]; double maxval = std::max(f1, f2); double minval = std::min(f1, f2); output = std::min(maxval, std::max(minval, output)); } } template double operator()(const double z, const array_t& input) const { grid::VerticalStencil stencil; compute_vertical_stencil_(z, stencil); Weights weights; compute_weights(z, stencil, weights); double output; interpolate(stencil, weights, input, output); return output; } }; //----------------------------------------------------------------------------- class CubicHorizontalInterpolation { functionspace::StructuredColumns fs_; grid::ComputeHorizontalStencil compute_horizontal_stencil_; static constexpr idx_t stencil_width() { return 4; } static constexpr idx_t stencil_size() { return stencil_width() * stencil_width(); } bool limiter_{false}; public: using Stencil = grid::HorizontalStencil<4>; public: CubicHorizontalInterpolation(const functionspace::StructuredColumns& fs): fs_(fs), compute_horizontal_stencil_(fs.grid(), stencil_width()) {} template void compute_weights(const double x, const double y, weights_t& weights) const { grid::HorizontalStencil stencil; compute_horizontal_stencil_(x, y, stencil); compute_weights(x, y, stencil, weights); } struct Weights { std::array, 4> weights_i; std::array weights_j; }; template void compute_stencil(const double x, const double y, stencil_t& stencil) const { compute_horizontal_stencil_(x, y, stencil); } template void compute_weights(const double x, const double y, const stencil_t& stencil, weights_t& weights) const { PointXY P1, P2; std::array yvec; for (idx_t j = 0; j < stencil_width(); ++j) { auto& weights_i = weights.weights_i[j]; fs_.compute_xy(stencil.i(1, j), stencil.j(j), P1); fs_.compute_xy(stencil.i(2, j), stencil.j(j), P2); double alpha = (P2.x() - x) / (P2.x() - P1.x()); double alpha_sqr = alpha * alpha; double two_minus_alpha = 2. - alpha; double one_minus_alpha_sqr = 1. - alpha_sqr; weights_i[0] = -alpha * one_minus_alpha_sqr / 6.; weights_i[1] = 0.5 * alpha * (1. + alpha) * two_minus_alpha; weights_i[2] = 0.5 * one_minus_alpha_sqr * two_minus_alpha; weights_i[3] = 1. - weights_i[0] - weights_i[1] - weights_i[2]; yvec[j] = P1.y(); } double dl12 = yvec[0] - yvec[1]; double dl13 = yvec[0] - yvec[2]; double dl14 = yvec[0] - yvec[3]; double dl23 = yvec[1] - yvec[2]; double dl24 = yvec[1] - yvec[3]; double dl34 = yvec[2] - yvec[3]; double dcl1 = dl12 * dl13 * dl14; double dcl2 = -dl12 * dl23 * dl24; double dcl3 = dl13 * dl23 * dl34; double dl1 = y - yvec[0]; double dl2 = y - yvec[1]; double dl3 = y - yvec[2]; double dl4 = y - yvec[3]; auto& weights_j = weights.weights_j; weights_j[0] = (dl2 * dl3 * dl4) / dcl1; #if defined(_CRAYC) || defined(__NVCOMPILER) && ATLAS_BUILD_TYPE_RELEASE // prevents FE_INVALID somehow (tested with Cray 8.7) ATLAS_ASSERT(!std::isnan(weights_j[0])); #endif weights_j[1] = (dl1 * dl3 * dl4) / dcl2; weights_j[2] = (dl1 * dl2 * dl4) / dcl3; weights_j[3] = 1. - weights_j[0] - weights_j[1] - weights_j[2]; } template void interpolate(const stencil_t& stencil, const weights_t& weights, const array_t& input, double& output) { std::array, stencil_width()> index; const auto& weights_j = weights.weights_j; output = 0.; for (idx_t j = 0; j < stencil_width(); ++j) { const auto& weights_i = weights.weights_i[j]; for (idx_t i = 0; i < stencil_width(); ++i) { idx_t n = fs_.index(stencil.i(i, j), stencil.j(j)); output += weights_i[i] * weights_j[j] * input[n]; index[j][i] = n; } } if (limiter_) { limit(output, index, input); } } template void limit(double& output, const std::array, 4>& index, const array_t& input) { // Limit output to max/min of values in stencil marked by '*' // x x x x // x *-----* x // / P | // x *------ * x // x x x x double maxval = std::numeric_limits::lowest(); double minval = std::numeric_limits::max(); for (idx_t j = 1; j < 3; ++j) { for (idx_t i = 1; i < 3; ++i) { idx_t n = index[j][i]; double val = input[n]; maxval = std::max(maxval, val); minval = std::min(minval, val); } } output = std::min(maxval, std::max(minval, output)); } template double operator()(const double x, const double y, const array_t& input) { grid::HorizontalStencil stencil; compute_horizontal_stencil_(x, y, stencil); Weights weights; compute_weights(x, y, stencil, weights); double output; interpolate(stencil, weights, input, output); return output; } struct WorkSpace { grid::HorizontalStencil<4> stencil; Weights weights; }; using Triplet = eckit::linalg::Triplet; using Triplets = std::vector; // Thread private workspace Triplets compute_triplets(const idx_t row, const double x, const double y, WorkSpace& ws) { Triplets triplets; triplets.reserve(stencil_size()); insert_triplets(row, x, y, triplets, ws); return triplets; } Triplets reserve_triplets(size_t N) { Triplets triplets; triplets.reserve(N * stencil_size()); return triplets; } void insert_triplets(const idx_t row, const double x, const double y, Triplets& triplets, WorkSpace& ws) { compute_horizontal_stencil_(x, y, ws.stencil); compute_weights(x, y, ws.stencil, ws.weights); const auto& wj = ws.weights.weights_j; for (idx_t j = 0; j < stencil_width(); ++j) { const auto& wi = ws.weights.weights_i[j]; for (idx_t i = 0; i < stencil_width(); ++i) { idx_t col = fs_.index(ws.stencil.i(i, j), ws.stencil.j(j)); double w = wi[i] * wj[j]; triplets.emplace_back(row, col, w); } } } }; //----------------------------------------------------------------------------- class Cubic3DInterpolation { functionspace::StructuredColumns fs_; CubicHorizontalInterpolation horizontal_interpolation_; CubicVerticalInterpolation vertical_interpolation_; static constexpr idx_t stencil_width() { return 4; } static constexpr idx_t stencil_size() { return stencil_width() * stencil_width(); } public: struct Weights { std::array, 4> weights_i; std::array weights_j; std::array weights_k; }; using Stencil = grid::Stencil3D<4>; Cubic3DInterpolation(const functionspace::StructuredColumns& fs): fs_(fs), horizontal_interpolation_(fs), vertical_interpolation_(fs.vertical()) {} template void compute_stencil(const double x, const double y, const double z, stencil_t& stencil) const { horizontal_interpolation_.compute_stencil(x, y, stencil); vertical_interpolation_.compute_stencil(z, stencil); } template void compute_weights(const double x, const double y, const double z, weights_t& weights) const { Stencil stencil; compute_stencil(x, y, z, stencil); compute_weights(x, y, z, stencil, weights); } template void compute_weights(const double x, const double y, const double z, const stencil_t& stencil, weights_t& weights) const { horizontal_interpolation_.compute_weights(x, y, stencil, weights); vertical_interpolation_.compute_weights(z, stencil, weights); } template void interpolate(const stencil_t& stencil, const weights_t& weights, const array_t& input, double& output) { output = 0.; const auto& wj = weights.weights_j; const auto& wk = weights.weights_k; for (idx_t j = 0; j < stencil_width(); ++j) { const auto& wi = weights.weights_i[j]; for (idx_t i = 0; i < stencil_width(); ++i) { idx_t n = fs_.index(stencil.i(i, j), stencil.j(j)); for (idx_t k = 0; k < stencil_width(); ++k) { output += wi[i] * wj[j] * wk[k] * input(n, stencil.k(k)); } } } } template double operator()(const double x, const double y, const double z, const array_t& input) { Stencil stencil; Weights weights; compute_stencil(x, y, z, stencil); compute_weights(x, y, z, stencil, weights); double output; interpolate(stencil, weights, input, output); return output; } template double operator()(const point_t& p, const array_t& input) { Stencil stencil; Weights weights; compute_stencil(p[0], p[1], p[2], stencil); compute_weights(p[0], p[1], p[2], stencil, weights); double output; interpolate(stencil, weights, input, output); return output; } }; //----------------------------------------------------------------------------- } // namespace test } // namespace atlas atlas-0.46.0/src/tests/interpolation/test_interpolation_finite_element.cc0000664000175000017500000001470315160212070027214 0ustar alastairalastair/* * (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/util/Config.h" #include "eckit/types/FloatCompare.h" #include "atlas/array.h" #include "atlas/functionspace.h" #include "atlas/functionspace/PointCloud.h" #include "atlas/grid.h" #include "atlas/interpolation.h" #include "atlas/mesh.h" #include "atlas/meshgenerator.h" #include "atlas/util/CoordinateEnums.h" #include "tests/AtlasTestEnvironment.h" using namespace eckit; using namespace atlas::functionspace; using namespace atlas::util; namespace atlas { namespace test { //----------------------------------------------------------------------------- std::array interpolationTypes = { util::Config("type", "finite-element") | util::Config("max_fraction_elems_to_try", 0.4), util::Config("type", "spherical-mean-value") | util::Config("normalisation", "true") | util::Config("max_fraction_elems_to_try", 0.4), util::Config("type", "spherical-mean-value") | util::Config("normalisation", "false") | util::Config("max_fraction_elems_to_try", 0.4)}; CASE("test_interpolation_finite_element") { Grid grid("O64"); Mesh mesh(grid); NodeColumns fs(mesh); // Some points at the equator PointCloud pointcloud( {{00., 0.}, {10., 0.}, {20., 0.}, {30., 0.}, {40., 0.}, {50., 0.}, {60., 0.}, {70., 0.}, {80., 0.}, {90., 0.}}); auto func = [](double x) -> double { return std::sin(x * M_PI / 180.); }; for (util::Config config : interpolationTypes) { std::string interpType = ""; std::string normalisationMode = ""; config.get("type", interpType); config.get("normalisation", normalisationMode); bool isNormalisationMode = 0; if (normalisationMode == "true") { isNormalisationMode = 1; } SECTION("using " + interpType + " " + (isNormalisationMode ? " w/ normalisation" : "")) { const auto scheme = config; Interpolation interpolation(scheme, fs, pointcloud); SECTION("test maximum nearest neighbour settings") { std::stringstream test_stream; interpolation.print(test_stream); std::string test_string = test_stream.str(); EXPECT((test_string.find("max_fraction_elems_to_try: 0.4") != std::string::npos)); } SECTION("test interpolation outputs") { Field field_source = fs.createField(option::name("source")); Field field_target("target", array::make_datatype(), array::make_shape(pointcloud.size())); auto lonlat = array::make_view(fs.nodes().lonlat()); auto source = array::make_view(field_source); for (idx_t j = 0; j < fs.nodes().size(); ++j) { source(j) = func(lonlat(j, LON)); } interpolation.execute(field_source, field_target); auto target = array::make_view(field_target); auto check = std::vector{func(00.), func(10.), func(20.), func(30.), func(40.), func(50.), func(60.), func(70.), func(80.), func(90.)}; for (idx_t j = 0; j < pointcloud.size(); ++j) { static double interpolation_tolerance = 1.e-4; Log::info() << target(j) << " " << check[j] << std::endl; EXPECT(eckit::types::is_approximately_equal(target(j), check[j], interpolation_tolerance)); } } } } } //----------------------------------------------------------------------------- CASE("test_interpolation_finite_element_from_healpix") { Grid grid("H64"); Mesh mesh(grid); NodeColumns fs(mesh); // Some points at the equator PointCloud pointcloud({{15., 90. - 0.001}, {15., -90 + 0.001}}); auto func = [](double x) -> double { return std::sin(x * M_PI / 180.); }; for (util::Config config : interpolationTypes) { std::string interpType = ""; std::string normalisationMode = ""; config.get("type", interpType); config.get("normalisation", normalisationMode); bool isNormalisationMode = 0; if (normalisationMode == "true") { isNormalisationMode = 1; } SECTION("using " + interpType + " " + (isNormalisationMode ? " w/ normalisation" : "")) { const auto scheme = config; Interpolation interpolation(scheme, fs, pointcloud); SECTION("test maximum nearest neighbour settings") { std::stringstream test_stream; interpolation.print(test_stream); std::string test_string = test_stream.str(); EXPECT((test_string.find("max_fraction_elems_to_try: 0.4") != std::string::npos)); } SECTION("test interpolation outputs") { Field field_source = fs.createField(option::name("source")); Field field_target("target", array::make_datatype(), array::make_shape(pointcloud.size())); auto lonlat = array::make_view(fs.nodes().lonlat()); auto source = array::make_view(field_source); for (idx_t j = 0; j < fs.nodes().size(); ++j) { source(j) = func(lonlat(j, LON)); } interpolation.execute(field_source, field_target); auto target = array::make_view(field_target); auto check = std::vector{0.998782444936, 0.998678866237}; for (idx_t j = 0; j < pointcloud.size(); ++j) { static double interpolation_tolerance = 1.e-4; Log::info() << target(j) << " " << check[j] << std::endl; EXPECT_APPROX_EQ(target(j), check[j], interpolation_tolerance); } } } } } //----------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/interpolation/test_Quad2D.cc0000664000175000017500000001462515160212070022341 0ustar alastairalastair/* * (C) Crown Copyright 2021 Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ #include "eckit/types/FloatCompare.h" #include "atlas/interpolation/element/Quad2D.h" #include "atlas/util/Point.h" #include "tests/AtlasTestEnvironment.h" using atlas::PointXY; using atlas::interpolation::element::Quad2D; using atlas::interpolation::method::Intersect; namespace atlas { namespace test { //---------------------------------------------------------------------------------------------------------------------- const double relative_error = 0.0001; CASE("test_quad_area") { PointXY v0(0., 0.); PointXY v1(1., 0.); PointXY v2(1., 1.); PointXY v3(0., 1.); Quad2D quad1(v0.data(), v1.data(), v2.data(), v3.data()); EXPECT(quad1.validate()); double area = quad1.area(); std::cout << "area " << area << std::endl; EXPECT_APPROX_EQ(area, 1.0, relative_error); PointXY c0(-2., -2.); // 4 PointXY c1(3., -2.); // 6 PointXY c2(3., 0.5); // 1.5 PointXY c3(-2., 0.5); // 1 Quad2D quad2(c0.data(), c1.data(), c2.data(), c3.data()); EXPECT(quad2.validate()); area = quad2.area(); std::cout << "area " << area << std::endl; EXPECT_APPROX_EQ(area, 12.5, relative_error); } CASE("test_quadrilateral_intersection_refquad") { PointXY v0(0., 0.); PointXY v1(1., 0.); PointXY v2(1., 1.); PointXY v3(0., 1.); Quad2D quad(v0.data(), v1.data(), v2.data(), v3.data()); EXPECT(quad.validate()); PointLonLat orig(0.25, 0.25); Intersect isect = quad.intersects(orig); EXPECT(isect); EXPECT_APPROX_EQ(isect.u, 0.25, relative_error); EXPECT_APPROX_EQ(isect.v, 0.25, relative_error); } CASE("test_quadrilateral_remap_refquad") { PointXY v0(0., 0.); PointXY v1(1., 0.); PointXY v2(1., 1.); PointXY v3(0., 1.); Quad2D quad(v0.data(), v1.data(), v2.data(), v3.data()); EXPECT(quad.validate()); PointXY orig(0.25, 0.25); Intersect isect = quad.localRemap(orig); EXPECT(isect); EXPECT_APPROX_EQ(isect.u, 0.25, relative_error); EXPECT_APPROX_EQ(isect.v, 0.25, relative_error); } CASE("test_quadrilateral_intersection_doublequad") { PointXY v0(0., 0.); PointXY v1(2., 0.); PointXY v2(2., 2.); PointXY v3(0., 2.); Quad2D quad(v0.data(), v1.data(), v2.data(), v3.data()); EXPECT(quad.validate()); PointXY orig(0.5, 0.5); Intersect isect = quad.localRemap(orig); EXPECT(isect); EXPECT_APPROX_EQ(isect.u, 0.25, relative_error); EXPECT_APPROX_EQ(isect.v, 0.25, relative_error); } CASE("test_quadrilateral_intersection_rotatedquad") { PointXY v0(0., -1.); PointXY v1(1., 0.); PointXY v2(0., 1.); PointXY v3(-1., 0.); Quad2D quad(v0.data(), v1.data(), v2.data(), v3.data()); EXPECT(quad.validate()); PointXY orig(0., 0.); Intersect isect = quad.localRemap(orig); EXPECT(isect); EXPECT_APPROX_EQ(isect.u, 0.5, relative_error); EXPECT_APPROX_EQ(isect.v, 0.5, relative_error); } CASE("test_quadrilateral_intersection_arbitrary") { PointXY v0(338.14, 54.6923); PointXY v1(340.273, 54.6778); PointXY v2(340.312, 55.9707); PointXY v3(338.155, 55.9852); Quad2D quad(v0.data(), v1.data(), v2.data(), v3.data()); EXPECT(quad.validate()); PointXY orig(339, 55); Intersect isect = quad.localRemap(orig); std::cout << isect.u << " " << isect.v << std::endl; EXPECT(isect); EXPECT_APPROX_EQ(isect.u, 0.400390, relative_error); EXPECT_APPROX_EQ(isect.v, 0.242483, relative_error); } CASE("test_quadrilateral_intersection_nointersect") { PointXY v0(0., -1.); PointXY v1(1., 0.); PointXY v2(0., 1.); PointXY v3(-1., 0.); Quad2D quad(v0.data(), v1.data(), v2.data(), v3.data()); EXPECT(quad.validate()); PointXY orig(2., 2.); Intersect isect = quad.localRemap(orig); EXPECT(!isect); } CASE("test_quadrilateral_intersection_corners") { PointXY v0(0.0, -2.0); PointXY v1(2.5, 0.0); PointXY v2(0.0, 3.5); PointXY v3(-1.5, 0.0); Quad2D quad(v0.data(), v1.data(), v2.data(), v3.data()); EXPECT(quad.validate()); std::vector corners; corners.emplace_back(0.0, -2.0); corners.emplace_back(2.5, 0.0); corners.emplace_back(0.0, 3.5); corners.emplace_back(-1.5, 0.0); std::vector> uvs; 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) { PointXY orig = corners[i]; Intersect isect = quad.localRemap(orig); EXPECT(isect); EXPECT_APPROX_EQ(isect.u, uvs[i].first, relative_error); EXPECT_APPROX_EQ(isect.v, uvs[i].second, relative_error); } } CASE("test_quadrilateral_intersection_arbitrary_corners") { PointXY v0(338.14, 54.6923); PointXY v1(340.273, 54.6778); PointXY v2(340.312, 55.9707); PointXY v3(338.155, 55.9852); Quad2D quad(v0.data(), v1.data(), v2.data(), v3.data()); EXPECT(quad.validate()); std::array orig{v0, v1, v2, v3}; std::array uTarget{0., 1., 1., 0.}; std::array vTarget{0., 0., 1., 1.}; for (size_t i = 0; i < 4; ++i) { Intersect isect = quad.localRemap(orig[i]); EXPECT(isect); EXPECT_APPROX_EQ(isect.u, uTarget[i], relative_error); EXPECT_APPROX_EQ(isect.v, vTarget[i], relative_error); } } CASE("test_quadrilateral_intersection_arbitrary_edges") { PointXY v0(338.14, 54.6923); PointXY v1(340.273, 54.6778); PointXY v2(340.312, 55.9707); PointXY v3(338.155, 55.9852); Quad2D quad(v0.data(), v1.data(), v2.data(), v3.data()); EXPECT(quad.validate()); std::array orig{(v0 + v1) * 0.5, (v1 + v2) * 0.5, (v2 + v3) * 0.5, (v3 + v0) * 0.5}; std::array uTarget{0.5, 1., 0.5, 0.}; std::array vTarget{0., 0.5, 1., 0.5}; for (size_t i = 0; i < 4; ++i) { Intersect isect = quad.localRemap(orig[i]); EXPECT(isect); EXPECT_APPROX_EQ(isect.u, uTarget[i], relative_error); EXPECT_APPROX_EQ(isect.v, vTarget[i], relative_error); } } //----------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/interpolation/test_interpolation_conservative.cc0000664000175000017500000002503715160212070026737 0ustar alastairalastair/* * (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 #include "eckit/geometry/Sphere.h" #include "eckit/types/FloatCompare.h" #include "atlas/array.h" #include "atlas/array/MakeView.h" #include "atlas/field.h" #include "atlas/grid.h" #include "atlas/interpolation.h" #include "atlas/interpolation/method/unstructured/ConservativeSphericalPolygonInterpolation.h" #include "atlas/mesh.h" #include "atlas/mesh/Mesh.h" #include "atlas/meshgenerator.h" #include "atlas/option.h" #include "atlas/util/Config.h" #include "atlas/util/function/VortexRollup.h" #include "tests/AtlasTestEnvironment.h" namespace atlas { namespace test { using ConservativeMethod = interpolation::method::ConservativeSphericalPolygonInterpolation; using Statistics = ConservativeMethod::Statistics; using Metadata = util::Metadata; void do_remapping_test(Grid src_grid, Grid tgt_grid, std::function func, Metadata& remap_stat_1, Metadata& remap_stat_2, bool src_cell_data, bool tgt_cell_data) { std::string src_data_type = (src_cell_data ? "CellColumns(" : "NodeColumns("); std::string tgt_data_type = (tgt_cell_data ? "CellColumns(" : "NodeColumns("); Log::info() << "+-----------------------\n"; Log::info() << src_data_type << src_grid.name() << ") --> " << tgt_data_type << tgt_grid.name() <<")\n"; Log::info() << "+-----------------------\n"; Log::info().indent(); // setup conservative remap: compute weights, polygon intersection, etc util::Config config("type", "conservative-spherical-polygon"); config.set("order", 1); config.set("validate", true); config.set("statistics.intersection", true); config.set("statistics.conservation", true); config.set("src_cell_data", src_cell_data); config.set("tgt_cell_data", tgt_cell_data); auto conservative_interpolation = Interpolation(config, src_grid, tgt_grid); Log::info() << conservative_interpolation << std::endl; Log::info() << std::endl; // create source field from analytic function "func" const auto& src_fs = conservative_interpolation.source(); const auto& tgt_fs = conservative_interpolation.target(); auto src_field = src_fs.createField(); auto tgt_field = tgt_fs.createField(); auto src_vals = array::make_view(src_field); // A bit of a hack here... ConservativeMethod& consMethod = dynamic_cast(*conservative_interpolation.get()); { ATLAS_TRACE("initial condition"); for (idx_t spt = 0; spt < src_vals.size(); ++spt) { auto p = consMethod.src_points(spt); PointLonLat pll; eckit::geometry::Sphere::convertCartesianToSpherical(1., p, pll); src_vals(spt) = func(pll); } } remap_stat_1 = conservative_interpolation.execute(src_field, tgt_field); tgt_field.haloExchange(); consMethod.statistics().compute_accuracy(conservative_interpolation, tgt_field, func, &remap_stat_1); ATLAS_TRACE_SCOPE("test caching") { // We can create the interpolation without polygon intersections auto cache = interpolation::Cache(conservative_interpolation); // cache = ConservativeMethod::Cache + MatrixCache (1st order) util::Config cfg(option::type("conservative-spherical-polygon")); config.set("validate", true); { ATLAS_TRACE("cached -> 1st order using cached matrix"); cfg.set("matrix_free", false); cfg.set("order", 1); auto interpolation = Interpolation(cfg, src_grid, tgt_grid, cache); Log::info() << interpolation << std::endl; interpolation.execute(src_field, tgt_field); Log::info() << std::endl; } { ATLAS_TRACE("cached -> 1st order constructing new matrix"); cfg.set("matrix_free", false); cfg.set("order", 1); auto cache_without_matrix = ConservativeMethod::Cache(cache); // to mimick when cache was created with matrix_free option auto interpolation = Interpolation(cfg, src_grid, tgt_grid, cache_without_matrix); Log::info() << interpolation << std::endl; interpolation.execute(src_field, tgt_field); Log::info() << std::endl; } { ATLAS_TRACE("cached -> 1st order matrix-free"); cfg.set("matrix_free", true); cfg.set("order", 1); auto interpolation = Interpolation(cfg, src_grid, tgt_grid, cache); Log::info() << interpolation << std::endl; interpolation.execute(src_field, tgt_field); Log::info() << std::endl; } auto cache_2 = interpolation::Cache{}; { ATLAS_TRACE("cached -> 2nd order constructing new matrix"); cfg.set("matrix_free", false); cfg.set("order", 2); auto interpolation = Interpolation(cfg, src_grid, tgt_grid, cache); Log::info() << interpolation << std::endl; interpolation.execute(src_field, tgt_field); cache_2 = interpolation.createCache(); Log::info() << std::endl; } // TODO: With the PR 318 we switch to interating over target elements, which in return requires a lot of changes. // The following code requires reimplementation of the matrix-free 2nd order method which will come after this PR. // Hence, we temporary disable this code. // // if (src_cell_data and tgt_cell_data) { // ATLAS_TRACE("cached -> 2nd order matrix-free"); // cfg.set("matrix_free", true); // cfg.set("order", 2); // auto interpolation = Interpolation(cfg, src_grid, tgt_grid, cache); // Log::info() << interpolation << std::endl; // interpolation.execute(src_field, tgt_field); // Log::info() << std::endl; // } { ATLAS_TRACE("cached -> 2nd order using cached matrix"); cfg.set("matrix_free", false); cfg.set("order", 2); auto interpolation = Interpolation(cfg, src_grid, tgt_grid, cache_2); Log::info() << interpolation << std::endl; interpolation.execute(src_field, tgt_field); Log::info() << std::endl; } } { ATLAS_TRACE("2nd order projection"); config.set("order", 2); conservative_interpolation = Interpolation(config, src_grid, tgt_grid); Log::info() << conservative_interpolation << std::endl; remap_stat_2 = conservative_interpolation.execute(src_field, tgt_field); auto& consMethod_2 = dynamic_cast(*conservative_interpolation.get()); tgt_field.haloExchange(); consMethod_2.statistics().compute_accuracy(conservative_interpolation, tgt_field, func, &remap_stat_2); } } void check(const Metadata remap_stat_1, Metadata remap_stat_2, std::array tol) { double err; // check polygon intersections remap_stat_1.get("errors.sum_src_areas_minus_sum_tgt_areas", err); Log::info() << "Polygon area computation (new < ref) = (" << err << " < " << tol[0] << ")" << std::endl; EXPECT(err < tol[0]); remap_stat_1.get("errors.intersections_covering_tgt_cells_sum", err); Log::info() << "Polygon intersection (new < ref) = (" << err << " < " << tol[1] << ")" << std::endl; EXPECT(err < tol[1]); // check remap accuracy remap_stat_1.get("errors.to_solution_sum", err); Log::info() << "1st order accuracy (new < ref) = (" << std::abs(err) << " < " << tol[2] << ")" << std::endl; EXPECT(std::abs(err) < tol[2]); remap_stat_2.get("errors.to_solution_sum", err); Log::info() << "2nd order accuracy (new < ref) = (" << std::abs(err) << " < " << tol[3] << ")" << std::endl; EXPECT(std::abs(err) < tol[3]); // check mass conservation remap_stat_1.get("errors.conservation_error", err); Log::info() << "1st order conservation (new < ref) = (" << std::abs(err) << " < " << tol[4] << ")" << std::endl; EXPECT(std::abs(err) < tol[4]); remap_stat_2.get("errors.conservation_error", err); Log::info() << "2nd order conservation (new < ref) = (" << std::abs(err) << " < " << tol[5] << ")" << std::endl; EXPECT(std::abs(err) < tol[5]); Log::info().unindent(); } CASE("test_interpolation_conservative") { SECTION("analytic constfunc") { auto func = [](const PointLonLat& p) { return 1.; }; Metadata remap_stat_1; Metadata remap_stat_2; bool src_cell_data = true; bool tgt_cell_data = true; do_remapping_test(Grid("O32"), Grid("H12"), func, remap_stat_1, remap_stat_2, src_cell_data, tgt_cell_data); check(remap_stat_1, remap_stat_2, {1.0e-13, 1.0e-13, 1.0e-13, 1.0e-13, 1.0e-13, 1.0e-13}); } SECTION("vortex_rollup") { auto func = [](const PointLonLat& p) { return util::function::vortex_rollup(p[0], p[1], 0.5); }; Metadata remap_stat_1; Metadata remap_stat_2; bool src_cell_data = true; bool tgt_cell_data = true; do_remapping_test(Grid("O16"), Grid("H12"), func, remap_stat_1, remap_stat_2, src_cell_data, tgt_cell_data); check(remap_stat_1, remap_stat_2, {1.0e-13, 1.0e-12, 0.0051927, 0.0025275, 1.0e-15, 1.5e-08}); src_cell_data = true; tgt_cell_data = false; do_remapping_test(Grid("O16"), Grid("H12"), func, remap_stat_1, remap_stat_2, src_cell_data, tgt_cell_data); check(remap_stat_1, remap_stat_2, {1.0e-13, 1.0e-12, 0.0054418, 0.0028355, 1.0e-15, 5.0e-09}); src_cell_data = false; tgt_cell_data = true; do_remapping_test(Grid("O16"), Grid("H12"), func, remap_stat_1, remap_stat_2, src_cell_data, tgt_cell_data); check(remap_stat_1, remap_stat_2, {1.0e-13, 1.0e-12, 0.0062715, 0.0029492, 1.0e-15, 2.0e-09}); src_cell_data = false; tgt_cell_data = false; do_remapping_test(Grid("O16"), Grid("H12"), func, remap_stat_1, remap_stat_2, src_cell_data, tgt_cell_data); check(remap_stat_1, remap_stat_2, {1.0e-12, 1.0e-12, 0.0064164, 0.0030295, 1.0e-15, 1.0e-12}); } } } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/interpolation/test_interpolation_structured2D.cc0000664000175000017500000004356115160212070026623 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "atlas/array.h" #include "atlas/field/Field.h" #include "atlas/field/FieldSet.h" #include "atlas/functionspace/NodeColumns.h" #include "atlas/functionspace/PointCloud.h" #include "atlas/functionspace/StructuredColumns.h" #include "atlas/grid/Grid.h" #include "atlas/grid/Iterator.h" #include "atlas/interpolation.h" #include "atlas/mesh/Mesh.h" #include "atlas/meshgenerator.h" #include "atlas/output/Gmsh.h" #include "atlas/util/CoordinateEnums.h" #include "atlas/util/function/VortexRollup.h" #include "tests/AtlasTestEnvironment.h" using atlas::functionspace::NodeColumns; using atlas::functionspace::StructuredColumns; using atlas::util::Config; namespace atlas { namespace test { //----------------------------------------------------------------------------- static Config scheme() { Config scheme; std::string scheme_str = eckit::Resource("--scheme", "linear"); if (scheme_str == "linear") { scheme.set("type", "structured-bilinear"); scheme.set("halo", 1); // The stencil does not require any halo, but we set it to 1 for pole treatment! } if (scheme_str == "cubic") { scheme.set("type", "structured-bicubic"); scheme.set("halo", 2); } if (scheme_str == "quasicubic") { scheme.set("type", "structured-biquasicubic"); scheme.set("halo", 2); } scheme.set("name", scheme_str); scheme.set("verbose",eckit::Resource("--verbose",false)); return scheme; } std::string input_gridname(const std::string& default_grid) { return eckit::Resource("--input-grid", default_grid); } std::string output_gridname(const std::string& default_grid) { return eckit::Resource("--output-grid", default_grid); } Grid rotated_mercator() { Config gridspec; gridspec.set("type", "regional"); gridspec.set("nx", 50); gridspec.set("ny", 40); gridspec.set("dx", 50000); gridspec.set("dy", 50000); gridspec.set("y_numbering", -1); gridspec.set("lonlat(centre)", std::vector{4., 50}); gridspec.set("projection", [] { Config projection; projection.set("type", "rotated_mercator"); projection.set("north_pole", std::vector{-176., 40.}); return projection; }()); return Grid{gridspec}; } Grid lambert() { Config gridspec; gridspec.set("type", "regional"); gridspec.set("nx", 50); gridspec.set("ny", 40); gridspec.set("dx", 50000); gridspec.set("dy", 50000); gridspec.set("y_numbering", -1); gridspec.set("lonlat(centre)", std::vector{4., 50}); gridspec.set("projection", [] { Config projection; projection.set("type", "lambert_conformal_conic"); projection.set("longitude0", 4.); projection.set("latitude0", 50.); return projection; }()); return Grid{gridspec}; } Grid rotated(const std::string& name) { Config gridspec; gridspec.set("name", name); gridspec.set("projection", [] { Config projection; projection.set("type", "rotated_lonlat"); projection.set("north_pole", std::vector{-176., 40.}); return projection; }()); return Grid{gridspec}; } FunctionSpace output_functionspace(const Grid& grid) { MeshGenerator meshgen("structured"); Mesh output_mesh = meshgen.generate(grid); return NodeColumns{output_mesh}; } CASE("which scheme?") { Log::info() << scheme().getString("type") << std::endl; } CASE("test_interpolation_structured using functionspace API") { Grid grid(input_gridname("O32")); // Cubic interpolation requires a StructuredColumns functionspace with 2 halos StructuredColumns input_fs(grid, scheme()); auto test = [&](const FunctionSpace& output_fs) { // The output functionspace can currently be either NodeColumns or PointCloud Interpolation interpolation(scheme(), input_fs, output_fs); Field field_source = input_fs.createField(option::name("source")); Field field_target = output_fs.createField(option::name("target")); auto lonlat = array::make_view(input_fs.xy()); auto source = array::make_view(field_source); for (idx_t n = 0; n < input_fs.size(); ++n) { source(n) = util::function::vortex_rollup(lonlat(n, LON), lonlat(n, LAT), 1.); } EXPECT(field_source.dirty()); interpolation.execute(field_source, field_target); }; SECTION("Interpolate from " + grid.name() + " to " + output_gridname("O64")) { EXPECT_NO_THROW(test(output_functionspace(Grid{output_gridname("O64")}))); } SECTION("Interpolate from " + grid.name() + " to rotated " + output_gridname("O64")) { EXPECT_NO_THROW(test(output_functionspace(rotated(output_gridname("O64"))))); } SECTION("Interpolate from " + grid.name() + " to lambert") { EXPECT_NO_THROW(test(output_functionspace(lambert()))); } SECTION("Interpolate from " + grid.name() + " to rotaded_mercator") { EXPECT_NO_THROW(test(output_functionspace(rotated_mercator()))); } } CASE("test_interpolation_structured using grid API") { // Using the grid API we can hide interpolation method specific requirements // such as which functionspace needs to be set-up. // Currently the assumption is that grids are serial Grid input_grid(input_gridname("O32")); auto test = [&](const Grid& output_grid) { Interpolation interpolation(scheme(), input_grid, output_grid); // Allocate and initialise own memory here to show possibilities // Note that allocated size must be possibly enlarged depending on interpolation method // allocates stencil halo std::vector src_data(interpolation.source().size()); std::vector tgt_data(interpolation.target().size()); idx_t n{0}; for (auto p : input_grid.lonlat()) { src_data[n++] = util::function::vortex_rollup(p.lon(), p.lat(), 1.); } // Wrap memory in atlas Fields and interpolate Field field_source{"source", src_data.data(), array::make_shape(src_data.size())}; Field field_target{"target", tgt_data.data(), array::make_shape(tgt_data.size())}; // Wrapping data does not set the field to have dirty halo's { EXPECT(not field_source.dirty()); field_source.set_dirty(); } EXPECT(field_source.dirty()); interpolation.execute(field_source, field_target); ATLAS_TRACE_SCOPE("output") { output::Gmsh gmsh(scheme().getString("name") + "-output-section" + std::to_string(_subsection) + ".msh", Config("coordinates", "xy")); gmsh.write(Mesh(output_grid)); gmsh.write(field_target, StructuredColumns(output_grid)); } }; SECTION("Interpolate from " + input_grid.name() + " to " + output_gridname("O64")) { EXPECT_NO_THROW(test(Grid{output_gridname("O64")})); } SECTION("Interpolate from " + input_grid.name() + " to rotated " + output_gridname("O64")) { EXPECT_NO_THROW(test(rotated(output_gridname("O64")))); } SECTION("Interpolate from " + input_grid.name() + " to lambert") { EXPECT_NO_THROW(test(lambert())); } SECTION("Interpolate from " + input_grid.name() + " to rotaded_mercator") { EXPECT_NO_THROW(test(rotated_mercator())); } } CASE("test_interpolation_structured using fs API multiple levels") { Grid input_grid(input_gridname("O32")); Grid output_grid(output_gridname("O64")); // Cubic interpolation requires a StructuredColumns functionspace with 2 halos StructuredColumns input_fs(input_grid, scheme() | option::levels(3)); MeshGenerator meshgen("structured"); Mesh output_mesh = meshgen.generate(output_grid); FunctionSpace output_fs = NodeColumns{output_mesh, option::levels(3)}; Interpolation interpolation(scheme(), input_fs, output_fs); Field field_source = input_fs.createField(option::name("source")); Field field_target = output_fs.createField(option::name("target")); auto lonlat = array::make_view(input_fs.xy()); auto source = array::make_view(field_source); for (idx_t n = 0; n < input_fs.size(); ++n) { for (idx_t k = 0; k < 3; ++k) { source(n, k) = util::function::vortex_rollup(lonlat(n, LON), lonlat(n, LAT), 0.5 + double(k) / 2); } } interpolation.execute(field_source, field_target); ATLAS_TRACE_SCOPE("output") { output::Gmsh gmsh( scheme().getString("name") + "-multilevel-output-section" + std::to_string(_subsection) + ".msh", Config("coordinates", "xy")); gmsh.write(output_mesh); output_fs.haloExchange(field_target); gmsh.write(field_target); } } template struct AdjointTolerance { static const Value value; }; template <> const double AdjointTolerance::value = 2.e-14; template <> const float AdjointTolerance::value = 2.e-5; template void test_interpolation_structured_using_fs_API_for_fieldset() { Grid input_grid(input_gridname("O32")); Grid output_grid(output_gridname("O64")); // Cubic interpolation requires a StructuredColumns functionspace with 2 halos StructuredColumns input_fs(input_grid, scheme() | option::levels(3)); MeshGenerator meshgen("structured"); Mesh output_mesh = meshgen.generate(output_grid); FunctionSpace output_fs = NodeColumns{output_mesh, option::levels(3)}; auto lonlat = array::make_view(input_fs.xy()); FieldSet fields_source; FieldSet fields_target; for (idx_t f = 0; f < 3; ++f) { auto field_source = fields_source.add(input_fs.createField(option::name("field " + std::to_string(f)))); fields_target.add(output_fs.createField(option::name("field " + std::to_string(f)))); auto source = array::make_view(field_source); for (idx_t n = 0; n < input_fs.size(); ++n) { for (idx_t k = 0; k < 3; ++k) { source(n, k) = util::function::vortex_rollup(lonlat(n, LON), lonlat(n, LAT), 0.5 + double(k) / 2); } } } ATLAS_TRACE_SCOPE("with matrix") { Interpolation interpolation(scheme(), input_fs, output_fs); interpolation.execute(fields_source, fields_target); ATLAS_TRACE_SCOPE("output") { output::Gmsh gmsh(scheme().getString("name") + "-multilevel-fieldset-output-with-matrix-" + array::make_datatype().str() + ".msh", Config("coordinates", "xy")); gmsh.write(output_mesh); output_fs.haloExchange(fields_target); gmsh.write(fields_target); } } ATLAS_TRACE_SCOPE("with matrix adjoint") { Interpolation interpolation(scheme() | Config("adjoint", true), input_fs, output_fs); std::vector AxAx(fields_source.field_names().size(), 0.); std::vector xAtAx(fields_source.field_names().size(), 0.); FieldSet fields_source_reference; for (atlas::Field& field : fields_source) { Field temp_field(field.name(), field.datatype().kind(), field.shape()); temp_field.set_levels(field.levels()); auto fieldInView = array::make_view(field); auto fieldOutView = array::make_view(temp_field); for (atlas::idx_t jn = 0; jn < temp_field.shape(0); ++jn) { for (atlas::idx_t jl = 0; jl < temp_field.levels(); ++jl) { fieldOutView(jn, jl) = fieldInView(jn, jl); } } fields_source_reference.add(temp_field); } interpolation.execute(fields_source, fields_target); std::size_t fIndx(0); auto source_names = fields_source.field_names(); for (const std::string& s : fields_target.field_names()) { auto target = array::make_view(fields_target[s]); auto source = array::make_view(fields_source[source_names[fIndx]]); for (idx_t n = 0; n < input_fs.size(); ++n) { for (idx_t k = 0; k < 3; ++k) { AxAx[fIndx] += source(n, k) * source(n, k); } } for (idx_t n = 0; n < output_fs.size(); ++n) { for (idx_t k = 0; k < 3; ++k) { AxAx[fIndx] += target(n, k) * target(n, k); } } fIndx += 1; } interpolation.execute_adjoint(fields_source, fields_target); fIndx = 0; for (const std::string& s : fields_source.field_names()) { auto source_reference = array::make_view(fields_source_reference[s]); auto source = array::make_view(fields_source[s]); for (idx_t n = 0; n < input_fs.size(); ++n) { for (idx_t k = 0; k < 3; ++k) { xAtAx[fIndx] += source(n, k) * source_reference(n, k); } } fIndx += 1; } for (std::size_t t = 0; t < AxAx.size(); ++t) { Log::debug() << " Adjoint test t = " << t << " (Ax).(Ax) = " << AxAx[t] << " x.(AtAx) = " << xAtAx[t] << " std::abs( 1.0 - xAtAx[t]/AxAx[t] ) " << std::abs(1.0 - xAtAx[t] / AxAx[t]) << " AdjointTolerance::value " << AdjointTolerance::value << std::endl; EXPECT(std::abs(1.0 - xAtAx[t] / AxAx[t]) < AdjointTolerance::value); } } ATLAS_TRACE_SCOPE("matrix free") { Interpolation interpolation(scheme() | Config("matrix_free", true), input_fs, output_fs); interpolation.execute(fields_source, fields_target); ATLAS_TRACE_SCOPE("output") { output::Gmsh gmsh(scheme().getString("name") + "-multilevel-fieldset-output-section-matrix-free-" + array::make_datatype().str() + ".msh", Config("coordinates", "xy")); gmsh.write(output_mesh); output_fs.haloExchange(fields_target); gmsh.write(fields_target); } } } CASE("test_interpolation_structured using fs API for fieldset (Value=double)") { test_interpolation_structured_using_fs_API_for_fieldset(); } CASE("test_interpolation_structured using fs API for fieldset (Value=float)") { test_interpolation_structured_using_fs_API_for_fieldset(); } /// @brief Compute magnitude of flow with rotation-angle beta /// (beta=0 --> zonal, beta=pi/2 --> meridional) Field rotated_flow(const StructuredColumns& fs, const double& beta) { const double radius = util::Earth::radius(); const double USCAL = 20.; const double pvel = USCAL / radius; const double deg2rad = M_PI / 180.; array::ArrayView lonlat_deg = array::make_view(fs.xy()); Field field = fs.createField(option::vector()); array::ArrayView var = array::make_view(field); idx_t nnodes = var.shape(0); for (idx_t jnode = 0; jnode < nnodes; ++jnode) { double x = lonlat_deg(jnode, LON) * deg2rad; double y = lonlat_deg(jnode, LAT) * deg2rad; double Ux = pvel * (std::cos(beta) + std::tan(y) * std::cos(x) * std::sin(beta)) * radius * std::cos(y); double Uy = -pvel * std::sin(x) * std::sin(beta) * radius; var(jnode, LON) = Ux; var(jnode, LAT) = Uy; } return field; } CASE("test_interpolation_structured for vectors") { Grid input_grid(input_gridname("O32")); Grid output_grid(output_gridname("O64")); // Cubic interpolation requires a StructuredColumns functionspace with 2 halos StructuredColumns input_fs(input_grid, option::halo(2)); MeshGenerator meshgen("structured"); Mesh output_mesh = meshgen.generate(output_grid); FunctionSpace output_fs = NodeColumns{output_mesh}; Interpolation interpolation(scheme(), input_fs, output_fs); Field field_source = rotated_flow(input_fs, M_PI_4); Field field_target = output_fs.createField(option::name("target") | option::vector()); interpolation.execute(field_source, field_target); ATLAS_TRACE_SCOPE("output") { output::Gmsh gmsh(scheme().getString("name") + "-vector-output-section" + std::to_string(_subsection) + ".msh", Config("coordinates", "xy")); gmsh.write(output_mesh); output_fs.haloExchange(field_target); gmsh.write(field_target); } } CASE("ATLAS-315: Target grid with domain West of 0 degrees Lon") { Grid input_grid(input_gridname("O256")); Grid output_grid(output_gridname("L45"), RectangularDomain({-10, 10}, {20, 60})); Interpolation interpolation(scheme() | Config("matrix_free", true), input_grid, output_grid); auto field_src = interpolation.source().createField(option::name("src")); auto field_tgt = interpolation.target().createField(option::name("tgt")); auto lonlat = array::make_view(StructuredColumns(interpolation.source()).xy()); auto source = array::make_view(field_src); constexpr double k = 1; for (idx_t n = 0; n < source.size(); ++n) { source(n) = util::function::vortex_rollup(lonlat(n, LON), lonlat(n, LAT), 0.5 + double(k) / 2); } interpolation.execute(field_src, field_tgt); } } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/interpolation/test_interpolation_structured2D_limiter.cc0000664000175000017500000002706015160212070030344 0ustar alastairalastair/* * (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 "atlas/library.h" #include "atlas/runtime/Log.h" #include "atlas/functionspace.h" #include "atlas/interpolation.h" #include "atlas/output/Gmsh.h" #include "tests/AtlasTestEnvironment.h" using atlas::functionspace::NodeColumns; using atlas::functionspace::StructuredColumns; using atlas::functionspace::PointCloud; using atlas::util::Config; namespace atlas { namespace test { //----------------------------------------------------------------------------- std::vector read_array(const std::string& filename) { std::string files_dir="/Users/willem/work/debug-ifs-mgrids-remap/files/H15p3_cubicatlasinterp/"; std::ifstream file (files_dir+filename); assert(file.is_open()); std::size_t size; file >> size; std::vector array(size); for(std::size_t j=0; j> array[j]; } return array; } std::vector to_points(const std::vector& lon, const std::vector& lat) { std::vector points(lon.size()); for( std::size_t j=0; j ignore_overshoot{6271, 6272, 7196, 7212}; std::set ignore_undershoot{43138,43319}; template std::pair view_max_loc(View v) { double m = -std::numeric_limits::max(); atlas::idx_t loc = 0; for(atlas::idx_t j=0; j m) { if (ignore_overshoot.find(j) == ignore_overshoot.end()) { m = v[j]; loc = j; } } } return {m,loc}; } std::pair view_max_loc(atlas::array::ArrayView v) { auto all = atlas::array::Range::all(); return view_max_loc( v.slice(all,0) ); } std::pair view_max_loc(atlas::array::ArrayView v) { auto all = atlas::array::Range::all(); return view_max_loc( v.slice(all,0,0) ); } std::pair max_loc(const Field& field) { if( field.rank() == 1 ) { return view_max_loc(atlas::array::make_view(field)); } else if( field.rank() == 2 ) { return view_max_loc(atlas::array::make_view(field)); } else if( field.rank() == 3 ) { return view_max_loc(atlas::array::make_view(field)); } else { ATLAS_NOTIMPLEMENTED; } } template std::pair view_min_loc(View v) { double m = std::numeric_limits::max(); atlas::idx_t loc = 0; for(atlas::idx_t j=0; j view_min_loc(atlas::array::ArrayView v) { auto all = atlas::array::Range::all(); return view_min_loc( v.slice(all,0) ); } std::pair view_min_loc(atlas::array::ArrayView v) { auto all = atlas::array::Range::all(); return view_min_loc( v.slice(all,0,0) ); } std::pair min_loc(const Field& field) { if( field.rank() == 1 ) { return view_min_loc(atlas::array::make_view(field)); } else if( field.rank() == 2 ) { return view_min_loc(atlas::array::make_view(field)); } else if( field.rank() == 3 ) { return view_min_loc(atlas::array::make_view(field)); } else { ATLAS_NOTIMPLEMENTED; } } std::ostream& operator<<(std::ostream& out, const std::pair& mloc) { out << std::setw(13) << std::fixed << std::setprecision(10) << mloc.first << " at index " << mloc.second; return out; } std::vector overshooting_dg_points() { return std::vector{ {180., 45.2048}, {0.,-88.9603}, // undershoot {360.,-88.9603} // undershoot }; } std::vector read_dg_points() { static auto dg_lon = read_array("lambda_dg_nod.txt"); static auto dg_lat = read_array("theta_dg_nod.txt"); return to_points(dg_lon,dg_lat); }; void read_tracer(atlas::Field& field_ifs) { // the tracer read from file should match closely the analytical function below 'init_tracer' static auto ifs_trc = read_array("trc_ifs_gp.txt"); if( field_ifs.rank() == 1 ) { auto view_ifs = array::make_view(field_ifs); for( std::size_t j=0; j(ifs_trc.size(), field_ifs.shape(0)); ++j) { view_ifs(j) = ifs_trc[j]; } } else if( field_ifs.rank() == 2 ) { auto view_ifs = array::make_view(field_ifs); for( std::size_t j=0; j(ifs_trc.size(), field_ifs.shape(0)); ++j) { view_ifs(j,0) = ifs_trc[j]; } } else if( field_ifs.rank() == 3 ) { auto view_ifs = array::make_view(field_ifs); for( std::size_t j=0; j(ifs_trc.size(), field_ifs.shape(0)); ++j) { view_ifs(j,0,0) = ifs_trc[j]; } } else { ATLAS_NOTIMPLEMENTED; } field_ifs.set_dirty(); } void init_tracer(atlas::Field& field_ifs) { auto func = [](double lon, double lat) -> double { constexpr double lon_ctr = M_PI; constexpr double lat_ctr = M_PI/4.; constexpr double sigma_lon = 2.*M_PI/5.; constexpr double sigma_lat = 2.*M_PI/5.; constexpr double to_rad = M_PI/180.; lon *= to_rad; lat *= to_rad; auto sqr = [](double x){ return x*x;}; return std::exp( - sqr( (lon-lon_ctr) / (std::sqrt(2.)*sigma_lon)) ) * std::exp( - sqr( (lat-lat_ctr) / (std::sqrt(2.)*sigma_lat)) ); }; auto ifs_lonlat = atlas::array::make_view(field_ifs.functionspace().lonlat()); if( field_ifs.rank() == 1 ) { auto view_ifs = array::make_view(field_ifs); for( std::size_t j=0; j(field_ifs); for( std::size_t j=0; j(field_ifs); for( std::size_t j=0; j(field).assign(99999999999.); } else if( field.rank() == 2 ) { array::make_view(field).assign(99999999999.); } else if( field.rank() == 3 ) { array::make_view(field).assign(99999999999.); } else { ATLAS_NOTIMPLEMENTED; } } // ---------------------------------------------------------------------- void do_test(bool limiter, int rank) { // std::vector dg_points = read_dg_points(); // This reads coordinates from files in ascii with format "nb_points value[0] value[1] ... value[nb_points-1]" std::vector dg_points = overshooting_dg_points(); atlas::functionspace::PointCloud dg_fs(dg_points); atlas::functionspace::StructuredColumns ifs_fs(atlas::Grid("O80"), atlas::option::halo(3)); atlas::Field field_ifs; atlas::Field field_dg; if( rank == 1 ) { field_ifs = ifs_fs.createField(); field_dg = dg_fs.createField(); } else if( rank == 2 ) { int nlev = 60; field_ifs = ifs_fs.createField(atlas::option::levels(nlev)); field_dg = dg_fs.createField(atlas::option::levels(nlev)); } else if( rank == 3 ) { int nlev = 60; int ntrc = 1; field_ifs = ifs_fs.createField(atlas::option::levels(nlev)|atlas::option::variables(ntrc)); field_dg = dg_fs.createField(atlas::option::levels(nlev)|atlas::option::variables(ntrc)); } else { ATLAS_NOTIMPLEMENTED; } init_crazy(field_dg); init_tracer(field_ifs); // read_tracer(field_ifs); atlas::util::Config config; config.set("type", "structured-quasicubic2D"); config.set("limiter", limiter); config.set("matrix_free", true); atlas::Interpolation interpolator(config, ifs_fs, dg_fs); interpolator.execute(field_ifs, field_dg); auto max_ifs = max_loc(field_ifs); auto min_ifs = min_loc(field_ifs); auto max_dg = max_loc(field_dg); auto min_dg = min_loc(field_dg); int precision = 10; std::cout << "max_ifs: " << max_ifs << std::endl; std::cout << "max_dg: " << max_dg << std::endl; std::cout << "max_diff: " << std::setw(13) << std::fixed << std::setprecision(precision) << max_ifs.first - max_dg.first << std::endl; std::cout << std::endl; std::cout << "min_ifs: " << min_ifs << std::endl; std::cout << "min_dg: " << min_dg << std::endl; std::cout << "min_diff: " << std::setw(13) << std::fixed << std::setprecision(precision) << min_dg.first - min_ifs.first << std::endl; if( limiter && max_dg.first > max_ifs.first ) { std::cout << std::endl; std::cout << "FAILURE: overshoot in interpolation detected " << std::endl; auto lonlat = atlas::array::make_view(dg_fs.lonlat()); std::cout << " coordinate ["<(dg_fs.lonlat()); std::cout << " coordinate ["<= min_ifs.first); } } CASE("structured-quasicubic2D without limiter, rank 1") { bool limiter = false; int rank = 1; do_test(limiter, rank); } CASE("structured-quasicubic2D with limiter, rank 1") { bool limiter = true; int rank = 1; do_test(limiter, rank); } CASE("structured-quasicubic2D without limiter, rank 2") { bool limiter = false; int rank = 2; do_test(limiter, rank); } CASE("structured-quasicubic2D with limiter, rank 2") { bool limiter = true; int rank = 2; do_test(limiter, rank); } CASE("structured-quasicubic2D without limiter, rank 3") { bool limiter = false; int rank = 3; do_test(limiter, rank); } CASE("structured-quasicubic2D with limiter, rank 3") { bool limiter = true; int rank = 3; do_test(limiter, rank); } } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/interpolation/test_interpolation_spherical_vector_to_point_6PE.cc0000664000175000017500000001461415160212070032147 0ustar alastairalastair/* * (C) Crown Copyright 2026 Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ #include #include #include #include #include #include "atlas/array.h" #include "atlas/array/helpers/ArrayForEach.h" #include "atlas/field/Field.h" #include "atlas/field/FieldSet.h" #include "atlas/functionspace/NodeColumns.h" #include "atlas/functionspace/PointCloud.h" #include "atlas/grid/Grid.h" #include "atlas/interpolation.h" #include "atlas/mesh/Mesh.h" #include "atlas/meshgenerator.h" #include "atlas/option.h" #include "atlas/util/Config.h" #include "atlas/util/Constants.h" #include "atlas/util/function/VortexRollup.h" #include "atlas/parallel/mpi/mpi.h" #include "tests/AtlasTestEnvironment.h" namespace atlas { namespace test { struct FieldSpecsFixture { static const util::Config& get(const std::string& fixture) { static const auto fieldSpecs = std::map{ {"source", option::name("sfield") | option::variables(2) | option::type("vector")}, {"target", option::name("tfield") | option::variables(2) | option::type("vector")}}; return fieldSpecs.at(fixture); } }; // function to define the 'source' function space auto generateFunctionspaceSource() { const auto csgrid = Grid("CS-LFR-48"); const auto csmesh = MeshGenerator("cubedsphere_dual").generate(csgrid); return functionspace::NodeColumns(csmesh); } // function to define the 'target' function space auto generateFunctionspaceTarget() { const mpi::Comm& comm_w = mpi::comm(); std::size_t mpi_rank = comm_w.rank(); // data structure storing a single point // (in a cubed sphere with 6 partitions, the point near // the North pole is part of partition #4) std::vector spoint; if (mpi_rank == 4) { spoint.push_back({360., 90.}); } return functionspace::PointCloud(spoint); } std::pair vortexHorizontal(double lon, double lat) { // set hLon and hLat step size. const double hLon = 0.0001; const double hLat = 0.0001; const double u = ( util::function::vortex_rollup(lon, lat + 0.5 * hLat, 0.1) - util::function::vortex_rollup(lon, lat - 0.5 * hLat, 0.1)) / hLat; const double v = -( util::function::vortex_rollup(lon + 0.5 * hLon, lat, 0.1) - util::function::vortex_rollup(lon - 0.5 * hLon, lat, 0.1)) / (hLon * std::cos(lat * util::Constants::degreesToRadians())); return std::make_pair(u, v); } // function to generate a data structure containing the 'source' field FieldSet generateFieldsSource(FunctionSpace& fsSource) { const auto fieldSourceSpecs = FieldSpecsFixture::get("source"); FieldSet fieldsSource; auto fieldSource = fieldsSource.add(fsSource.createField(fieldSourceSpecs)); auto fieldSourceView = array::make_view(fieldSource); const auto lonLatSource = array::make_view(fsSource.lonlat()); array::helpers::ArrayForEach<0>::apply( std::tie(lonLatSource, fieldSourceView), [](auto&& lonLat, auto&& columnSource) { const auto setElems = [&](auto&& elemSource) { std::tie(elemSource(0), elemSource(1)) = vortexHorizontal(lonLat(0), lonLat(1)); }; setElems(columnSource); }); return fieldsSource; } // function to generate a data structure containing the 'target' field FieldSet generateFieldsTarget(FunctionSpace& fs) { const auto fieldTargetSpecs = FieldSpecsFixture::get("target"); FieldSet fieldsTarget; fieldsTarget.add(fs.createField(fieldTargetSpecs)); return fieldsTarget; } double dotProduct(const array::ArrayView& a, const array::ArrayView& b) { auto dotProd = 0.; array::helpers::arrayForEachDim( std::make_integer_sequence{}, std::tie(a, b), [&](const double& aElem, const double& bElem) { dotProd += aElem * bElem; }); return dotProd; } CASE("source grid: cubed sphere (CS-LFR-48); " "target grid: point cloud (single point); " "field distribution: 3D-field, single level, 2D-vector; " "procedure: vector interpolation; " "parallelization: MPI (6 PEs)") { auto fsSource = generateFunctionspaceSource(); auto fsTarget = generateFunctionspaceTarget(); auto fieldsSource = generateFieldsSource(fsSource); auto fieldSourceView = array::make_view(fieldsSource["sfield"]); auto fieldsTarget = generateFieldsTarget(fsTarget); auto fieldTargetView = array::make_view(fieldsTarget["tfield"]); const auto ansScheme = option::type("cubedsphere-bilinear") | util::Config("adjoint", true); const auto scheme = util::Config("type", "spherical-vector") | util::Config("adjoint", true) | util::Config("scheme", ansScheme); Interpolation interp(scheme, fsSource, fsTarget); interp.execute(fieldsSource, fieldsTarget); fieldsTarget.haloExchange(); //-- // performing a dot-product test ... const auto fieldTargetSpecs = FieldSpecsFixture::get("target"); auto adjointTarget = fsTarget.createField(fieldTargetSpecs); adjointTarget.array().copy(fieldsTarget["tfield"]); adjointTarget.adjointHaloExchange(); const auto fieldSourceSpecs = FieldSpecsFixture::get("source"); auto adjointSource = fsSource.createField(fieldSourceSpecs); auto adjointSourceView = array::make_view(adjointSource); adjointSourceView.assign(0.); interp.execute_adjoint(adjointSource, adjointTarget); constexpr auto tinyNum = 1e-13; const auto targetDotTarget = dotProduct(fieldTargetView, fieldTargetView); const auto sourceDotAdjointSource = dotProduct(fieldSourceView, adjointSourceView); const mpi::Comm& comm_w = mpi::comm(); std::size_t mpi_rank = comm_w.rank(); if (mpi_rank == 4) { const auto dotProdRatio = targetDotTarget / sourceDotAdjointSource; EXPECT_APPROX_EQ(dotProdRatio, 1., tinyNum); } } } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/interpolation/test_interpolation_cubic_prototype.cc0000664000175000017500000003577215160212070027450 0ustar alastairalastair/* * (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 "atlas/array.h" #include "atlas/field/Field.h" #include "atlas/functionspace/NodeColumns.h" #include "atlas/functionspace/PointCloud.h" #include "atlas/functionspace/StructuredColumns.h" #include "atlas/grid/Partitioner.h" #include "atlas/grid/StructuredGrid.h" #include "atlas/interpolation.h" #include "atlas/mesh/Mesh.h" #include "atlas/meshgenerator.h" #include "atlas/output/Gmsh.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/util/CoordinateEnums.h" #include "atlas/util/MicroDeg.h" #if ATLAS_ECKIT_HAVE_ECKIT_585 #include "eckit/linalg/LinearAlgebraSparse.h" #else #include "eckit/linalg/LinearAlgebra.h" #endif #include "eckit/linalg/SparseMatrix.h" #include "eckit/linalg/Vector.h" #include "eckit/types/Types.h" #include "CubicInterpolationPrototype.h" #include "tests/AtlasTestEnvironment.h" using namespace atlas::functionspace; using namespace atlas::grid; using namespace atlas::util; using namespace atlas::array; namespace atlas { namespace test { //----------------------------------------------------------------------------- std::vector IFS_full_levels_uniform(idx_t nlev) { std::vector zcoord(nlev); double dzcoord = 1. / double(nlev); for (idx_t jlev = 0; jlev < nlev; ++jlev) { zcoord[jlev] = 0.5 * dzcoord + jlev * dzcoord; } return zcoord; } std::vector zrange(idx_t nlev, double min, double max) { std::vector zcoord(nlev); double dzcoord = (max - min) / double(nlev - 1); for (idx_t jlev = 0; jlev < nlev; ++jlev) { zcoord[jlev] = min + jlev * dzcoord; } return zcoord; } double cubic(double x, double min, double max) { double x0 = min; double x1 = 0.5 * (max + min); double x2 = max; double xmax = 0.5 * (x0 + x1); return (x - x0) * (x - x1) * (x - x2) / ((xmax - x0) * (xmax - x1) * (xmax - x2)); } CASE("test vertical cubic interpolation") { idx_t nlev = 10; auto vertical = Vertical{nlev, IFS_full_levels_uniform(nlev)}; bool limiter = true; CubicVerticalInterpolation interpolate(vertical, util::Config("limiter", limiter)); std::vector departure_points{0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0, 0.3246}; array::ArrayT array(nlev); auto view = array::make_view(array); Log::info() << "source:" << std::endl; for (idx_t k = 0; k < nlev; ++k) { view(k) = cubic(vertical(k), 0., 1.); Log::info() << " " << vertical(k) << " : " << view(k) << std::endl; } Log::info() << "interpolation:" << std::endl; for (auto p : departure_points) { Log::info() << " " << std::setw(6) << p << " : " << std::setw(12) << interpolate(p, view) << " expected : " << cubic(p, 0., 1.) << std::endl; if (p >= vertical.front() && p <= vertical.back() && !limiter) { EXPECT(eckit::types::is_approximately_equal(interpolate(p, view), cubic(p, 0., 1.))); } } } //----------------------------------------------------------------------------- CASE("test horizontal cubic interpolation") { //if ( mpi::comm().size() == 1 ) { std::string gridname = eckit::Resource("--grid", "O8"); StructuredGrid grid(gridname); int halo = eckit::Resource("--halo", 2); Config config; config.set("halo", halo); config.set("levels", 9); config.set("periodic_points", true); StructuredColumns fs(grid, Partitioner("equal_regions"), config); Field field = fs.createField(option::levels(0)); auto f = array::make_view(field); auto xy = array::make_view(fs.xy()); auto fx = [](double x) { return cubic(x, 0., 360.); }; auto fy = [](double y) { return cubic(y, -90., 90.); }; auto fxy = [fx, fy](double x, double y) { return fx(x) * fy(y); }; for (idx_t j = 0; j < fs.size(); ++j) { f(j) = fxy(xy(j, XX), xy(j, YY)); } CubicHorizontalInterpolation cubic_interpolation(fs); auto departure_points = { PointXY(0.13257, 45.6397), }; for (auto p : departure_points) { Log::info() << p << " --> " << cubic_interpolation(p.x(), p.y(), f) << std::endl; EXPECT(is_approximately_equal(cubic_interpolation(p.x(), p.y(), f), fxy(p.x(), p.y()))); } } //----------------------------------------------------------------------------- bool operator==(const eckit::linalg::Triplet& t1, const eckit::linalg::Triplet& t2) { if (t1.row() != t2.row()) { return false; } if (t1.col() != t2.col()) { return false; } if (!is_approximately_equal(t1.value(), t2.value())) { return false; } return true; } bool operator!=(const eckit::linalg::Triplet& t1, const eckit::linalg::Triplet& t2) { return !(t1 == t2); } bool operator==(const std::vector& t1, const std::vector& t2) { if (t1.size() != t2.size()) { return false; } for (size_t i = 0; i < t1.size(); ++i) { if (t1[i] != t2[i]) { return false; } } return true; } //std::ostream& operator<<( std::ostream& out, const LocalView& array ) { // out << "{ "; // for ( idx_t i = 0; i < array.size(); ++i ) { // out << array( i ); // if ( i < array.size() - 1 ) { // out << ","; // } // out << " "; // } // out << "}"; // return out; //} //----------------------------------------------------------------------------- CASE("test horizontal cubic interpolation triplets") { //if ( mpi::comm().size() == 1 ) { std::string gridname = eckit::Resource("--grid", "O8"); StructuredGrid grid(gridname); int halo = eckit::Resource("--halo", 2); Config config; config.set("halo", halo); config.set("levels", 9); config.set("periodic_points", true); StructuredColumns fs(grid, Partitioner("equal_regions"), config); Field field = fs.createField(option::levels(0)); auto f = array::make_view(field); auto xy = array::make_view(fs.xy()); for (idx_t j = 0; j < fs.size(); ++j) { f(j) = xy(j, XX); } CubicHorizontalInterpolation cubic_interpolation(fs); auto departure_points = PointCloud{{ {0.13257, 45.6397}, {360., -90.}, }}; auto departure_lonlat = make_view(departure_points.lonlat()); CubicHorizontalInterpolation::WorkSpace ws; CubicHorizontalInterpolation::Triplets triplets; for (idx_t row = 0; row < departure_points.size(); ++row) { auto triplets_row = cubic_interpolation.compute_triplets(row, departure_lonlat(row, XX), departure_lonlat(row, YY), ws); Log::info() << departure_lonlat.slice(row, array::Range::all()) << " --> {\n"; for (auto triplet : triplets_row) { Log::info() << " " << triplet << " ,\n"; } Log::info() << " } " << std::endl; std::copy(triplets_row.begin(), triplets_row.end(), std::back_inserter(triplets)); } auto triplets2 = cubic_interpolation.reserve_triplets(departure_points.size()); { idx_t row{0}; for (auto p : departure_points.iterate().xy()) { cubic_interpolation.insert_triplets(row++, p.x(), p.y(), triplets2, ws); } } EXPECT(triplets2 == triplets); eckit::linalg::SparseMatrix matrix(departure_points.size(), fs.size(), triplets2); Log::info() << matrix << std::endl; std::vector tgt(departure_points.size()); eckit::linalg::Vector v_src(const_cast(f.data()), f.size()); eckit::linalg::Vector v_tgt(tgt.data(), tgt.size()); #if ATLAS_ECKIT_HAVE_ECKIT_585 eckit::linalg::LinearAlgebraSparse::backend().spmv(matrix, v_src, v_tgt); #else eckit::linalg::LinearAlgebra::backend().spmv(matrix, v_src, v_tgt); #endif Log::info() << "output = " << tgt << std::endl; } //----------------------------------------------------------------------------- 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; Vertical vertical(nlev, zrange(nlev, 0., 1.)); Log::info() << zrange(nlev, 0., 1.) << std::endl; ; StructuredGrid grid(gridname); int halo = eckit::Resource("--halo", 2); Config config; config.set("halo", halo); config.set("periodic_points", true); StructuredColumns fs(grid, vertical, Partitioner("equal_regions"), config); Field input = fs.createField(); auto f = array::make_view(input); auto xy = array::make_view(fs.xy()); const auto& z = fs.vertical(); auto fx = [](double x) { return cubic(x, 0., 360.); }; auto fy = [](double y) { return cubic(y, -90., 90.); }; auto fz = [](double z) { return cubic(z, 0., 1.); }; auto fp = [fx, fy, fz](const PointXYZ& p) { return fx(p.x()) * fy(p.y()) * fz(p.z()); }; for (idx_t n = 0; n < fs.size(); ++n) { for (idx_t k = fs.k_begin(); k < fs.k_end(); ++k) { PointXYZ p{ xy(n, XX), xy(n, YY), z(k), }; f(n, k) = fp(p); } } input.set_dirty(false); // to avoid halo-exchange auto departure_points = PointCloud({ {90., -45., 0.25}, {0., -45., 0.25}, {180., -45., 0.25}, {360., -45., 0.25}, {90., -90., 0.25}, {90., 0., 0.25}, {90., 90., 0.25}, {10., -45., 0.25}, {20., -45., 0.25}, {30., -45., 0.25}, {40., -45., 0.25}, {50., -45., 0.25}, {60., -45., 0.25}, {70., -45., 0.25}, {80., -45., 0.25}, {90., -45., 0.25}, {10., -10., 0.25}, {20., -20., 0.25}, {30., -30., 0.25}, {40., -40., 0.25}, {50., -50., 0.25}, {60., -60., 0.25}, {70., -70., 0.25}, {80., -80., 0.25}, {90., -90., 0.25}, {60., -60., 0.6}, {90., -45., 0.16}, {90., -45., 0.6}, {90., -45., 1.}, {90., -45., 0.}, {90., -45., 0.1}, {45., 33., 0.7}, {359., 20., 0.1}, {360., 88., 0.9}, {0.1, -89., 1.}, {90., -45., 0.25}, {0., -45., 0.25}, {180., -45., 0.25}, {360., -45., 0.25}, {90., -90., 0.25}, {90., 0., 0.25}, {90., 90., 0.25}, {10., -45., 0.25}, {20., -45., 0.25}, {30., -45., 0.25}, {40., -45., 0.25}, {50., -45., 0.25}, {60., -45., 0.25}, {70., -45., 0.25}, {80., -45., 0.25}, {90., -45., 0.25}, {10., -10., 0.25}, {20., -20., 0.25}, {30., -30., 0.25}, {40., -40., 0.25}, {50., -50., 0.25}, {60., -60., 0.25}, {70., -70., 0.25}, {80., -80., 0.25}, {90., -90., 0.25}, {60., -60., 0.6}, {90., -45., 0.16}, {90., -45., 0.6}, {90., -45., 1.}, {90., -45., 0.}, {90., -45., 0.1}, {45., 33., 0.7}, {359., 20., 0.1}, {360., 88., 0.9}, {0.1, -89., 1.}, {90., -45., 0.25}, {0., -45., 0.25}, {180., -45., 0.25}, {360., -45., 0.25}, {90., -90., 0.25}, {90., 0., 0.25}, {90., 90., 0.25}, {10., -45., 0.25}, {20., -45., 0.25}, {30., -45., 0.25}, {40., -45., 0.25}, {50., -45., 0.25}, {60., -45., 0.25}, {70., -45., 0.25}, {80., -45., 0.25}, {90., -45., 0.25}, {10., -10., 0.25}, {20., -20., 0.25}, {30., -30., 0.25}, {40., -40., 0.25}, {50., -50., 0.25}, {60., -60., 0.25}, {70., -70., 0.25}, {80., -80., 0.25}, {90., -90., 0.25}, {60., -60., 0.6}, {90., -45., 0.16}, {90., -45., 0.6}, {90., -45., 1.}, {90., -45., 0.}, {90., -45., 0.1}, {45., 33., 0.7}, {359., 20., 0.1}, {360., 88., 0.9}, {0.1, -89., 1.}, }); SECTION("prototype") { Cubic3DInterpolation cubic_interpolation(fs); for (auto p : departure_points.iterate().xyz()) { double interpolated = cubic_interpolation(p, f); double exact = fp(p); Log::info() << p << " --> " << interpolated << " [exact] " << exact << std::endl; EXPECT(is_approximately_equal(interpolated, exact, tolerance)); } } SECTION("official version") { auto matrix_free = Config("matrix_free", true); Interpolation interpolation(option::type("structured-tricubic") | matrix_free, fs, departure_points); Field output = Field("output", make_datatype(), make_shape(departure_points.size())); interpolation.execute(input, output); auto output_view = array::make_view(output); idx_t n{0}; for (auto p : departure_points.iterate().xyz()) { double interpolated = output_view(n++); double exact = fp(p); Log::info() << p << " --> " << interpolated << " [exact] " << exact << std::endl; EXPECT(is_approximately_equal(interpolated, exact, tolerance)); } } SECTION("SL-like") { auto matrix_free = Config("matrix_free", true); auto dp_field = fs.createField(option::variables(3)); { auto iterator = departure_points.iterate().xyz().begin(); auto iterator_end = departure_points.iterate().xyz().end(); auto dp = array::make_view(dp_field); for (idx_t n = 0; n < dp.shape(0); ++n) { for (idx_t k = 0; k < dp.shape(1); ++k) { PointXYZ p{0, 0, 0}; if (iterator != iterator_end) { p = *iterator; ++iterator; } dp(n, k, LON) = p.x(); dp(n, k, LAT) = p.y(); dp(n, k, ZZ) = p.z(); } } } Interpolation interpolation(option::type("structured-tricubic") | matrix_free, fs, dp_field); Field output = fs.createField(); interpolation.execute(input, output); auto output_view = array::make_view(output); auto iterator = departure_points.iterate().xyz().begin(); auto iterator_end = departure_points.iterate().xyz().end(); for (idx_t n = 0; n < output_view.shape(0); ++n) { for (idx_t k = 0; k < output_view.shape(1); ++k) { PointXYZ p{0, 0, 0}; if (iterator != iterator_end) { p = *iterator; ++iterator; double interpolated = output_view(n, k); double exact = fp(p); Log::info() << p << " --> " << interpolated << " [exact] " << exact << std::endl; EXPECT(is_approximately_equal(interpolated, exact, tolerance)); } } } } } } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/interpolation/test_Quad3D.cc0000664000175000017500000001402615160212070022335 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "eckit/types/FloatCompare.h" #include "atlas/interpolation/element/Quad3D.h" #include "atlas/interpolation/method/Ray.h" #include "atlas/util/Point.h" #include "tests/AtlasTestEnvironment.h" using atlas::PointXYZ; using atlas::interpolation::element::Quad3D; using atlas::interpolation::method::Intersect; using atlas::interpolation::method::Ray; namespace atlas { namespace test { //---------------------------------------------------------------------------------------------------------------------- const double relative_error = 0.0001; CASE("test_quad_area") { PointXYZ v0(0., 0., 0.); PointXYZ v1(1., 0., 0.); PointXYZ v2(1., 1., 0.); PointXYZ v3(0., 1., 0.); Quad3D quad1(v0.data(), v1.data(), v2.data(), v3.data()); EXPECT(quad1.validate()); double area = quad1.area(); EXPECT(eckit::types::is_approximately_equal(area, 1.0, relative_error)); PointXYZ c0(-2., -2., 3.); // 4 PointXYZ c1(3., -2., 3.); // 6 PointXYZ c2(3., 0.5, 3.); // 1.5 PointXYZ c3(-2., 0.5, 3.); // 1 Quad3D quad2(c0.data(), c1.data(), c2.data(), c3.data()); EXPECT(quad2.validate()); area = quad2.area(); EXPECT(eckit::types::is_approximately_equal(area, 12.5, relative_error)); } CASE("test_quadrilateral_intersection_refquad") { PointXYZ v0(0., 0., 0.); PointXYZ v1(1., 0., 0.); PointXYZ v2(1., 1., 0.); PointXYZ v3(0., 1., 0.); Quad3D quad(v0.data(), v1.data(), v2.data(), v3.data()); EXPECT(quad.validate()); PointXYZ orig(0.25, 0.25, 1.); PointXYZ dir(0., 0., -1.); Ray ray(orig.data(), dir.data()); Intersect isect = quad.intersects(ray); EXPECT(isect); EXPECT(eckit::types::is_approximately_equal(isect.u, 0.25, relative_error)); EXPECT(eckit::types::is_approximately_equal(isect.v, 0.25, relative_error)); } CASE("test_quadrilateral_intersection_doublequad") { PointXYZ v0(0., 0., 0.); PointXYZ v1(2., 0., 0.); PointXYZ v2(2., 2., 0.); PointXYZ v3(0., 2., 0.); Quad3D quad(v0.data(), v1.data(), v2.data(), v3.data()); EXPECT(quad.validate()); PointXYZ orig(0.5, 0.5, 1.); PointXYZ dir(0., 0., -1.); Ray ray(orig.data(), dir.data()); Intersect isect = quad.intersects(ray); EXPECT(isect); EXPECT(eckit::types::is_approximately_equal(isect.u, 0.25, relative_error)); EXPECT(eckit::types::is_approximately_equal(isect.v, 0.25, relative_error)); } CASE("test_quadrilateral_intersection_rotatedquad") { PointXYZ v0(0., -1., 0.); PointXYZ v1(1., 0., 0.); PointXYZ v2(0., 1., 0.); PointXYZ v3(-1., 0., 0.); Quad3D quad(v0.data(), v1.data(), v2.data(), v3.data()); EXPECT(quad.validate()); PointXYZ orig(0., 0., 1.); PointXYZ dir(0., 0., -1.); Ray ray(orig.data(), dir.data()); Intersect isect = quad.intersects(ray); EXPECT(isect); EXPECT(eckit::types::is_approximately_equal(isect.u, 0.5, relative_error)); EXPECT(eckit::types::is_approximately_equal(isect.v, 0.5, relative_error)); } CASE("test_quadrilateral_intersection_slopequad") { PointXYZ v0(2., 0., 2.); PointXYZ v1(2., 0., 0.); PointXYZ v2(0., 2., 0.); PointXYZ v3(0., 2., 2.); Quad3D quad(v0.data(), v1.data(), v2.data(), v3.data()); EXPECT(quad.validate()); PointXYZ orig(2., 2., 1.); PointXYZ dir(-1., -1., 0.); Ray ray(orig.data(), dir.data()); Intersect isect = quad.intersects(ray); EXPECT(isect); EXPECT(eckit::types::is_approximately_equal(isect.u, 0.5, relative_error)); EXPECT(eckit::types::is_approximately_equal(isect.v, 0.5, relative_error)); } CASE("test_quadrilateral_intersection_nointersect") { PointXYZ v0(0., -1., 0.); PointXYZ v1(1., 0., 0.); PointXYZ v2(0., 1., 0.); PointXYZ v3(-1., 0., 0.); Quad3D quad(v0.data(), v1.data(), v2.data(), v3.data()); EXPECT(quad.validate()); PointXYZ orig(2., 2., 1.); PointXYZ dir(0., 0., -1.); Ray ray(orig.data(), dir.data()); Intersect isect = quad.intersects(ray); EXPECT(!isect); } CASE("test_quadrilateral_intersection_nointersect_aimoff") { PointXYZ v0(0., -1., 0.); PointXYZ v1(1., 0., 0.); PointXYZ v2(0., 1., 0.); PointXYZ v3(-1., 0., 0.); Quad3D quad(v0.data(), v1.data(), v2.data(), v3.data()); EXPECT(quad.validate()); PointXYZ orig(0., 0., 1.); PointXYZ dir(0., 1., 0.); // aim off Ray ray(orig.data(), dir.data()); Intersect isect = quad.intersects(ray); EXPECT(!isect); } CASE("test_quadrilateral_intersection_corners") { PointXYZ v0(0.0, -2.0, 0.); PointXYZ v1(2.5, 0.0, 0.); PointXYZ v2(0.0, 3.5, 0.); PointXYZ v3(-1.5, 0.0, 0.); Quad3D quad(v0.data(), v1.data(), v2.data(), v3.data()); EXPECT(quad.validate()); std::vector corners; 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.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]; PointXYZ dir(0., 0., -1.); Ray ray(orig.data(), dir.data()); Intersect isect = quad.intersects(ray); EXPECT(isect); EXPECT(eckit::types::is_approximately_equal(isect.u, uvs[i].first, relative_error)); EXPECT(eckit::types::is_approximately_equal(isect.v, uvs[i].second, relative_error)); } } //----------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/interpolation/test_interpolation_unstructured_bilinear_lonlat.cc0000664000175000017500000001325615160212070032214 0ustar alastairalastair/* * (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 "eckit/types/FloatCompare.h" #include "atlas/array.h" #include "atlas/functionspace.h" #include "atlas/functionspace/PointCloud.h" #include "atlas/grid.h" #include "atlas/interpolation.h" #include "atlas/mesh.h" #include "atlas/meshgenerator.h" #include "atlas/util/CoordinateEnums.h" #include "tests/AtlasTestEnvironment.h" using namespace eckit; using namespace atlas::functionspace; using namespace atlas::util; namespace atlas { namespace test { //----------------------------------------------------------------------------- // CASE("test_interpolation_O64_to_empty_PointCloud") { Grid grid("O64"); Mesh mesh(grid); NodeColumns fs(mesh); atlas::Field points("lonlat", atlas::array::make_datatype(), atlas::array::make_shape(0, 2)); // Some points at the equator PointCloud pointcloud(points); Interpolation interpolation(option::type("unstructured-bilinear-lonlat"), fs, pointcloud); Field field_source = fs.createField(option::name("source")); Field field_target("target", array::make_datatype(), array::make_shape(pointcloud.size())); auto source = array::make_view(field_source); for (idx_t j = 0; j < fs.nodes().size(); ++j) { source(j) = 0; } interpolation.execute(field_source, field_target); } CASE("test_interpolation_O64_to_points_bilinear_remapping") { Grid grid("O64"); Mesh mesh(grid); NodeColumns fs(mesh); // Some points at the equator PointCloud pointcloud( {{00., 0.}, {10., 0.}, {20., 0.}, {30., 0.}, {40., 0.}, {50., 0.}, {60., 0.}, {70., 0.}, {80., 0.}, {90., 0.}}); auto func = [](double x) -> double { return std::sin(x * M_PI / 180.); }; Interpolation interpolation( option::type("unstructured-bilinear-lonlat"), fs, pointcloud); SECTION("test interpolation outputs") { Field field_source = fs.createField(option::name("source")); Field field_target("target", array::make_datatype(), array::make_shape(pointcloud.size())); auto lonlat = array::make_view(fs.nodes().lonlat()); auto source = array::make_view(field_source); for (idx_t j = 0; j < fs.nodes().size(); ++j) { source(j) = func(lonlat(j, LON)); } interpolation.execute(field_source, field_target); auto target = array::make_view(field_target); auto check = std::vector{func(00.), func(10.), func(20.), func(30.), func(40.), func(50.), func(60.), func(70.), func(80.), func(90.)}; for (idx_t j = 0; j < pointcloud.size(); ++j) { static double interpolation_tolerance = 1.e-4; Log::info() << target(j) << " " << check[j] << std::endl; EXPECT_APPROX_EQ(target(j), check[j], interpolation_tolerance); } } } CASE("test_interpolation_N64_to_O32_bilinear_remapping") { Grid grid_src("N64"); Mesh mesh_src(grid_src); NodeColumns fs_src(mesh_src); Grid grid_tgt("O32"); Mesh mesh_tgt(grid_tgt); NodeColumns fs_tgt(mesh_tgt); Interpolation interpolation( option::type("unstructured-bilinear-lonlat"), fs_src, fs_tgt); Field field_source = fs_src.createField(option::name("source")); Field field_target = fs_tgt.createField(option::name("target")); const double deg2rad = M_PI / 180.; auto func = [](double lon, double lat, double t) { return std::cos(lat) * std::sin(lon); }; array::ArrayView lonlat = array::make_view(fs_src.nodes().lonlat()); array::ArrayView source = array::make_view(field_source); for (idx_t j = 0; j < lonlat.shape(0); ++j) { const double lon = deg2rad * lonlat(j, 0); const double lat = deg2rad * lonlat(j, 1); source(j) = func(lon, lat, 1.); } interpolation.execute(field_source, field_target); array::ArrayView target = array::make_view(field_target); array::ArrayView lonlat_tgt = array::make_view(fs_tgt.nodes().lonlat()); std::vector diffs; ATLAS_ASSERT(target.shape(0) == lonlat_tgt.shape(0)); for (idx_t j = 0; j < lonlat_tgt.shape(0); ++j) { const double lon = deg2rad * lonlat_tgt(j, 0); const double lat = deg2rad * lonlat_tgt(j, 1); const double check = func(lon, lat, 1.); diffs.push_back((target(j) - check) * (target(j) - check)); if (std::abs(target(j) - check) > 0.1) { Log::info() << "lon, lat:" << lon << ", " << lat << " target, check: " << target(j) << ", " << check << std::endl; } } static double interpolation_tolerance = 1.e-3; double std_dev = std::accumulate(diffs.begin(), diffs.end(), decltype(diffs)::value_type(0)) / diffs.size(); double max_dev = std::sqrt(*std::max_element(diffs.begin(), diffs.end())); Log::info() << " standard deviation " << std_dev << " max " << max_dev << std::endl; EXPECT_APPROX_EQ(std_dev, 0.0, 1.e-6); EXPECT_APPROX_EQ(max_dev, 0.0, interpolation_tolerance); } //----------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/interpolation/test_interpolation_non_linear.cc0000664000175000017500000002274415160212070026355 0ustar alastairalastair/* * (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 #include #include "atlas/array.h" #include "atlas/field/MissingValue.h" #include "atlas/functionspace.h" #include "atlas/grid.h" #include "atlas/interpolation.h" #include "atlas/interpolation/NonLinear.h" #include "atlas/mesh.h" #include "atlas/meshgenerator.h" #include "atlas/runtime/Exception.h" #include "tests/AtlasTestEnvironment.h" namespace atlas { namespace test { const double missingValue = 42.; const double missingValueEps = 1e-9; const double nan = std::numeric_limits::quiet_NaN(); using field::MissingValue; using util::Config; CASE("Interpolation with MissingValue") { /* Set input field full of 1's, with 9 nodes 1 ... 1 ... 1 : : : 1-----m ... 1 m: missing value |i i| : i: interpolation on two points, this quadrilateral only 1-----1 ... 1 */ RectangularDomain domain({0, 2}, {0, 2}, "degrees"); Grid gridA("L90", domain); const idx_t nbNodes = 9; ATLAS_ASSERT(gridA.size() == nbNodes); Mesh meshA = MeshGenerator("structured").generate(gridA); functionspace::NodeColumns fsA(meshA); Field fieldA = fsA.createField(option::name("A")); fieldA.metadata().set("missing_value", missingValue); fieldA.metadata().set("missing_value_epsilon", missingValueEps); auto viewA = array::make_view(fieldA); for (idx_t j = 0; j < fsA.nodes().size(); ++j) { viewA(j) = 1; } // Set output field (2 points) functionspace::PointCloud fsB({PointLonLat{0.1, 0.1}, PointLonLat{0.9, 0.9}}); SECTION("missing-if-all-missing") { Interpolation interpolation(Config("type", "finite-element").set("non_linear", "missing-if-all-missing"), fsA, fsB); for (std::string type : {"equals", "approximately-equals", "nan"}) { Field fieldB("B", array::make_datatype(), array::make_shape(fsB.size())); auto viewB = array::make_view(fieldB); fieldA.metadata().set("missing_value_type", type); viewA(4) = type == "nan" ? nan : missingValue; EXPECT(MissingValue(fieldA)); interpolation.execute(fieldA, fieldB); MissingValue mv(fieldB); EXPECT(mv); EXPECT(mv(viewB(0)) == false); EXPECT(mv(viewB(1)) == false); } } SECTION("missing-if-any-missing") { Interpolation interpolation(Config("type", "finite-element").set("non_linear", "missing-if-any-missing"), fsA, fsB); for (std::string type : {"equals", "approximately-equals", "nan"}) { Field fieldB("B", array::make_datatype(), array::make_shape(fsB.size())); auto viewB = array::make_view(fieldB); fieldA.metadata().set("missing_value_type", type); viewA(4) = type == "nan" ? nan : missingValue; EXPECT(MissingValue(fieldA)); interpolation.execute(fieldA, fieldB); MissingValue mv(fieldB); EXPECT(mv); EXPECT(mv(viewB(0))); EXPECT(mv(viewB(1))); } } SECTION("missing-if-heaviest-missing") { Interpolation interpolation(Config("type", "finite-element").set("non_linear", "missing-if-heaviest-missing"), fsA, fsB); for (std::string type : {"equals", "approximately-equals", "nan"}) { Field fieldB("B", array::make_datatype(), array::make_shape(fsB.size())); auto viewB = array::make_view(fieldB); fieldA.metadata().set("missing_value_type", type); viewA(4) = type == "nan" ? nan : missingValue; EXPECT(MissingValue(fieldA)); interpolation.execute(fieldA, fieldB); MissingValue mv(fieldB); EXPECT(mv); EXPECT(mv(viewB(0)) == false); EXPECT(mv(viewB(1))); } } } CASE("Interpolation of rank 2 field with MissingValue") { RectangularDomain domain({0, 2}, {0, 2}, "degrees"); Grid gridA("L90", domain); Mesh meshA = MeshGenerator("structured").generate(gridA); int nlevels = 3; functionspace::NodeColumns fsA(meshA); Field fieldA = fsA.createField(option::name("A") | option::levels(nlevels)); fieldA.metadata().set("missing_value", missingValue); fieldA.metadata().set("missing_value_epsilon", missingValueEps); auto viewA = array::make_view(fieldA); for (idx_t j = 0; j < viewA.shape(0); ++j) { viewA(j, 0) = 10 + j; viewA(j, 1) = missingValue; viewA(j, 2) = 30 + j; } const array::ArraySpec spec(array::ArrayShape{fieldA.shape(0)}, array::ArrayStrides{fieldA.shape(1)}); // Set output field (2 points) functionspace::PointCloud fsB({PointLonLat{0.1, 0.1}, PointLonLat{0.9, 0.9}}); SECTION("check wrapped data can be indexed with []") { for (int lev = 0; lev < nlevels; ++lev) { double* data = const_cast(fieldA.array().data()) + lev; atlas::Field fieldA_slice = atlas::Field("s", data, spec); fieldA_slice.metadata().set("missing_value", fieldA.metadata().get("missing_value")); auto FlatViewA1 = array::make_view(fieldA_slice); auto viewA_slice = viewA.slice(array::Range::all(), lev); EXPECT(FlatViewA1.shape(0) == viewA_slice.shape(0)); for (idx_t j = 0; j < FlatViewA1.shape(0); ++j) { EXPECT(FlatViewA1[j] == viewA_slice(j)); } } } SECTION("missing-if-all-missing") { Interpolation interpolation(Config("type", "finite-element").set("non_linear", "missing-if-all-missing"), fsA, fsB); for (std::string type : {"equals", "approximately-equals", "nan"}) { fieldA.metadata().set("missing_value_type", type); for (idx_t j = 0; j < viewA.shape(0); ++j) { viewA(j, 1) = type == "nan" ? nan : missingValue; } EXPECT(MissingValue(fieldA)); Field fieldB = fsB.createField(option::name("B") | option::levels(nlevels)); auto viewB = array::make_view(fieldB); interpolation.execute(fieldA, fieldB); MissingValue mv(fieldB); EXPECT(mv(viewB(0, 0)) == false); EXPECT(mv(viewB(1, 0)) == false); EXPECT(mv(viewB(0, 1))); EXPECT(mv(viewB(1, 1))); EXPECT(mv(viewB(0, 2)) == false); EXPECT(mv(viewB(1, 2)) == false); } } } CASE("Interpolation with MissingValue on fieldset with heterogeneous type") { auto init_field = [](Field& field){ field.metadata().set("missing_value", missingValue); field.metadata().set("missing_value_epsilon", missingValueEps); field.metadata().set("missing_value_type", "equals"); auto init_view = [](auto&& view) { for (idx_t j = 0; j < view.shape(0); ++j) { view(j) = 1; } view(4) = missingValue; }; if (field.datatype().kind() == DataType::KIND_REAL32) { init_view(array::make_view(field)); } if (field.datatype().kind() == DataType::KIND_REAL64) { init_view(array::make_view(field)); } return false; }; /* Set input field full of 1's, with 9 nodes 1 ... 1 ... 1 : : : 1-----m ... 1 m: missing value |i i| : i: interpolation on two points, this quadrilateral only 1-----1 ... 1 */ RectangularDomain domain({0, 2}, {0, 2}, "degrees"); Grid gridA("L90", domain); const idx_t nbNodes = 9; ATLAS_ASSERT(gridA.size() == nbNodes); Mesh meshA = MeshGenerator("structured").generate(gridA); functionspace::NodeColumns fsA(meshA); FieldSet fieldsA; fieldsA.add(fsA.createField(option::name("A_r64"))); fieldsA.add(fsA.createField(option::name("A_r32"))); init_field(fieldsA["A_r64"]); init_field(fieldsA["A_r32"]); // Set output field (2 points) functionspace::PointCloud fsB({PointLonLat{0.1, 0.1}, PointLonLat{0.9, 0.9}}); FieldSet fieldsB; fieldsB.add(fsB.createField(option::name("B_r64"))); fieldsB.add(fsB.createField(option::name("B_r32"))); auto interpolate = [&](const std::string& missing_type) { Config config; config.set("type", "finite-element"); config.set("non_linear", missing_type); Interpolation interpolation(config, fsA, fsB); interpolation.execute(fieldsA, fieldsB); }; SECTION( "missing-if-any-missing" ) { interpolate("missing-if-any-missing"); } SECTION( "missing-if-all-missing" ) { interpolate("missing-if-all-missing"); } SECTION( "missing-if-heaviest-missing" ) { interpolate("missing-if-heaviest-missing"); } } } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/interpolation/test_interpolation_k_nearest_neighbours.cc0000664000175000017500000000675715160212070030437 0ustar alastairalastair/* * (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 "eckit/types/FloatCompare.h" #include "eckit/utils/MD5.h" #include "atlas/array.h" #include "atlas/field.h" #include "atlas/functionspace/NodeColumns.h" #include "atlas/grid.h" #include "atlas/interpolation.h" #include "atlas/mesh/Mesh.h" #include "atlas/meshgenerator.h" #include "atlas/option.h" #include "atlas/util/Config.h" #include "tests/AtlasTestEnvironment.h" using atlas::functionspace::NodeColumns; using atlas::util::Config; namespace atlas { namespace test { class Access { public: Access(const Interpolation& interpolation): interpolation_{interpolation} {} const Interpolation::Implementation::Matrix& matrix() const { return *interpolation_.get()->matrix_; } Interpolation interpolation_; std::string hash() { eckit::MD5 hash; const auto m = atlas::linalg::make_host_view(matrix()); const auto outer = m.outer(); const auto index = m.inner(); //const auto weight = m.value(); idx_t rows = static_cast(m.rows()); hash.add(rows); for (idx_t r = 0; r < rows; ++r) { //v_tgt( r ) = 0.; for (idx_t c = outer[r]; c < outer[r + 1]; ++c) { hash.add(c); idx_t n = index[c]; hash.add(n); //Value w = static_cast( weight[c] ); //v_tgt( r ) += w * v_src( n ); } } return hash.digest(); } }; CASE("test_interpolation_k_nearest_neighbours") { Log::info().precision(16); Grid gridA("O32"); Grid gridB("O64"); Grid gridC("O32"); Interpolation a(option::type("k-nearest-neighbours"), gridA, gridB); Interpolation b(option::type("k-nearest-neighbours"), gridB, gridC); ATLAS_DEBUG_VAR(Access{a}.hash()); ATLAS_DEBUG_VAR(Access{b}.hash()); // Following EXPECT are commented because they are not bit-identical across platforms. // This indicates that not every platform finds the same nearest neighbours!!! //EXPECT( Access{a}.hash() == "5ecc9d615dcf7112b5a97b38341099a5" ); //EXPECT( Access{b}.hash() == "bf4b4214ef50387ba00a1950b95e0d93" ); } //----------------------------------------------------------------------------- CASE("test_multiple_fs") { Grid grid1("L90x45"); Grid grid2("O8"); Mesh mesh1 = StructuredMeshGenerator().generate(grid1); Mesh mesh2 = StructuredMeshGenerator().generate(grid2); functionspace::NodeColumns fs11(mesh1, option::halo(1)); functionspace::NodeColumns fs12(mesh1, option::halo(2)); auto fs1 = fs11; functionspace::NodeColumns fs2(mesh2, option::halo(1)); Interpolation interpolation12(Config("type", "k-nearest-neighbours") | Config("k-nearest-neighbours", 5), fs1, fs2); auto f1 = fs1.createField(Config("name", "source")); auto f2 = fs2.createField(Config("name", "target")); auto v1 = array::make_view(f1); v1.assign(1.); interpolation12.execute(f1, f2); } } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/interpolation/test_Triag2D.cc0000664000175000017500000001165115160212070022511 0ustar alastairalastair/* * (C) Crown Copyright 2021 Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ #include "eckit/types/FloatCompare.h" #include "atlas/interpolation/element/Triag2D.h" #include "atlas/util/Point.h" #include "tests/AtlasTestEnvironment.h" using atlas::PointXY; using atlas::interpolation::element::Triag2D; using atlas::interpolation::method::Intersect; namespace atlas { namespace test { //---------------------------------------------------------------------------------------------------------------------- const double relative_error = 0.0001; CASE("test_triag_area") { PointXY v0(0., 0.); PointXY v1(1., 0.); PointXY v2(1., 1.); Triag2D triangle1(v0.data(), v1.data(), v2.data()); EXPECT(triangle1.validate()); double area = triangle1.area(); std::cout << "area " << area << std::endl; EXPECT_APPROX_EQ(area, 0.5, relative_error); PointXY c0(-2., -2.); // 4 PointXY c1(3., -2.); // 6 PointXY c2(3., 0.5); // 1.5 Triag2D triangle2(c0.data(), c1.data(), c2.data()); EXPECT(triangle2.validate()); area = triangle2.area(); std::cout << "area " << area << std::endl; EXPECT_APPROX_EQ(area, 6.25, relative_error); } CASE("test_intersection_equilateral_triangle") { PointXY v0(0., 0.); PointXY v1(1., 0.); PointXY v2(0.5, std::sqrt(3.0) / 2.0); Triag2D triangle(v0.data(), v1.data(), v2.data()); EXPECT(triangle.validate()); PointXY orig(0.5, std::sqrt(3.0) / 6.0); Intersect isect = triangle.intersects(orig); EXPECT(isect); std::cout << "isect.u " << isect.u << std::endl; std::cout << "isect.v " << isect.v << std::endl; EXPECT_APPROX_EQ(isect.u, 1. / 3., relative_error); EXPECT_APPROX_EQ(isect.v, 1. / 3., relative_error); } CASE("test_intersection_right_angled_triangle") { PointXY v0(0., 0.); PointXY v1(3., 0.); PointXY v2(3., 4.); Triag2D triangle(v0.data(), v1.data(), v2.data()); EXPECT(triangle.validate()); // average of the coordinates is the centre PointXY orig(2.0, (4.0 / 3.0)); Intersect isect = triangle.intersects(orig); EXPECT(isect); std::cout << "isect.u " << isect.u << std::endl; std::cout << "isect.v " << isect.v << std::endl; EXPECT_APPROX_EQ(isect.u, 1. / 3., relative_error); EXPECT_APPROX_EQ(isect.v, 1. / 3., relative_error); } CASE("test_intersection_offset_right_angled_triangle") { PointXY v0(0., 0.); PointXY v1(3., 0.); PointXY v2(3., 4.); Triag2D triangle(v0.data(), v1.data(), v2.data()); EXPECT(triangle.validate()); PointXY orig(1.5, 1); Intersect isect = triangle.intersects(orig); EXPECT(isect); std::cout << "isect.u " << isect.u << std::endl; std::cout << "isect.v " << isect.v << std::endl; EXPECT_APPROX_EQ(isect.u, 0.25, relative_error); EXPECT_APPROX_EQ(isect.v, 0.25, relative_error); } CASE("test_intersection_rotatedtriangle") { const double root2 = std::sqrt(2.); PointXY v0(0., 0.); PointXY v1(3. / root2, -3. / root2); PointXY v2(7. / root2, 1. / root2); Triag2D triangle(v0.data(), v1.data(), v2.data()); EXPECT(triangle.validate()); PointXY orig(10. / (3. * root2), -2. / (3. * root2)); Intersect isect = triangle.intersects(orig); EXPECT(isect); std::cout << "isect.u " << isect.u << std::endl; std::cout << "isect.v " << isect.v << std::endl; EXPECT_APPROX_EQ(isect.u, 1. / 3., relative_error); EXPECT_APPROX_EQ(isect.v, 1. / 3., relative_error); } CASE("test_intersection_nointersect") { PointXY v0(0., -1.); PointXY v1(1., 0.); PointXY v2(0., 1.); Triag2D triangle(v0.data(), v1.data(), v2.data()); EXPECT(triangle.validate()); PointXY orig(2., 2.); Intersect isect = triangle.intersects(orig); EXPECT(!isect); } CASE("test_intersection_corners") { PointXY v0(0.0, -2.0); PointXY v1(2.5, 0.0); PointXY v2(0.0, 3.5); Triag2D triangle(v0.data(), v1.data(), v2.data()); EXPECT(triangle.validate()); std::vector corners; corners.emplace_back(0.0, -2.0); corners.emplace_back(2.5, 0.0); corners.emplace_back(0.0, 3.5); std::vector> uvs; uvs.emplace_back(0., 0.); uvs.emplace_back(1., 0.); uvs.emplace_back(0., 1.); for (size_t i = 0; i < 3; ++i) { PointXY orig = corners[i]; Intersect isect = triangle.intersects(orig); EXPECT(isect); std::cout << "isect.u " << isect.u << std::endl; std::cout << "isect.v " << isect.v << std::endl; EXPECT_APPROX_EQ(isect.u, uvs[i].first, relative_error); EXPECT_APPROX_EQ(isect.v, uvs[i].second, relative_error); } } //----------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/interpolation/test_interpolation_structured2D_to_points_gpu.cc0000664000175000017500000001166015160212070031567 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "atlas/array.h" #include "atlas/field/Field.h" #include "atlas/field/FieldSet.h" #include "atlas/functionspace/PointCloud.h" #include "atlas/functionspace/StructuredColumns.h" #include "atlas/grid/Grid.h" #include "atlas/grid/Iterator.h" #include "atlas/interpolation.h" #include "atlas/mesh/Mesh.h" #include "atlas/meshgenerator.h" #include "atlas/output/Gmsh.h" #include "atlas/util/CoordinateEnums.h" #include "atlas/util/function/VortexRollup.h" #include "atlas/util/PolygonXY.h" #include "tests/AtlasTestEnvironment.h" using atlas::functionspace::PointCloud; using atlas::functionspace::StructuredColumns; using atlas::util::Config; namespace atlas { namespace test { //----------------------------------------------------------------------------- std::string input_gridname(const std::string& default_grid) { return eckit::Resource("--input-grid", default_grid); } FunctionSpace output_functionspace( const FunctionSpace& input_functionspace, bool expect_fail ) { std::vector all_points { {360., 90.}, {360., 0.}, {360., -90.}, {0.,0.}, }; // Only keep points that match the input partitioning. // Note that with following implementation it could be that some points // are present in two partitions, but it is not a problem for this test purpose. std::vector points; auto polygon = util::PolygonXY{input_functionspace.polygon()}; for (const auto& p : all_points ) { if (polygon.contains(p)) { points.emplace_back(p); } } if( expect_fail && mpi::rank() == mpi::size() - 1 ) { points.emplace_back(720,0.); } return PointCloud(points); } FieldSet create_source_fields(StructuredColumns& fs, idx_t nb_fields, idx_t nb_levels) { using Value = double; FieldSet fields_source; auto lonlat = array::make_view(fs.xy()); for (idx_t f = 0; f < nb_fields; ++f) { auto field_source = fields_source.add(fs.createField()); auto source = array::make_view(field_source); for (idx_t n = 0; n < fs.size(); ++n) { for (idx_t k = 0; k < nb_levels; ++k) { source(n, k) = util::function::vortex_rollup(lonlat(n, LON), lonlat(n, LAT), 0.5 + double(k) / 2); } }; field_source.updateDevice(); } return fields_source; } FieldSet create_target_fields(FunctionSpace& fs, idx_t nb_fields, idx_t nb_levels) { using Value = double; FieldSet fields_target; for (idx_t f = 0; f < nb_fields; ++f) { auto field_target = fields_target.add(fs.createField(option::levels(nb_levels))); field_target.allocateDevice(); } return fields_target; } void do_test( std::string type, int input_halo, bool matrix_free, bool expect_failure ) { idx_t nb_fields = 2; idx_t nb_levels = 3; Grid input_grid(input_gridname("O32")); StructuredColumns input_fs(input_grid, option::levels(nb_levels) | option::halo(input_halo)); FunctionSpace output_fs = output_functionspace(input_fs, expect_failure); Interpolation interpolation(option::type(type) | util::Config("matrix_free",matrix_free) | util::Config("sparse_matrix_multiply", "hicsparse") | util::Config("verbose",eckit::Resource("--verbose",false)), input_fs, output_fs); FieldSet fields_source = create_source_fields(input_fs, nb_fields, nb_levels); FieldSet fields_target = create_target_fields(output_fs, nb_fields, nb_levels); interpolation.execute(fields_source, fields_target); } CASE("test structured-bilinear, halo 2, with matrix") { EXPECT_NO_THROW( do_test("structured-bilinear",2,false,false) ); } CASE("test structured-bilinear, halo 2, with matrix, expected failure") { EXPECT_THROWS_AS( do_test("structured-bilinear",2,false,true), eckit::Exception ); } CASE("test structured-bilinear, halo 2, without matrix, expected failure") { EXPECT_THROWS_AS( do_test("structured-bilinear",2,false,true), eckit::Exception ); } CASE("test structured-bilinear, halo 1, with matrix, expected failure") { EXPECT_THROWS_AS( do_test("structured-bilinear",1,false,false), eckit::Exception ); } CASE("test structured-bicubic, halo 3, with matrix") { EXPECT_NO_THROW( do_test("structured-bicubic",3,false,false) ); } CASE("test structured-bicubic, halo 2, with matrix") { EXPECT_THROWS_AS( do_test("structured-bicubic",2,false,false), eckit::Exception ); } } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/interpolation/test_interpolation_grid_box_average.cc0000664000175000017500000002221715160212070027513 0ustar alastairalastair/* * (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 #include "eckit/log/Bytes.h" #include "atlas/array.h" #include "atlas/field.h" #include "atlas/grid.h" #include "atlas/interpolation.h" #include "atlas/interpolation/method/knn/GridBox.h" #include "atlas/option.h" #include "atlas/util/Config.h" #include "tests/AtlasTestEnvironment.h" namespace atlas { namespace test { using interpolation::method::GridBoxes; double integral(const Grid& grid, const Field& field) { auto values = array::make_view(field); auto boxes = GridBoxes(grid); ATLAS_ASSERT(boxes.size() == size_t(field.shape(0))); double i = 0.; for (size_t c = 0; c < boxes.size(); c++) { i += boxes[c].area() * values(c); } return i; } Field create_field(std::string name, idx_t size, double init = double()) { auto f = Field(name, array::make_datatype(), array::make_shape(size)); array::make_view(f).assign(init); return f; } FieldSet create_fieldset(std::string name, idx_t size, size_t number) { ATLAS_ASSERT(1 <= number); FieldSet set; for (size_t i = 1; i <= number; ++i) { auto f = set.add(Field(name + std::to_string(i), array::make_datatype(), array::make_shape(size))); array::make_view(f).assign(0.); } return set; } CASE("test_interpolation_grid_box_average") { Log::info().precision(16); Grid gridA("O32"); Grid gridB("O64"); Grid gridC("O32"); struct reference_t { reference_t(double value, double tolerance): value_(value), tolerance_(tolerance) {} void check(std::string label, double value) { double relerr = eckit::types::is_approximately_equal(value_, 0.) ? 0. : std::abs((value - value_) / value_); Log::info() << label << value << ", compared to " << value_ << " +- " << tolerance_ << ", with relative error = " << relerr << std::endl; EXPECT(eckit::types::is_approximately_equal(value, value_, tolerance_)); } const double value_; const double tolerance_; }; // the integral of a constant field = 1 is Earth's surface area (and tolerance has to be large) // (also field = 2 and 3 just to test a bit further, too) reference_t surface1(util::Earth::area(), 1.e3); reference_t surface2(util::Earth::area() * 2., 1.e3); reference_t surface3(util::Earth::area() * 3., 1.e3); reference_t countA(double(gridA.size()), 1.e-8); reference_t countB(double(gridB.size()), 1.e-8); auto configMB = option::type("grid-box-average").set("matrix_free", false); auto configMF = option::type("grid-box-average").set("matrix_free", true); SECTION("Earth's surface area using Field interpolation") { Field fieldA(create_field("A", gridA.size(), 1.)); Field fieldB(create_field("B", gridB.size())); Field fieldC(create_field("C", gridC.size())); surface1.check("Integral A: ", integral(gridA, fieldA)); Interpolation(configMB, gridA, gridB).execute(fieldA, fieldB); Interpolation(configMB, gridB, gridC).execute(fieldB, fieldC); surface1.check("Integral B (MB): ", integral(gridB, fieldB)); surface1.check("Integral C (MB): ", integral(gridC, fieldC)); fieldB = create_field("B", gridB.size()); fieldC = create_field("C", gridC.size()); Interpolation(configMF, gridA, gridB).execute(fieldA, fieldB); Interpolation(configMF, gridB, gridC).execute(fieldB, fieldC); surface1.check("Integral B (MF): ", integral(gridB, fieldB)); surface1.check("Integral C (MF): ", integral(gridC, fieldC)); } SECTION("Earth's surface area using FieldSet interpolation") { FieldSet fieldsA(create_fieldset("A", gridA.size(), 3)); array::make_view(fieldsA[0]).assign(1.); array::make_view(fieldsA[1]).assign(2.); array::make_view(fieldsA[2]).assign(3.); FieldSet fieldsB(create_fieldset("B", gridB.size(), 3)); FieldSet fieldsC(create_fieldset("C", gridC.size(), 3)); surface1.check("Integral A1: ", integral(gridA, fieldsA[0])); surface2.check("Integral A2: ", integral(gridA, fieldsA[1])); surface3.check("Integral A3: ", integral(gridA, fieldsA[2])); Interpolation(configMB, gridA, gridB).execute(fieldsA, fieldsB); Interpolation(configMB, gridB, gridC).execute(fieldsB, fieldsC); surface1.check("Integral B1 (MB): ", integral(gridB, fieldsB[0])); surface2.check("Integral B2 (MB): ", integral(gridB, fieldsB[1])); surface3.check("Integral B3 (MB): ", integral(gridB, fieldsB[2])); surface1.check("Integral C1 (MB): ", integral(gridC, fieldsC[0])); surface2.check("Integral C2 (MB): ", integral(gridC, fieldsC[1])); surface3.check("Integral C3 (MB): ", integral(gridC, fieldsC[2])); fieldsB = create_fieldset("B", gridB.size(), 3); fieldsC = create_fieldset("C", gridC.size(), 3); Interpolation(configMF, gridA, gridB).execute(fieldsA, fieldsB); Interpolation(configMF, gridB, gridC).execute(fieldsB, fieldsC); surface1.check("Integral B1 (MF): ", integral(gridB, fieldsB[0])); surface2.check("Integral B2 (MF): ", integral(gridB, fieldsB[1])); surface3.check("Integral B3 (MF): ", integral(gridB, fieldsB[2])); surface1.check("Integral C1 (MF): ", integral(gridC, fieldsC[0])); surface2.check("Integral C2 (MF): ", integral(gridC, fieldsC[1])); surface3.check("Integral C3 (MF): ", integral(gridC, fieldsC[2])); } SECTION("Count as integral value, low >> high >> low resolution") { Field fieldA(create_field("A", gridA.size(), 1.)); Field fieldB(create_field("B", gridB.size())); Field fieldC(create_field("C", gridA.size())); size_t i = 0; auto values = array::make_view(fieldA); for (auto& box : GridBoxes(gridA)) { ATLAS_ASSERT(box.area() > 0.); values(i++) = 1. / box.area(); } countA.check("Count A: ", integral(gridA, fieldA)); Interpolation(configMB, gridA, gridB).execute(fieldA, fieldB); Interpolation(configMB, gridB, gridA).execute(fieldB, fieldC); countA.check("Count B (MB): ", integral(gridB, fieldB)); countA.check("Count A (MB): ", integral(gridA, fieldC)); } SECTION("Count as integral value, high >> low >> high resolution") { Field fieldA(create_field("A", gridB.size(), 1.)); Field fieldB(create_field("B", gridA.size())); Field fieldC(create_field("C", gridB.size())); size_t i = 0; auto values = array::make_view(fieldA); for (auto& box : GridBoxes(gridB)) { ATLAS_ASSERT(box.area() > 0.); values(i++) = 1. / box.area(); } countB.check("Count B: ", integral(gridB, fieldA)); Interpolation(configMB, gridB, gridA).execute(fieldA, fieldB); Interpolation(configMB, gridA, gridB).execute(fieldB, fieldC); countB.check("Count A (MB): ", integral(gridA, fieldB)); countB.check("Count B (MB): ", integral(gridB, fieldC)); fieldB = create_field("B", gridA.size()); fieldC = create_field("C", gridB.size()); Interpolation(configMF, gridB, gridA).execute(fieldA, fieldB); Interpolation(configMF, gridA, gridB).execute(fieldB, fieldC); countB.check("Count A (MF): ", integral(gridA, fieldB)); countB.check("Count B (MF): ", integral(gridB, fieldC)); } } CASE("test_interpolation_grid_box_average matrix with cache") { Grid gridA("O32"); Grid gridB("O64"); auto config = option::type("grid-box-average").set("matrix_free", false); Field fieldA(create_field("A", gridA.size(), 1.)); Field fieldB(create_field("B", gridB.size())); size_t i = 0; auto values = array::make_view(fieldA); for (auto& box : GridBoxes(gridA)) { ATLAS_ASSERT(box.area() > 0.); values(i++) = 1. / box.area(); } interpolation::Cache cache; ATLAS_TRACE_SCOPE("Create cache") { cache = interpolation::Cache(Interpolation(config, gridA, gridB)); } size_t size; size_t footprint; ATLAS_TRACE_SCOPE("iterate tree") { size = interpolation::IndexKDTreeCache(cache).tree().size(); footprint = interpolation::IndexKDTreeCache(cache).tree().footprint(); } ATLAS_DEBUG_VAR(size); Log::info() << "kdtree footprint = " << eckit::Bytes(footprint) << std::endl; Log::info() << "all cache footprint = " << eckit::Bytes(cache.footprint()) << std::endl; ATLAS_TRACE_SCOPE("Interpolate with cache") { Interpolation(config, gridA, gridB, cache).execute(fieldA, fieldB); } } } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/interpolation/test_interpolation_binning.cc0000664000175000017500000003105415160212070025647 0ustar alastairalastair/* * (C) Crown Copyright 2024 Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ #include #include #include "atlas/field/FieldSet.h" #include "atlas/functionspace/CubedSphereColumns.h" #include "atlas/functionspace/FunctionSpace.h" #include "atlas/functionspace/NodeColumns.h" #include "atlas/functionspace/StructuredColumns.h" #include "atlas/interpolation.h" #include "atlas/meshgenerator/MeshGenerator.h" #include "atlas/option/Options.h" #include "atlas/output/Gmsh.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Log.h" #include "atlas/util/Config.h" #include "atlas/util/CoordinateEnums.h" #include "atlas/util/function/VortexRollup.h" #include "tests/AtlasTestEnvironment.h" namespace atlas { namespace test { /// function to generate a synthetic field (vortex field) Field getVortexField(const FunctionSpace& functionSpace) { const auto lonlat = array::make_view(functionSpace.lonlat()); auto vField = functionSpace.createField(option::name{"vortex field"}); auto vFieldView = array::make_view(vField); for (idx_t idx = 0; idx < vField.shape(0); ++idx) { vFieldView(idx) = util::function::vortex_rollup(lonlat(idx, atlas::LON), lonlat(idx, atlas::LAT), 1.); } return vField; } /// Generate and the difference between a target field and the analytic solution Field getErrorField(const Field& targetField) { auto errorField = targetField.functionspace().createField(option::name("error field")); const auto referenceField = getVortexField(targetField.functionspace()); auto errorFieldView = array::make_view(errorField); const auto referenceFieldView = array::make_view(referenceField); const auto targetFieldView = array::make_view(targetField); for (idx_t idx = 0; idx < errorField.shape(0); ++idx) { errorFieldView(idx) = targetFieldView(idx) - referenceFieldView(idx); } return errorField; } /// function to write a field set in a Gmsh file void makeGmshOutput(const std::string& fileNamePrefix, const FieldSet& fields) { const auto& fs = fields[0].functionspace(); const auto mesh = [&]() { if (const auto nc = functionspace::NodeColumns(fs)) { return nc.mesh(); } if (const auto sc = functionspace::StructuredColumns(fs)) { return MeshGenerator{"structured", option::halo(sc.halo())}.generate(sc.grid()); } throw_Exception("Unsupported function space type for Gmsh output: " + fs.type()); }(); const auto gmshConfig = util::Config("coordinates", "xyz") | util::Config("ghost", true) | util::Config("info", true); const auto gmsh = output::Gmsh(fileNamePrefix, gmshConfig); gmsh.write(mesh); gmsh.write(fields, fs); } /// function to carry out a dot product double dotProd(const Field& fieldA, const Field& fieldB) { double dprod{}; const auto field01_view = array::make_view(fieldA); const auto field02_view = array::make_view(fieldB); for (idx_t i = 0; i < field01_view.shape(0); ++i) { dprod += field01_view(i) * field02_view(i); } eckit::mpi::comm().allReduceInPlace(dprod, eckit::mpi::Operation::SUM); return dprod; } void regriddingTest(const util::Config& config) { const auto sourceGridName = config.getString("source_grid"); const auto targetGridName = config.getString("target_grid"); const auto sourceGrid = Grid{sourceGridName}; const auto targetGrid = Grid{targetGridName}; const auto [sourceFunctionSpace, targetFunctionSpace] = [&]() -> std::pair { const auto fSpaceName = config.getString("functionspace"); const size_t haloSize = config.getInt("halo"); if (fSpaceName == "NodeColumns") { const auto meshGenerator = MeshGenerator{config.getString("mesh_generator"), option::halo{haloSize}}; const auto sourceMesh = meshGenerator.generate(sourceGrid); const auto targetMesh = meshGenerator.generate(targetGrid); return {functionspace::NodeColumns{sourceMesh, option::halo{haloSize}}, functionspace::NodeColumns{targetMesh}}; } if (fSpaceName == "StructuredColumns") { return {functionspace::StructuredColumns{sourceGrid, option::halo{haloSize}}, functionspace::StructuredColumns{targetGrid, option::halo{3}}}; } throw_Exception("Unsupported functionspace type: " + fSpaceName); }(); const auto sourceField = getVortexField(sourceFunctionSpace); auto targetField = targetFunctionSpace.createField(option::name("field_target")); const auto binningScheme = option::type{"binning"} | util::Config{"scheme", config.getSubConfiguration("scheme")}; auto binning = Interpolation{binningScheme, sourceFunctionSpace, targetFunctionSpace}; sourceField.haloExchange(); binning.execute(sourceField, targetField); targetField.haloExchange(); auto targetFieldSet = FieldSet{}; targetFieldSet.add(targetField); targetFieldSet.add(getErrorField(targetField)); auto sourceFieldSet = FieldSet{}; sourceFieldSet.add(sourceField); const auto binningType = binningScheme.getString("scheme.type"); makeGmshOutput(sourceGridName + "_to_" + targetGridName + "_" + binningType + "_source.msh", sourceFieldSet); makeGmshOutput(sourceGridName + "_to_" + targetGridName + "_" + binningType + "_target.msh", targetFieldSet); } CASE("Regridding from high to low resolution: cubed sphere, bilinear") { const auto config = util::Config{"source_grid", "CS-LFR-112"} | util::Config{"target_grid", "CS-LFR-28"} | util::Config{"functionspace", "NodeColumns"} | util::Config{"halo", 1} | util::Config{"mesh_generator", "cubedsphere_dual"} | util::Config{"scheme", option::type{"cubedsphere-bilinear"}}; regriddingTest(config); } CASE("Regridding from high to low resolution: cubed sphere, nearest neighbour") { const auto config = util::Config{"source_grid", "CS-LFR-112"} | util::Config{"target_grid", "CS-LFR-28"} | util::Config{"functionspace", "NodeColumns"} | util::Config{"halo", 0} | util::Config{"mesh_generator", "cubedsphere_dual"} | util::Config{"scheme", option::type{"nearest-neighbour"}}; regriddingTest(config); } CASE("Regridding from high to low resolution: cubed sphere, finite element") { const auto config = util::Config{"source_grid", "CS-LFR-112"} | util::Config{"target_grid", "CS-LFR-28"} | util::Config{"functionspace", "NodeColumns"} | util::Config{"halo", 1} | util::Config{"mesh_generator", "cubedsphere_dual"} | util::Config{"scheme", option::type{"finite-element"}}; regriddingTest(config); } CASE("Regridding from high to low resolution: gaussian, structured bilinear") { const auto config = util::Config{"source_grid", "O48"} | util::Config{"target_grid", "O24"} | util::Config{"functionspace", "StructuredColumns"} | util::Config{"halo", 3} | util::Config{"scheme", option::type{"structured-bilinear"}}; regriddingTest(config); } CASE("plot binning kernel") { const auto sourceGrid = Grid{"CS-LFR-20"}; const auto targetGrid = Grid{"CS-LFR-5"}; const auto haloOption = option::halo(1); const auto sourceMesh = MeshGenerator("cubedsphere_dual", haloOption).generate(sourceGrid); const auto targetMesh = MeshGenerator("cubedsphere_dual").generate(targetGrid); const auto sourceFunctionSpace = functionspace::NodeColumns{sourceMesh, haloOption}; const auto targetFunctionSpace = functionspace::NodeColumns{targetMesh}; const auto binningScheme = option::type{"binning"} | util::Config{"scheme", option::type{"cubedsphere-bilinear"}}; auto binning = Interpolation{binningScheme, sourceFunctionSpace, targetFunctionSpace}; const auto binningMatrixStorage = interpolation::MatrixCache(binning).matrix(); const auto binningMatrix = linalg::make_non_owning_eckit_sparse_matrix(binningMatrixStorage); const auto targetGhostView = array::make_view(targetFunctionSpace.ghost()); const auto targetRidxView = array::make_indexview(targetFunctionSpace.remote_index()); const auto targetPartView = array::make_view(targetFunctionSpace.partition()); const auto targetRemoteIndices = std::vector{0, 2, 12}; const auto targetPartition = 0; auto kernelField = sourceFunctionSpace.createField(option::name("kernel_field")); auto kernelFieldView = array::make_view(kernelField); kernelFieldView.assign(0.); for (auto matrixItr = binningMatrix.begin(); matrixItr != binningMatrix.end(); ++matrixItr) { const auto rowIdx = matrixItr.row(); const auto colIdx = matrixItr.col(); if (targetGhostView(rowIdx)) { continue; } for (const auto targetRemoteIndex : targetRemoteIndices) { if (targetRidxView(rowIdx) == targetRemoteIndex && targetPartView(rowIdx) == targetPartition) { kernelFieldView(colIdx) += *matrixItr; } } } // Force halo weights into owned region of field. sourceFunctionSpace.adjointHaloExchange(kernelField); sourceFunctionSpace.haloExchange(kernelField); makeGmshOutput("binning_kernel.msh", kernelField); } /// test to carry out the 'dot-product' test for the rigridding from /// 'high' to 'low' resolution, for a given type of grid (CS-LFR) /// CASE("dot-product test for the rigridding from high to low resolution; grid type: Cubed Sphere") { // source grid (high res.) const auto sourceGrid = Grid("CS-LFR-100"); const auto sourceMesh = MeshGenerator("cubedsphere_dual").generate(sourceGrid); const auto sourceFunctionSpace = functionspace::NodeColumns(sourceMesh); // target grid (low res.) const auto targetGrid = Grid("CS-LFR-50"); const auto targetMesh = MeshGenerator("cubedsphere_dual").generate(targetGrid); const auto targetFunctionSpace = functionspace::NodeColumns(targetMesh); // source field const auto sourceField = getVortexField(sourceFunctionSpace); auto sourceFieldSet = FieldSet{}; sourceFieldSet.add(sourceField); // target field auto targetField = targetFunctionSpace.createField(option::name("field_01_t")); auto targetFieldSet = FieldSet{}; targetFieldSet.add(targetField); const auto scheme = util::Config("type", "binning") | util::Config("scheme", option::type("cubedsphere-bilinear")) | util::Config("adjoint", true); Interpolation binning(scheme, sourceFunctionSpace, targetFunctionSpace); // performing the regridding from high to low resolution binning.execute(sourceFieldSet, targetFieldSet); targetFieldSet["field_01_t"].haloExchange(); // target field (adjoint) auto targetFieldStar = targetFunctionSpace.createField(option::name("field_01_ad_t")); array::make_view(targetFieldStar).assign(array::make_view(targetField)); targetFieldStar.adjointHaloExchange(); auto targetFieldSetStar = FieldSet{}; targetFieldSetStar.add(targetFieldStar); // source field (adjoint) auto sourceFieldStar = sourceFunctionSpace.createField(option::name("field_01_ad_s")); array::make_view(sourceFieldStar).assign(0.); auto sourceFieldSetStar = FieldSet{}; sourceFieldSetStar.add(sourceFieldStar); // performing adjoint operation binning.execute_adjoint(sourceFieldSetStar, targetFieldSetStar); const auto targetDotTarget = dotProd(targetField, targetField); const auto sourceDotSourceStar = dotProd(sourceField, sourceFieldStar); double scaled_diff = std::abs(targetDotTarget - sourceDotSourceStar) / std::abs(targetDotTarget); // carrrying out a dot-product test ... Log::info() << "\n- dot-product test:\n" << "(Ax) . (Ax) = " << targetDotTarget << "; " << "x . (A^t A x) = " << sourceDotSourceStar << "; " << "scaled difference = " << scaled_diff << "\n" << std::endl; EXPECT(scaled_diff < 1e-12); } } // namespace test } // namespace atlas //-- int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/interpolation/test_interpolation_structured2D_gpu.cc0000664000175000017500000002715715160212070027501 0ustar alastairalastair/* * (C) Copyright 2024 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "atlas/array.h" #include "atlas/field/Field.h" #include "atlas/field/FieldSet.h" #include "atlas/functionspace/NodeColumns.h" #include "atlas/functionspace/StructuredColumns.h" #include "atlas/grid/Grid.h" #include "atlas/grid/Iterator.h" #include "atlas/interpolation.h" #include "atlas/mesh/Mesh.h" #include "atlas/meshgenerator.h" #include "atlas/output/Gmsh.h" #include "atlas/util/CoordinateEnums.h" #include "atlas/util/function/VortexRollup.h" #include "pluto/pluto.h" #include "tests/AtlasTestEnvironment.h" using atlas::functionspace::NodeColumns; using atlas::functionspace::StructuredColumns; using atlas::util::Config; namespace atlas { namespace test { //----------------------------------------------------------------------------- static Config scheme() { Config scheme; scheme.set("type", "structured-cubic2D"); scheme.set("halo", 2); scheme.set("name", "cubic"); scheme.set("verbose",eckit::Resource("--verbose",false)); scheme.set("sparse_matrix_multiply", "hicsparse"); return scheme; } std::string input_gridname(const std::string& default_grid) { return eckit::Resource("--input-grid", default_grid); } std::string output_gridname(const std::string& default_grid) { return eckit::Resource("--output-grid", default_grid); } CASE("which scheme?") { Log::info() << scheme().getString("type") << std::endl; } template struct AdjointTolerance { static const Value value; }; template <> const double AdjointTolerance::value = 2.e-14; template <> const float AdjointTolerance::value = 2.e-5; template void test_interpolation_structured_using_fs_API_for_fieldset_w_hicsparse_backend(const bool start_with_data_on_device) { auto pluto_trace = pluto::trace::enabled(); pluto::trace::enable(false); Grid input_grid(input_gridname("O32")); Grid output_grid(output_gridname("O64")); // Cubic interpolation requires a StructuredColumns functionspace with 2 halos StructuredColumns input_fs(input_grid, scheme() | option::levels(rank == 1 ? 0 : 3)); MeshGenerator meshgen("structured"); Mesh output_mesh = meshgen.generate(output_grid); FunctionSpace output_fs = NodeColumns{output_mesh, option::levels(rank == 1 ? 0 : 3)}; auto lonlat = array::make_view(input_fs.xy()); pluto::trace::enable(pluto_trace); FieldSet fields_source; FieldSet fields_target; for (idx_t f = 0; f < 3; ++f) { auto field_source = fields_source.add(input_fs.createField(option::name("source field " + std::to_string(f)))); fields_target.add(output_fs.createField(option::name("target field " + std::to_string(f)))); if (rank==1) { auto source = array::make_view(field_source); for (idx_t n = 0; n < input_fs.size(); ++n) { source(n) = util::function::vortex_rollup(lonlat(n, LON), lonlat(n, LAT), 0.5); } } else if (rank == 2) { auto source = array::make_view(field_source); for (idx_t n = 0; n < input_fs.size(); ++n) { for (idx_t k = 0; k < 3; ++k) { source(n, k) = util::function::vortex_rollup(lonlat(n, LON), lonlat(n, LAT), 0.5 + double(k) / 2); } } } else { ATLAS_NOTIMPLEMENTED; } } if (start_with_data_on_device) { ATLAS_TRACE("Copy fields_source to device"); fields_source.syncDevice(); } ATLAS_TRACE_SCOPE("halo-exchange (fields_source)") { fields_source.haloExchange(start_with_data_on_device); } ATLAS_TRACE_SCOPE("with matrix") { Interpolation interpolation(scheme(), input_fs, output_fs); interpolation.execute(fields_source, fields_target); fields_target.syncHost(); fields_target.haloExchange(); ATLAS_TRACE_SCOPE("output") { output::Gmsh gmsh(scheme().getString("name") + "-multilevel-fieldset-output-with-matrix-" + array::make_datatype().str() + ".msh", Config("coordinates", "xy")); gmsh.write(output_mesh); gmsh.write(fields_target); } } ATLAS_TRACE_SCOPE("with matrix adjoint") { Interpolation interpolation(scheme() | Config("adjoint", true), input_fs, output_fs); std::vector AxAx(fields_source.field_names().size(), 0.); std::vector xAtAx(fields_source.field_names().size(), 0.); FieldSet fields_source_reference; for (const atlas::Field& field : fields_source) { Field field_ref(field.name(), field.datatype().kind(), field.shape()); field_ref.set_levels(field.levels()); if (rank == 1) { auto fieldInView = array::make_view(field); auto fieldOutView = array::make_view(field_ref); for (atlas::idx_t jn = 0; jn < field_ref.shape(0); ++jn) { fieldOutView(jn) = fieldInView(jn); } } else if (rank == 2) { auto fieldInView = array::make_view(field); auto fieldOutView = array::make_view(field_ref); for (atlas::idx_t jn = 0; jn < field_ref.shape(0); ++jn) { for (atlas::idx_t jl = 0; jl < field_ref.levels(); ++jl) { fieldOutView(jn, jl) = fieldInView(jn, jl); } } } else { ATLAS_NOTIMPLEMENTED; } fields_source_reference.add(field_ref); } interpolation.execute(fields_source, fields_target); fields_target.syncHost(); std::size_t fIndx(0); auto source_names = fields_source.field_names(); for (const std::string& s : fields_target.field_names()) { if (rank == 1) { auto target = array::make_view(fields_target[s]); auto source = array::make_view(fields_source[source_names[fIndx]]); for (idx_t n = 0; n < input_fs.size(); ++n) { AxAx[fIndx] += source(n) * source(n); } for (idx_t n = 0; n < output_fs.size(); ++n) { AxAx[fIndx] += target(n) * target(n); } } else if (rank == 2) { auto target = array::make_view(fields_target[s]); auto source = array::make_view(fields_source[source_names[fIndx]]); for (idx_t n = 0; n < input_fs.size(); ++n) { for (idx_t k = 0; k < 3; ++k) { AxAx[fIndx] += source(n, k) * source(n, k); } } for (idx_t n = 0; n < output_fs.size(); ++n) { for (idx_t k = 0; k < 3; ++k) { AxAx[fIndx] += target(n, k) * target(n, k); } } } else { ATLAS_NOTIMPLEMENTED; } fIndx += 1; } if (!start_with_data_on_device) { fields_source.syncHost(); fields_target.syncHost(); fields_source.deallocateDevice(); fields_target.deallocateDevice(); } interpolation.execute_adjoint(fields_source, fields_target); fields_source.syncHost(); fIndx = 0; for (const std::string& s : fields_source.field_names()) { if (rank == 1) { auto source_reference = array::make_view(fields_source_reference[s]); auto source = array::make_view(fields_source[s]); for (idx_t n = 0; n < input_fs.size(); ++n) { xAtAx[fIndx] += source(n) * source_reference(n); } } else if (rank == 2) { auto source_reference = array::make_view(fields_source_reference[s]); auto source = array::make_view(fields_source[s]); for (idx_t n = 0; n < input_fs.size(); ++n) { for (idx_t k = 0; k < 3; ++k) { xAtAx[fIndx] += source(n, k) * source_reference(n, k); } } } else { ATLAS_NOTIMPLEMENTED; } fIndx += 1; } for (std::size_t t = 0; t < AxAx.size(); ++t) { Log::debug() << " Adjoint test t = " << t << " (Ax).(Ax) = " << AxAx[t] << " x.(AtAx) = " << xAtAx[t] << " std::abs( 1.0 - xAtAx[t]/AxAx[t] ) " << std::abs(1.0 - xAtAx[t] / AxAx[t]) << " AdjointTolerance::value " << AdjointTolerance::value << std::endl; EXPECT_APPROX_EQ(xAtAx[t] / AxAx[t] , 1.0, AdjointTolerance::value); } } pluto::trace::enable(pluto_trace); } constexpr bool start_on_host = false; constexpr bool start_on_device = true; CASE("test_interpolation_structured using fs API for fieldset with hicsparse backend (start with rank-2 double-precision data on host)") { test_interpolation_structured_using_fs_API_for_fieldset_w_hicsparse_backend(start_on_host); } CASE("test_interpolation_structured using fs API for fieldset with hicsparse backend (start with rank-1 double-precision data on host)") { test_interpolation_structured_using_fs_API_for_fieldset_w_hicsparse_backend(start_on_host); } CASE("test_interpolation_structured using fs API for fieldset with hicsparse backend (start with rank-2 double-precision data on device)") { test_interpolation_structured_using_fs_API_for_fieldset_w_hicsparse_backend(start_on_device); } CASE("test_interpolation_structured using fs API for fieldset with hicsparse backend (start with rank-1 double-precision data on device)") { test_interpolation_structured_using_fs_API_for_fieldset_w_hicsparse_backend(start_on_device); } #define SINGLE_PRECISION_WORKS 0 #if SINGLE_PRECISION_WORKS CASE("test_interpolation_structured using fs API for fieldset with hicsparse backend (start with rank-2 single-precision data on host)") { test_interpolation_structured_using_fs_API_for_fieldset_w_hicsparse_backend(start_on_host); } CASE("test_interpolation_structured using fs API for fieldset with hicsparse backend (start with rank-1 single-precision data on host)") { test_interpolation_structured_using_fs_API_for_fieldset_w_hicsparse_backend(start_on_host); } CASE("test_interpolation_structured using fs API for fieldset with hicsparse backend (start with rank-2 single-precision data on device)") { test_interpolation_structured_using_fs_API_for_fieldset_w_hicsparse_backend(start_on_device); } CASE("test_interpolation_structured using fs API for fieldset with hicsparse backend (start with rank-1 single-precision data on device)") { test_interpolation_structured_using_fs_API_for_fieldset_w_hicsparse_backend(start_on_device); } #endif } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/interpolation/test_interpolation_cubedsphere.cc0000664000175000017500000002413415160212070026515 0ustar alastairalastair/* * (C) Crown Copyright 2022 Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ #include "atlas/field/FieldSet.h" #include "atlas/functionspace/CellColumns.h" #include "atlas/functionspace/CubedSphereColumns.h" #include "atlas/functionspace/NodeColumns.h" #include "atlas/functionspace/PointCloud.h" #include "atlas/grid/Distribution.h" #include "atlas/grid/Grid.h" #include "atlas/grid/Partitioner.h" #include "atlas/interpolation/Interpolation.h" #include "atlas/mesh/Mesh.h" #include "atlas/meshgenerator/MeshGenerator.h" #include "atlas/output/Gmsh.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/redistribution/Redistribution.h" #include "atlas/util/Config.h" #include "atlas/util/CoordinateEnums.h" #include "atlas/util/function/VortexRollup.h" #include "tests/AtlasTestEnvironment.h" namespace atlas { namespace test { struct CubedSphereInterpolationFixture { atlas::Grid sourceGrid_ = Grid("CS-LFR-24"); atlas::Mesh sourceMesh_ = MeshGenerator("cubedsphere_dual").generate(sourceGrid_); atlas::FunctionSpace sourceFunctionSpace_ = functionspace::NodeColumns(sourceMesh_); atlas::grid::Partitioner targetPartitioner_ = grid::MatchingPartitioner(sourceMesh_, util::Config("type", "cubedsphere")); atlas::Grid targetGrid_ = Grid("O24"); atlas::Mesh targetMesh_ = MeshGenerator("structured").generate(targetGrid_, targetPartitioner_); atlas::FunctionSpace targetFunctionSpace_ = functionspace::NodeColumns(targetMesh_); }; void gmshOutput(const std::string& fileName, const FieldSet& fieldSet) { const auto functionSpace = fieldSet[0].functionspace(); const auto mesh = functionspace::NodeColumns(functionSpace).mesh(); const auto gmshConfig = util::Config("coordinates", "xyz") | util::Config("ghost", true) | util::Config("info", true); const auto gmsh = output::Gmsh(fileName, gmshConfig); gmsh.write(mesh); gmsh.write(fieldSet, functionSpace); } double dotProd(const Field& a, const Field& b) { double prod{}; const auto aView = array::make_view(a); const auto bView = array::make_view(b); for (size_t i = 0; i < a.size(); ++i) { prod += aView(i) * bView(i); } mpi::comm().allReduceInPlace(prod, eckit::mpi::Operation::SUM); return prod; } std::array interpolationTypes = { util::Config("type", "finite-element"), util::Config("type", "spherical-mean-value") | util::Config("normalisation", "true"), util::Config("type", "spherical-mean-value") | util::Config("normalisation", "false"), util::Config("type", "cubedsphere-bilinear")}; CASE("cubedsphere_to_cubedsphere_interpolation") { // Check that interpolation scheme correctly maps a cubedsphere on to itself. const auto fixture = CubedSphereInterpolationFixture{}; auto sourceField = fixture.sourceFunctionSpace_.createField(option::name("test_field")); { const auto lonlat = array::make_view(fixture.sourceFunctionSpace_.lonlat()); auto view = array::make_view(sourceField); for (idx_t i = 0; i < fixture.sourceFunctionSpace_.size(); ++i) { view(i) = util::function::vortex_rollup(lonlat(i, LON), lonlat(i, LAT), 1.); } } const auto targetMesh = MeshGenerator("cubedsphere_dual").generate(fixture.sourceGrid_, fixture.targetPartitioner_); const auto targetFunctionSpace = functionspace::CubedSphereNodeColumns(targetMesh); for (util::Config config : interpolationTypes) { std::string interpType = ""; std::string normalisationMode = ""; config.get("type", interpType); config.get("normalisation", normalisationMode); bool isNormalisationMode = 0; if (normalisationMode == "true") { isNormalisationMode = 1; } SECTION("using " + interpType + (isNormalisationMode ? " w/ normalisation" : "")) { const auto scheme = config; const auto interp = Interpolation(scheme, fixture.sourceFunctionSpace_, targetFunctionSpace); auto targetField = targetFunctionSpace.createField(option::name("test_field")); interp.execute(sourceField, targetField); targetField.haloExchange(); // iterate over target field and check that error is zero. { const auto lonlat = array::make_view(targetFunctionSpace.lonlat()); auto targetView = array::make_view(targetField); for (idx_t i = 0; i < targetFunctionSpace.size(); ++i) { const auto val = util::function::vortex_rollup(lonlat(i, LON), lonlat(i, LAT), 1.); EXPECT_APPROX_EQ(targetView(i), val, 1e-14); } } // output source and target fields. if (normalisationMode != "") { normalisationMode = "_" + normalisationMode; } gmshOutput("cubedsphere_to_cubedsphere_source_" + interpType + normalisationMode + ".msh", FieldSet{sourceField}); gmshOutput("cubedsphere_to_cubedsphere_target_" + interpType + normalisationMode + ".msh", FieldSet{targetField}); } } } CASE("cubedsphere_scalar_interpolation") { const auto fixture = CubedSphereInterpolationFixture{}; //-------------------------------------------------------------------------- // Interpolation test. //-------------------------------------------------------------------------- // Populate analytic source field. double stDev{}; auto sourceField = fixture.sourceFunctionSpace_.createField(option::name("test_field")); { const auto lonlat = array::make_view(fixture.sourceFunctionSpace_.lonlat()); const auto ghost = array::make_view(fixture.sourceFunctionSpace_.ghost()); auto view = array::make_view(sourceField); for (idx_t i = 0; i < fixture.sourceFunctionSpace_.size(); ++i) { view(i) = util::function::vortex_rollup(lonlat(i, LON), lonlat(i, LAT), 1.); if (!ghost(i)) { stDev += view(i) * view(i); } } } mpi::comm().allReduceInPlace(stDev, eckit::mpi::Operation::SUM); stDev = std::sqrt(stDev / fixture.sourceGrid_.size()); for (util::Config config : interpolationTypes) { std::string interpType = ""; std::string normalisationMode = ""; config.get("type", interpType); config.get("normalisation", normalisationMode); bool isNormalisationMode = 0; if (normalisationMode == "true") { isNormalisationMode = 1; } SECTION("using " + interpType + (isNormalisationMode ? " w/ normalisation" : "")) { // Set up interpolation object. const auto scheme = config | util::Config("adjoint", true); const auto interp = Interpolation(scheme, fixture.sourceFunctionSpace_, fixture.targetFunctionSpace_); // Interpolate from source to target field. auto targetField = fixture.targetFunctionSpace_.createField(option::name("test_field")); interp.execute(sourceField, targetField); targetField.haloExchange(); // Make some diagnostic output fields. auto errorField = fixture.targetFunctionSpace_.createField(option::name("error_field")); auto partField = fixture.targetFunctionSpace_.createField(option::name("partition")); { const auto lonlat = array::make_view(fixture.targetFunctionSpace_.lonlat()); auto targetView = array::make_view(targetField); auto errorView = array::make_view(errorField); auto partView = array::make_view(partField); for (idx_t i = 0; i < fixture.targetFunctionSpace_.size(); ++i) { const auto val = util::function::vortex_rollup(lonlat(i, LON), lonlat(i, LAT), 1.); errorView(i) = std::abs((targetView(i) - val) / stDev); partView(i) = mpi::rank(); } } partField.haloExchange(); bool isNormalisationMode = 0; if (normalisationMode == "true") { isNormalisationMode = 1; } gmshOutput("cubedsphere_source_" + interpType + (isNormalisationMode ? "_normalised" : "") + ".msh", FieldSet(sourceField)); auto targetFields = FieldSet{}; targetFields.add(targetField); targetFields.add(errorField); targetFields.add(partField); gmshOutput("cubedsphere_target_" + interpType + (isNormalisationMode ? "_normalised" : "") + ".msh", targetFields); //-------------------------------------------------------------------------- // Adjoint test. //-------------------------------------------------------------------------- // Ensure that the adjoint identity relationship holds. auto targetAdjoint = fixture.targetFunctionSpace_.createField(option::name("target adjoint")); array::make_view(targetAdjoint).assign(array::make_view(targetField)); targetAdjoint.adjointHaloExchange(); auto sourceAdjoint = fixture.sourceFunctionSpace_.createField(option::name("source adjoint")); array::make_view(sourceAdjoint).assign(0.); interp.execute_adjoint(sourceAdjoint, targetAdjoint); const auto yDotY = dotProd(targetField, targetField); const auto xDotXAdj = dotProd(sourceField, sourceAdjoint); EXPECT_APPROX_EQ(yDotY / xDotXAdj, 1., 1e-14); } } } } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/interpolation/test_interpolation_finite_element_cached.cc0000664000175000017500000001610515160212070030501 0ustar alastairalastair/* * (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 "eckit/log/Bytes.h" #include "atlas/array.h" #include "atlas/functionspace.h" #include "atlas/functionspace/PointCloud.h" #include "atlas/grid.h" #include "atlas/interpolation.h" #include "atlas/linalg/sparse.h" #include "atlas/mesh.h" #include "atlas/meshgenerator.h" #include "atlas/util/CoordinateEnums.h" #include "tests/AtlasTestEnvironment.h" using namespace eckit; using namespace atlas::functionspace; using namespace atlas::util; namespace atlas { namespace test { //----------------------------------------------------------------------------- void set_field(Field& field, Grid& grid, std::function func) { auto view = array::make_view(field); idx_t j{0}; for (auto& p : grid.lonlat()) { view(j) = func(p.lon(), p.lat()); ++j; } } void set_field(Field& field, double v) { auto view = array::make_view(field); for (idx_t j = 0; j < view.shape(0); ++j) { view(j) = v; } } void check_field(Field& field, Grid& grid, std::function func, double tolerance) { auto view = array::make_view(field); idx_t j{0}; for (auto& p : grid.lonlat()) { EXPECT_APPROX_EQ(view(j), func(p.lon(), p.lat()), tolerance); ++j; } } interpolation::MatrixCache get_or_create_cache(Grid& source, Grid& target) { ATLAS_TRACE(); static std::map map; std::string key = source.uid() + target.uid(); if (map.find(key) == map.end()) { // Create interpolator, extract matrix, and store it in map map[key] = interpolation::MatrixCache(Interpolation(option::type("finite-element"), source, target)); } return map[key]; } auto func = [](double x, double) -> double { return std::sin(x * M_PI / 180.); }; //----------------------------------------------------------------------------- CASE("extract cache and use") { Grid grid_source("F32"); Grid grid_target("F16"); Field field_source("source", array::make_datatype(), array::make_shape(grid_source.size())); Field field_target("target", array::make_datatype(), array::make_shape(grid_target.size())); set_field(field_source, grid_source, func); interpolation::Cache cache = get_or_create_cache(grid_source, grid_target); Log::info() << "Cache contains " << eckit::Bytes(cache.footprint()) << std::endl; ATLAS_TRACE_SCOPE("Interpolate with cache") { Interpolation interpolation_using_cache(option::type("finite-element"), grid_source, grid_target, cache); interpolation_using_cache.execute(field_source, field_target); } check_field(field_target, grid_target, func, 1.e-4); } //----------------------------------------------------------------------------- CASE("extract cache, copy it, and move it for use") { Grid grid_source("F32"); Grid grid_target("F16"); Field field_source("source", array::make_datatype(), array::make_shape(grid_source.size())); Field field_target("target", array::make_datatype(), array::make_shape(grid_target.size())); set_field(field_source, grid_source, func); interpolation::MatrixCache created_cache = get_or_create_cache(grid_source, grid_target); eckit::linalg::SparseMatrix eckit_matrix_view = atlas::linalg::make_non_owning_eckit_sparse_matrix(created_cache.matrix()); eckit::linalg::SparseMatrix eckit_matrix_copy = atlas::linalg::make_eckit_sparse_matrix(created_cache.matrix()); EXPECT(not created_cache.matrix().empty()); EXPECT(not eckit_matrix_view.empty()); EXPECT(not eckit_matrix_copy.empty()); auto cache = interpolation::MatrixCache(atlas::linalg::make_sparse_matrix_storage(std::move(eckit_matrix_copy))); EXPECT(not eckit_matrix_view.empty()); // We didn't touch the view EXPECT(eckit_matrix_copy.empty()); // This has been moved into the new 'cache' ATLAS_TRACE_SCOPE("Interpolate with cache") { Interpolation interpolation_using_cache(option::type("finite-element"), grid_source, grid_target, cache); interpolation_using_cache.execute(field_source, field_target); } check_field(field_target, grid_target, func, 1.e-4); } //----------------------------------------------------------------------------- CASE("extract cache, copy it, and pass non-owning pointer") { Grid grid_source("F32"); Grid grid_target("F16"); Field field_source("source", array::make_datatype(), array::make_shape(grid_source.size())); Field field_target("target", array::make_datatype(), array::make_shape(grid_target.size())); set_field(field_source, grid_source, func); interpolation::MatrixCache created_cache = get_or_create_cache(grid_source, grid_target); const auto& matrix_storage = created_cache.matrix(); atlas::linalg::SparseMatrixStorage matrix_storage_copy(matrix_storage); auto matrix = atlas::linalg::make_host_view(matrix_storage_copy); EXPECT(not matrix.empty()); ATLAS_TRACE_SCOPE("Interpolate with [eckit_linalg] sparse_matrix_multiply of ArrayView") { atlas::linalg::sparse::current_backend("eckit_linalg"); atlas::linalg::sparse::default_backend("eckit_linalg").set("backend", "generic"); auto src = array::make_view(field_source); auto tgt = array::make_view(field_target); atlas::linalg::sparse_matrix_multiply(matrix, src, tgt); } check_field(field_target, grid_target, func, 1.e-4); set_field(field_target, 0.); ATLAS_TRACE_SCOPE("Interpolate with [openmp] sparse_matrix_multiply of eckit::linalg::Vector") { atlas::linalg::sparse::current_backend("openmp"); eckit::linalg::Vector src{array::make_view(field_source).data(), field_source.size()}; eckit::linalg::Vector tgt{array::make_view(field_target).data(), field_target.size()}; atlas::linalg::sparse_matrix_multiply(matrix, src, tgt); } check_field(field_target, grid_target, func, 1.e-4); set_field(field_target, 0.); auto cache = interpolation::MatrixCache( &matrix_storage_copy ); EXPECT(not matrix.empty()); ATLAS_TRACE_SCOPE("Interpolate with cache") { set_field(field_target, 0.); Interpolation interpolation_using_cache(option::type("finite-element"), grid_source, grid_target, cache); interpolation_using_cache.execute(field_source, field_target); } check_field(field_target, grid_target, func, 1.e-4); set_field(field_target, 0.); } //----------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/interpolation/test_interpolation_from_lam.cc0000664000175000017500000003775715160212070026037 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "atlas/array.h" #include "atlas/field/Field.h" #include "atlas/field/FieldSet.h" #include "atlas/functionspace/PointCloud.h" #include "atlas/functionspace/StructuredColumns.h" #include "atlas/functionspace/NodeColumns.h" #include "atlas/grid/Grid.h" #include "atlas/grid/Distribution.h" #include "atlas/grid/Iterator.h" #include "atlas/interpolation.h" #include "atlas/mesh/Mesh.h" #include "atlas/meshgenerator.h" #include "atlas/output/Gmsh.h" #include "atlas/util/CoordinateEnums.h" #include "atlas/util/function/VortexRollup.h" #include "atlas/util/PolygonXY.h" #include "tests/AtlasTestEnvironment.h" using atlas::util::Config; namespace atlas { namespace test { //----------------------------------------------------------------------------- static Grid get_input_grid() { Config config; config.set("type", "regional"); config.set("lonlat(xmin,ymin)", std::vector{4.03896, 51.1994}); config.set("nx", 49); config.set("ny", 69); config.set("dx", 2500.); config.set("dy", 2500.); config.set("projection.type", "lambert_conformal_conic"); config.set("projection.latitude0", 51.967); config.set("projection.longitude0", 4.9); return Grid{config}; /* This grid extracted from a GRIB2 file received by Ulf Andrae to investigate support of LAM grids in the IFS using Atlas ***** FILE: grib_example_lambert.grib2 #============== MESSAGE 1 ( length=208 ) ============== GRIB { # Meteorological products (grib2/tables/15/0.0.table) discipline = 0; editionNumber = 2; # French Weather Service - Toulouse (common/c-11.table) centre = 85; subCentre = 84; # Start of forecast (grib2/tables/15/1.2.table) significanceOfReferenceTime = 1; dataDate = 20251104; dataTime = 0; # Operational products (grib2/tables/15/1.3.table) productionStatusOfProcessedData = 0; # Forecast products (grib2/tables/15/1.4.table) typeOfProcessedData = 1; numberOfDataPoints = 3381; # There is no appended list (grib2/tables/15/3.11.table) interpretationOfNumberOfPoints = 0; # Unknown code table entry (grib2/tables/15/3.1.table) gridDefinitionTemplateNumber = 33; # Earth assumed spherical with radius of 6 371 229.0 m (grib2/tables/15/3.2.table) shapeOfTheEarth = 6; Nx = 49; Ny = 69; latitudeOfFirstGridPointInDegrees = 51.1994; longitudeOfFirstGridPointInDegrees = 4.03896; LaDInDegrees = 51.967; LoVInDegrees = 4.9; DxInMetres = 2500; DyInMetres = 2500; # (1=0) North Pole is on the projection plane ;(2=0) Only one projection centre is used :grib2/tables/15/3.5.table # flags: 00000000 projectionCentreFlag = 0; iScansNegatively = 0; jScansPositively = 1; jPointsAreConsecutive = 0; alternativeRowScanning = 0; Latin1InDegrees = 51.967; Latin2 = 51967000; Latin2InDegrees = 51.967; latitudeOfSouthernPoleInDegrees = -90; longitudeOfSouthernPoleInDegrees = 0; Nux = 49; Ncx = 8; Nuy = 69; Ncy = 8; gridType = lambert_lam; NV = 0; */ } static Grid get_output_grid(const std::string& name) { if (name == "lonlat_centred") { Config config; config.set("type", "regional"); config.set("lonlat(xmin,ymin)", std::vector{4.9-0.5, 51.967-0.5}); config.set("nx", 41); config.set("ny", 41); config.set("dx", 0.5/20.); config.set("dy", 0.5/20.); config.set("projection.type", "lonlat"); return Grid{config}; } else if (name == "lonlat_bottomleft") { Config config; config.set("type", "regional"); config.set("lonlat(xmin,ymin)", std::vector{4.2, 51.3}); config.set("nx", 21); config.set("ny", 21); config.set("dx", 0.5/20.); config.set("dy", 0.5/20.); config.set("projection.type", "lonlat"); return Grid{config}; } ATLAS_NOTIMPLEMENTED; } void check_input_structured_partition_polygon(FunctionSpace input_fs) { ATLAS_TRACE(); input_fs.polygon().outputPythonScript("input_partitions_n"+std::to_string(mpi::size())+".py"); if (mpi::size() == 4) { const auto& polygons = input_fs.polygons(); double tol_xy = 0.05; // in metres! EXPECT_APPROX_EQ(polygons[0].xy(),(std::vector{{-60000,-85003.7},{1250.01,-85003.7},{1250.01,-71253.7},{-1249.99,-71253.7},{-1249.99,1246.27},{-60000,1246.27},{-60000,-85003.7}}), tol_xy); EXPECT_APPROX_EQ(polygons[1].xy(),(std::vector{{1250.01,-85003.7},{60000,-85003.7},{60000,-1253.73},{1250.01,-1253.73},{1250.01,1246.27},{-1249.99,1246.27},{-1249.99,-71253.7},{1250.01,-71253.7},{1250.01,-85003.7}}), tol_xy); EXPECT_APPROX_EQ(polygons[2].xy(),(std::vector{{-60000,1246.27},{1250.01,1246.27},{1250.01,73746.3},{-1249.99,73746.3},{-1249.99,84996.3},{-60000,84996.3},{-60000,1246.27}}), tol_xy); EXPECT_APPROX_EQ(polygons[3].xy(),(std::vector{{1250.01,-1253.73},{60000,-1253.73},{60000,84996.3},{-1249.99,84996.3},{-1249.99,73746.3},{1250.01,73746.3},{1250.01,-1253.73}}), tol_xy); } } void check_output_structured_partition_polygon(FunctionSpace output_fs, const std::string& name) { ATLAS_TRACE(); output_fs.polygon().outputPythonScript("output_partitions_n"+std::to_string(mpi::size())+"_"+name+".py"); if (name == "lonlat_centred" && mpi::size() == 4) { const auto& polygons = output_fs.polygons(); double tol_xy = 0.0005; // in degrees! EXPECT_APPROX_EQ(polygons[0].xy(),(std::vector{{4.4,51.467},{4.8875,51.467},{4.8875,51.9795},{4.4,51.9795},{4.4,51.467}}), tol_xy); EXPECT_APPROX_EQ(polygons[1].xy(),(std::vector{{4.8875,51.467},{5.4,51.467},{5.4,51.9545},{4.9125,51.9545},{4.9125,51.9795},{4.8875,51.9795},{4.8875,51.467}}), tol_xy); EXPECT_APPROX_EQ(polygons[2].xy(),(std::vector{{4.4,51.9795},{4.9125,51.9795},{4.9125,52.467},{4.4,52.467},{4.4,51.9795}}), tol_xy); EXPECT_APPROX_EQ(polygons[3].xy(),(std::vector{{4.9125,51.9545},{5.4,51.9545},{5.4,52.467},{4.9125,52.467},{4.9125,51.9545}}), tol_xy); } else if (name == "lonlat_bottomleft" && mpi::size() == 4) { const auto& polygons = output_fs.polygons(); double tol_xy = 0.0005; // in degrees! EXPECT_APPROX_EQ(polygons[0].xy(),(std::vector{{4.2,51.3},{4.7,51.3},{4.7,51.8},{4.2,51.8},{4.2,51.3}}), tol_xy); EXPECT_APPROX_EQ(polygons[1].xy(),(std::vector{}), tol_xy); EXPECT_APPROX_EQ(polygons[2].xy(),(std::vector{}), tol_xy); EXPECT_APPROX_EQ(polygons[3].xy(),(std::vector{}), tol_xy); } } void check_input_mesh_partition_polygon(FunctionSpace input_fs) { ATLAS_TRACE(); input_fs.polygon().outputPythonScript("input_mesh_partitions_n"+std::to_string(mpi::size())+".py"); if (mpi::size() == 4) { [[maybe_unused]] const auto& polygons = input_fs.polygons(); // double tol_xy = 0.05; // in metres! // EXPECT_APPROX_EQ(polygons[0].xy(),(std::vector{{-60000,-85003.7},{1250.01,-85003.7},{1250.01,-71253.7},{-1249.99,-71253.7},{-1249.99,1246.27},{-60000,1246.27},{-60000,-85003.7}}), tol_xy); // EXPECT_APPROX_EQ(polygons[1].xy(),(std::vector{{1250.01,-85003.7},{60000,-85003.7},{60000,-1253.73},{1250.01,-1253.73},{1250.01,1246.27},{-1249.99,1246.27},{-1249.99,-71253.7},{1250.01,-71253.7},{1250.01,-85003.7}}), tol_xy); // EXPECT_APPROX_EQ(polygons[2].xy(),(std::vector{{-60000,1246.27},{1250.01,1246.27},{1250.01,73746.3},{-1249.99,73746.3},{-1249.99,84996.3},{-60000,84996.3},{-60000,1246.27}}), tol_xy); // EXPECT_APPROX_EQ(polygons[3].xy(),(std::vector{{1250.01,-1253.73},{60000,-1253.73},{60000,84996.3},{-1249.99,84996.3},{-1249.99,73746.3},{1250.01,73746.3},{1250.01,-1253.73}}), tol_xy); } } void check_output_mesh_partition_polygon(FunctionSpace output_fs, const std::string& name) { ATLAS_TRACE(); output_fs.polygon().outputPythonScript("output_mesh_partitions_n"+std::to_string(mpi::size())+"_"+name+".py"); if (name == "lonlat_centred" && mpi::size() == 4) { [[maybe_unused]] const auto& polygons = output_fs.polygons(); // double tol_xy = 0.0005; // in degrees! // EXPECT_APPROX_EQ(polygons[0].xy(),(std::vector{{4.4,51.467},{4.8875,51.467},{4.8875,51.9795},{4.4,51.9795},{4.4,51.467}}), tol_xy); // EXPECT_APPROX_EQ(polygons[1].xy(),(std::vector{{4.8875,51.467},{5.4,51.467},{5.4,51.9545},{4.9125,51.9545},{4.9125,51.9795},{4.8875,51.9795},{4.8875,51.467}}), tol_xy); // EXPECT_APPROX_EQ(polygons[2].xy(),(std::vector{{4.4,51.9795},{4.9125,51.9795},{4.9125,52.467},{4.4,52.467},{4.4,51.9795}}), tol_xy); // EXPECT_APPROX_EQ(polygons[3].xy(),(std::vector{{4.9125,51.9545},{5.4,51.9545},{5.4,52.467},{4.9125,52.467},{4.9125,51.9545}}), tol_xy); } else if (name == "lonlat_bottomleft" && mpi::size() == 4) { [[maybe_unused]] const auto& polygons = output_fs.polygons(); // double tol_xy = 0.0005; // in degrees! // EXPECT_APPROX_EQ(polygons[0].xy(),(std::vector{{4.2,51.3},{4.7,51.3},{4.7,51.8},{4.2,51.8},{4.2,51.3}}), tol_xy); // EXPECT_APPROX_EQ(polygons[1].xy(),(std::vector{}), tol_xy); // EXPECT_APPROX_EQ(polygons[2].xy(),(std::vector{}), tol_xy); // EXPECT_APPROX_EQ(polygons[3].xy(),(std::vector{}), tol_xy); } } FunctionSpace output_functionspace( const std::string& name, const FunctionSpace& input_functionspace) { ATLAS_TRACE(); Grid grid(get_output_grid(name)); auto partitioner = grid::MatchingPartitioner(input_functionspace); grid::Distribution dist = partitioner.partition(grid); return functionspace::StructuredColumns(grid, dist); } FieldSet create_source_fields(FunctionSpace& fs, idx_t nb_fields, idx_t nb_levels) { using Value = double; FieldSet fields_source; auto lonlat = array::make_view(fs.lonlat()); for (idx_t f = 0; f < nb_fields; ++f) { auto field_source = fields_source.add(fs.createField()); auto source = array::make_view(field_source); for (idx_t n = 0; n < fs.size(); ++n) { for (idx_t k = 0; k < nb_levels; ++k) { source(n, k) = util::function::vortex_rollup(lonlat(n, LON), lonlat(n, LAT), 100. + double(k) / 2); } }; } return fields_source; } FieldSet create_target_fields(FunctionSpace& fs, idx_t nb_fields, idx_t nb_levels) { using Value = double; FieldSet fields_target; for (idx_t f = 0; f < nb_fields; ++f) { fields_target.add(fs.createField(option::levels(nb_levels))); } return fields_target; } void do_structured_test( std::string type, int input_halo, bool matrix_free, const std::string& output_grid_name ) { idx_t nb_fields = 2; idx_t nb_levels = 3; Grid input_grid(get_input_grid()); output::Gmsh gmsh("input_mesh.msh", util::Config("coordinates","lonlat")); gmsh.write(Mesh(input_grid)); { output::Gmsh gmsh("input_mesh_xy.msh", util::Config("coordinates","xy")); gmsh.write(Mesh(input_grid)); } functionspace::StructuredColumns input_fs(input_grid, option::levels(nb_levels) | option::halo(input_halo)); check_input_structured_partition_polygon(input_fs); FieldSet fields_source = create_source_fields(input_fs, nb_fields, nb_levels); gmsh.write(fields_source); FunctionSpace output_fs = output_functionspace(output_grid_name, input_fs); check_output_structured_partition_polygon(output_fs, output_grid_name); output::Gmsh output_gmsh("output_mesh.msh", util::Config("coordinates","lonlat")); output_gmsh.write( MeshGenerator("structured").generate(get_output_grid(output_grid_name),grid::MatchingPartitioner(input_fs))); Interpolation interpolation(option::type(type) | util::Config("matrix_free",matrix_free) | util::Config("verbose",eckit::Resource("--verbose",false)), input_fs, output_fs); FieldSet fields_target = create_target_fields(output_fs, nb_fields, nb_levels); interpolation.execute(fields_source, fields_target); output_gmsh.write(fields_target); } void do_nodes_test( std::string type, int input_halo, bool matrix_free, const std::string& output_grid_name ) { idx_t nb_fields = 2; idx_t nb_levels = 3; Grid input_grid(get_input_grid()); Mesh input_mesh(input_grid); output::Gmsh gmsh("input_mesh.msh", util::Config("coordinates","lonlat")); gmsh.write(input_mesh); { output::Gmsh gmsh("input_mesh_xy.msh", util::Config("coordinates","xy")); gmsh.write(input_mesh); } functionspace::NodeColumns input_fs(input_mesh, option::levels(nb_levels) | option::halo(input_halo)); check_input_mesh_partition_polygon(input_fs); FieldSet fields_source = create_source_fields(input_fs, nb_fields, nb_levels); gmsh.write(fields_source); FunctionSpace output_fs = output_functionspace(output_grid_name, input_fs); check_output_mesh_partition_polygon(output_fs, output_grid_name); output::Gmsh output_gmsh("output_mesh_"+output_grid_name+"_"+type+".msh", util::Config("coordinates","lonlat")); output_gmsh.write( MeshGenerator("structured").generate(get_output_grid(output_grid_name),grid::MatchingPartitioner(input_fs))); Interpolation interpolation(option::type(type) | util::Config("matrix_free",matrix_free) | util::Config("verbose",eckit::Resource("--verbose",false)), input_fs, output_fs); FieldSet fields_target = create_target_fields(output_fs, nb_fields, nb_levels); interpolation.execute(fields_source, fields_target); output_gmsh.write(fields_target); } // For now regional-bilinear and (k-)nearest-neighbour is the only method that works with StructuredColumns. // There are still issues with StructuredInterpolation methods (cubic, ...) which expect a global grid CASE("test structured regional-bilinear, output_grid=lonlat_centred, halo 0") { EXPECT_NO_THROW( do_structured_test("regional-bilinear",0,false, "lonlat_centred") ); } CASE("test structured regional-bilinear, output_grid=lonlat_bottomleft, halo 0") { // With MPI_SIZE=4, only rank 0 will have output grid points in this case, which is also a good test EXPECT_NO_THROW( do_structured_test("regional-bilinear",0,false, "lonlat_bottomleft") ); } CASE("test structured nearest-neighbour, output_grid=lonlat_centred, halo 0") { EXPECT_NO_THROW( do_structured_test("nearest-neighbour",0,false, "lonlat_centred") ); } CASE("test structured nearest-neighbour, output_grid=lonlat_bottomleft, halo 0") { // With MPI_SIZE=4, only rank 0 will have output grid points in this case, which is also a good test EXPECT_NO_THROW( do_structured_test("nearest-neighbour",0,false, "lonlat_bottomleft") ); } // Interpolation methods that require the mesh CASE("test meshed finite-element, output_grid=lonlat_centred, halo 0") { EXPECT_NO_THROW( do_nodes_test("finite-element",0,false, "lonlat_centred") ); } CASE("test meshed finite-element, output_grid=lonlat_bottomleft, halo 0") { // With MPI_SIZE=4, only rank 0 will have output grid points in this case, which is also a good test EXPECT_NO_THROW( do_nodes_test("finite-element",0,false, "lonlat_bottomleft") ); } CASE("test meshed nearest-neighbour, output_grid=lonlat_centred, halo 0") { EXPECT_NO_THROW( do_nodes_test("nearest-neighbour",0,false, "lonlat_centred") ); } CASE("test meshed nearest-neighbour, output_grid=lonlat_bottomleft, halo 0") { // With MPI_SIZE=4, only rank 0 will have output grid points in this case, which is also a good test EXPECT_NO_THROW( do_nodes_test("nearest-neighbour",0,false, "lonlat_bottomleft") ); } } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/interpolation/test_interpolation_structured2D_to_unstructured.cc0000664000175000017500000001420415160212070032144 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "atlas/array.h" #include "atlas/field/Field.h" #include "atlas/field/FieldSet.h" #include "atlas/functionspace/NodeColumns.h" #include "atlas/functionspace/PointCloud.h" #include "atlas/functionspace/StructuredColumns.h" #include "atlas/grid/Grid.h" #include "atlas/grid/Iterator.h" #include "atlas/interpolation.h" #include "atlas/mesh/Mesh.h" #include "atlas/meshgenerator.h" #include "atlas/output/Gmsh.h" #include "atlas/util/CoordinateEnums.h" #include "atlas/util/function/VortexRollup.h" #include "tests/AtlasTestEnvironment.h" using atlas::functionspace::PointCloud; using atlas::functionspace::StructuredColumns; using atlas::util::Config; namespace atlas { namespace test { //----------------------------------------------------------------------------- std::string input_gridname(const std::string& default_grid) { return eckit::Resource("--input-grid", default_grid); } FunctionSpace output_functionspace_match() { std::vector points; if (mpi::size() == 2) { if (mpi::rank() == 0) { points = std::vector{ {45., 45.}, {90., 45.}, {135., 45.}, {180., 45.}, {225., 45.}, {270., 45.}, {315., 45.}, }; } if (mpi::rank() == 1) { points = std::vector{ {45., -45.}, {90., -45.}, {135., -45.}, {180., -45.}, {225., -45.}, {270., -45.}, {315., -45.}, }; } } else if (mpi::size() == 1) { points = std::vector{ {45., 45.}, {90., 45.}, {135., 45.}, {180., 45.}, {225., 45.}, {270., 45.}, {315., 45.}, {45., -45.}, {90., -45.}, {135., -45.}, {180., -45.}, {225., -45.}, {270., -45.}, {315., -45.}, }; } else { ATLAS_NOTIMPLEMENTED; } return PointCloud(points); } FunctionSpace output_functionspace_nomatch() { std::vector points; if (mpi::size() == 2) { if (mpi::rank() == 0) { points = std::vector{ {45., 45.}, {90., 45.}, {135., 45.}, {45., -45.}, {90., -45.}, {135., -45.}, }; } if (mpi::rank() == 1) { points = std::vector{ {180., 45.}, {225., 45.}, {270., 45.}, {315., 45.}, {180., -45.}, {225., -45.}, {270., -45.}, {315., -45.}, }; } } else if (mpi::size() == 1) { points = std::vector{ {45., 45.}, {90., 45.}, {135., 45.}, {180., 45.}, {225., 45.}, {270., 45.}, {315., 45.}, {45., -45.}, {90., -45.}, {135., -45.}, {180., -45.}, {225., -45.}, {270., -45.}, {315., -45.}, }; } else { ATLAS_NOTIMPLEMENTED; } return PointCloud(points); } FieldSet create_source_fields(StructuredColumns& fs, idx_t nb_fields, idx_t nb_levels) { using Value = double; FieldSet fields_source; auto lonlat = array::make_view(fs.xy()); for (idx_t f = 0; f < nb_fields; ++f) { auto field_source = fields_source.add(fs.createField()); auto source = array::make_view(field_source); for (idx_t n = 0; n < fs.size(); ++n) { for (idx_t k = 0; k < nb_levels; ++k) { source(n, k) = util::function::vortex_rollup(lonlat(n, LON), lonlat(n, LAT), 0.5 + double(k) / 2); } }; } return fields_source; } FieldSet create_target_fields(FunctionSpace& fs, idx_t nb_fields, idx_t nb_levels) { using Value = double; FieldSet fields_target; for (idx_t f = 0; f < nb_fields; ++f) { fields_target.add(fs.createField(option::levels(nb_levels))); } return fields_target; } CASE("test_match") { if(mpi::size() > 2) { return; } idx_t nb_fields = 2; idx_t nb_levels = 3; Grid input_grid(input_gridname("O32")); StructuredColumns input_fs(input_grid, option::halo(1) | option::levels(nb_levels)); FunctionSpace output_fs = output_functionspace_match(); Interpolation interpolation(option::type("structured-bilinear"), input_fs, output_fs); FieldSet fields_source = create_source_fields(input_fs, nb_fields, nb_levels); FieldSet fields_target = create_target_fields(output_fs, nb_fields, nb_levels); interpolation.execute(fields_source, fields_target); } CASE("test_nomatch") { if(mpi::size() > 2) { return; } idx_t nb_fields = 2; idx_t nb_levels = 3; Grid input_grid(input_gridname("O32")); StructuredColumns input_fs(input_grid, option::halo(1) | option::levels(nb_levels)); FunctionSpace output_fs = output_functionspace_nomatch(); if (false) // expected to throw { Interpolation interpolation(option::type("structured-bilinear"), input_fs, output_fs); FieldSet fields_source = create_source_fields(input_fs, nb_fields, nb_levels); FieldSet fields_target = create_target_fields(output_fs, nb_fields, nb_levels); interpolation.execute(fields_source, fields_target); } } CASE("test_nomatch 2") { if(mpi::size() > 2) { return; } idx_t nb_fields = 2; idx_t nb_levels = 3; Grid input_grid(input_gridname("O32")); StructuredColumns input_fs(input_grid, option::halo(1) | option::levels(nb_levels)); FunctionSpace output_fs = output_functionspace_nomatch(); if (false) // expected to throw { Interpolation interpolation(option::type("structured-bilinear"), input_fs, output_fs); FieldSet fields_source = create_source_fields(input_fs, nb_fields, nb_levels); FieldSet fields_target = create_target_fields(output_fs, nb_fields, nb_levels); interpolation.execute(fields_source, fields_target); } } } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/interpolation/test_interpolation_structured2D_regional.cc0000664000175000017500000002050515160212070030474 0ustar alastairalastair/* * (C) Copyright 2024 Meteorologisk Institutt * * 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. */ #include "atlas/array.h" #include "atlas/field.h" #include "atlas/functionspace/StructuredColumns.h" #include "atlas/grid.h" #include "atlas/interpolation.h" #include "atlas/option.h" #include "atlas/util/Config.h" #include "tests/AtlasTestEnvironment.h" using atlas::functionspace::StructuredColumns; using atlas::util::Config; namespace atlas { namespace test { //----------------------------------------------------------------------------- static Config gridConfigs() { Config gridConfigs; Config projectionConfig; projectionConfig.set("type", "lambert_conformal_conic"); projectionConfig.set("latitude0", 56.3); projectionConfig.set("longitude0", 0.0); Config gridConfig; gridConfig.set("type", "regional"); std::vector lonlat = {9.9, 56.3}; gridConfig.set("lonlat(centre)", lonlat); gridConfig.set("projection", projectionConfig); const size_t sourceNx = 21; const size_t sourceNy = 31; const double sourceDx = 8.0e3; const double sourceDy = 9.0e3; const size_t xFactor = 4; const size_t yFactor = 3; const size_t targetNx = (sourceNx-1)*xFactor+1; const size_t targetNy = (sourceNy-1)*yFactor+1; const double targetDx = static_cast(sourceNx-1)/static_cast(targetNx-1)*sourceDx; const double targetDy = static_cast(sourceNy-1)/static_cast(targetNy-1)*sourceDy; gridConfig.set("nx", sourceNx); gridConfig.set("ny", sourceNy); gridConfig.set("dx", sourceDx); gridConfig.set("dy", sourceDy); gridConfigs.set("source", gridConfig); gridConfigs.set("source normalization", (sourceNx-1)*(sourceNy-1)); gridConfig.set("nx", targetNx); gridConfig.set("ny", targetNy); gridConfig.set("dx", targetDx); gridConfig.set("dy", targetDy); gridConfigs.set("target", gridConfig); gridConfigs.set("target normalization", (targetNx-1)*(targetNy-1)); return gridConfigs; } // Dot product double dotProd(const Field& field01, const Field& field02) { double dprod{}; const size_t ndim = field01.levels() > 0 ? 2 : 1; if (ndim == 1) { const auto field01_view = array::make_view(field01); const auto field02_view = array::make_view(field02); for (idx_t i=0; i < field01_view.shape(0); ++i) { dprod += field01_view(i) * field02_view(i); } } else if (ndim == 2) { const auto field01_view = array::make_view(field01); const auto field02_view = array::make_view(field02); for (idx_t l=0; l < field01_view.shape(1); ++l) { for (idx_t i=0; i < field01_view.shape(0); ++i) { dprod += field01_view(i, l) * field02_view(i, l); } } } eckit::mpi::comm().allReduceInPlace(dprod, eckit::mpi::Operation::SUM); return dprod; } CASE("test_interpolation_structured2D_regional_1d") { Grid sourceGrid(gridConfigs().getSubConfiguration("source")); Grid targetGrid(gridConfigs().getSubConfiguration("target")); StructuredColumns sourceFs(sourceGrid, option::halo(1)); StructuredColumns targetFs(targetGrid, option::halo(1)); Interpolation interpolation(Config("type", "regional-bilinear"), sourceFs, targetFs); auto sourceField = sourceFs.createField(Config("name", "source")); auto targetField = targetFs.createField(Config("name", "target")); // Accuracy test const auto sourceIView = array::make_indexview(sourceFs.index_i()); const auto sourceJView = array::make_indexview(sourceFs.index_j()); auto sourceView = array::make_view(sourceField); const auto sourceGhostView = atlas::array::make_view(sourceFs.ghost()); const double source_normalization = static_cast(gridConfigs().getInt("source normalization")); sourceView.assign(0.0); for (idx_t i = 0; i < sourceFs.size(); ++i) { if (sourceGhostView(i) == 0) { sourceView(i) = static_cast(sourceIView(i)*sourceJView(i)) / source_normalization; } } interpolation.execute(sourceField, targetField); const auto targetIView = array::make_indexview(targetFs.index_i()); const auto targetJView = array::make_indexview(targetFs.index_j()); const auto targetView = array::make_view(targetField); const auto targetGhostView = atlas::array::make_view(targetFs.ghost()); const double tolerance = 1.e-12; const double target_normalization = static_cast(gridConfigs().getInt("target normalization")); for (idx_t i = 0; i < targetFs.size(); ++i) { if (targetGhostView(i) == 0) { const double targetTest = static_cast(targetIView(i)*targetJView(i)) / target_normalization; EXPECT_APPROX_EQ(targetView(i), targetTest, tolerance); } } // Adjoint test auto targetAdjoint = targetFs.createField(); array::make_view(targetAdjoint).assign(array::make_view(targetField)); targetAdjoint.adjointHaloExchange(); auto sourceAdjoint = sourceFs.createField(); array::make_view(sourceAdjoint).assign(0.); interpolation.execute_adjoint(sourceAdjoint, targetAdjoint); const auto yDotY = dotProd(targetField, targetField); const auto xDotXAdj = dotProd(sourceField, sourceAdjoint); EXPECT_APPROX_EQ(yDotY / xDotXAdj, 1., 1e-14); } CASE("test_interpolation_structured2D_regional_2d") { Grid sourceGrid(gridConfigs().getSubConfiguration("source")); Grid targetGrid(gridConfigs().getSubConfiguration("target")); const idx_t nlevs = 2; StructuredColumns sourceFs(sourceGrid, option::halo(1) | option::levels(nlevs)); StructuredColumns targetFs(targetGrid, option::halo(1) | option::levels(nlevs)); Interpolation interpolation(Config("type", "regional-bilinear"), sourceFs, targetFs); auto sourceField = sourceFs.createField(Config("name", "source")); auto targetField = targetFs.createField(Config("name", "target")); // Accuracy test const auto sourceIView = array::make_indexview(sourceFs.index_i()); const auto sourceJView = array::make_indexview(sourceFs.index_j()); auto sourceView = array::make_view(sourceField); const auto sourceGhostView = atlas::array::make_view(sourceFs.ghost()); const double source_normalization = static_cast(gridConfigs().getInt("source normalization")); sourceView.assign(0.0); for (idx_t i = 0; i < sourceFs.size(); ++i) { if (sourceGhostView(i) == 0) { for (idx_t k = 0; k < nlevs; ++k) { sourceView(i, k) = static_cast(sourceIView(i)*sourceJView(i)) / source_normalization; } } } interpolation.execute(sourceField, targetField); const auto targetIView = array::make_indexview(targetFs.index_i()); const auto targetJView = array::make_indexview(targetFs.index_j()); const auto targetView = array::make_view(targetField); const auto targetGhostView = atlas::array::make_view(targetFs.ghost()); const double tolerance = 1.e-12; const double target_normalization = static_cast(gridConfigs().getInt("target normalization")); for (idx_t i = 0; i < targetFs.size(); ++i) { if (targetGhostView(i) == 0) { const double targetTest = static_cast(targetIView(i)*targetJView(i)) / target_normalization; for (idx_t k = 0; k < nlevs; ++k) { EXPECT_APPROX_EQ(targetView(i, k), targetTest, tolerance); } } } // Adjoint test auto targetAdjoint = targetFs.createField(); array::make_view(targetAdjoint).assign(array::make_view(targetField)); targetAdjoint.adjointHaloExchange(); auto sourceAdjoint = sourceFs.createField(); array::make_view(sourceAdjoint).assign(0.); interpolation.execute_adjoint(sourceAdjoint, targetAdjoint); const auto yDotY = dotProd(targetField, targetField); const auto xDotXAdj = dotProd(sourceField, sourceAdjoint); EXPECT_APPROX_EQ(yDotY / xDotXAdj, 1., 1e-14); } } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/interpolation/CMakeLists.txt0000664000175000017500000001437115160212070022451 0ustar alastairalastair# (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. ecbuild_add_test( TARGET atlas_test_Quad3D CONDITION eckit_EIGEN_FOUND SOURCES test_Quad3D.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) ecbuild_add_test( TARGET atlas_test_Quad2D SOURCES test_Quad2D.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) ecbuild_add_test( TARGET atlas_test_Triag2D SOURCES test_Triag2D.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) if( atlas_HAVE_ATLAS_INTERPOLATION ) ecbuild_add_test( TARGET atlas_test_interpolation_conservative SOURCES test_interpolation_conservative.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) ecbuild_add_test( TARGET atlas_test_interpolation_finite_element SOURCES test_interpolation_finite_element.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) ecbuild_add_test( TARGET atlas_test_interpolation_unstructured_bilinear_lonlat SOURCES test_interpolation_unstructured_bilinear_lonlat.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) ecbuild_add_test( TARGET atlas_test_interpolation_finite_element_cached SOURCES test_interpolation_finite_element_cached.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) ecbuild_add_test( TARGET atlas_test_interpolation_grid_box_average SOURCES test_interpolation_grid_box_average.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) ecbuild_add_test( TARGET atlas_test_interpolation_k_nearest_neighbours SOURCES test_interpolation_k_nearest_neighbours.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) ecbuild_add_test( TARGET atlas_test_interpolation_cubic_prototype SOURCES test_interpolation_cubic_prototype.cc CubicInterpolationPrototype.h LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) ecbuild_add_executable( TARGET atlas_test_interpolation_structured2D SOURCES test_interpolation_structured2D.cc LIBS atlas NOINSTALL ) ecbuild_add_executable( TARGET atlas_test_interpolation_structured_limiter SOURCES test_interpolation_structured2D_limiter.cc LIBS atlas NOINSTALL ) ecbuild_add_test( TARGET atlas_test_interpolation_structured2D_regional SOURCES test_interpolation_structured2D_regional.cc LIBS atlas MPI 2 CONDITION eckit_HAVE_MPI AND MPI_SLOTS GREATER_EQUAL 2 ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) ecbuild_add_test( TARGET atlas_test_interpolation_non_linear SOURCES test_interpolation_non_linear.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) ecbuild_add_test( TARGET atlas_test_interpolation_bilinear COMMAND atlas_test_interpolation_structured2D ARGS --scheme linear ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) ecbuild_add_test( TARGET atlas_test_interpolation_bicubic COMMAND atlas_test_interpolation_structured2D ARGS --scheme cubic ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) ecbuild_add_test( TARGET atlas_test_interpolation_biquasicubic COMMAND atlas_test_interpolation_structured2D ARGS --scheme quasicubic ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) ecbuild_add_test( TARGET atlas_test_interpolation_bicubic_gpu SOURCES test_interpolation_structured2D_gpu.cc LIBS atlas CONDITION atlas_HAVE_GPU ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ATLAS_RUN_NGPUS=1 ) if( TEST atlas_test_interpolation_bicubic_gpu ) set_tests_properties(atlas_test_interpolation_bicubic_gpu PROPERTIES LABELS "gpu") endif() ecbuild_add_test( TARGET atlas_test_interpolation_structured2D_to_unstructured SOURCES test_interpolation_structured2D_to_unstructured.cc LIBS atlas MPI 2 CONDITION eckit_HAVE_MPI AND MPI_SLOTS GREATER_EQUAL 2 ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) ecbuild_add_test( TARGET atlas_test_interpolation_from_lam SOURCES test_interpolation_from_lam.cc LIBS atlas MPI 4 CONDITION eckit_HAVE_MPI AND MPI_SLOTS GREATER_EQUAL 4 ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) ecbuild_add_test( TARGET atlas_test_interpolation_structured2D_to_points SOURCES test_interpolation_structured2D_to_points.cc LIBS atlas MPI 4 CONDITION eckit_HAVE_MPI AND MPI_SLOTS GREATER_EQUAL 4 ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) ecbuild_add_test( TARGET atlas_test_interpolation_structured2D_to_points_gpu SOURCES test_interpolation_structured2D_to_points_gpu.cc LIBS atlas MPI 4 CONDITION eckit_HAVE_MPI AND atlas_HAVE_GPU ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ATLAS_RUN_NGPUS=1 ) if( TEST atlas_test_interpolation_structured2D_to_points_gpu ) set_tests_properties(atlas_test_interpolation_structured2D_to_points_gpu PROPERTIES LABELS "gpu") endif() ecbuild_add_test( TARGET atlas_test_interpolation_cubedsphere SOURCES test_interpolation_cubedsphere.cc LIBS atlas MPI 3 OMP 3 CONDITION eckit_HAVE_MPI AND MPI_SLOTS GREATER_EQUAL 6 ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) ecbuild_add_test( TARGET atlas_test_interpolation_spherical_vector SOURCES test_interpolation_spherical_vector.cc LIBS atlas OMP 4 CONDITION atlas_HAVE_EIGEN ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) ecbuild_add_test( TARGET atlas_test_interpolation_spherical_vector_to_point_6PE SOURCES test_interpolation_spherical_vector_to_point_6PE.cc LIBS atlas MPI 6 CONDITION eckit_HAVE_MPI AND MPI_SLOTS GREATER_EQUAL 6 AND atlas_HAVE_EIGEN ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) ecbuild_add_test( TARGET atlas_test_interpolation_binning SOURCES test_interpolation_binning.cc LIBS atlas MPI 6 CONDITION eckit_HAVE_MPI AND MPI_SLOTS GREATER_EQUAL 6 ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) ecbuild_add_test( TARGET atlas_test_par_interpolation_global_matrix SOURCES test_interpolation_global_matrix.cc LIBS atlas CONDITION eckit_HAVE_MPI AND MPI_SLOTS GREATER_EQUAL 6 MPI 6 OMP 1 ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) ecbuild_add_test( TARGET atlas_test_interpolation_global_matrix SOURCES test_interpolation_global_matrix.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) endif() atlas-0.46.0/src/tests/interpolation/test_interpolation_structured2D_to_points.cc0000664000175000017500000001207315160212070030713 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "atlas/array.h" #include "atlas/field/Field.h" #include "atlas/field/FieldSet.h" #include "atlas/functionspace/PointCloud.h" #include "atlas/functionspace/StructuredColumns.h" #include "atlas/grid/Grid.h" #include "atlas/grid/Iterator.h" #include "atlas/interpolation.h" #include "atlas/mesh/Mesh.h" #include "atlas/meshgenerator.h" #include "atlas/output/Gmsh.h" #include "atlas/util/CoordinateEnums.h" #include "atlas/util/function/VortexRollup.h" #include "atlas/util/PolygonXY.h" #include "tests/AtlasTestEnvironment.h" using atlas::functionspace::PointCloud; using atlas::functionspace::StructuredColumns; using atlas::util::Config; namespace atlas { namespace test { //----------------------------------------------------------------------------- std::string input_gridname(const std::string& default_grid) { return eckit::Resource("--input-grid", default_grid); } FunctionSpace output_functionspace( const FunctionSpace& input_functionspace, bool expect_fail ) { std::vector all_points { {360., 90.}, {360., 0.}, {360., -90.}, {0.,0.}, }; // Only keep points that match the input partitioning. // Note that with following implementation it could be that some points // are present in two partitions, but it is not a problem for this test purpose. std::vector points; auto polygon = util::PolygonXY{input_functionspace.polygon()}; for (const auto& p : all_points ) { if (polygon.contains(p)) { points.emplace_back(p); } } if( expect_fail && mpi::rank() == mpi::size() - 1 ) { points.emplace_back(720,0.); } return PointCloud(points); } FieldSet create_source_fields(StructuredColumns& fs, idx_t nb_fields, idx_t nb_levels) { using Value = double; FieldSet fields_source; auto lonlat = array::make_view(fs.xy()); for (idx_t f = 0; f < nb_fields; ++f) { auto field_source = fields_source.add(fs.createField()); auto source = array::make_view(field_source); for (idx_t n = 0; n < fs.size(); ++n) { for (idx_t k = 0; k < nb_levels; ++k) { source(n, k) = util::function::vortex_rollup(lonlat(n, LON), lonlat(n, LAT), 0.5 + double(k) / 2); } }; } return fields_source; } FieldSet create_target_fields(FunctionSpace& fs, idx_t nb_fields, idx_t nb_levels) { using Value = double; FieldSet fields_target; for (idx_t f = 0; f < nb_fields; ++f) { fields_target.add(fs.createField(option::levels(nb_levels))); } return fields_target; } void do_test( std::string type, int input_halo, bool matrix_free, bool expect_failure ) { idx_t nb_fields = 2; idx_t nb_levels = 3; Grid input_grid(input_gridname("O32")); StructuredColumns input_fs(input_grid, option::levels(nb_levels) | option::halo(input_halo)); FunctionSpace output_fs = output_functionspace(input_fs, expect_failure); Interpolation interpolation(option::type(type) | util::Config("matrix_free",matrix_free) | util::Config("verbose",eckit::Resource("--verbose",false)), input_fs, output_fs); FieldSet fields_source = create_source_fields(input_fs, nb_fields, nb_levels); FieldSet fields_target = create_target_fields(output_fs, nb_fields, nb_levels); interpolation.execute(fields_source, fields_target); } CASE("test structured-bilinear, halo 2, with matrix") { EXPECT_NO_THROW( do_test("structured-bilinear",2,false,false) ); } CASE("test structured-bilinear, halo 2, without matrix") { EXPECT_NO_THROW( do_test("structured-bilinear",2,true,false) ); } CASE("test structured-bilinear, halo 2, with matrix, expected failure") { EXPECT_THROWS_AS( do_test("structured-bilinear",2,false,true), eckit::Exception ); } CASE("test structured-bilinear, halo 2, without matrix, expected failure") { EXPECT_THROWS_AS( do_test("structured-bilinear",2,false,true), eckit::Exception ); } CASE("test structured-bilinear, halo 1, with matrix, expected failure") { EXPECT_THROWS_AS( do_test("structured-bilinear",1,false,false), eckit::Exception ); } CASE("test structured-bilinear, halo 1, without matrix, expected failure") { EXPECT_THROWS_AS( do_test("structured-bilinear",1,true,false), eckit::Exception ); } CASE("test structured-bicubic, halo 3, with matrix") { EXPECT_NO_THROW( do_test("structured-bicubic",3,false,false) ); } CASE("test structured-bicubic, halo 2, with matrix") { EXPECT_THROWS_AS( do_test("structured-bicubic",2,false,false), eckit::Exception ); } } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/acceptance_tests/0000775000175000017500000000000015160212070020324 5ustar alastairalastairatlas-0.46.0/src/tests/acceptance_tests/compare.cmake0000664000175000017500000000201615160212070022753 0ustar alastairalastair# (C) Copyright 2019 ECMWF. # # This file is covered by the LICENSING file in the root of this project. # 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. function( run_command COMMAND ) set( COMMAND ${ARGV} ) string(REPLACE ";" " " PRINT_COMMAND "${COMMAND}" ) message( "cd ${CMAKE_CURRENT_BINARY_DIR} && ${PRINT_COMMAND}" ) execute_process( WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} COMMAND ${COMMAND} RESULT_VARIABLE res ) if(res) message(FATAL_ERROR "[ ${COMMAND} ] failed") endif() endfunction() message( "Working directory: ${CMAKE_CURRENT_BINARY_DIR}") list( GET FILES 0 ref ) foreach( file ${FILES} ) if( EXISTS ${file} ) if( NOT ${file} STREQUAL ${ref} ) run_command( ${CMAKE_COMMAND} -E compare_files ${ref} ${file} ) endif() else() message( STATUS "Skipping ${file} as it does not exist" ) endif() endforeach() atlas-0.46.0/src/tests/acceptance_tests/atest_mgrids.cc0000664000175000017500000002063415160212070023325 0ustar alastairalastair/* * (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 "eckit/log/OStreamTarget.h" #include "atlas/field.h" #include "atlas/functionspace.h" #include "atlas/grid.h" #include "atlas/interpolation/Interpolation.h" #include "atlas/mesh.h" #include "atlas/meshgenerator.h" #include "atlas/option.h" #include "atlas/output/Gmsh.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/runtime/AtlasTool.h" #include "atlas/runtime/Log.h" #include "atlas/util/Config.h" #include "atlas/util/Constants.h" #include "atlas/util/CoordinateEnums.h" #include "atlas/util/function/VortexRollup.h" using namespace atlas; //------------------------------------------------------------------------------ class Program : public AtlasTool { int execute(const Args& args) override; public: Program(int argc, char** argv); }; //----------------------------------------------------------------------------- Program::Program(int argc, char** argv): AtlasTool(argc, argv) { add_option(new SimpleOption("gridA", "grid A")); add_option(new SimpleOption("gridB", "grid B")); add_option(new SimpleOption("ghost", "Output ghost elements")); add_option(new SimpleOption("haloA", "Halo size for A")); add_option(new SimpleOption("haloB", "Halo size for B")); add_option(new SimpleOption("matrix-free", "Interpolation without matrix")); add_option(new SimpleOption("checksum", "File to write that will contains checksums of run")); add_option(new SimpleOption("gmsh", "Output gmsh")); add_option(new SimpleOption("no-validate", "Avoid validation of results to increase runtime")); add_option(new SimpleOption("partitioner", "Partitioner for gridA (default=equal_regions)")); } //----------------------------------------------------------------------------- int Program::execute(const Args& args) { auto matrix_free = util::Config("matrix_free", args.getBool("matrix-free", false)); auto ghost = util::Config("ghost", args.getBool("ghost", false)); auto haloA = option::halo(args.getLong("haloA", 2)); auto haloB = option::halo(args.getLong("haloB", 2)); auto checksum_filepath = args.getString("checksum", displayName() + ".checksum"); auto partitioner = args.getString("partitioner", "equal_regions"); auto gridA = Grid(args.getString("gridA")); auto gridB = Grid(args.getString("gridB")); auto meshgenerator = MeshGenerator("structured"); Trace setup_A(Here(), "Setup A"); auto distA = grid::Distribution(gridA, grid::Partitioner(partitioner)); auto meshA = meshgenerator.generate(gridA, distA); functionspace::StructuredColumns fsA(gridA, distA, haloA); setup_A.stop(); Trace setup_B(Here(), "Setup B"); auto distB = grid::Distribution(gridB, grid::MatchingMeshPartitioner(meshA)); functionspace::StructuredColumns fsB(gridB, distB, haloB); setup_B.stop(); Trace setup_interpolation_AtoB(Here(), "Setup interpolation AtoB"); Interpolation AtoB(option::type("bicubic") | matrix_free, fsA, fsB); setup_interpolation_AtoB.stop(); Trace setup_interpolation_BtoA(Here(), "Setup interpolation BtoA"); Interpolation BtoA(option::type("bicubic") | matrix_free, fsB, fsA); setup_interpolation_BtoA.stop(); Field fieldA = fsA.createField(option::name("fieldA")); Field fieldB = fsB.createField(option::name("fieldB")); auto lonlat = array::make_view(fsA.xy()); auto A = array::make_view(fieldA); auto B = array::make_view(fieldB); double meanA = 1.; for (idx_t n = 0; n < fsA.size(); ++n) { A(n) = meanA + util::function::vortex_rollup(lonlat(n, LON), lonlat(n, LAT), 1.); } fieldA.set_dirty(); fieldA.haloExchange(); Log::Channel checksums; checksums.addStream(Log::info()); if (mpi::comm().rank() == 0) { checksums.addFile(checksum_filepath); } checksums << "Field A checksum before interpolation AtoB: " << fsA.checksum(fieldA) << std::endl; Trace interpolation_AtoB(Here(), "Interpolation AtoB"); AtoB.execute(fieldA, fieldB); interpolation_AtoB.stop(); checksums << "Field B checksum after interpolation AtoB: " << fsB.checksum(fieldB) << std::endl; for (idx_t n = 0; n < fsB.size(); ++n) { B(n) = meanA - B(n); } fieldB.set_dirty(); fieldB.haloExchange(); Trace interpolation_BtoA(Here(), "Interpolation BtoA"); BtoA.execute(fieldB, fieldA); interpolation_BtoA.stop(); checksums << "Field A checksum after interpolation BtoA: " << fsA.checksum(fieldA) << std::endl; Log::info() << "Timers:" << std::endl; Log::info() << " Setup A " << setup_A.elapsed() << " s" << std::endl; Log::info() << " Setup B " << setup_B.elapsed() << " s" << std::endl; Log::info() << " -----------------------------------------" << std::endl; Log::info() << " Interpolation AtoB Setup " << setup_interpolation_AtoB.elapsed() << " s" << std::endl; Log::info() << " Interpolation BtoA Setup " << setup_interpolation_BtoA.elapsed() << " s" << std::endl; Log::info() << " Interpolation AtoB Execute " << interpolation_AtoB.elapsed() << " s" << std::endl; Log::info() << " Interpolation BtoA Execute " << interpolation_BtoA.elapsed() << " s" << std::endl; Log::info() << " -----------------------------------------" << std::endl; Log::info() << " Interpolation AtoB Total " << setup_interpolation_AtoB.elapsed() + interpolation_AtoB.elapsed() << " s" << std::endl; Log::info() << " Interpolation BtoA Total " << setup_interpolation_BtoA.elapsed() + interpolation_BtoA.elapsed() << " s" << std::endl; int status = success(); Mesh meshB; if (not args.getBool("no-validate", false)) { double tolerance = 1.e-8; meshB = meshgenerator.generate(gridB, distB); auto norm_computerB = functionspace::NodeColumns(meshB); double sumB; idx_t countB; norm_computerB.sum(fieldB, sumB, countB); if (std::abs(sumB) < tolerance) { Log::info() << "Validation B correct: " << std::abs(sumB) << " < tolerance [" << tolerance << "]" << std::endl; } else { Log::error() << "Validation B failed: " << std::abs(sumB) << " > tolerance [" << tolerance << "]" << std::endl; norm_computerB.orderIndependentSum(fieldB, sumB, countB); Log::info() << " order_independent_sum: " << sumB << " count = " << countB << std::endl; status = failed(); } auto norm_computerA = functionspace::NodeColumns(meshA); double sumA; idx_t countA; norm_computerA.sum(fieldA, sumA, countA); if (std::abs(sumA) < tolerance) { Log::info() << "Validation A correct: " << std::abs(sumA) << " < tolerance [" << tolerance << "]" << std::endl; } else { Log::error() << "Validation A failed: " << std::abs(sumA) << " > tolerance [" << tolerance << "]" << std::endl; norm_computerA.orderIndependentSum(fieldA, sumA, countA); Log::info() << " order_independent_sum: " << sumA << " count = " << countA << std::endl; status = failed(); } } // Gmsh output if (args.getBool("gmsh", false)) { if (not meshB) { meshB = meshgenerator.generate(gridB, distB); } auto gmshA = output::Gmsh(displayName() + "_meshA.msh", ghost); gmshA.write(meshA); gmshA.write(fieldA); auto gmshB = output::Gmsh(displayName() + "_meshB.msh", ghost); gmshB.write(meshB); gmshB.write(fieldB); } return status; } //------------------------------------------------------------------------------ int main(int argc, char** argv) { Program tool(argc, argv); return tool.start(); } atlas-0.46.0/src/tests/acceptance_tests/CMakeLists.txt0000664000175000017500000000563115160212070023071 0ustar alastairalastair# (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. ecbuild_add_executable( TARGET atlas-atest-mgrids SOURCES atest_mgrids.cc LIBS atlas CONDITION atlas_HAVE_ATLAS_NUMERICS ) if( HAVE_TESTS ) ecbuild_add_option( FEATURE ACCEPTANCE_TESTS DEFAULT OFF ) endif() if( HAVE_ACCEPTANCE_TESTS ) set( HAVE_ACCEPTANCE_TESTS_SMALL ON ) set( HAVE_ACCEPTANCE_TESTS_LARGE ON ) function( atlas_atest_mgrids category case nprocs ) if( atlas_HAVE_ECTRANS ) set( PARTITIONER "--partitioner=trans" ) endif() string(REPLACE "/" ";" case ${case} ) list( GET case 0 gridA ) list( GET case 1 gridB ) unset( checksums ) unset( case_tests ) foreach( n ${nprocs} ) if( MPI_SLOTS GREATER_EQUAL ${n} ) foreach( t 1 4 12 ) set( test_name atlas_atest_${category}_mgrids_${gridA}_to_${gridB}_n${n}_t${t} ) ecbuild_add_test( TARGET ${test_name} COMMAND atlas-atest-mgrids ARGS --gridA=${gridA} --gridB=${gridB} --haloA=3 --haloB=3 --display-name=${test_name} --checksum=${test_name}.checksum --matrix-free ${PARTITIONER} MPI ${n} OMP ${t} ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) list( APPEND checksums ${test_name}.checksum ) list( APPEND case_tests ${test_name} ) endforeach() endif() endforeach() add_test( NAME atlas_atest_${category}_mgrids_${gridA}_to_${gridB}_BitReproducible COMMAND ${CMAKE_COMMAND} "-DFILES=${checksums}" -P ${CMAKE_CURRENT_SOURCE_DIR}/compare.cmake ) foreach( dep ${case_tests} ) set_tests_properties( atlas_atest_${category}_mgrids_${gridA}_to_${gridB}_BitReproducible PROPERTIES DEPENDS ${dep} ) endforeach() endfunction() if( atlas_HAVE_ATLAS_NUMERICS ) if( HAVE_ACCEPTANCE_TESTS_SMALL ) unset( cases ) list( APPEND cases O32/O16 O32/L16 O64/F20 ) list( APPEND nprocs 1 4 32 ) foreach( case ${cases} ) atlas_atest_mgrids( small ${case} "${nprocs}" ) endforeach() endif() if( HAVE_ACCEPTANCE_TESTS_LARGE ) unset( cases ) list( APPEND cases O640/O320 ) set( nprocs 1 4 36 72 ) foreach( case ${cases} ) atlas_atest_mgrids( large ${case} "${nprocs}" ) endforeach() endif() endif() endif() atlas-0.46.0/src/tests/trans/0000775000175000017500000000000015160212070016143 5ustar alastairalastairatlas-0.46.0/src/tests/trans/test_trans_localcache.cc0000664000175000017500000003262115160212070023002 0ustar alastairalastair/* * (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/MD5.h" #include "atlas/grid.h" #include "atlas/option.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/runtime/Trace.h" #include "atlas/trans/LegendreCacheCreator.h" #include "atlas/trans/Trans.h" #include "atlas/util/Constants.h" #include "tests/AtlasTestEnvironment.h" namespace atlas { namespace test { //----------------------------------------------------------------------------- struct AtlasTransEnvironment : public AtlasTestEnvironment { AtlasTransEnvironment(int argc, char* argv[]): AtlasTestEnvironment(argc, argv) { trans::Trans::backend("local"); trans::Trans::config(option::warning(1)); } }; using trans::Cache; using trans::LegendreCache; using trans::LegendreCacheCreator; using trans::Trans; using XSpace = StructuredGrid::XSpace; using YSpace = StructuredGrid::YSpace; using LinearSpacing = grid::LinearSpacing; eckit::PathName CacheFile(const std::string& path) { eckit::PathName cachefile(path); if (cachefile.exists()) { cachefile.unlink(); } return cachefile; } std::string hash(const trans::Cache& c) { return eckit::MD5(c.legendre().data(), c.legendre().size()).digest(); } std::string hash(const eckit::PathName& f) { return hash(LegendreCache(f)); } std::string F(int n) { return "F" + std::to_string(n); } std::string O(int n) { return "O" + std::to_string(n); } std::string N(int n) { return "N" + std::to_string(n); } std::string L(int n) { return "L" + std::to_string(n); } std::string S(int n) { return "S" + std::to_string(n); } std::string Slon(int n) { return "Slon" + std::to_string(n); } std::string Slat(int n) { return "Slat" + std::to_string(n); } //----------------------------------------------------------------------------- CASE("test_global_grids") { // auto resolutions = { 32, 64, 160, 320, 640 }; auto resolutions = {32, 64}; for (int n : resolutions) { int t = n - 1; auto cases = { std::make_pair(F(n), t), std::make_pair(O(n), t), std::make_pair(N(n), t), std::make_pair(L(n), t), std::make_pair(S(n), t), std::make_pair(Slon(n), t), std::make_pair(Slat(n), t), }; LegendreCacheCreator F_cache_creator(Grid(F(n)), t); EXPECT(F_cache_creator.supported()); auto F_cachefile = CacheFile("leg_" + F_cache_creator.uid() + ".bin"); F_cache_creator.create(F_cachefile); Cache F_cache = LegendreCache(F_cachefile); auto F_cache_hash = hash(F_cache); for (auto _case : cases) { auto gridname = _case.first; auto truncation = _case.second; Log::info() << "Case " + gridname + " T" + std::to_string(truncation) << std::endl; ATLAS_TRACE("Case " + gridname + " T" + std::to_string(truncation)); Grid grid(gridname); LegendreCacheCreator cache_creator(grid, truncation); EXPECT(cache_creator.supported()); auto cachefile = CacheFile("leg_" + cache_creator.uid() + ".bin"); cache_creator.create(cachefile); if (GaussianGrid(grid)) { EXPECT(hash(cachefile) == F_cache_hash); } ATLAS_TRACE_SCOPE("create without cache") Trans(grid, truncation); Cache cache; ATLAS_TRACE_SCOPE("read cache") cache = LegendreCache(cachefile); ATLAS_TRACE_SCOPE("create with cache") Trans(cache, grid, truncation); } } } CASE("test_global_grids_with_subdomain") { int n = 64; int t = n - 1; auto cases = {std::make_pair(F(n), t), std::make_pair(O(n), t), std::make_pair(N(n), t), std::make_pair(L(n), t), std::make_pair(S(n), t), std::make_pair(Slon(n), t), std::make_pair(Slat(n), t)}; auto domains = std::vector{ ZonalBandDomain({-10., 5.}), RectangularDomain({-1., 1.}, {50., 55.}), RectangularDomain({-1., 1.}, {-5., 40.}), }; for (auto _case : cases) { auto gridname = _case.first; auto truncation = _case.second; ATLAS_TRACE("Case " + gridname + " T" + std::to_string(truncation)); Grid global_grid(gridname); LegendreCacheCreator global_cache_creator(Grid(gridname), truncation); EXPECT(global_cache_creator.supported()); auto global_cachefile = CacheFile("leg_" + global_cache_creator.uid() + ".bin"); ATLAS_TRACE_SCOPE("Creating cache " + std::string(global_cachefile)) global_cache_creator.create(global_cachefile); Cache global_cache; ATLAS_TRACE_SCOPE("read cache") global_cache = LegendreCache(global_cachefile); auto global_hash = hash(global_cache); for (auto domain : domains) { Grid grid(gridname, domain); ATLAS_TRACE_SCOPE("create with cache") Trans(global_cache, global_grid, domain, truncation); } } } CASE("test_regional_grids nested_in_global") { auto cachefile = CacheFile("regional_lonlat.bin"); auto truncation = 89; Cache cache; StructuredGrid grid_global(LinearSpacing({0., 360.}, 360, false), LinearSpacing({90., -90.}, 181, true)); EXPECT(grid_global.domain().global()); LegendreCacheCreator global_cache_creator(grid_global, truncation); EXPECT(global_cache_creator.supported()); auto global_cachefile = CacheFile("leg_" + global_cache_creator.uid() + ".bin"); ATLAS_TRACE_SCOPE("Creating cache " + std::string(cachefile)) global_cache_creator.create(global_cachefile); StructuredGrid regional(LinearSpacing({0., 180.}, 181), LinearSpacing({0., 45.}, 46)); ATLAS_TRACE_SCOPE("create without cache") Trans(grid_global, regional.domain(), truncation); ATLAS_TRACE_SCOPE("read cache") cache = LegendreCache(global_cachefile); ATLAS_TRACE_SCOPE("create with cache") Trans(cache, grid_global, regional.domain(), truncation); } CASE("test_regional_grids not nested") { auto truncation = 89; Cache cache; StructuredGrid grid(LinearSpacing({0., 180.}, 181), LinearSpacing({0., 45.}, 46)); LegendreCacheCreator cache_creator(grid, truncation); EXPECT(cache_creator.supported()); auto cachefile = CacheFile("leg_" + cache_creator.uid() + ".bin"); ATLAS_TRACE_SCOPE("Creating cache " + std::string(cachefile)) cache_creator.create(cachefile); ATLAS_TRACE_SCOPE("create without cache") Trans(grid, truncation); ATLAS_TRACE_SCOPE("read cache") cache = LegendreCache(cachefile); ATLAS_TRACE_SCOPE("create with cache") Trans(cache, grid, truncation); } CASE("test_regional_grids with projection") { auto cachefile = CacheFile("cache-regional.bin"); auto truncation = 89; Cache cache; Projection projection(util::Config("type", "rotated_lonlat")("north_pole", std::vector{4., 54.})); StructuredGrid grid(LinearSpacing({0., 180.}, 181), LinearSpacing({0., 45.}, 46), projection); Trans trans; ATLAS_TRACE_SCOPE("create without cache") trans = Trans(grid, truncation); // Note: caching not yet implemented for unstructured and projected grids LegendreCacheCreator legendre_cache_creator(grid, truncation); ATLAS_DEBUG_VAR(legendre_cache_creator.uid()); EXPECT(not legendre_cache_creator.supported()); std::vector rspecg(trans.spectralCoefficients(), 0.); std::vector rgp(trans.grid().size()); trans.invtrans(1, rspecg.data(), rgp.data()); } CASE("test cache creator to file") { auto truncation = 89; StructuredGrid grid_global(LinearSpacing({0., 360.}, 360, false), LinearSpacing({90., -90.}, 181, true)); LegendreCacheCreator legendre_cache_creator(grid_global, truncation); auto cachefile = CacheFile(legendre_cache_creator.uid()); ATLAS_TRACE_SCOPE("Creating cache " + std::string(cachefile)) legendre_cache_creator.create(cachefile); Cache c = legendre_cache_creator.create(); auto trans1 = Trans(c, grid_global, truncation); auto trans2 = Trans(c, grid_global, truncation); } CASE("test cache creator in memory") { auto truncation = 89; StructuredGrid grid_global(LinearSpacing({0., 360.}, 360, false), LinearSpacing({90., -90.}, 181, true)); LegendreCacheCreator legendre_cache_creator(grid_global, truncation); Cache cache; ATLAS_TRACE_SCOPE("Creating cache in memory") cache = legendre_cache_creator.create(); auto trans1 = Trans(cache, grid_global, truncation); auto trans2 = Trans(cache, grid_global, truncation); } CASE("ATLAS-256: Legendre coefficient expected unique identifiers") { util::Config options; options.set(option::type("local")); options.set("flt", false); auto uids = { // clang-format off "local-T20-GaussianN320-OPT4189816c2e", "local-T20-GaussianN640-OPT4189816c2e", "local-T20-GaussianN1280-OPT4189816c2e", "local-T20-GaussianN320-OPT4189816c2e", "local-T20-GaussianN640-OPT4189816c2e", "local-T20-GaussianN1280-OPT4189816c2e", "local-T20-GaussianN320-OPT4189816c2e", "local-T20-GaussianN640-OPT4189816c2e", "local-T20-GaussianN1280-OPT4189816c2e", "local-T20-L-ny181-OPT4189816c2e", "local-T20-L-ny1801-OPT4189816c2e", "local-T639-GaussianN320-OPT4189816c2e", "local-T639-GaussianN640-OPT4189816c2e", "local-T639-GaussianN1280-OPT4189816c2e", "local-T639-GaussianN320-OPT4189816c2e", "local-T639-GaussianN640-OPT4189816c2e", "local-T639-GaussianN1280-OPT4189816c2e", "local-T639-GaussianN320-OPT4189816c2e", "local-T639-GaussianN640-OPT4189816c2e", "local-T639-GaussianN1280-OPT4189816c2e", "local-T639-L-ny181-OPT4189816c2e", "local-T639-L-ny1801-OPT4189816c2e", "local-T1279-GaussianN320-OPT4189816c2e", "local-T1279-GaussianN640-OPT4189816c2e", "local-T1279-GaussianN1280-OPT4189816c2e", "local-T1279-GaussianN320-OPT4189816c2e", "local-T1279-GaussianN640-OPT4189816c2e", "local-T1279-GaussianN1280-OPT4189816c2e", "local-T1279-GaussianN320-OPT4189816c2e", "local-T1279-GaussianN640-OPT4189816c2e", "local-T1279-GaussianN1280-OPT4189816c2e", "local-T1279-L-ny181-OPT4189816c2e", "local-T1279-L-ny1801-OPT4189816c2e", "local-T20-grid-800ac12540-OPT4189816c2e", "local-T20-grid-0915e0f040-OPT4189816c2e", "local-T20-grid-7c400822f0-OPT4189816c2e", "local-T20-grid-800ac12540-OPT4189816c2e", "local-T20-grid-0915e0f040-OPT4189816c2e", "local-T20-grid-7c400822f0-OPT4189816c2e", "local-T20-grid-800ac12540-OPT4189816c2e", "local-T20-grid-0915e0f040-OPT4189816c2e", "local-T20-grid-7c400822f0-OPT4189816c2e", "local-T20-grid-7824deccdf-OPT4189816c2e", "local-T20-grid-7d1771559e-OPT4189816c2e", "local-T639-grid-800ac12540-OPT4189816c2e", "local-T639-grid-0915e0f040-OPT4189816c2e", "local-T639-grid-7c400822f0-OPT4189816c2e", "local-T639-grid-800ac12540-OPT4189816c2e", "local-T639-grid-0915e0f040-OPT4189816c2e", "local-T639-grid-7c400822f0-OPT4189816c2e", "local-T639-grid-800ac12540-OPT4189816c2e", "local-T639-grid-0915e0f040-OPT4189816c2e", "local-T639-grid-7c400822f0-OPT4189816c2e", "local-T639-grid-7824deccdf-OPT4189816c2e", "local-T639-grid-7d1771559e-OPT4189816c2e", "local-T1279-grid-800ac12540-OPT4189816c2e", "local-T1279-grid-0915e0f040-OPT4189816c2e", "local-T1279-grid-7c400822f0-OPT4189816c2e", "local-T1279-grid-800ac12540-OPT4189816c2e", "local-T1279-grid-0915e0f040-OPT4189816c2e", "local-T1279-grid-7c400822f0-OPT4189816c2e", "local-T1279-grid-800ac12540-OPT4189816c2e", "local-T1279-grid-0915e0f040-OPT4189816c2e", "local-T1279-grid-7c400822f0-OPT4189816c2e", "local-T1279-grid-7824deccdf-OPT4189816c2e", "local-T1279-grid-7d1771559e-OPT4189816c2e", // clang-format on }; auto uid = uids.begin(); auto domains = std::vector{GlobalDomain(), RectangularDomain({-10, 10}, {-20, 20})}; auto spectral_T = std::vector{20, 639, 1279}; auto grids = std::vector{"F320", "F640", "F1280", "N320", "N640", "N1280", "O320", "O640", "O1280", "L90", "L900"}; for (auto& domain : domains) { for (int T : spectral_T) { for (auto name : grids) { Log::info() << "Case name:'" << name << "', T:" << T << ", domain:" << domain << ", UID:'" << *uid << "'" << std::endl; Grid grid(name, domain); auto test = trans::LegendreCacheCreator(grid, T, options).uid(); ATLAS_DEBUG_VAR(test); EXPECT(test == *uid); uid++; } } } } } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/trans/fctest_trans_unstructured.F900000664000175000017500000000565115160212070023760 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. ! @author Willem Deconinck #include "fckit/fctest.h" ! ----------------------------------------------------------------------------- TESTSUITE(fctest_atlas_trans_unstr) ! ----------------------------------------------------------------------------- TESTSUITE_INIT use atlas_module type(atlas_Trans) :: trans call atlas_library%initialise() call trans%set_backend("local") END_TESTSUITE_INIT ! ----------------------------------------------------------------------------- TESTSUITE_FINALIZE use atlas_module call atlas_library%finalise() END_TESTSUITE_FINALIZE ! ----------------------------------------------------------------------------- TEST( test_trans ) use atlas_module implicit none type(atlas_UnstructuredGrid) :: grid type(atlas_Trans) :: trans type(atlas_functionspace_Spectral) :: spectral type(atlas_Field) :: gp_scal_field, gp_wind_field type(atlas_Field) :: sp_scal_field, sp_vor_field, sp_div_field type(atlas_Config) :: config integer :: truncation real(c_double) :: xy(2,10) xy(:,1) = [107.196,26.0167] xy(:,2) = [144.768,28.3721] xy(:,3) = [150.891,60.0343] xy(:,4) = [164.566,25.5053] xy(:,5) = [116.851,14.0295] xy(:,6) = [124.84,28.3978] xy(:,7) = [157.901,42.042] xy(:,8) = [111.41,43.1056] xy(:,9) = [134.333,44.6677] xy(:,10) = [120.307,59.7167] grid = atlas_UnstructuredGrid( xy ) truncation = 21; trans = atlas_Trans(grid,truncation) FCTEST_CHECK_EQUAL( trans%truncation(), truncation ) spectral = trans%spectral() FCTEST_CHECK_EQUAL( spectral%truncation(), truncation ) sp_scal_field = spectral%create_field(name="spectral_scalar",kind=atlas_real(8)) sp_vor_field = spectral%create_field(name="spectral_vorticity",kind=atlas_real(8)) sp_div_field = spectral%create_field(name="spectral_divergence",kind=atlas_real(8)) gp_scal_field = atlas_Field(name="gridpoint_scalar",kind=atlas_real(8),shape=[10]) gp_wind_field = atlas_Field(name="gridpoint_wind",kind=atlas_real(8),shape=[10,2]) call set_zero( sp_scal_field ) call set_zero( sp_vor_field ) call set_zero( sp_div_field ) config = atlas_Config() call config%set("warning",0) ! turn off warnings for unstructured grids call trans%invtrans( sp_scal_field, gp_scal_field, config ) call trans%invtrans_vordiv2wind( sp_vor_field, sp_div_field, gp_wind_field, config ) contains subroutine set_zero( field ) use, intrinsic :: iso_c_binding type(atlas_Field) :: field real(c_double), pointer :: data(:) call field%data(data) data(:) = 0. end subroutine END_TEST END_TESTSUITE atlas-0.46.0/src/tests/trans/test_trans.cc0000664000175000017500000006335115160212070020650 0ustar alastairalastair/* * (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 "eckit/exception/Exceptions.h" #include "eckit/filesystem/PathName.h" #include "eckit/io/DataHandle.h" #include "atlas/array/MakeView.h" #include "atlas/field/FieldSet.h" #include "atlas/functionspace/NodeColumns.h" #include "atlas/functionspace/Spectral.h" #include "atlas/functionspace/StructuredColumns.h" #include "atlas/grid.h" #include "atlas/grid/Distribution.h" #include "atlas/grid/Partitioner.h" #include "atlas/grid/detail/partitioner/EqualRegionsPartitioner.h" #include "atlas/grid/detail/partitioner/TransPartitioner.h" #include "atlas/mesh/Mesh.h" #include "atlas/mesh/Nodes.h" #include "atlas/meshgenerator.h" #include "atlas/output/Gmsh.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/trans/LegendreCacheCreator.h" #include "atlas/trans/Trans.h" #include "atlas/trans/VorDivToUV.h" #include "atlas/trans/detail/TransFactory.h" #include "tests/AtlasTestEnvironment.h" #if ATLAS_HAVE_TRANS #include "atlas/library/config.h" #include "atlas/trans/ifs/TransIFS.h" #include "atlas/trans/ifs/TransIFSNodeColumns.h" #include "atlas/trans/ifs/TransIFSStructuredColumns.h" #if ATLAS_HAVE_ECTRANS #include "ectrans/transi.h" #else #include "transi/trans.h" #endif #endif using namespace eckit; using atlas::grid::detail::partitioner::EqualRegionsPartitioner; using atlas::grid::detail::partitioner::TransPartitioner; namespace atlas { namespace test { //----------------------------------------------------------------------------- struct AtlasTransEnvironment : public AtlasTestEnvironment { AtlasTransEnvironment(int argc, char* argv[]): AtlasTestEnvironment(argc, argv) { if (mpi::comm().size() == 1) { trans_use_mpi(false); } trans_init(); } ~AtlasTransEnvironment() { trans_finalize(); } }; //----------------------------------------------------------------------------- void read_rspecg(const trans::TransImpl& trans, std::vector& rspecg, std::vector& nfrom, int& nfld) { Log::info() << "read_rspecg ...\n"; nfld = 2; if (mpi::comm().rank() == 0) { rspecg.resize(nfld * trans.nb_spectral_coefficients_global()); for (size_t i = 0; i < trans.nb_spectral_coefficients_global(); ++i) { rspecg[i * nfld + 0] = (i == 0 ? 1. : 0.); // scalar field 1 rspecg[i * nfld + 1] = (i == 0 ? 2. : 0.); // scalar field 2 } } nfrom.resize(nfld); for (int jfld = 0; jfld < nfld; ++jfld) { nfrom[jfld] = 1; } Log::info() << "read_rspecg ... done" << std::endl; } //----------------------------------------------------------------------------- void read_rspecg(Field spec) { Log::info() << "read_rspecg ...\n"; if (mpi::comm().rank() == 0) { functionspace::Spectral funcspace = spec.functionspace(); int nb_spectral_coefficients_global = functionspace::Spectral(spec.functionspace()).nb_spectral_coefficients_global(); auto view = array::make_view(spec); ATLAS_ASSERT(view.shape(0) == nb_spectral_coefficients_global); ATLAS_ASSERT(view.shape(1) >= 2); for (int i = 0; i < nb_spectral_coefficients_global; ++i) { view(i, 0) = (i == 0 ? 1. : 0.); // scalar field 1 view(i, 1) = (i == 0 ? 2. : 0.); // scalar field 2 } } Log::info() << "read_rspecg ... done" << std::endl; } //----------------------------------------------------------------------------- CASE("test_trans_distribution_matches_atlas") { EXPECT(grid::Partitioner::exists("ectrans")); // Create grid and trans object Grid g("N80"); EXPECT(StructuredGrid(g).ny() == 160); auto trans_partitioner = new TransPartitioner(); grid::Partitioner partitioner(trans_partitioner); grid::Distribution distribution(g, partitioner); trans::TransIFS trans(g, 159); ::Trans_t* t = trans; ATLAS_DEBUG_VAR(trans.truncation()); EXPECT(trans.truncation() == 159); // -------------- do checks -------------- // EXPECT(t->nproc == int(mpi::comm().size())); EXPECT(t->myproc == int(mpi::comm().rank() + 1)); if (mpi::comm().rank() == 0) // all tasks do the same, so only one needs to check { #if 0 int max_nb_regions_EW(0); 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); #endif EXPECT(distribution.nb_partitions() == idx_t(mpi::comm().size())); EXPECT(idx_t(distribution.size()) == g.size()); std::vector npts(distribution.nb_partitions(), 0); 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())); #if 0 // array::LocalView n_regions ( trans.n_regions() ) ; for (int j = 0; j < trans_partitioner->nb_bands(); ++j) { EXPECT(t->n_regions[j] == trans_partitioner->nb_regions(j)); } #endif } } CASE("test_trans_options") { util::Config opts(option::fft("FFTW") | option::split_y(false) | option::read_legendre("readfile")); Log::info() << "trans_opts = " << opts << std::endl; } #ifdef TRANS_HAVE_IO CASE("test_write_read_cache") { Log::info() << "test_write_read_cache" << std::endl; using namespace trans; if (mpi::comm().size() == 1) { // Create trans that will write file Trans trans_write_F24(Grid("F24"), 23, option::write_legendre("cached_legendre_coeffs-F24") | option::flt(false)); Trans trans_write_N24(Grid("N24"), 23, option::write_legendre("cached_legendre_coeffs-N24") | option::flt(false)); Trans trans_write_O24(Grid("O24"), 23, option::write_legendre("cached_legendre_coeffs-O24") | option::flt(false)); // Create trans that will read from file Trans trans_read_F24(Grid("F24"), 23, option::read_legendre("cached_legendre_coeffs-F24") | option::flt(false)); Trans trans_read_N24(Grid("N24"), 23, option::read_legendre("cached_legendre_coeffs-N24") | option::flt(false)); Trans trans_read_O24(Grid("O24"), 23, option::read_legendre("cached_legendre_coeffs-O24") | option::flt(false)); LegendreCache legendre_cache_F24("cached_legendre_coeffs-F24"); LegendreCache legendre_cache_N24("cached_legendre_coeffs-N24"); LegendreCache legendre_cache_O24("cached_legendre_coeffs-O24"); Trans trans_cache_F24(legendre_cache_F24, Grid("F24"), 23, option::flt(false)); Trans trans_cache_N24(legendre_cache_N24, Grid("N24"), 23, option::flt(false)); Trans trans_cache_O24(legendre_cache_O24, Grid("O24"), 23, option::flt(false)); } } #endif CASE("test_distspec") { trans::TransIFS trans(Grid("F80"), 159); Log::info() << "Trans initialized" << std::endl; std::vector rspecg; std::vector nfrom; int nfld; Log::info() << "Read rspecg" << std::endl; read_rspecg(trans, rspecg, nfrom, nfld); std::vector rspec(nfld * trans.trans()->nspec2); std::vector nto(nfld, 1); std::vector rgp(nfld * trans.trans()->ngptot); std::vector rgpg(nfld * trans.grid().size()); std::vector specnorms(nfld, 0); trans.distspec(nfld, nfrom.data(), rspecg.data(), rspec.data()); trans.specnorm(nfld, rspec.data(), specnorms.data()); trans.invtrans(nfld, rspec.data(), rgp.data()); trans.gathgrid(nfld, nto.data(), rgp.data(), rgpg.data()); if (mpi::comm().rank() == 0) { ATLAS_DEBUG_VAR(specnorms[0]); ATLAS_DEBUG_VAR(specnorms[1]); EXPECT(eckit::types::is_approximately_equal(specnorms[0], 1., 1.e-10)); EXPECT(eckit::types::is_approximately_equal(specnorms[1], 2., 1.e-10)); } Log::info() << "end test_distspec" << std::endl; } CASE("test_distspec_speconly") { functionspace::Spectral fs(159); int nfld = 2; Field glb = fs.createField(option::global() | option::levels(nfld)); read_rspecg(glb); std::vector specnorms(nfld, 0); Field dist = fs.createField(glb); if (not dist.contiguous()) { EXPECT_THROWS_AS(fs.scatter(glb, dist), eckit::Exception); if (mpi::comm().size() == 1) { dist = glb; EXPECT_THROWS_AS(fs.norm(dist, specnorms), eckit::Exception); } return; } else { EXPECT_NO_THROW(fs.scatter(glb, dist)); EXPECT_NO_THROW(fs.norm(dist, specnorms)); } if (mpi::comm().rank() == 0) { ATLAS_DEBUG_VAR(specnorms[0]); ATLAS_DEBUG_VAR(specnorms[1]); EXPECT(eckit::types::is_approximately_equal(specnorms[0], 1., 1.e-10)); EXPECT(eckit::types::is_approximately_equal(specnorms[1], 2., 1.e-10)); } Log::info() << "end test_distspec_only" << std::endl; } CASE("test_distribution") { Grid g("O80"); Log::info() << "test_distribution" << std::endl; grid::Distribution d_trans = grid::Partitioner(new TransPartitioner()).partition(g); Log::info() << "trans distribution created" << std::endl; grid::Distribution d_eqreg = grid::Partitioner(new EqualRegionsPartitioner()).partition(g); Log::info() << "eqregions distribution created" << std::endl; if (mpi::comm().rank() == 0) { EXPECT(d_trans.nb_partitions() == d_eqreg.nb_partitions()); EXPECT(d_trans.max_pts() == d_eqreg.max_pts()); EXPECT(d_trans.min_pts() == d_eqreg.min_pts()); EXPECT(d_trans.nb_pts() == d_eqreg.nb_pts()); } } CASE("test_generate_mesh") { Log::info() << "test_generate_mesh" << std::endl; Grid g("O80"); StructuredMeshGenerator generate(atlas::util::Config("angle", 0)("triangulate", true)); Mesh m_default = generate(g); Log::info() << "trans_distribution" << std::endl; grid::Distribution trans_distribution = grid::Partitioner(new TransPartitioner()).partition(g); Mesh m_trans = generate(g, trans_distribution); Log::info() << "eqreg_distribution" << std::endl; grid::Distribution eqreg_distribution = grid::Partitioner(new EqualRegionsPartitioner()).partition(g); Mesh m_eqreg = generate(g, eqreg_distribution); array::ArrayView p_default = array::make_view(m_default.nodes().partition()); array::ArrayView p_trans = array::make_view(m_trans.nodes().partition()); array::ArrayView p_eqreg = array::make_view(m_eqreg.nodes().partition()); for (idx_t j = 0; j < p_default.shape(0); ++j) { EXPECT(p_default(j) == p_trans(j)); EXPECT(p_default(j) == p_eqreg(j)); } output::Gmsh("N16_trans.msh").write(m_trans); } CASE("test_spectral_fields") { Log::info() << "test_spectral_fields" << std::endl; Grid g("O48"); StructuredMeshGenerator generate(atlas::util::Config("angle", 0)("triangulate", false)); Mesh m = generate(g, grid::Partitioner("ectrans")); trans::Trans trans(g, 47); functionspace::NodeColumns nodal(m); functionspace::Spectral spectral(trans); Field spf = spectral.createField(option::name("spf")); Field gpf = nodal.createField(option::name("gpf")); array::make_view(gpf).assign(0); EXPECT_NO_THROW(trans.dirtrans(gpf, spf)); EXPECT_NO_THROW(trans.invtrans(spf, gpf)); FieldSet gpfields; gpfields.add(gpf); FieldSet spfields; spfields.add(spf); EXPECT_NO_THROW(trans.dirtrans(gpfields, spfields)); EXPECT_NO_THROW(trans.invtrans(spfields, gpfields)); gpfields.add(gpf); EXPECT_THROWS_AS(trans.dirtrans(gpfields, spfields), eckit::Exception); } CASE("test_nomesh") { Log::info() << "test_spectral_fields" << std::endl; Grid g("O48"); trans::Trans trans(g, 47); functionspace::Spectral spectral(trans.truncation()); functionspace::StructuredColumns gridpoints(g, grid::Partitioner("ectrans")); Field spfg = spectral.createField(option::name("spf") | option::global()); Field spf = spectral.createField(option::name("spf")); Field gpf = gridpoints.createField(option::name("gpf")); Field gpfg = gridpoints.createField(option::name("gpf") | option::global()); array::ArrayView spg = array::make_view(spfg); spectral.parallel_for(option::global(), [&](idx_t real, idx_t imag, int n, int m) { spg(real) = +m * spectral.truncation() + n; spg(imag) = (n == 0 ? 0 : -m * spectral.truncation() + n); }); if (not spf.contiguous()) { EXPECT_THROWS_AS(spectral.scatter(spfg, spf), eckit::Exception); if (mpi::comm().size() == 1) { spf = spfg; } else { return; } } else { EXPECT_NO_THROW(spectral.scatter(spfg, spf)); } array::ArrayView sp = array::make_view(spf); spectral.parallel_for([&](idx_t real, idx_t imag, int n, int m) { EXPECT_EQ(int(sp(real)), +m * spectral.truncation() + n); EXPECT_EQ(int(sp(imag)), (n == 0 ? 0 : -m * spectral.truncation() + n)); sp(real) = (n == 0 ? 4. : 0.); sp(imag) = 0.; }); EXPECT_NO_THROW(trans.invtrans(spf, gpf)); EXPECT_NO_THROW(gridpoints.gather(gpf, gpfg)); if (mpi::comm().rank() == 0) { array::ArrayView gpg = array::make_view(gpfg); for (idx_t jp = 0; jp < gpg.size(); ++jp) { EXPECT(is_approximately_equal(gpg(jp), 4., 0.001)); Log::debug() << "gpg(" << jp << ") : " << gpg(jp) << std::endl; } } EXPECT_NO_THROW(gridpoints.scatter(gpfg, gpf)); EXPECT_NO_THROW(trans.dirtrans(gpf, spf)); if (not spf.contiguous()) { EXPECT_THROWS_AS(spectral.gather(spf, spfg), eckit::Exception); if (mpi::comm().size() == 1) { spfg = spf; } else { return; } } else { EXPECT_NO_THROW(spectral.gather(spf, spfg)); } spectral.parallel_for(option::global(), [&](idx_t real, idx_t imag, int n) { EXPECT(is_approximately_equal(spg(real), (n == 0 ? 4. : 0.), 0.001)); EXPECT(is_approximately_equal(spg(imag), 0., 0.001)); }); } CASE("test_trans_factory") { Log::info() << "test_trans_factory" << std::endl; trans::TransFactory::list(Log::info()); Log::info() << std::endl; functionspace::StructuredColumns gp(Grid("O48"), grid::Partitioner("ectrans")); functionspace::Spectral sp(47); trans::Trans trans1 = trans::Trans(gp, sp); EXPECT(bool(trans1) == true); trans::Trans trans2 = trans::Trans(Grid("O48"), 47); EXPECT(bool(trans2) == true); } CASE("test_trans_using_grid") { Log::info() << "test_trans_using_grid" << std::endl; trans::Trans trans(Grid("O48"), 47); functionspace::StructuredColumns gp(trans.grid(), grid::Partitioner("ectrans")); functionspace::Spectral sp(trans.truncation()); Field spf = sp.createField(option::name("spf")); Field gpf = gp.createField(option::name("gpf")); array::make_view(gpf).assign(0); EXPECT_NO_THROW(trans.dirtrans(gpf, spf)); EXPECT_NO_THROW(trans.invtrans(spf, gpf)); FieldSet gpfields; gpfields.add(gpf); FieldSet spfields; spfields.add(spf); EXPECT_NO_THROW(trans.dirtrans(gpfields, spfields)); EXPECT_NO_THROW(trans.invtrans(spfields, gpfields)); gpfields.add(gpf); EXPECT_THROWS_AS(trans.dirtrans(gpfields, spfields), eckit::Exception); } CASE("test_trans_using_functionspace_NodeColumns") { Log::info() << "test_trans_using_functionspace_NodeColumns" << std::endl; functionspace::NodeColumns gp(MeshGenerator("structured").generate(Grid("O48"), grid::Partitioner("ectrans"))); functionspace::Spectral sp(47); trans::Trans trans(gp, sp); Field spf = sp.createField(option::name("spf")); Field gpf = gp.createField(option::name("gpf")); array::make_view(gpf).assign(0); EXPECT_NO_THROW(trans.dirtrans(gpf, spf)); EXPECT_NO_THROW(trans.invtrans(spf, gpf)); FieldSet gpfields; gpfields.add(gpf); FieldSet spfields; spfields.add(spf); EXPECT_NO_THROW(trans.dirtrans(gpfields, spfields)); EXPECT_NO_THROW(trans.invtrans(spfields, gpfields)); gpfields.add(gpf); EXPECT_THROWS_AS(trans.dirtrans(gpfields, spfields), eckit::Exception); } CASE("test_trans_using_functionspace_StructuredColumns") { Log::info() << "test_trans_using_functionspace_StructuredColumns" << std::endl; functionspace::StructuredColumns gp(Grid("O48"), grid::Partitioner("ectrans")); functionspace::Spectral sp(47); trans::Trans trans(gp, sp); Field spf = sp.createField(option::name("spf")); Field gpf = gp.createField(option::name("gpf")); array::make_view(gpf).assign(0); EXPECT_NO_THROW(trans.dirtrans(gpf, spf)); EXPECT_NO_THROW(trans.invtrans(spf, gpf)); FieldSet gpfields; gpfields.add(gpf); FieldSet spfields; spfields.add(spf); EXPECT_NO_THROW(trans.dirtrans(gpfields, spfields)); EXPECT_NO_THROW(trans.invtrans(spfields, gpfields)); gpfields.add(gpf); EXPECT_THROWS_AS(trans.dirtrans(gpfields, spfields), eckit::Exception); } CASE("test_trans_MIR_lonlat") { Log::info() << "test_trans_MIR_lonlat" << std::endl; Grid grid("L48"); trans::Trans trans(grid, 47); // global fields std::vector spf(trans.spectralCoefficients(), 0.); std::vector gpf(grid.size()); if (mpi::comm().size() == 1) { EXPECT_NO_THROW(trans.invtrans(1, spf.data(), gpf.data(), option::global())); EXPECT_NO_THROW(trans.dirtrans(1, gpf.data(), spf.data(), option::global())); } } CASE("test_trans_VorDivToUV") { int nfld = 1; // TODO: test for nfld>1 std::vector truncation_array{1}; // truncation_array{159,160,1279}; for (size_t i = 0; i < truncation_array.size(); ++i) { int truncation = truncation_array[i]; int nspec2 = (truncation + 1) * (truncation + 2); Log::info() << "truncation = " << truncation << std::endl; std::vector field_vor(nfld * nspec2, 0.); std::vector field_div(nfld * nspec2, 0.); // 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++) { Log::info() << field_vor[j] << " "; } Log::info() << std::endl; // With IFS if (trans::VorDivToUVFactory::has("ectrans")) { trans::VorDivToUV vordiv_to_UV(truncation, option::type("ectrans")); EXPECT(vordiv_to_UV.truncation() == truncation); std::vector field_U(nfld * nspec2); std::vector field_V(nfld * nspec2); vordiv_to_UV.execute(nspec2, nfld, field_vor.data(), field_div.data(), field_U.data(), field_V.data()); // TODO: do some meaningful checks Log::info() << "Trans library" << std::endl; Log::info() << "U: " << std::endl; for (int j = 0; j < nfld * nspec2; j++) { Log::info() << field_U[j] << " "; } Log::info() << std::endl; } // With Local { trans::VorDivToUV vordiv_to_UV(truncation, option::type("local")); EXPECT(vordiv_to_UV.truncation() == truncation); std::vector field_U(nfld * nspec2); std::vector field_V(nfld * nspec2); vordiv_to_UV.execute(nspec2, nfld, field_vor.data(), field_div.data(), field_U.data(), field_V.data()); // TODO: do some meaningful checks Log::info() << "Local transform" << std::endl; Log::info() << "U: " << std::endl; for (int j = 0; j < nfld * nspec2; j++) { Log::info() << field_U[j] << " "; } Log::info() << std::endl; } } } #ifdef TRANS_HAVE_IO CASE("ATLAS-256: Legendre coefficient expected unique identifiers") { if (mpi::comm().size() == 1) { util::Config options; options.set(option::type("ectrans")); options.set("flt", false); auto uids = { // clang-format off "ifs-T20-RegularGaussianN320-OPT4189816c2e", "ifs-T20-RegularGaussianN640-OPT4189816c2e", "ifs-T20-RegularGaussianN1280-OPT4189816c2e", "ifs-T20-ReducedGaussianN320-PL52955330f8-OPT4189816c2e", "ifs-T20-ReducedGaussianN640-PL296ba3f6fb-OPT4189816c2e", "ifs-T20-ReducedGaussianN1280-PL9e14f63837-OPT4189816c2e", "ifs-T20-ReducedGaussianN320-PL440b952c43-OPT4189816c2e", "ifs-T20-ReducedGaussianN640-PLcbba2659c1-OPT4189816c2e", "ifs-T20-ReducedGaussianN1280-PL8eadc35e89-OPT4189816c2e", "ifs-T20-L360x181-OPT4189816c2e", "ifs-T20-L3600x1801-OPT4189816c2e", "ifs-T639-RegularGaussianN320-OPT4189816c2e", "ifs-T639-RegularGaussianN640-OPT4189816c2e", "ifs-T639-RegularGaussianN1280-OPT4189816c2e", "ifs-T639-ReducedGaussianN320-PL52955330f8-OPT4189816c2e", "ifs-T639-ReducedGaussianN640-PL296ba3f6fb-OPT4189816c2e", "ifs-T639-ReducedGaussianN1280-PL9e14f63837-OPT4189816c2e", "ifs-T639-ReducedGaussianN320-PL440b952c43-OPT4189816c2e", "ifs-T639-ReducedGaussianN640-PLcbba2659c1-OPT4189816c2e", "ifs-T639-ReducedGaussianN1280-PL8eadc35e89-OPT4189816c2e", "ifs-T639-L360x181-OPT4189816c2e", "ifs-T639-L3600x1801-OPT4189816c2e", "ifs-T1279-RegularGaussianN320-OPT4189816c2e", "ifs-T1279-RegularGaussianN640-OPT4189816c2e", "ifs-T1279-RegularGaussianN1280-OPT4189816c2e", "ifs-T1279-ReducedGaussianN320-PL52955330f8-OPT4189816c2e", "ifs-T1279-ReducedGaussianN640-PL296ba3f6fb-OPT4189816c2e", "ifs-T1279-ReducedGaussianN1280-PL9e14f63837-OPT4189816c2e", "ifs-T1279-ReducedGaussianN320-PL440b952c43-OPT4189816c2e", "ifs-T1279-ReducedGaussianN640-PLcbba2659c1-OPT4189816c2e", "ifs-T1279-ReducedGaussianN1280-PL8eadc35e89-OPT4189816c2e", "ifs-T1279-L360x181-OPT4189816c2e", "ifs-T1279-L3600x1801-OPT4189816c2e", "ifs-T20-grid-800ac12540-OPT4189816c2e", "ifs-T20-grid-0915e0f040-OPT4189816c2e", "ifs-T20-grid-7c400822f0-OPT4189816c2e", "ifs-T20-grid-800ac12540-OPT4189816c2e", "ifs-T20-grid-0915e0f040-OPT4189816c2e", "ifs-T20-grid-7c400822f0-OPT4189816c2e", "ifs-T20-grid-800ac12540-OPT4189816c2e", "ifs-T20-grid-0915e0f040-OPT4189816c2e", "ifs-T20-grid-7c400822f0-OPT4189816c2e", "ifs-T20-grid-7824deccdf-OPT4189816c2e", "ifs-T20-grid-7d1771559e-OPT4189816c2e", "ifs-T639-grid-800ac12540-OPT4189816c2e", "ifs-T639-grid-0915e0f040-OPT4189816c2e", "ifs-T639-grid-7c400822f0-OPT4189816c2e", "ifs-T639-grid-800ac12540-OPT4189816c2e", "ifs-T639-grid-0915e0f040-OPT4189816c2e", "ifs-T639-grid-7c400822f0-OPT4189816c2e", "ifs-T639-grid-800ac12540-OPT4189816c2e", "ifs-T639-grid-0915e0f040-OPT4189816c2e", "ifs-T639-grid-7c400822f0-OPT4189816c2e", "ifs-T639-grid-7824deccdf-OPT4189816c2e", "ifs-T639-grid-7d1771559e-OPT4189816c2e", "ifs-T1279-grid-800ac12540-OPT4189816c2e", "ifs-T1279-grid-0915e0f040-OPT4189816c2e", "ifs-T1279-grid-7c400822f0-OPT4189816c2e", "ifs-T1279-grid-800ac12540-OPT4189816c2e", "ifs-T1279-grid-0915e0f040-OPT4189816c2e", "ifs-T1279-grid-7c400822f0-OPT4189816c2e", "ifs-T1279-grid-800ac12540-OPT4189816c2e", "ifs-T1279-grid-0915e0f040-OPT4189816c2e", "ifs-T1279-grid-7c400822f0-OPT4189816c2e", "ifs-T1279-grid-7824deccdf-OPT4189816c2e", "ifs-T1279-grid-7d1771559e-OPT4189816c2e", // clang-format on }; auto uid = uids.begin(); auto domains = std::vector{GlobalDomain(), RectangularDomain({-10, 10}, {-20, 20})}; auto spectral_T = std::vector{20, 639, 1279}; auto grids = std::vector{"F320", "F640", "F1280", "N320", "N640", "N1280", "O320", "O640", "O1280", "L90", "L900"}; for (auto& domain : domains) { for (int T : spectral_T) { for (auto name : grids) { Log::info() << "Case name:" << name << ", T:" << T << ", domain:" << domain << ", UID:'" << *uid << "'" << std::endl; Grid grid(name, domain); auto test = trans::LegendreCacheCreator(grid, T, options).uid(); ATLAS_DEBUG_VAR(test); EXPECT(test == *uid); uid++; } } } } } #endif //----------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/trans/test_trans_invtrans_grad.cc0000664000175000017500000001535315160212070023570 0ustar alastairalastair/* * (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/array.h" #include "atlas/field/FieldSet.h" #include "atlas/functionspace/NodeColumns.h" #include "atlas/functionspace/Spectral.h" #include "atlas/functionspace/StructuredColumns.h" #include "atlas/grid/Distribution.h" #include "atlas/grid/detail/partitioner/EqualRegionsPartitioner.h" #include "atlas/grid/detail/partitioner/TransPartitioner.h" #include "atlas/mesh/Mesh.h" #include "atlas/mesh/Nodes.h" #include "atlas/meshgenerator.h" #include "atlas/option.h" #include "atlas/output/Gmsh.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/trans/Trans.h" #include "atlas/util/CoordinateEnums.h" #include "atlas/util/Earth.h" #include "tests/AtlasTestEnvironment.h" #if ATLAS_HAVE_TRANS #include "atlas/library/config.h" #include "atlas/trans/ifs/TransIFS.h" #if ATLAS_HAVE_ECTRANS #include "ectrans/transi.h" #else #include "transi/trans.h" #endif #endif namespace atlas { namespace test { //----------------------------------------------------------------------------- struct AtlasTransEnvironment : public AtlasTestEnvironment { AtlasTransEnvironment(int argc, char* argv[]): AtlasTestEnvironment(argc, argv) { if (mpi::comm().size() == 1) { trans_use_mpi(false); } trans_init(); } ~AtlasTransEnvironment() { trans_finalize(); } }; /// @brief Compute magnitude of flow with rotation-angle beta /// (beta=0 --> zonal, beta=pi/2 --> meridional) static void rotated_flow_magnitude(StructuredGrid& grid, double var[], const double& beta) { const double radius = util::Earth::radius(); const double USCAL = 20.; const double pvel = USCAL / radius; const double deg2rad = M_PI / 180.; idx_t n(0); for (idx_t jlat = 0; jlat < grid.ny(); ++jlat) { for (idx_t jlon = 0; jlon < grid.nx(jlat); ++jlon) { const double x = grid.x(jlon, jlat) * deg2rad; const double y = grid.y(jlat) * deg2rad; const double Ux = pvel * (std::cos(beta) + std::tan(y) * std::cos(x) * std::sin(beta)) * radius * std::cos(y); const double Uy = -pvel * std::sin(x) * std::sin(beta) * radius; var[n] = std::sqrt(Ux * Ux + Uy * Uy); ++n; } } } /// @brief Compute magnitude of flow with rotation-angle beta /// (beta=0 --> zonal, beta=pi/2 --> meridional) void rotated_flow_magnitude(const functionspace::NodeColumns& fs, Field& field, const double& beta) { const double radius = util::Earth::radius(); const double USCAL = 20.; const double pvel = USCAL / radius; const double deg2rad = M_PI / 180.; array::ArrayView lonlat_deg = array::make_view(fs.nodes().lonlat()); array::ArrayView var = array::make_view(field); size_t nnodes = fs.nodes().size(); for (size_t jnode = 0; jnode < nnodes; ++jnode) { double x = lonlat_deg(jnode, (size_t)LON) * deg2rad; double y = lonlat_deg(jnode, (size_t)LAT) * deg2rad; double Ux = pvel * (std::cos(beta) + std::tan(y) * std::cos(x) * std::sin(beta)) * radius * std::cos(y); double Uy = -pvel * std::sin(x) * std::sin(beta) * radius; var(jnode) = std::sqrt(Ux * Ux + Uy * Uy); } } //----------------------------------------------------------------------------- CASE("test_invtrans_ifsStyle") { std::string grid_uid("O80"); StructuredGrid g(grid_uid); long N = g.ny() / 2; trans::TransIFS trans(g, 2 * N - 1); Log::info() << "Trans initialized" << std::endl; std::vector rspecg; int nfld = 1; std::vector init_gpg(trans.grid().size()); std::vector init_gp(trans.trans()->ngptot); std::vector init_sp(trans.trans()->nspec2); std::vector nfrom(nfld, 1); if (mpi::comm().rank() == 0) { double beta = M_PI * 0.5; rotated_flow_magnitude(g, init_gpg.data(), beta); } trans.distgrid(nfld, nfrom.data(), init_gpg.data(), init_gp.data()); trans.dirtrans(nfld, init_gp.data(), init_sp.data()); std::vector rgp(3 * nfld * trans.trans()->ngptot); double *no_vorticity(nullptr), *no_divergence(nullptr); int nb_vordiv(0); int nb_scalar(nfld); trans.invtrans(nb_scalar, init_sp.data(), nb_vordiv, no_vorticity, no_divergence, rgp.data(), option::scalar_derivatives(true)); std::vector nto(nfld, 1); std::vector rgpg(3 * nfld * trans.grid().size()); trans.gathgrid(nfld, nto.data(), rgp.data(), rgpg.data()); // Output { Mesh mesh = StructuredMeshGenerator().generate(g); functionspace::StructuredColumns gp(g); output::Gmsh gmsh(grid_uid + "-grid.msh"); Field scalar("scalar", rgp.data(), array::make_shape(gp.size())); Field scalar_dNS("scalar_dNS", rgp.data() + nfld * gp.size(), array::make_shape(gp.size())); Field scalar_dEW("scalar_dEW", rgp.data() + 2 * nfld * gp.size(), array::make_shape(gp.size())); gmsh.write(mesh); gmsh.write(scalar, gp); gmsh.write(scalar_dEW, gp); gmsh.write(scalar_dNS, gp); } } CASE("test_invtrans_grad") { std::string grid_uid("O48"); StructuredGrid g(grid_uid); Mesh mesh = StructuredMeshGenerator().generate(g); idx_t N = g.ny() / 2; trans::Trans trans(g, 2 * N - 1); functionspace::NodeColumns gp(mesh); functionspace::Spectral sp(trans); Field scalar_sp = sp.createField(option::name("scalar_sp")); Field scalar = gp.createField(option::name("scalar")); Field grad = gp.createField(option::name("grad") | option::variables(2)); // Initial condition double beta = M_PI * 0.5; rotated_flow_magnitude(gp, scalar, beta); // Transform to spectral trans.dirtrans(scalar, scalar_sp); // Inverse transform for gradient trans.invtrans_grad(scalar_sp, grad); gp.haloExchange(grad); // Output { Mesh mesh = StructuredMeshGenerator().generate(g); functionspace::StructuredColumns gp(g); output::Gmsh gmsh(grid_uid + "-nodes.msh"); gmsh.write(mesh); gmsh.write(scalar, gp); gmsh.write(grad, gp); } } //----------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/trans/fctest_trans_invtrans_grad.F900000664000175000017500000001023315160212070024042 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. ! This File contains Unit Tests for testing the ! C++ / Fortran Interfaces to the Mesh Datastructure ! @author Willem Deconinck #include "fckit/fctest.h" ! ----------------------------------------------------------------------------- module fctest_atlas_trans_igrad_fixture use atlas_module use iso_c_binding implicit none character(len=1024) :: msg real(c_double), parameter :: pi = 2.0_c_double*asin(1.0_c_double) real(c_double), parameter :: deg2rad = pi/180._c_double contains ! @brief Compute magnitude of flow with rotation-angle beta ! (beta=0 --> zonal, beta=pi/2 --> meridional) subroutine rotated_flow_magnitude(fs,field,beta) use, intrinsic :: iso_c_binding type(atlas_functionspace_NodeColumns) :: fs type(atlas_Field) :: field real(c_double) :: beta real(c_double) :: radius real(c_double) :: USCAL real(c_double) :: pvel real(c_double) :: pi real(c_double) :: deg2rad real(c_double), pointer :: lonlat_deg(:,:) real(c_double), pointer :: var(:) type(atlas_mesh_Nodes) :: nodes type(atlas_Field) :: lonlat integer :: jnode real(c_double) :: x,y, Ux, Uy radius = 6371229._c_double USCAL = 20._c_double pvel = USCAL/radius pi = 2.0_c_double*asin(1.0_c_double) deg2rad = pi/180._c_double nodes = fs%nodes() lonlat = nodes%lonlat() call lonlat%data(lonlat_deg) call field%data(var) do jnode = 1, nodes%size() x = lonlat_deg(1,jnode) * deg2rad y = lonlat_deg(2,jnode) * deg2rad Ux = pvel*(cos(beta)+tan(y)*cos(x)*sin(beta))*radius*cos(y) Uy = -pvel*sin(x)*sin(beta)*radius var(jnode) = sqrt(Ux*Ux+Uy*Uy) enddo call lonlat%final() call nodes%final() end subroutine end module ! ----------------------------------------------------------------------------- TESTSUITE_WITH_FIXTURE(fctest_atlas_trans_igrad,fctest_atlas_trans_igrad_fixture) ! ----------------------------------------------------------------------------- TESTSUITE_INIT call atlas_library%initialise() END_TESTSUITE_INIT ! ----------------------------------------------------------------------------- TESTSUITE_FINALIZE write(0,*) "FINALIZE" call atlas_library%finalise() END_TESTSUITE_FINALIZE ! ----------------------------------------------------------------------------- TEST( test_trans_invtrans_grad ) type(atlas_GaussianGrid) :: grid type(atlas_MeshGenerator) :: meshgenerator type(atlas_Mesh) :: mesh type(atlas_Trans) :: trans type(atlas_mesh_Nodes) :: nodes type(atlas_functionspace_NodeColumns) :: nodes_fs type(atlas_functionspace_Spectral) :: spectral_fs type(atlas_Field) :: scalar type(atlas_Field) :: scalar_sp type(atlas_Field) :: grad type(atlas_Output) :: gmsh real(c_double) :: beta grid = atlas_GaussianGrid("O32") meshgenerator = atlas_MeshGenerator() mesh = meshgenerator%generate(grid) call meshgenerator%final() trans = atlas_Trans(grid, int(grid%N()-1) ) nodes_fs = atlas_functionspace_NodeColumns(mesh,0) spectral_fs = atlas_functionspace_Spectral(trans%truncation()) scalar = nodes_fs%create_field(name="scalar",kind=atlas_real(c_double)) grad = nodes_fs%create_field(name="grad",kind=atlas_real(c_double),variables=2) scalar_sp = spectral_fs%create_field(name="spectral_sp",kind=atlas_real(c_double)) beta = pi*0.5; call rotated_flow_magnitude(nodes_fs,scalar,beta) call trans%dirtrans(scalar,scalar_sp) call trans%invtrans_grad(scalar_sp,grad); call nodes_fs%halo_exchange(grad); ! output gmsh = atlas_output_Gmsh("O32-nodes-fort.msh") call gmsh%write(mesh) call gmsh%write(scalar) call gmsh%write(grad) ! cleanup call gmsh%final() call nodes_fs%final() call spectral_fs%final() call scalar%final() call scalar_sp%final() call grad%final() call nodes%final() call mesh%final() call trans%final() call grid%final() END_TEST END_TESTSUITE atlas-0.46.0/src/tests/trans/test_transgeneral.cc0000664000175000017500000025120715160212070022205 0ustar alastairalastair/* * (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/MakeView.h" #include "atlas/field/FieldSet.h" #include "atlas/functionspace/NodeColumns.h" #include "atlas/functionspace/Spectral.h" #include "atlas/functionspace/StructuredColumns.h" #include "atlas/grid.h" #include "atlas/grid/Distribution.h" #include "atlas/grid/Partitioner.h" #include "atlas/grid/detail/partitioner/EqualRegionsPartitioner.h" #include "atlas/grid/detail/partitioner/TransPartitioner.h" #include "atlas/mesh/Mesh.h" #include "atlas/mesh/Nodes.h" #include "atlas/meshgenerator.h" #include "atlas/output/Gmsh.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/runtime/Trace.h" #include "atlas/trans/Trans.h" #include "atlas/trans/ifs/TransIFS.h" #include "atlas/trans/local/TransLocal.h" #include "atlas/util/Constants.h" #include "atlas/util/Earth.h" #include "tests/AtlasTestEnvironment.h" #if ATLAS_HAVE_TRANS #include "atlas/library/config.h" #if ATLAS_HAVE_ECTRANS #include "ectrans/transi.h" #else #include "transi/trans.h" #endif #endif using namespace eckit; using atlas::array::Array; using atlas::array::ArrayView; using atlas::array::make_view; namespace atlas { namespace test { //----------------------------------------------------------------------------- struct AtlasTransEnvironment : public AtlasTestEnvironment { AtlasTransEnvironment(int argc, char* argv[]): AtlasTestEnvironment(argc, argv) { #if ATLAS_HAVE_TRANS trans_use_mpi(mpi::comm().size() > 1); trans_init(); #endif } #if ATLAS_HAVE_TRANS ~AtlasTransEnvironment() { trans_finalize(); } #endif }; //----------------------------------------------------------------------------- // Routine to compute the spherical harmonics analytically at one point // (up to wave number 3) // // Author: // Andreas Mueller *ECMWF* // double sphericalharmonics_analytic_point( const int n, // total wave number (implemented so far for n<4 const int m, // zonal wave number (implemented so far for m<4, m 0) { rft *= 2.; // the famous factor 2 that noone really understands } if (imag == 0) { rft *= loncos; } else { rft *= -lonsin; } // Legendre part of the spherical harmonics (following // http://mathworld.wolfram.com/SphericalHarmonic.html // multiplied with -2*sqrt(pi) due to different normalization and different // coordinates): // (can also be computed on http://www.wolframalpha.com with: // LegendreP[n, m, x]/Sqrt[1/2*Integrate[LegendreP[n, m, y]^2, {y, -1, 1}]]) // n, m need to be replaced by hand with the correct values // (otherwise the command will be too long for the free version of // wolframalpha) // scalar: if (ivar_in == 2) { if (ivar_out == 2) { if (m == 0 && n == 0) { return rft; } if (m == 0 && n == 1) { return std::sqrt(3.) * latsin * rft; } if (m == 0 && n == 2) { return std::sqrt(5.) / 2. * (3. * latsin * latsin - 1.) * rft; // sign? } if (m == 0 && n == 3) { return std::sqrt(7.) / 2. * (5. * latsin * latsin - 3.) * latsin * rft; // sign? } if (m == 1 && n == 1) { return std::sqrt(3. / 2.) * latcos * rft; // sign? } if (m == 1 && n == 2) { return std::sqrt(15. / 2.) * latsin * latcos * rft; // sign? } if (m == 1 && n == 3) { return std::sqrt(21.) / 4. * latcos * (5. * latsin * latsin - 1.) * rft; // sign? } if (m == 2 && n == 2) { return std::sqrt(15. / 2.) / 2. * latcos * latcos * rft; } if (m == 2 && n == 3) { return std::sqrt(105. / 2.) / 2. * latcos * latcos * latsin * rft; } if (m == 3 && n == 3) { return std::sqrt(35.) / 4. * latcos * latcos * latcos * rft; // sign? } if (m == 4 && n == 4) { return (3 * std::sqrt(17.5) * std::pow(latcos, 4)) / 8. * rft; } if (m == 5 && n == 5) { return (3 * std::sqrt(77) * std::pow(latcos, 5)) / 16. * rft; } if (m == 6 && n == 6) { return (std::sqrt(3003) * std::pow(latcos, 6)) / 32. * rft; } if (m == 7 && n == 7) { return (3 * std::sqrt(357.5) * std::pow(latcos, 7)) / 32. * rft; } if (m == 8 && n == 8) { return (3 * std::sqrt(6077.5) * std::pow(latcos, 8)) / 128. * rft; } if (m == 9 && n == 9) { return (std::sqrt(230945) * std::pow(latcos, 9)) / 256. * rft; } if (m == 10 && n == 10) { return (std::sqrt(969969) * std::pow(latcos, 10)) / 512. * rft; } if (m == 11 && n == 11) { return (std::sqrt(1.0140585e6) * std::pow(latcos, 11)) / 512. * rft; } if (m == 12 && n == 12) { return (5 * std::sqrt(676039) * std::pow(latcos, 12)) / 2048. * rft; } if (m == 13 && n == 13) { return (15 * std::sqrt(78004.5) * std::pow(latcos, 13)) / 2048. * rft; } if (m == 14 && n == 14) { return (15 * std::sqrt(323161.5) * std::pow(latcos, 14)) / 4096. * rft; } if (m == 15 && n == 15) { return (3 * std::sqrt(33393355) * std::pow(latcos, 15)) / 8192. * rft; } if (m == 16 && n == 16) { return (3 * std::sqrt(5.509903575e8) * std::pow(latcos, 16)) / 32768. * rft; } if (m == 17 && n == 17) { return (15 * std::sqrt(90751353) * std::pow(latcos, 17)) / 65536. * rft; } if (m == 18 && n == 18) { return (5 * std::sqrt(3357800061) * std::pow(latcos, 18)) / 131072. * rft; } if (m == 19 && n == 19) { return (15 * std::sqrt(3.829070245e8) * std::pow(latcos, 19)) / 131072. * rft; } if (m == 20 && n == 20) { return (3 * std::sqrt(156991880045) * std::pow(latcos, 20)) / 524288. * rft; } if (m == 21 && n == 21) { return (std::sqrt(1.4465680375575e12) * std::pow(latcos, 21)) / 524288. * rft; } if (m == 22 && n == 22) { return (15 * std::sqrt(2.63012370465e10) * std::pow(latcos, 22)) / 1.048576e6 * rft; } if (m == 23 && n == 23) { return (15 * std::sqrt(107492012277) * std::pow(latcos, 23)) / 2.097152e6 * rft; } if (m == 24 && n == 24) { return (105 * std::sqrt(35830670759) * std::pow(latcos, 24)) / 8.388608e6 * rft; } if (m == 25 && n == 25) { return (21 * std::sqrt(9.136821043545e11) * std::pow(latcos, 25)) / 8.388608e6 * rft; } if (m == 26 && n == 26) { return (21 * std::sqrt(3.7250116562145e12) * std::pow(latcos, 26)) / 1.6777216e7 * rft; } if (m == 27 && n == 27) { return (7 * std::sqrt(136583760727865.) * std::pow(latcos, 27)) / 3.3554432e7 * rft; } if (m == 28 && n == 28) { return (std::sqrt(2.7248460265209068e16) * std::pow(latcos, 28)) / 6.7108864e7 * rft; } if (m == 29 && n == 29) { return (std::sqrt(110873045217057585.) * std::pow(latcos, 29)) / 1.34217728e8 * rft; } if (m == 30 && n == 30) { return (std::sqrt(450883717216034179.) * std::pow(latcos, 30)) / 2.68435456e8 * rft; } if (m == 31 && n == 31) { return (21 * std::sqrt(1.0389025742304935e15) * std::pow(latcos, 31)) / 2.68435456e8 * rft; } if (m == 32 && n == 32) { return (21 * std::sqrt(6.752866732498208e16) * std::pow(latcos, 32)) / 2.147483648e9 * rft; } if (m == 33 && n == 33) { return (7 * std::sqrt(2467865842240254105.) * std::pow(latcos, 33)) / 4.294967296e9 * rft; } if (m == 34 && n == 34) { return (21 * std::sqrt(1112959105324036165.) * std::pow(latcos, 34)) / 8.589934592e9 * rft; } if (m == 35 && n == 35) { return (3 * std::sqrt(5.53140675346046e19) * std::pow(latcos, 35)) / 8.589934592e9 * rft; } if (m == 36 && n == 36) { return (std::sqrt(8075853860052271220473.) * std::pow(latcos, 36)) / 3.4359738368e10 * rft; } if (m == 37 && n == 37) { return (5 * std::sqrt(3.2739948081292994e20) * std::pow(latcos, 37)) / 3.4359738368e10 * rft; } if (m == 38 && n == 38) { return (35 * std::sqrt(2.707815254843781e19) * std::pow(latcos, 38)) / 6.8719476736e10 * rft; } if (m == 39 && n == 39) { return (35 * std::sqrt(109701233401363445369.) * std::pow(latcos, 39)) / 1.37438953472e11 * rft; } if (m == 40 && n == 40) { return (63 * std::sqrt(548506167006817226845.) * std::pow(latcos, 40)) / 5.49755813888e11 * rft; } if (m == 41 && n == 41) { return (63 * std::sqrt(5.551952666044613e20) * std::pow(latcos, 41)) / 5.49755813888e11 * rft; } if (m == 42 && n == 42) { return (15 * std::sqrt(3.964094203555854e22) * std::pow(latcos, 42)) / 1.099511627776e12 * rft; } if (m == 43 && n == 43) { return (45 * std::sqrt(17823059209786010066579.) * std::pow(latcos, 43)) / 2.199023255552e12 * rft; } if (m == 44 && n == 44) { return (45 * std::sqrt(7.210237589413431e22) * std::pow(latcos, 44)) / 4.398046511104e12 * rft; } if (m == 45 && n == 45) { return (21 * std::sqrt(1339044123748208678378695.) * std::pow(latcos, 45)) / 8.796093022208e12 * rft; } // return // std::pow(latcos,45)*rft*21.*std::sqrt(1339044123748208678378695.)/8796093022208.; // // sign? } else { return 0.; } } // for the remainder the factor 2 from rft is already included in the // formulas: // vorticity: if (ivar_in == 0) { if (ivar_out == 0) { // u: if (m == 0 && n == 0) { return 0.; } if (m == 0 && n == 1) { if (imag == 0) { return std::sqrt(3.) * a / 2. * latcos; } else { return 0.; } } if (m == 1 && n == 1) { if (imag == 0) { return -a * std::sqrt(3. / 2.) * loncos * latsin; } else { return a * std::sqrt(3. / 2.) * lonsin * latsin; } } } else if (ivar_out == 1) { // v: if (m == 0 && n == 0) { return 0.; } if (m == 0 && n == 1) { return 0.; } if (m == 1 && n == 1) { if (imag == 0) { return a * std::sqrt(3. / 2.) * lonsin; } else { return a * std::sqrt(3. / 2.) * loncos; } } } else { return 0.; } } // divergence: if (ivar_in == 1) { if (ivar_out == 0) { // u: if (m == 0 && n == 0) { return 0.; } if (m == 0 && n == 1) { return 0.; } if (m == 1 && n == 1) { if (imag == 0) { return a * std::sqrt(3. / 2.) * lonsin; } else { return a * std::sqrt(3. / 2.) * loncos; } } } else if (ivar_out == 1) { // v: if (m == 0 && n == 0) { return 0.; } if (m == 0 && n == 1) { if (imag == 0) { return -std::sqrt(3.) * a / 2. * latcos; } else { return 0.; } } if (m == 1 && n == 1) { if (imag == 0) { return a * std::sqrt(3. / 2.) * loncos * latsin; } else { return -a * std::sqrt(3. / 2.) * lonsin * latsin; // sign? } } } else { return 0.; } } return -1.; } //----------------------------------------------------------------------------- // Routine to compute the spherical harmonics analytically on a grid // (up to wave number 3) // // Author: // Andreas Mueller *ECMWF* // void spectral_transform_grid_analytic(const int trc, // truncation (in) bool trcFT, // truncation for Fourier transformation (in) const int n, // total wave number (implemented so far for n<4) const int m, // zonal wave number (implemented so far for m<4, m g.y(0)) { jlatMin++; } } } int idx = 0; for (idx_t j = 0; j < g.ny(); ++j) { double lat = g.y(j) * util::Constants::degreesToRadians(); int ftrc = trc + 1; if (trcFT) { ftrc = trans::fourier_truncation(trc, gs_global.nx(jlatMin + j), gs_global.nxmax(), gs_global.ny(), lat, RegularGrid(gs_global)); } /*Log::info() << "j=" << j << " ftrc=" << ftrc << " trc=" << trc << " nx=" << gs_global.nx( jlatMin + j ) << " nxmax=" << gs_global.nxmax() << " nlats=" << gs_global.ny() << " lat=" << g.y( j ) << " jlatMin=" << jlatMin << std::endl;*/ for (idx_t i = 0; i < g.nx(j); ++i) { double lon = g.x(i, j) * util::Constants::degreesToRadians(); // compute spherical harmonics: if (ftrc > m) { rgp[idx++] = sphericalharmonics_analytic_point(n, m, imag, lon, lat, ivar_in, ivar_out); } else { rgp[idx++] = 0.; } } } } else { int idx = 0; for (const PointXY& p : grid.xy()) { double lon = p.x() * util::Constants::degreesToRadians(); double lat = p.y() * util::Constants::degreesToRadians(); // compute spherical harmonics: rgp[idx++] = sphericalharmonics_analytic_point(n, m, imag, lon, lat, ivar_in, ivar_out); } } } //----------------------------------------------------------------------------- // Compute root mean square of difference between two arrays divided by maximum // absolute value of second array // // Author: // Andreas Mueller *ECMWF* // double compute_rms(const size_t N, // length of the arrays double array1[], // first of the two arrays double array2[]) // second of the two arrays { double rms = 0., rmax = 0.; for (size_t idx = 0; idx < N; idx++) { double diff = array1[idx] - array2[idx]; rms += diff * diff; rmax = std::max(rmax, std::abs(array2[idx])); } if (rmax == 0.) { rms = 0.; } else { rms = std::sqrt(rms / N) / rmax; } return rms; } //----------------------------------------------------------------------------- #if 1 CASE("test_trans_vordiv_with_translib") { Log::info() << "test_trans_vordiv_with_translib" << std::endl; // test transgeneral by comparing its result with the trans library // this test is based on the test_nomesh case in test_trans.cc double tolerance = 1.e-13; // Grid: (Adjust the following line if the test takes too long!) Grid g("F64"); StructuredGrid gs(g); int ndgl = gs.ny(); //int trc = ndgl - 1; // linear int trc = ndgl / 2. - 1; // cubic #if ATLAS_HAVE_TRANS trans::Trans transIFS(g, trc, util::Config("type", "ectrans")); double rav = 0.; // compute average rms error of trans library in rav #endif trans::Trans transLocal1(g, trc, util::Config("type", "local")); trans::Trans transLocal2(g, trc, util::Config("type", "local")); double rav1 = 0., rav2 = 0.; // compute average rms errors of transLocal1 and transLocal2 functionspace::Spectral spectral(trc); functionspace::StructuredColumns gridpoints(g); int nb_scalar = 1, nb_vordiv = 0; int N = (trc + 2) * (trc + 1) / 2, nb_all = nb_scalar + 2 * nb_vordiv; std::vector sp(2 * N * nb_scalar); std::vector vor(2 * N * nb_vordiv); std::vector div(2 * N * nb_vordiv); std::vector rspecg(2 * N); std::vector gp(nb_all * g.size()); std::vector rgp1(nb_all * g.size()); std::vector rgp2(nb_all * g.size()); std::vector rgp_analytic(g.size()); int icase = 0; for (int ivar_in = 2; ivar_in < 3; ivar_in++) { // vorticity, divergence, scalar for (int ivar_out = 2; ivar_out < 3; ivar_out++) { // u, v, scalar int nb_fld = 1; if (ivar_out == 2) { tolerance = 1.e-13; nb_fld = nb_scalar; } else { tolerance = 2.e-6; nb_fld = nb_vordiv; } for (int jfld = 0; jfld < nb_fld; jfld++) { // multiple fields int k = 0; for (int m = 0; m <= trc; m++) { // zonal wavenumber for (int n = m; n <= trc; n++) { // total wavenumber for (int imag = 0; imag <= 1; imag++) { // real and imaginary part if (sphericalharmonics_analytic_point(n, m, true, 0., 0., ivar_in, ivar_in) == 0.) { for (int j = 0; j < 2 * N * nb_scalar; j++) { sp[j] = 0.; } for (int j = 0; j < 2 * N * nb_vordiv; j++) { vor[j] = 0.; div[j] = 0.; } if (ivar_in == 0) { vor[k * nb_vordiv + jfld] = 1.; } if (ivar_in == 1) { div[k * nb_vordiv + jfld] = 1.; } if (ivar_in == 2) { sp[k * nb_scalar + jfld] = 1.; } for (int j = 0; j < nb_all * g.size(); j++) { gp[j] = 0.; rgp1[j] = 0.; rgp2[j] = 0.; } for (int j = 0; j < g.size(); j++) { rgp_analytic[j] = 0.; } spectral_transform_grid_analytic(trc, trc, n, m, imag, g, rspecg.data(), rgp_analytic.data(), ivar_in, ivar_out); EXPECT_NO_THROW(transLocal1.invtrans(nb_scalar, sp.data(), nb_vordiv, vor.data(), div.data(), rgp1.data())); EXPECT_NO_THROW(transLocal2.invtrans(nb_scalar, sp.data(), nb_vordiv, vor.data(), div.data(), rgp2.data())); int pos = (ivar_out * nb_vordiv + jfld); double rms_gen1 = compute_rms(g.size(), rgp1.data() + pos * g.size(), rgp_analytic.data()); double rms_gen2 = compute_rms(g.size(), rgp2.data() + pos * g.size(), rgp_analytic.data()); rav1 += rms_gen1; rav2 += rms_gen2; if (!(rms_gen1 < tolerance) || !(rms_gen2 < tolerance)) { Log::info() << "Case " << icase << " ivar_in=" << ivar_in << " ivar_out=" << ivar_out << " m=" << m << " n=" << n << " imag=" << imag << " k=" << k << std::endl; ATLAS_DEBUG_VAR(rms_gen1); ATLAS_DEBUG_VAR(rms_gen2); ATLAS_DEBUG_VAR(tolerance); } EXPECT(rms_gen1 < tolerance); EXPECT(rms_gen2 < tolerance); icase++; #if ATLAS_HAVE_TRANS EXPECT_NO_THROW(transIFS.invtrans(nb_scalar, sp.data(), nb_vordiv, vor.data(), div.data(), gp.data())); double rms_trans = compute_rms(g.size(), gp.data() + pos * g.size(), rgp_analytic.data()); rav += rms_trans; double rms_diff = compute_rms(g.size(), rgp1.data() + pos * g.size(), gp.data() + pos * g.size()); EXPECT(rms_trans < tolerance); if (!(rms_trans < tolerance) || !(rms_diff < tolerance)) { Log::info() << "Case " << icase << " ivar_in=" << ivar_in << " ivar_out=" << ivar_out << " m=" << m << " n=" << n << " imag=" << imag << " k=" << k << std::endl; ATLAS_DEBUG_VAR(rms_gen1); ATLAS_DEBUG_VAR(rms_gen2); ATLAS_DEBUG_VAR(rms_trans); ATLAS_DEBUG_VAR(rms_diff); ATLAS_DEBUG_VAR(tolerance); } #endif EXPECT(icase < 300); } k++; } } } } } } Log::info() << "Vordiv+scalar comparison with trans: all " << icase << " cases successfully passed!" << std::endl; rav1 /= icase; Log::info() << "average RMS error of transLocal1: " << rav1 << std::endl; rav2 /= icase; Log::info() << "average RMS error of transLocal2: " << rav2 << std::endl; #if ATLAS_HAVE_TRANS rav /= icase; Log::info() << "average RMS error of transIFS: " << rav << std::endl; #endif } #endif //----------------------------------------------------------------------------- #if 1 CASE("test_trans_hires") { Log::info() << "test_trans_hires" << std::endl; // test transgeneral by comparing its result with the trans library // this test is based on the test_nomesh case in test_trans.cc //#if ATLAS_HAVE_TRANS // //std::string transTypes[4] = {"localopt", "localopt2", "Local", "ectrans"}; // //std::string transTypes[2] = {"localopt2", "Local"}; // //std::string transTypes[3] = {"Local", "localopt2", "localopt"}; // //std::string transTypes[1] = {"Local"}; //#else // //std::string transTypes[1] = {"localopt2"}; //#endif std::vector trans_configs; std::vector transTypes = {"local"}; if (trans::Trans::hasBackend("ectrans")) { transTypes.emplace_back("ectrans"); } std::map> backends{{"ectrans", {"lapack"}}, {"local", {"generic", "openmp", "eigen"}}}; //Domain testdomain =F ZonalBandDomain( {-90., 90.} ); //Domain testdomain = ZonalBandDomain( {-.5, .5} ); //Domain testdomain = RectangularDomain( {0., 30.}, {-.05, .05} ); //Domain testdomain = ZonalBandDomain( {-85., -86.} ); //Domain testdomain = RectangularDomain( {-.01, .01}, {-.01, .01} ); //Domain testdomain = RectangularDomain( {-1, 1}, {-1, 1} ); Domain testdomain = GlobalDomain(); // Grid: (Adjust the following line if the test takes too long!) Grid g("O160", testdomain); Grid g_global(g.name()); StructuredGrid gs(g); StructuredGrid gs_global(g_global); Log::info() << "nlats: " << gs.ny() << " nlons:" << gs.nxmax() << std::endl; int ndgl = gs_global.ny(); //int trc = ndgl - 1; // linear int trc = ndgl / 2. - 1; // cubic Log::info() << "truncation: " << trc << std::endl; int nb_scalar = 1, nb_vordiv = 0; for (auto& trans_type : transTypes) { ATLAS_TRACE(trans_type); int N = (trc + 2) * (trc + 1) / 2, nb_all = nb_scalar + 2 * nb_vordiv; int icase = 0; trans::Trans trans(g, trc, option::type(trans_type)); for (int ivar_in = 2; ivar_in < 3; ivar_in++) { // vorticity, divergence, scalar for (int ivar_out = 2; ivar_out < 3; ivar_out++) { // u, v, scalar // int nb_fld = 1; // if (ivar_out == 2) { // nb_fld = nb_scalar; // } // else { // nb_fld = nb_vordiv; // } for (int jfld = 0; jfld < 1; jfld++) { // multiple fields int k = 0; for (int m = 0; m <= trc; m++) { // zonal wavenumber for (int n = m; n <= trc; n++) { // total wavenumber for (int imag = 0; imag <= 1; imag++) { // real and imaginary part if (sphericalharmonics_analytic_point(n, m, true, 0., 0., ivar_in, ivar_in) == 0. && icase < 1) { std::vector sp(2 * N * nb_scalar); std::vector gp(nb_all * g.size()); if (ivar_in == 2) { sp[k * nb_scalar + jfld] = 1.; } auto original_backend = linalg::dense::current_backend(); for (auto& backend : backends[trans_type]) { if (linalg::dense::Backend(backend).available()) { linalg::dense::current_backend(backend); ATLAS_TRACE("invtrans [backend=" + backend + "]"); auto start = std::chrono::system_clock::now(); EXPECT_NO_THROW(trans.invtrans(nb_scalar, sp.data(), nb_vordiv, nullptr, nullptr, gp.data())); auto end = std::chrono::system_clock::now(); // std::chrono::duration elapsed_seconds = end - start; std::time_t end_time = std::chrono::system_clock::to_time_t(end); std::string time_str = std::ctime(&end_time); Log::info() << trans_type << "[backend=" << backend << "] : case " << icase << ", elapsed time: " << elapsed_seconds.count() << "s. Now: " << time_str.substr(0, time_str.length() - 1) << std::endl; } } linalg::dense::current_backend(original_backend); icase++; } k++; } } } } } } //.Log::info() << "Vordiv+scalar comparison with trans::" << description << ": all " << icase // << " cases successfully passed!" << std::endl; } } #endif //----------------------------------------------------------------------------- #if 1 CASE("test_trans_domain") { Log::info() << "test_trans_domain" << std::endl; // test transgeneral by comparing with analytic solution on a cropped domain // this test also includes testing caching //Domain testdomain = ZonalBandDomain( {-90., 90.} ); //Domain testdomain = ZonalBandDomain( {-.5, .5} ); //Domain testdomain = RectangularDomain( {0., 30.}, {-.05, .05} ); //Domain testdomain1 = ZonalBandDomain( {-10., 5.} ); Domain testdomain1 = RectangularDomain({-5., 5.}, {-2.5, 0.}); //Domain testdomain1 = RectangularDomain( {-1., 1.}, {50., 55.} ); Domain testdomain2 = RectangularDomain({-5., 5.}, {-2.5, 0.}); // Grid: (Adjust the following line if the test takes too long!) Grid global_grid("O64"); //Grid g1( global_grid, testdomain1 ); Grid g2(global_grid, testdomain2); Grid g1(global_grid); //Grid g2( global_grid ); bool fourierTrc1 = true; bool fourierTrc2 = false; // fourierTrc1, fourierTrc2: need to be false if no global grid can be constructed // (like for grids created with LinearSpacing) //using LinearSpacing = grid::LinearSpacing; //StructuredGrid g2( LinearSpacing( {0., 180.}, 3 ), LinearSpacing( {89., 90.}, 2 ) ); // when using LinearSpacing: set fourierTrc2 to false std::string legcache = "legcache"; if (not atlas::Library::instance().linalgFFTBackend().empty()) { // We append fft backend because this test may be run side by side // with another test with differently configured FFT backend, // causing a race condition. legcache += "-"+atlas::Library::instance().linalgFFTBackend(); } int trc = 63; //Log::info() << "rgp1:" << std::endl; if (eckit::PathName(legcache+".bin").exists()) { eckit::PathName(legcache+".bin").unlink(); } Trace t1(Here(), "translocal1 construction"); trans::Trans transLocal1(global_grid, g1.domain(), trc, option::type("local") | option::write_legendre(legcache+".bin")); t1.stop(); //Log::info() << "rgp2:" << std::endl; trans::Cache cache; ATLAS_TRACE_SCOPE("Read cache") cache = trans::LegendreCache(legcache+".bin"); Trace t2(Here(), "translocal2 construction"); trans::Trans transLocal2(cache, global_grid, g2.domain(), trc, option::type("local") | option::write_legendre(legcache+"2.bin")); //trans::Trans transLocal2( cache, g2, trc, option::type( "local" ) ); //trans::Trans transLocal2( cache, g2, trc, // option::type( "local" ) | option::no_fft() ); //trans::Trans transLocal2( g2, trc, option::type( "local" ) ); t2.stop(); double rav1 = 0., rav2 = 0.; // compute average rms errors of transLocal1 and transLocal2 functionspace::Spectral spectral(trc); int nb_scalar = 1, nb_vordiv = 1; int N = (trc + 2) * (trc + 1) / 2, nb_all = nb_scalar + 2 * nb_vordiv; std::vector sp(2 * N * nb_scalar); std::vector vor(2 * N * nb_vordiv); std::vector div(2 * N * nb_vordiv); std::vector rspecg(2 * N); std::vector rgp1(nb_all * g1.size()); std::vector rgp2(nb_all * g2.size()); std::vector rgp1_analytic(g1.size()); std::vector rgp2_analytic(g2.size()); StructuredGrid gs1(g1); StructuredGrid gs2(g2); double latPole = 89.9999999; bool gridHasPole = gs1.y(0) > latPole || gs2.y(0) > latPole; double tolerance; int icase = 0; for (int ivar_in = 0; ivar_in < 3; ivar_in++) { // vorticity, divergence, scalar for (int ivar_out = 0; ivar_out < 3; ivar_out++) { // u, v, scalar int nb_fld = 1; if (ivar_out == 2 && !gridHasPole) { tolerance = 1.e-13; nb_fld = nb_scalar; } else { tolerance = 2.e-6; nb_fld = nb_vordiv; } for (int jfld = 0; jfld < nb_fld; jfld++) { // multiple fields int k = 0; for (int m = 0; m <= trc; m++) { // zonal wavenumber for (int n = m; n <= trc; n++) { // total wavenumber for (int imag = 0; imag <= 1; imag++) { // real and imaginary part if (sphericalharmonics_analytic_point(n, m, true, 0., 0., ivar_in, ivar_in) == 0. && icase < 1000) { auto start = std::chrono::system_clock::now(); for (int j = 0; j < 2 * N * nb_scalar; j++) { sp[j] = 0.; } for (int j = 0; j < 2 * N * nb_vordiv; j++) { vor[j] = 0.; div[j] = 0.; } if (ivar_in == 0) { vor[k * nb_vordiv + jfld] = 1.; } if (ivar_in == 1) { div[k * nb_vordiv + jfld] = 1.; } if (ivar_in == 2) { sp[k * nb_scalar + jfld] = 1.; } for (int j = 0; j < nb_all * g1.size(); j++) { rgp1[j] = 0.; } for (int j = 0; j < nb_all * g2.size(); j++) { rgp2[j] = 0.; } for (int j = 0; j < g1.size(); j++) { rgp1_analytic[j] = 0.; } for (int j = 0; j < g2.size(); j++) { rgp2_analytic[j] = 0.; } spectral_transform_grid_analytic(trc, fourierTrc1, n, m, imag, g1, rspecg.data(), rgp1_analytic.data(), ivar_in, ivar_out); spectral_transform_grid_analytic(trc, fourierTrc2, n, m, imag, g2, rspecg.data(), rgp2_analytic.data(), ivar_in, ivar_out); //Log::info() << std::endl << "rgp1:"; ATLAS_TRACE_SCOPE("translocal1") EXPECT_NO_THROW(transLocal1.invtrans(nb_scalar, sp.data(), nb_vordiv, vor.data(), div.data(), rgp1.data())); //Log::info() << std::endl << "rgp2:"; ATLAS_TRACE_SCOPE("translocal2") EXPECT_NO_THROW(transLocal2.invtrans(nb_scalar, sp.data(), nb_vordiv, vor.data(), div.data(), rgp2.data())); int pos = (ivar_out * nb_vordiv + jfld); double rms_gen1 = compute_rms(g1.size(), rgp1.data() + pos * g1.size(), rgp1_analytic.data()); double rms_gen2 = compute_rms(g2.size(), rgp2.data() + pos * g2.size(), rgp2_analytic.data()); //Log::info() << "Case " << icase << " ivar_in=" << ivar_in << " ivar_out=" << ivar_out // << " m=" << m << " n=" << n << " imag=" << imag << " k=" << k << std::endl // << "rgp1:"; //for ( int j = 0; j < g1.size(); j++ ) { // Log::info() << rgp1[pos * g1.size() + j] << " "; //}; //Log::info() << std::endl << "analytic1:"; //for ( int j = 0; j < g1.size(); j++ ) { // Log::info() << rgp1_analytic[j] << " "; //}; //Log::info() << std::endl << "rgp2:"; //for ( int j = 0; j < g2.size(); j++ ) { // Log::info() << rgp2[pos * g2.size() + j] << " "; //}; //Log::info() << std::endl << "analytic2:"; //for ( int j = 0; j < g2.size(); j++ ) { // Log::info() << rgp2_analytic[j] << " "; //}; //Log::info() << std::endl; rav1 += rms_gen1; rav2 += rms_gen2; if (!(rms_gen1 < tolerance) || !(rms_gen2 < tolerance)) { Log::info() << "Case " << icase << " ivar_in=" << ivar_in << " ivar_out=" << ivar_out << " m=" << m << " n=" << n << " imag=" << imag << " k=" << k << std::endl; ATLAS_DEBUG_VAR(rms_gen1); ATLAS_DEBUG_VAR(rms_gen2); ATLAS_DEBUG_VAR(tolerance); } EXPECT(rms_gen1 < tolerance); EXPECT(rms_gen2 < tolerance); icase++; auto end = std::chrono::system_clock::now(); // std::chrono::duration elapsed_seconds = end - start; std::time_t end_time = std::chrono::system_clock::to_time_t(end); std::string time_str = std::ctime(&end_time); Log::debug() << "case " << icase << ", elapsed time: " << elapsed_seconds.count() << "s. Now: " << time_str.substr(0, time_str.length() - 1) << std::endl; } k++; } } } } } } Log::info() << "Vordiv+scalar comparison with trans: all " << icase << " cases successfully passed!" << std::endl; rav1 /= icase; Log::info() << "average RMS error of transLocal1: " << rav1 << std::endl; rav2 /= icase; Log::info() << "average RMS error of transLocal2: " << rav2 << std::endl; } //----------------------------------------------------------------------------- CASE("test_trans_pole") { Log::info() << "test_trans_pole" << std::endl; // test transform at the pole and with LinearSpacing grids // not using caching in this test because not useful for LinearSpacing grids //Domain testdomain = ZonalBandDomain( {-90., 90.} ); //Domain testdomain = ZonalBandDomain( {-.5, .5} ); //Domain testdomain = RectangularDomain( {0., 30.}, {-.05, .05} ); //Domain testdomain1 = ZonalBandDomain( {-10., 5.} ); Domain testdomain1 = RectangularDomain({-5., 5.}, {-2.5, 0.}); //Domain testdomain1 = RectangularDomain( {-1., 1.}, {50., 55.} ); Domain testdomain2 = RectangularDomain({-5., 5.}, {-2.5, 0.}); // Grid: (Adjust the following line if the test takes too long!) Grid global_grid1("L3"); Grid global_grid2("L3"); //Grid g1( global_grid, testdomain1 ); //Grid g2( global_grid, testdomain2 ); Grid g1(global_grid1); //Grid g2( global_grid2 ); bool fourierTrc1 = true; bool fourierTrc2 = false; // fourierTrc1, fourierTrc2: need to be false if no global grid can be constructed // (like for grids created with LinearSpacing) using LinearSpacing = grid::LinearSpacing; StructuredGrid g2(LinearSpacing({0., 180.}, 3), LinearSpacing({89., 90.}, 2)); // when using LinearSpacing: set fourierTrc2 to false int trc = 2; Trace t1(Here(), "translocal1 construction"); trans::Trans transLocal1(global_grid1, g1.domain(), trc, option::type("local")); t1.stop(); Trace t2(Here(), "translocal2 construction"); trans::Trans transLocal2(g2, trc, option::type("local")); t2.stop(); double rav1 = 0., rav2 = 0.; // compute average rms errors of transLocal1 and transLocal2 functionspace::Spectral spectral(trc); int nb_scalar = 1, nb_vordiv = 1; int N = (trc + 2) * (trc + 1) / 2, nb_all = nb_scalar + 2 * nb_vordiv; std::vector sp(2 * N * nb_scalar); std::vector vor(2 * N * nb_vordiv); std::vector div(2 * N * nb_vordiv); std::vector rspecg(2 * N); std::vector rgp1(nb_all * g1.size()); std::vector rgp2(nb_all * g2.size()); std::vector rgp1_analytic(g1.size()); std::vector rgp2_analytic(g2.size()); StructuredGrid gs1(g1); StructuredGrid gs2(g2); double latPole = 89.9999999; bool gridHasPole = gs1.y(0) > latPole || gs2.y(0) > latPole; double tolerance; int icase = 0; for (int ivar_in = 0; ivar_in < 3; ivar_in++) { // vorticity, divergence, scalar for (int ivar_out = 0; ivar_out < 3; ivar_out++) { // u, v, scalar int nb_fld = 1; if (ivar_out == 2 && !gridHasPole) { tolerance = 1.e-13; nb_fld = nb_scalar; } else { tolerance = 2.e-5; nb_fld = nb_vordiv; } for (int jfld = 0; jfld < nb_fld; jfld++) { // multiple fields int k = 0; for (int m = 0; m <= trc; m++) { // zonal wavenumber for (int n = m; n <= trc; n++) { // total wavenumber for (int imag = 0; imag <= 1; imag++) { // real and imaginary part if (sphericalharmonics_analytic_point(n, m, true, 0., 0., ivar_in, ivar_in) == 0. && icase < 1000) { auto start = std::chrono::system_clock::now(); for (int j = 0; j < 2 * N * nb_scalar; j++) { sp[j] = 0.; } for (int j = 0; j < 2 * N * nb_vordiv; j++) { vor[j] = 0.; div[j] = 0.; } if (ivar_in == 0) { vor[k * nb_vordiv + jfld] = 1.; } if (ivar_in == 1) { div[k * nb_vordiv + jfld] = 1.; } if (ivar_in == 2) { sp[k * nb_scalar + jfld] = 1.; } for (int j = 0; j < nb_all * g1.size(); j++) { rgp1[j] = 0.; } for (int j = 0; j < nb_all * g2.size(); j++) { rgp2[j] = 0.; } for (int j = 0; j < g1.size(); j++) { rgp1_analytic[j] = 0.; } for (int j = 0; j < g2.size(); j++) { rgp2_analytic[j] = 0.; } spectral_transform_grid_analytic(trc, fourierTrc1, n, m, imag, g1, rspecg.data(), rgp1_analytic.data(), ivar_in, ivar_out); spectral_transform_grid_analytic(trc, fourierTrc2, n, m, imag, g2, rspecg.data(), rgp2_analytic.data(), ivar_in, ivar_out); //Log::info() << std::endl << "rgp1:"; ATLAS_TRACE_SCOPE("translocal1") EXPECT_NO_THROW(transLocal1.invtrans(nb_scalar, sp.data(), nb_vordiv, vor.data(), div.data(), rgp1.data())); //Log::info() << std::endl << "rgp2:"; ATLAS_TRACE_SCOPE("translocal2") EXPECT_NO_THROW(transLocal2.invtrans(nb_scalar, sp.data(), nb_vordiv, vor.data(), div.data(), rgp2.data())); int pos = (ivar_out * nb_vordiv + jfld); double rms_gen1 = compute_rms(g1.size(), rgp1.data() + pos * g1.size(), rgp1_analytic.data()); double rms_gen2 = compute_rms(g2.size(), rgp2.data() + pos * g2.size(), rgp2_analytic.data()); //Log::info() << "Case " << icase << " ivar_in=" << ivar_in << " ivar_out=" << ivar_out // << " m=" << m << " n=" << n << " imag=" << imag << " k=" << k << std::endl // << "rgp1:"; //for ( int j = 0; j < g1.size(); j++ ) { // Log::info() << rgp1[pos * g1.size() + j] << " "; //}; //Log::info() << std::endl << "analytic1:"; //for ( int j = 0; j < g1.size(); j++ ) { // Log::info() << rgp1_analytic[j] << " "; //}; //Log::info() << std::endl << "rgp2:"; //for ( int j = 0; j < g2.size(); j++ ) { // Log::info() << rgp2[pos * g2.size() + j] << " "; //}; //Log::info() << std::endl << "analytic2:"; //for ( int j = 0; j < g2.size(); j++ ) { // Log::info() << rgp2_analytic[j] << " "; //}; //Log::info() << std::endl; rav1 += rms_gen1; rav2 += rms_gen2; if (!(rms_gen1 < tolerance) || !(rms_gen2 < tolerance)) { Log::info() << "Case " << icase << " ivar_in=" << ivar_in << " ivar_out=" << ivar_out << " m=" << m << " n=" << n << " imag=" << imag << " k=" << k << std::endl; ATLAS_DEBUG_VAR(rms_gen1); ATLAS_DEBUG_VAR(rms_gen2); ATLAS_DEBUG_VAR(tolerance); } EXPECT(rms_gen1 < tolerance); EXPECT(rms_gen2 < tolerance); icase++; auto end = std::chrono::system_clock::now(); // std::chrono::duration elapsed_seconds = end - start; std::time_t end_time = std::chrono::system_clock::to_time_t(end); std::string time_str = std::ctime(&end_time); Log::debug() << "case " << icase << ", elapsed time: " << elapsed_seconds.count() << "s. Now: " << time_str.substr(0, time_str.length() - 1) << std::endl; } k++; } } } } } } Log::info() << "Vordiv+scalar comparison with trans: all " << icase << " cases successfully passed!" << std::endl; rav1 /= icase; Log::info() << "average RMS error of transLocal1: " << rav1 << std::endl; rav2 /= icase; Log::info() << "average RMS error of transLocal2: " << rav2 << std::endl; } #endif //----------------------------------------------------------------------------- #if 1 CASE("test_trans_southpole") { Log::info() << "test_trans_southpole" << std::endl; // test created for MIR-283 (limited area domain on the southern hemisphere with L-grid) //Domain testdomain = ZonalBandDomain( {-90., 90.} ); //Domain testdomain = ZonalBandDomain( {-.5, .5} ); //Domain testdomain = RectangularDomain( {0., 30.}, {-.05, .05} ); //Domain testdomain1 = ZonalBandDomain( {-10., 5.} ); //Domain testdomain1 = RectangularDomain( {-5., 5.}, {-2.5, 0.} ); Domain testdomain1 = RectangularDomain({0., 10.}, {-90., -10.}); //Domain testdomain1 = RectangularDomain( {-1., 1.}, {50., 55.} ); //Domain testdomain2 = RectangularDomain( {-5., 5.}, {-2.5, 0.} ); Domain testdomain2 = RectangularDomain({0., 10.}, {10., 90.}); // Grid: (Adjust the following line if the test takes too long!) Grid global_grid1("L9"); Grid global_grid2("L9"); Grid g1(global_grid1, testdomain1); Grid g2(global_grid2, testdomain2); //Grid g1( global_grid1 ); //Grid g2( global_grid2 ); bool fourierTrc1 = true; bool fourierTrc2 = true; // fourierTrc1, fourierTrc2: need to be false if no global grid can be constructed // (like for grids created with LinearSpacing) //using LinearSpacing = grid::LinearSpacing; //StructuredGrid g2( LinearSpacing( {0., 10.}, 2 ), LinearSpacing( {-10., -90.}, 9 ) ); // when using LinearSpacing: set fourierTrc2 to false int trc = 8; Trace t1(Here(), "translocal1 construction"); trans::Trans transLocal1(global_grid1, g1.domain(), trc, option::type("local")); t1.stop(); Trace t2(Here(), "translocal2 construction"); //trans::Trans transLocal2( g2, trc, option::type( "local" ) ); trans::Trans transLocal2(global_grid2, g2.domain(), trc, option::type("local")); t2.stop(); double rav1 = 0., rav2 = 0.; // compute average rms errors of transLocal1 and transLocal2 functionspace::Spectral spectral(trc); int nb_scalar = 1, nb_vordiv = 1; int N = (trc + 2) * (trc + 1) / 2, nb_all = nb_scalar + 2 * nb_vordiv; std::vector sp(2 * N * nb_scalar); std::vector vor(2 * N * nb_vordiv); std::vector div(2 * N * nb_vordiv); std::vector rspecg(2 * N); std::vector rgp1(nb_all * g1.size()); std::vector rgp2(nb_all * g2.size()); std::vector rgp1_analytic(g1.size()); std::vector rgp2_analytic(g2.size()); StructuredGrid gs1(g1); StructuredGrid gs2(g2); double latPole = 89.9999999; bool gridHasPole = gs1.y(0) > latPole || gs2.y(0) > latPole; double tolerance; int icase = 0; for (int ivar_in = 0; ivar_in < 3; ivar_in++) { // vorticity, divergence, scalar for (int ivar_out = 0; ivar_out < 3; ivar_out++) { // u, v, scalar int nb_fld = 1; if (ivar_out == 2 && !gridHasPole) { tolerance = 1.e-13; nb_fld = nb_scalar; } else { tolerance = 2.e-6; nb_fld = nb_vordiv; } for (int jfld = 0; jfld < nb_fld; jfld++) { // multiple fields int k = 0; for (int m = 0; m <= trc; m++) { // zonal wavenumber for (int n = m; n <= trc; n++) { // total wavenumber for (int imag = 0; imag <= 1; imag++) { // real and imaginary part if (sphericalharmonics_analytic_point(n, m, true, 0., 0., ivar_in, ivar_in) == 0. && icase < 1000) { auto start = std::chrono::system_clock::now(); for (int j = 0; j < 2 * N * nb_scalar; j++) { sp[j] = 0.; } for (int j = 0; j < 2 * N * nb_vordiv; j++) { vor[j] = 0.; div[j] = 0.; } if (ivar_in == 0) { vor[k * nb_vordiv + jfld] = 1.; } if (ivar_in == 1) { div[k * nb_vordiv + jfld] = 1.; } if (ivar_in == 2) { sp[k * nb_scalar + jfld] = 1.; } for (int j = 0; j < nb_all * g1.size(); j++) { rgp1[j] = 0.; } for (int j = 0; j < nb_all * g2.size(); j++) { rgp2[j] = 0.; } for (int j = 0; j < g1.size(); j++) { rgp1_analytic[j] = 0.; } for (int j = 0; j < g2.size(); j++) { rgp2_analytic[j] = 0.; } spectral_transform_grid_analytic(trc, fourierTrc1, n, m, imag, g1, rspecg.data(), rgp1_analytic.data(), ivar_in, ivar_out); spectral_transform_grid_analytic(trc, fourierTrc2, n, m, imag, g2, rspecg.data(), rgp2_analytic.data(), ivar_in, ivar_out); //Log::info() << "Case " << icase << " ivar_in=" << ivar_in << " ivar_out=" << ivar_out // << " m=" << m << " n=" << n << " imag=" << imag << " k=" << k << std::endl; ATLAS_TRACE_SCOPE("translocal1") EXPECT_NO_THROW(transLocal1.invtrans(nb_scalar, sp.data(), nb_vordiv, vor.data(), div.data(), rgp1.data())); int pos = (ivar_out * nb_vordiv + jfld); double rms_gen1 = compute_rms(g1.size(), rgp1.data() + pos * g1.size(), rgp1_analytic.data()); //Log::info() << "rgp1:"; //for ( int j = 0; j < g1.size(); j++ ) { // Log::info() << rgp1[pos * g1.size() + j] << " "; //}; //Log::info() << std::endl << "analytic1:"; //for ( int j = 0; j < g1.size(); j++ ) { // Log::info() << rgp1_analytic[j] << " "; //}; //Log::info() << std::endl; ATLAS_TRACE_SCOPE("translocal2") EXPECT_NO_THROW(transLocal2.invtrans(nb_scalar, sp.data(), nb_vordiv, vor.data(), div.data(), rgp2.data())); double rms_gen2 = compute_rms(g2.size(), rgp2.data() + pos * g2.size(), rgp2_analytic.data()); //Log::info() << std::endl << "rgp2:"; //for ( int j = 0; j < g2.size(); j++ ) { // Log::info() << rgp2[pos * g2.size() + j] << " "; //}; //Log::info() << std::endl << "analytic2:"; //for ( int j = 0; j < g2.size(); j++ ) { // Log::info() << rgp2_analytic[j] << " "; //}; //Log::info() << std::endl; rav1 += rms_gen1; rav2 += rms_gen2; if (!(rms_gen1 < tolerance) || !(rms_gen2 < tolerance)) { Log::info() << "Case " << icase << " ivar_in=" << ivar_in << " ivar_out=" << ivar_out << " m=" << m << " n=" << n << " imag=" << imag << " k=" << k << std::endl; ATLAS_DEBUG_VAR(rms_gen1); ATLAS_DEBUG_VAR(rms_gen2); ATLAS_DEBUG_VAR(tolerance); } EXPECT(rms_gen1 < tolerance); EXPECT(rms_gen2 < tolerance); icase++; auto end = std::chrono::system_clock::now(); // std::chrono::duration elapsed_seconds = end - start; std::time_t end_time = std::chrono::system_clock::to_time_t(end); std::string time_str = std::ctime(&end_time); Log::debug() << "case " << icase << ", elapsed time: " << elapsed_seconds.count() << "s. Now: " << time_str.substr(0, time_str.length() - 1) << std::endl; } k++; } } } } } } Log::info() << "Vordiv+scalar comparison with trans: all " << icase << " cases successfully passed!" << std::endl; rav1 /= icase; Log::info() << "average RMS error of transLocal1: " << rav1 << std::endl; rav2 /= icase; Log::info() << "average RMS error of transLocal2: " << rav2 << std::endl; } #endif //----------------------------------------------------------------------------- #if 1 CASE("test_trans_unstructured") { Log::info() << "test_trans_unstructured" << std::endl; // test transgeneral by comparing with analytic solution on an unstructured grid double tolerance = 1.e-13; //Domain testdomain = RectangularDomain( {20., 25.}, {40., 60.} ); Domain testdomain = RectangularDomain({0., 90.}, {0., 90.}); // Grid: (Adjust the following line if the test takes too long!) Grid grid_global("F32"); Grid g(grid_global, testdomain); int trc = 31; StructuredGrid gs(g); std::vector pts(g.size()); idx_t idx(0); for (idx_t j = 0; j < gs.ny(); ++j) { double lat = gs.y(j); for (idx_t i = 0; i < gs.nx(j); ++i) { double lon = gs.x(i, j); if (i == j && lat > 0) { //Log::info() << "idx=" << idx << " lon=" << lon << " lat=" << lat << std::endl; pts[idx++].assign(lon, lat); } } } Grid gu = UnstructuredGrid(new std::vector(&pts[0], &pts[idx])); Log::info() << "gu: size=" << gu.size() << std::endl; double rav1 = 0., rav2 = 0.; // compute average rms errors of transLocal1 and transLocal2 int nb_scalar = 1, nb_vordiv = 1; int N = (trc + 2) * (trc + 1) / 2, nb_all = nb_scalar + 2 * nb_vordiv; std::vector sp(2 * N * nb_scalar); std::vector vor(2 * N * nb_vordiv); std::vector div(2 * N * nb_vordiv); std::vector rspecg(2 * N); std::vector gp(nb_all * g.size()); std::vector rgp1(nb_all * g.size()); std::vector rgp2(nb_all * g.size()); std::vector rgp_analytic1(g.size()); std::vector rgp_analytic2(gu.size()); trans::Trans transLocal1(grid_global, testdomain, trc, option::type("local")); // ATLAS-173 : This should also work with precompute = true, ans should give same. trans::Trans transLocal2(gu, trc, option::type("local") | util::Config("precompute", false)); int icase = 0; for (int ivar_in = 0; ivar_in < 3; ivar_in++) { // vorticity, divergence, scalar for (int ivar_out = 0; ivar_out < 3; ivar_out++) { // u, v, scalar int nb_fld = 1; if (ivar_out == 2) { tolerance = 1.e-13; nb_fld = nb_scalar; } else { tolerance = 2.e-6; nb_fld = nb_vordiv; } for (int jfld = 0; jfld < nb_fld; jfld++) { // multiple fields int k = 0; for (int m = 0; m <= trc; m++) { // zonal wavenumber for (int n = m; n <= trc; n++) { // total wavenumber for (int imag = 0; imag <= 1; imag++) { // real and imaginary part if (sphericalharmonics_analytic_point(n, m, true, 0., 0., ivar_in, ivar_in) == 0. && icase < 1000) { auto start = std::chrono::system_clock::now(); for (int j = 0; j < 2 * N * nb_scalar; j++) { sp[j] = 0.; } for (int j = 0; j < 2 * N * nb_vordiv; j++) { vor[j] = 0.; div[j] = 0.; } if (ivar_in == 0) { vor[k * nb_vordiv + jfld] = 1.; } if (ivar_in == 1) { div[k * nb_vordiv + jfld] = 1.; } if (ivar_in == 2) { sp[k * nb_scalar + jfld] = 1.; } for (int j = 0; j < nb_all * g.size(); j++) { gp[j] = 0.; rgp1[j] = 0.; rgp2[j] = 0.; } for (int j = 0; j < g.size(); j++) { rgp_analytic1[j] = 0.; } for (int j = 0; j < gu.size(); j++) { rgp_analytic2[j] = 0.; } spectral_transform_grid_analytic(trc, false, n, m, imag, g, rspecg.data(), rgp_analytic1.data(), ivar_in, ivar_out); //Log::info() << icase << " m=" << m << " n=" << n << " imag=" << imag << " structured: "; ATLAS_TRACE_SCOPE("structured") EXPECT_NO_THROW(transLocal1.invtrans(nb_scalar, sp.data(), nb_vordiv, vor.data(), div.data(), rgp1.data())); spectral_transform_grid_analytic(trc, false, n, m, imag, gu, rspecg.data(), rgp_analytic2.data(), ivar_in, ivar_out); //Log::info() << icase << " m=" << m << " n=" << n << " imag=" << imag << " unstructured: "; ATLAS_TRACE_SCOPE("unstructured") EXPECT_NO_THROW(transLocal2.invtrans(nb_scalar, sp.data(), nb_vordiv, vor.data(), div.data(), rgp2.data())); int pos = (ivar_out * nb_vordiv + jfld); double rms_gen1 = compute_rms(g.size(), rgp1.data() + pos * g.size(), rgp_analytic1.data()); double rms_gen2 = compute_rms(gu.size(), rgp2.data() + pos * gu.size(), rgp_analytic2.data()); rav1 += rms_gen1; rav2 += rms_gen2; if (!(rms_gen1 < tolerance) || !(rms_gen2 < tolerance)) { Log::info() << "Case " << icase << " ivar_in=" << ivar_in << " ivar_out=" << ivar_out << " m=" << m << " n=" << n << " imag=" << imag << " k=" << k << std::endl; ATLAS_DEBUG_VAR(rms_gen1); ATLAS_DEBUG_VAR(rms_gen2); ATLAS_DEBUG_VAR(tolerance); } EXPECT(rms_gen1 < tolerance); EXPECT(rms_gen2 < tolerance); icase++; auto end = std::chrono::system_clock::now(); // std::chrono::duration elapsed_seconds = end - start; std::time_t end_time = std::chrono::system_clock::to_time_t(end); std::string time_str = std::ctime(&end_time); Log::debug() << "case " << icase << ", elapsed time: " << elapsed_seconds.count() << "s. Now: " << time_str.substr(0, time_str.length() - 1) << std::endl; } k++; } } } } } } Log::info() << "Vordiv+scalar comparison with trans: all " << icase << " cases successfully passed!" << std::endl; rav1 /= icase; Log::info() << "average RMS error of transLocal1: " << rav1 << std::endl; rav2 /= icase; Log::info() << "average RMS error of transLocal2: " << rav2 << std::endl; } #endif //----------------------------------------------------------------------------- #if ATLAS_HAVE_TRANS CASE("test_trans_levels") { Log::info() << "test_trans_levels" << std::endl; // test trans_levels test puts // the real component of spherical harmonic (m=1, n=2) on level 0 // and the imaginary component of spherical harmomic (m=2, n=3) on level 1 // of a regular Gaussian field with two levels. // it then runs directtrans and checks that the spectral coefficients are // correct. // (Note that this test is assuming we are running on 1PE.) // it finally runs the inverse transform and checks that we end up where we // started. std::string grid_uid("F2"); // Regular Gaussian F ( 8 N^2) StructuredGrid g(grid_uid); auto N = GaussianGrid(g).N(); // -> cast to Gaussian grid and get the Gaussian number std::size_t levels = 2; std::vector n{2, 3}; // total wavenumber on levels O and 1 std::vector m{1, 2}; // meridional wavenumber on levels O and 1 std::vector imag{0, 1}; // imaginary component or not const std::vector level_sequence{0, 1}; functionspace::Spectral specFS(2 * N - 1, atlas::option::levels(levels)); functionspace::StructuredColumns gridFS(g, atlas::option::levels(levels)); atlas::trans::Trans transIFS(gridFS, specFS); Log::info() << "transIFS backend" << transIFS.backend() << std::endl; std::vector pointsLonLat; pointsLonLat.reserve(static_cast(g.size())); for (auto ll : g.lonlat()) { pointsLonLat.push_back(ll); } atlas::Field gpf = gridFS.createField(atlas::option::name("gpf")); atlas::Field gpf2 = gridFS.createField(atlas::option::name("gpf2")); atlas::Field spf = specFS.createField(); auto view = atlas::array::make_view(gpf); for (atlas::idx_t j = gridFS.j_begin(); j < gridFS.j_end(); ++j) { for (atlas::idx_t i = gridFS.i_begin(j); i < gridFS.i_end(j); ++i) { atlas::idx_t jn = gridFS.index(i, j); for (atlas::idx_t jl : level_sequence) { view(jn, jl) = sphericalharmonics_analytic_point(n[jl], m[jl], imag[jl], ((pointsLonLat[jn].lon() * M_PI) / 180.), ((pointsLonLat[jn].lat() * M_PI) / 180.), 2, 2); } } } // transform fields to spectral and view transIFS.dirtrans(gpf, spf); auto spView = atlas::array::make_view(spf); Log::info() << "trans spectral coefficients = " << transIFS.spectralCoefficients() << std::endl; Log::info() << "checking the spectral coefficients are correct " << std::endl; int imagSize = 2; std::vector index; for (int jl : level_sequence) { int offset(0); for (int k = 0; k < m[jl]; ++k) { offset += (2 * N - k) * imagSize; } index.push_back(offset + imag[jl] + (n[jl] - m[jl]) * imagSize); } for (int i = 0; i < transIFS.spectralCoefficients(); ++i) { for (int jl : level_sequence) { double value = (i == index[jl] ? 1.0 : 0.0); EXPECT(std::abs(spView(i, jl) - value) < 1e-14); } } transIFS.invtrans(spf, gpf2); Log::info() << "comparing going through direct and inverse transforms " << std::endl; auto view2 = atlas::array::make_view(gpf2); for (atlas::idx_t j = gridFS.j_begin(); j < gridFS.j_end(); ++j) { for (atlas::idx_t i = gridFS.i_begin(j); i < gridFS.i_end(j); ++i) { atlas::idx_t jn = gridFS.index(i, j); for (idx_t jl : level_sequence) { EXPECT(std::abs(view(jn, jl) - view2(jn, jl)) < 1e-14); } } } } #endif #if ATLAS_HAVE_TRANS #if ATLAS_HAVE_ECTRANS || defined(TRANS_HAVE_INVTRANS_ADJ) CASE("test_2level_adjoint_test_with_powerspectrum_convolution") { std::string grid_uid("F64"); // Regular Gaussian F ( 8 N^2) atlas::StructuredGrid g2(grid_uid); auto N = atlas::GaussianGrid(g2).N(); // -> cast to Gaussian grid and get the Gaussian number auto levels = 2; std::vector n{2, 3}; // total wavenumber on levels O and 1 std::vector m{1, 2}; // meridional wavenumber on levels O and 1 std::vector imag{false, true}; // imaginary component or not atlas::functionspace::Spectral specFS(2 * N - 1, atlas::option::levels(levels)); atlas::functionspace::StructuredColumns gridFS( g2, atlas::grid::Partitioner(new atlas::grid::detail::partitioner::TransPartitioner()), atlas::option::levels(levels)); atlas::trans::Trans transIFS(gridFS, specFS); Log::info() << "transIFS backend" << transIFS.backend() << std::endl; std::vector powerSpectrum(2 * N, 0.0); for (std::size_t w = 0; w < powerSpectrum.size(); ++w) { powerSpectrum[w] = 1.0 / static_cast(w + 1); } float tot = std::accumulate(powerSpectrum.cbegin(), powerSpectrum.cend(), 0.0); for (std::size_t w = 0; w < powerSpectrum.size(); ++w) { powerSpectrum[w] = powerSpectrum[w] / tot; } Log::info() << "create a fictitous power spectrum" << atlas::mpi::rank() << " " << powerSpectrum[0] << " " << powerSpectrum[1] << " " << tot << std::endl; atlas::Field gpf = gridFS.createField(atlas::option::name("gpf")); atlas::Field gpf2 = gridFS.createField(atlas::option::name("gpf2")); atlas::Field spf = specFS.createField(atlas::option::name("spf")); atlas::Field spfdiv = specFS.createField(atlas::option::name("spfdv")); auto gpfView = atlas::array::make_view(gpf); for (atlas::idx_t j = gridFS.j_begin(); j < gridFS.j_end(); ++j) { for (atlas::idx_t i = gridFS.i_begin(j); i < gridFS.i_end(j); ++i) { atlas::idx_t jn = gridFS.index(i, j); for (atlas::idx_t jl : {0, 1}) { gpfView(jn, jl) = 0.0; if ((j == gridFS.j_end() - 1) && (i == gridFS.i_end(gridFS.j_end() - 1) - 1) && (jl == 0) && (atlas::mpi::rank() == 0)) { gpfView(jn, jl) = 1.0; } if ((j == gridFS.j_end() - 1) && (i == gridFS.i_end(gridFS.j_end() - 1) - 1) && (jl == 1) && (atlas::mpi::rank() == 1)) { gpfView(jn, jl) = 1.0; } } } } std::vector test_name{"inverse", "direct"}; for (std::size_t test_type = 0; test_type < test_name.size(); ++test_type) { auto spfView = atlas::array::make_view(spf); auto gpf2View = atlas::array::make_view(gpf2); spfView.assign(0.0); gpf2View.assign(0.0); // transform fields to spectral and view if (test_name[test_type].compare("inverse") == 0) { transIFS.invtrans_adj(gpf, spf); } else if (test_name[test_type].compare("direct") == 0) { transIFS.dirtrans(gpf, spf); } const auto zonal_wavenumbers = specFS.zonal_wavenumbers(); const int nb_zonal_wavenumbers = zonal_wavenumbers.size(); double adj_value(0.0); int i{0}; for (int jm = 0; jm < nb_zonal_wavenumbers; ++jm) { const std::size_t m1 = zonal_wavenumbers(jm); for (std::size_t n1 = m1; n1 <= static_cast(2 * N - 1); ++n1) { for ([[maybe_unused]] int imag1 : {0, 1}) { for (int jl : {0, 1}) { // scale by the square root of the power spectrum // note here that we need the modal power spectrum // i.e. divide powerSpectra by (2n +1) spfView(i, jl) = spfView(i, jl) * std::sqrt(std::sqrt(static_cast(powerSpectrum[n1]))) / std::sqrt(static_cast(2 * n1 + 1)); // adjoint at the heart double temp = spfView(i, jl) * spfView(i, jl); adj_value += (m1 > 0 ? 2 * temp : temp); // to take account of -ve m. // scale by the square root of the power spectrum (again!) spfView(i, jl) = spfView(i, jl) * std::sqrt(std::sqrt(static_cast(powerSpectrum[n1]))) / std::sqrt(static_cast(2 * n1 + 1)); } ++i; } } } atlas::mpi::comm().allReduceInPlace(adj_value, eckit::mpi::sum()); // transform fields to spectral and view if (test_name[test_type].compare("inverse") == 0) { transIFS.invtrans(spf, gpf2); } else if (test_name[test_type].compare("direct") == 0) { transIFS.dirtrans_adj(spf, gpf2); } Log::info() << "adjoint test transforms " << test_name[test_type] << std::endl; double adj_value2(0.0); for (atlas::idx_t j = gridFS.j_begin(); j < gridFS.j_end(); ++j) { for (atlas::idx_t i = gridFS.i_begin(j); i < gridFS.i_end(j); ++i) { atlas::idx_t jn = gridFS.index(i, j); for (atlas::idx_t jl : {0, 1}) { adj_value2 += gpfView(jn, jl) * gpf2View(jn, jl); } } } atlas::mpi::comm().allReduceInPlace(adj_value2, eckit::mpi::sum()); Log::info() << "adjoint test " << test_name[test_type] << " " << adj_value << " " << adj_value2 << std::endl; EXPECT(std::abs(adj_value - adj_value2) / adj_value < 1e-12); } } CASE("test_2level_adjoint_test_with_vortdiv") { std::string grid_uid("F64"); // Regular Gaussian F ( 8 N^2) atlas::StructuredGrid g2(grid_uid); auto N = atlas::GaussianGrid(g2).N(); // -> cast to Gaussian grid and get the Gaussian number auto levels = 2; std::vector imag{false, true}; // imaginary component or not atlas::functionspace::Spectral specFS(2 * N - 1, atlas::option::levels(levels)); atlas::functionspace::StructuredColumns gridFS( g2, atlas::grid::Partitioner(new atlas::grid::detail::partitioner::TransPartitioner()), atlas::option::levels(levels)); atlas::trans::Trans transIFS(gridFS, specFS); atlas::Field spfvor = specFS.createField(atlas::option::name("spfvor")); atlas::Field spfdiv = specFS.createField(atlas::option::name("spfdiv")); atlas::Field gpfuv = gridFS.createField(atlas::option::name("gpfuv") | atlas::option::variables(2)); atlas::Field gpfuv2 = gridFS.createField(atlas::option::name("gpfuv") | atlas::option::variables(2)); auto gpfuvView = atlas::array::make_view(gpfuv); for (atlas::idx_t j = gridFS.j_begin(); j < gridFS.j_end(); ++j) { for (atlas::idx_t i = gridFS.i_begin(j); i < gridFS.i_end(j); ++i) { atlas::idx_t jn = gridFS.index(i, j); for (atlas::idx_t jl : {0, 1}) { gpfuvView(jn, jl, 0) = 0.0; gpfuvView(jn, jl, 1) = 0.0; if ((j == gridFS.j_end() - 1) && (i == gridFS.i_end(gridFS.j_end() - 1) - 1) && (jl == 0) && (atlas::mpi::rank() == 0)) { gpfuvView(jn, jl, 0) = 10.0; gpfuvView(jn, jl, 1) = 1.0; } if ((j == gridFS.j_end() - 1) && (i == gridFS.i_end(gridFS.j_end() - 1) - 1) && (jl == 1) && (atlas::mpi::rank() == 1)) { gpfuvView(jn, jl, 0) = 10.0; gpfuvView(jn, jl, 1) = 1.0; } } } } auto spfdivView = atlas::array::make_view(spfdiv); auto spfvorView = atlas::array::make_view(spfvor); auto gpfuv2View = atlas::array::make_view(gpfuv2); spfdivView.assign(0.0); spfvorView.assign(0.0); gpfuv2View.assign(0.0); transIFS.dirtrans_wind2vordiv(gpfuv, spfvor, spfdiv); const auto zonal_wavenumbers = specFS.zonal_wavenumbers(); const int nb_zonal_wavenumbers = zonal_wavenumbers.size(); double adj_value(0.0); int i{0}; for (int jm = 0; jm < nb_zonal_wavenumbers; ++jm) { const std::size_t m1 = zonal_wavenumbers(jm); for (std::size_t n1 = m1; n1 <= static_cast(2 * N - 1); ++n1) { for ([[maybe_unused]] int imag1 : {0, 1}) { for (int jl : {0, 1}) { // adjoint at the heart double temp = spfdivView(i, jl) * spfdivView(i, jl) + spfvorView(i, jl) * spfvorView(i, jl); adj_value += (m1 > 0 ? 2 * temp : temp); // to take account of -ve m. } ++i; } } } atlas::mpi::comm().allReduceInPlace(adj_value, eckit::mpi::sum()); transIFS.dirtrans_wind2vordiv_adj(spfvor, spfdiv, gpfuv2); double adj_value2(0.0); for (atlas::idx_t j = gridFS.j_begin(); j < gridFS.j_end(); ++j) { for (atlas::idx_t i = gridFS.i_begin(j); i < gridFS.i_end(j); ++i) { atlas::idx_t jn = gridFS.index(i, j); for (atlas::idx_t jl : {0, 1}) { adj_value2 += gpfuv2View(jn, jl, 0) * gpfuvView(jn, jl, 0) + gpfuv2View(jn, jl, 1) * gpfuvView(jn, jl, 1); } } } atlas::mpi::comm().allReduceInPlace(adj_value2, eckit::mpi::sum()); Log::info() << "adjoint test for wind2vordiv" << " " << adj_value << " " << adj_value2 << std::endl; EXPECT(std::abs(adj_value - adj_value2) / adj_value < 1e-12); } #endif #endif #if 0 CASE( "test_trans_fourier_truncation" ) { Log::info() << "test_trans_fourier_truncation" << std::endl; // test transgeneral by comparing its result with the trans library // this test is based on the test_nomesh case in test_trans.cc Grid g( "F640" ); grid::StructuredGrid gs( g ); int ndgl = gs.ny(); //int trc = 2*ndgl; // extreme high truncation (below linear) int trc = ndgl - 1; // linear //int trc = 5./6.*ndgl-1; // between linear and quadratic //int trc = 2./3.*ndgl-1; // quadratic //int trc = ndgl/2. -1; // cubic trans::Trans trans( g, trc ); for ( int j = 0; j < gs.ny(); j += 80 ) { double lat = gs.y( j ) * util::Constants::degreesToRadians(); int trcFT = trans::fourier_truncation( trc, gs.nx( j ), gs.nxmax(), gs.ny(), lat, grid::RegularGrid( g ) ); Log::info() << trcFT << " " << gs.nx( j ) << std::endl; } // TODO: create some real criterion to test fourier_truncation. So far only comparison with trans library through print statements. } #endif //----------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/trans/test_trans_split_comm.cc0000664000175000017500000000667415160212070023103 0ustar alastairalastair/* * (C) Crown Copyright 2025 Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * */ #include #include #include #include #include "eckit/mpi/Comm.h" #include "atlas/array.h" #include "atlas/functionspace/Spectral.h" #include "atlas/functionspace/StructuredColumns.h" #include "atlas/grid.h" #include "atlas/grid/Partitioner.h" #include "atlas/grid/detail/partitioner/TransPartitioner.h" #include "atlas/mesh/Mesh.h" #include "atlas/meshgenerator.h" #include "atlas/option.h" #include "atlas/output/Gmsh.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/trans/Trans.h" #include "atlas/util/function/VortexRollup.h" #include "tests/AtlasTestEnvironment.h" using atlas::grid::detail::partitioner::TransPartitioner; namespace atlas { namespace test { int getColour(const int world_rank = mpi::comm("world").rank(), const int world_size = mpi::comm("world").size()) { // 1:3 ratio. constexpr float ratio = 0.25; if (static_cast(world_rank + 1) / static_cast(world_size) <= ratio) { return 0; } else { return 1; } } struct AtlasSplitCommEnv : public AtlasTestEnvironment { AtlasSplitCommEnv(int argc, char* argv[]): AtlasTestEnvironment(argc, argv) { ATLAS_ASSERT(mpi::comm().size() > 1); // Split world communicator and set default comm to the split communicator. mpi::comm().split(getColour(), "split_comm"); eckit::mpi::setCommDefault("split_comm"); } }; // Each MPI colour in the test does a different resolution transform. size_t gaussResolutionForComm() { static constexpr std::array resols = {20, 60}; return resols[getColour()]; } CASE("test_trans_split_comm") { const size_t resol = gaussResolutionForComm(); const std::string grid_uid = "O" + std::to_string(resol); StructuredGrid g(grid_uid); auto N = GaussianGrid(g).N(); functionspace::Spectral specFS(2 * N - 1); functionspace::StructuredColumns gridFS(g, grid::Partitioner(new grid::detail::partitioner::TransPartitioner())); trans::Trans transIFS(gridFS, specFS); Field specField = specFS.createField(option::name("specfield")); // Make vortex rollup field on the grid. atlas::Field vortexField = gridFS.createField(atlas::option::name("vortex")); atlas::Field fieldOut = gridFS.createField(atlas::option::name("out")); const auto lonlat = atlas::array::make_view(vortexField.functionspace().lonlat()); auto view = atlas::array::make_view(vortexField); for (atlas::idx_t ij = 0; ij < view.shape(0); ++ij) { constexpr double zz = 1.0; view(ij) = atlas::util::function::vortex_rollup(lonlat(ij, 0), lonlat(ij, 1), zz); } // Go to and from spectral space. transIFS.dirtrans(vortexField, specField); transIFS.invtrans(specField, fieldOut); // Output. Writes two files - one for each split communicator. { Mesh mesh = StructuredMeshGenerator().generate(g); output::Gmsh gmsh(grid_uid + "-grid-split_comm_" + std::to_string(getColour()) + ".msh"); gmsh.write(mesh); gmsh.write(fieldOut, gridFS); } } } // namespace test } // namespace atlas //-- int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/trans/fctest_trans.F900000664000175000017500000003653115160212070021132 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. ! @author Willem Deconinck #include "fckit/fctest.h" ! ----------------------------------------------------------------------------- module fctest_atlas_trans_fixture use atlas_module use fckit_module use iso_c_binding implicit none character(len=1024) :: msg type(fckit_mpi_comm) :: mpi end module fctest_atlas_trans_fixture ! ----------------------------------------------------------------------------- TESTSUITE_WITH_FIXTURE(fctest_atlas_trans,fctest_atlas_trans_fixture) ! ----------------------------------------------------------------------------- TESTSUITE_INIT call fckit_main%init() mpi = fckit_mpi_comm() if( mpi%rank() /= 0 ) then call fckit_log%reset() endif call atlas_library%initialise() END_TESTSUITE_INIT ! ----------------------------------------------------------------------------- TESTSUITE_FINALIZE write(msg,*) "TESTSUITE_FINALIZE"; call fckit_log%info(msg) call atlas_library%finalise() END_TESTSUITE_FINALIZE ! ----------------------------------------------------------------------------- TEST( test_trans_backend ) type(atlas_Trans) :: trans FCTEST_CHECK( trans%has_backend("local") ) FCTEST_CHECK( trans%has_backend("ectrans") ) if( trans%has_backend("ectrans") ) then FCTEST_CHECK_EQUAL( trans%backend(), "ectrans" ) else FCTEST_CHECK_EQUAL( trans%backend(), "local" ) endif END_TEST ! ----------------------------------------------------------------------------- TEST( test_trans ) type(atlas_StructuredGrid) :: grid type(atlas_StructuredGrid) :: trans_grid type(atlas_MeshGenerator) :: meshgenerator type(atlas_Partitioner) :: partitioner type(atlas_Mesh) :: mesh type(atlas_Trans) :: trans type(atlas_mesh_Nodes) :: nodes type(atlas_functionspace_NodeColumns) :: nodes_fs type(atlas_functionspace_Spectral) :: spectral_fs, trans_spectral type(atlas_Field) :: scalarfield1, scalarfield2 type(atlas_Field) :: windfield type(atlas_Field) :: vorfield,divfield type(atlas_Field) :: spectralfield1, spectralfield2 type(atlas_FieldSet) :: scalarfields type(atlas_FieldSet) :: spectralfields real(c_double), pointer :: scal1(:,:), scal2(:), spec1(:,:), spec2(:), wind(:,:,:), vor(:,:), div(:,:) real(c_double), allocatable :: check(:) integer :: nlev, truncation type(atlas_Field) :: glb_vorfield real(c_double) :: tol tol = 1.e-8 nlev=10 truncation = 21 grid = atlas_StructuredGrid("O24") FCTEST_CHECK_EQUAL( grid%owners(), 1 ) partitioner = atlas_Partitioner(type="ectrans") meshgenerator = atlas_MeshGenerator() mesh = meshgenerator%generate(grid,partitioner) call meshgenerator%final() FCTEST_CHECK_EQUAL( mesh%owners(), 1 ) FCTEST_CHECK_EQUAL( grid%owners(), 2 ) ! mesh tracks grid trans = atlas_Trans(grid,truncation) FCTEST_CHECK_EQUAL( grid%owners(), 3 ) ! trans tracks grid ! FCTEST_CHECK_EQUAL( trans%nb_gridpoints_global(), int(grid%size()) ) trans_grid = trans%grid() FCTEST_CHECK_EQUAL( trans_grid%owners(), 4 ) FCTEST_CHECK( .not. trans%is_null() ) FCTEST_CHECK_EQUAL( trans%truncation(), truncation ) spectral_fs = trans%spectral() FCTEST_CHECK_EQUAL( spectral_fs%nb_spectral_coefficients_global(), (truncation+1)*(truncation+2) ) nodes = mesh%nodes() nodes_fs = atlas_functionspace_NodeColumns(mesh,0) write(msg,*) "nodes_fs%owners()",nodes_fs%owners(); call fckit_log%info(msg) scalarfield1 = nodes_fs%create_field(name="scalar1",kind=atlas_real(c_double),levels=nlev) write(msg,*) "nodes_fs%owners()",nodes_fs%owners(); call fckit_log%info(msg) scalarfield2 = nodes_fs%create_field(name="scalar2",kind=atlas_real(c_double)) write(msg,*) "nodes_fs%owners()",nodes_fs%owners(); call fckit_log%info(msg) write(msg,*) "spectral_fs%owners()",spectral_fs%owners(); call fckit_log%info(msg) FCTEST_CHECK_EQUAL( spectral_fs%nb_spectral_coefficients_global(), (truncation+1)*(truncation+2) ) spectralfield1 = spectral_fs%create_field(name="spectral1",kind=atlas_real(c_double),levels=nlev) write(msg,*) "spectral_fs%owners()",spectral_fs%owners(); call fckit_log%info(msg) spectralfield2 = spectral_fs%create_field(name="spectral2",kind=atlas_real(c_double)) write(msg,*) "spectral_fs%owners()",spectral_fs%owners(); call fckit_log%info(msg) call scalarfield1%data(scal1) call scalarfield2%data(scal2) call spectralfield1%data(spec1) call spectralfield2%data(spec2) write(msg,*) "shape = ", spectralfield2%shape(); call fckit_log%info(msg) ! All waves to zero except wave 1 to 3 spec1(1:nlev,:) = 0 spec1(1:nlev,1) = 3 ! All waves to zero except wave 1 to 4 spec2(:) = 0 spec2(1) = 4 call trans%invtrans(spectralfield1,scalarfield1) call trans%dirtrans(scalarfield1,spectralfield1) allocate( check(nlev) ) check(:) = 3 FCTEST_CHECK_CLOSE( spec1(:,1), check, tol ) deallocate( check ) scalarfields = atlas_FieldSet("scalarfields") call scalarfields%add(scalarfield1) call scalarfields%add(scalarfield2) spectralfields = atlas_FieldSet("spectralfields") call spectralfields%add(spectralfield1) call spectralfields%add(spectralfield2) call trans%invtrans(spectralfields,scalarfields) call trans%dirtrans(scalarfields,spectralfields) allocate( check(nlev) ) check(:) = 3 FCTEST_CHECK_CLOSE( spec1(:,1), check, tol ) check(:) = 0 FCTEST_CHECK_CLOSE( spec1(:,2), check, tol ) FCTEST_CHECK_CLOSE( spec1(:,3), check, tol ) FCTEST_CHECK_CLOSE( spec1(:,4), check, tol ) FCTEST_CHECK_CLOSE( spec1(:,5), check, tol ) deallocate( check ) FCTEST_CHECK_CLOSE( spec2(1), 4._c_double, tol ) FCTEST_CHECK_CLOSE( spec2(2), 0._c_double, tol ) FCTEST_CHECK_CLOSE( spec2(3), 0._c_double, tol ) FCTEST_CHECK_CLOSE( spec2(4), 0._c_double, tol ) FCTEST_CHECK_CLOSE( spec2(5), 0._c_double, tol ) windfield = nodes_fs%create_field(name="wind",kind=atlas_real(c_double),levels=nlev,variables=3) call windfield%data(wind) wind(:,:,:) = 0._c_double write(msg,*) "nodes_fs%owners()",nodes_fs%owners(); call fckit_log%info(msg) vorfield = spectral_fs%create_field(name="vorticity",kind=atlas_real(c_double),levels=nlev) write(msg,*) "spectral_fs%owners()",spectral_fs%owners() call vorfield%data(vor) divfield = spectral_fs%create_field(name="divergence",kind=atlas_real(c_double),levels=nlev) write(msg,*) "spectral_fs%owners()",spectral_fs%owners(); call fckit_log%info(msg) call divfield%data(div) call trans%dirtrans_wind2vordiv(windfield,vorfield,divfield) call trans%invtrans_vordiv2wind(vorfield,divfield,windfield) glb_vorfield = spectral_fs%create_field(name="vorticity",kind=atlas_real(c_double),levels=nlev,global=.true.) if( .not. vorfield%contiguous() ) then call fckit_log%error("No support for gather if fields are not contiguous") return endif call spectral_fs%gather(vorfield,glb_vorfield) call spectral_fs%scatter(glb_vorfield,vorfield) write(msg,*) "cleaning up"; call fckit_log%info(msg) write(msg,*) "nodes_fs%owners()",nodes_fs%owners(); call fckit_log%info(msg) write(msg,*) "spectral_fs%owners()",spectral_fs%owners(); call fckit_log%info(msg) call scalarfield1%final() call scalarfield2%final() call spectralfield1%final() write(msg,*) "spectral_fs%owners()",spectral_fs%owners(); call fckit_log%info(msg) call spectralfield2%final() write(msg,*) "spectral_fs%owners()",spectral_fs%owners(); call fckit_log%info(msg) call windfield%final() call vorfield%final() call divfield%final() call glb_vorfield%final() write(msg,*) "nodes_fs%owners()",nodes_fs%owners(); call fckit_log%info(msg) call nodes_fs%final() write(msg,*) "spectral_fs%owners()",spectral_fs%owners(); call fckit_log%info(msg) call spectral_fs%final() call scalarfields%final() call spectralfields%final() call mesh%final() call trans%final() call grid%final() END_TEST ! ----------------------------------------------------------------------------- TEST( test_trans_nomesh ) type(atlas_StructuredGrid) :: grid type(atlas_Trans) :: trans type(atlas_Partitioner) :: partitioner type(atlas_functionspace_StructuredColumns) :: gridpoints_fs type(atlas_functionspace_Spectral) :: spectral_fs type(atlas_Field) :: scalarfield1, scalarfield2 type(atlas_Field) :: spectralfield1, spectralfield2 type(atlas_FieldSet) :: scalarfields type(atlas_FieldSet) :: spectralfields real(c_double), pointer :: scal1(:,:), scal2(:), spec1(:,:), spec2(:) real(c_double), allocatable :: check(:) integer :: nlev, truncation real(c_double) :: tol tol = 1.e-8 nlev=10 truncation = 21 grid = atlas_StructuredGrid("O24") trans = atlas_Trans(grid,truncation) partitioner = atlas_Partitioner(type="ectrans") gridpoints_fs = atlas_functionspace_StructuredColumns(grid,partitioner) scalarfield1 = gridpoints_fs%create_field(name="scalar1",kind=atlas_real(c_double),levels=nlev) scalarfield2 = gridpoints_fs%create_field(name="scalar2",kind=atlas_real(c_double)) spectral_fs = atlas_functionspace_Spectral(trans%truncation()) spectralfield1 = spectral_fs%create_field(name="spectral1",kind=atlas_real(c_double),levels=nlev) spectralfield2 = spectral_fs%create_field(name="spectral2",kind=atlas_real(c_double)) call scalarfield1%data(scal1) call scalarfield2%data(scal2) call spectralfield1%data(spec1) call spectralfield2%data(spec2) ! All waves to zero except wave 1 to 3 spec1(1:nlev,:) = 0 spec1(1:nlev,1) = 3 ! All waves to zero except wave 1 to 4 spec2(:) = 0 spec2(1) = 4 call atlas_log%debug("invtrans") call trans%invtrans(spectralfield1,scalarfield1) call atlas_log%debug("dirtrans") call trans%dirtrans(scalarfield1,spectralfield1) allocate( check(nlev) ) check(:) = 3 FCTEST_CHECK_CLOSE( spec1(:,1), check, tol ) deallocate( check ) scalarfields = atlas_FieldSet("scalarfields") call scalarfields%add(scalarfield1) call scalarfields%add(scalarfield2) spectralfields = atlas_FieldSet("spectralfields") call spectralfields%add(spectralfield1) call spectralfields%add(spectralfield2) call trans%invtrans(spectralfields,scalarfields) call trans%dirtrans(scalarfields,spectralfields) allocate( check(nlev) ) check(:) = 3 FCTEST_CHECK_CLOSE( spec1(:,1), check, tol ) check(:) = 0 FCTEST_CHECK_CLOSE( spec1(:,2), check, tol ) FCTEST_CHECK_CLOSE( spec1(:,3), check, tol ) FCTEST_CHECK_CLOSE( spec1(:,4), check, tol ) FCTEST_CHECK_CLOSE( spec1(:,5), check, tol ) deallocate( check ) FCTEST_CHECK_CLOSE( spec2(1), 4._c_double, tol ) FCTEST_CHECK_CLOSE( spec2(2), 0._c_double, tol ) FCTEST_CHECK_CLOSE( spec2(3), 0._c_double, tol ) FCTEST_CHECK_CLOSE( spec2(4), 0._c_double, tol ) FCTEST_CHECK_CLOSE( spec2(5), 0._c_double, tol ) write(msg,*) "cleaning up"; call fckit_log%info(msg) call scalarfield1%final() call scalarfield2%final() call spectralfield1%final() call spectralfield2%final() call spectral_fs%final() call gridpoints_fs%final() call scalarfields%final() call spectralfields%final() call trans%final() call grid%final() END_TEST TEST( test_transdwarf ) type(atlas_StructuredGrid) :: grid type(atlas_Partitioner) :: partitioner type(atlas_Trans) :: trans type(atlas_functionspace_StructuredColumns) :: gridpoints type(atlas_functionspace_Spectral) :: spectral type(atlas_Field) :: fieldg, field type(atlas_FieldSet) :: gpfields, spfields integer :: jfld, nfld real(c_double) :: norm real(c_double), pointer :: gvar(:) grid = atlas_StructuredGrid("O24") trans = atlas_Trans(grid,23) partitioner = atlas_Partitioner("ectrans") gridpoints = atlas_functionspace_StructuredColumns(grid,partitioner) spectral = atlas_functionspace_Spectral(trans%truncation()) gpfields = atlas_FieldSet("gridpoint") spfields = atlas_FieldSet("spectral") nfld=10 do jfld=1,nfld fieldg = gridpoints%create_field(atlas_real(c_double),global=.true.) field = gridpoints%create_field(atlas_real(c_double)) ! Read global field data ! ... FCTEST_CHECK_EQUAL( fieldg%rank(), 1 ) call fieldg%data(gvar) if( mpi%rank() == 0 ) then gvar(:) = 0. endif call gridpoints%scatter(fieldg,field) call gpfields%add( field ) call spfields%add( spectral%create_field(atlas_real(c_double)) ) FCTEST_CHECK_EQUAL( field%owners(), 2 ) enddo call trans%dirtrans(gpfields,spfields) call trans%invtrans(spfields,gpfields) do jfld=1,spfields%size() field = spfields%field(jfld) write(msg,*) "spectral field ",field%name(); call atlas_log%info(msg) if( .not. field%contiguous() ) then call fckit_log%error("No support for norm if fields are not contiguous") return endif call spectral%norm(field,norm) write(msg,*) "norm = ",norm; call atlas_log%info(msg) enddo call field%final() call fieldg%final() call gpfields%final() call spfields%final() call gridpoints%final() call spectral%final() call trans%final() call grid%final() END_TEST TEST( test_spectral_only ) type(atlas_functionspace_Spectral) :: spectral type(atlas_Field) :: field, fieldg integer :: jfld, nfld, T integer, parameter :: RE=0 integer, parameter :: IM=1 integer :: jm, jn, n, m, jc real(c_double), pointer :: sp(:,:) integer, pointer :: nmyms(:), nvalue(:), nasm0(:) spectral = atlas_functionspace_Spectral(truncation=21,levels=10) T = spectral%truncation() nfld = spectral%levels() field = spectral%create_field(kind=atlas_real(c_double),levels=0) FCTEST_CHECK_EQUAL(field%rank(), 1) field = spectral%create_field(atlas_real(c_double)) FCTEST_CHECK_EQUAL(field%rank(), 2) FCTEST_CHECK_EQUAL(field%shape(1), nfld) ! Initialise distributed field, plus checks call field%data(sp) nmyms => spectral%nmyms() nvalue => spectral%nvalue() nasm0 => spectral%nasm0() jc = 1 do jm=1,spectral%nump() m = nmyms(jm) FCTEST_CHECK_EQUAL( nasm0(m), jc ) do jn=0,T-m n = m + jn FCTEST_CHECK( jc <= spectral%nb_spectral_coefficients() - 1 ) FCTEST_CHECK_EQUAL( nvalue(jc), n ) do jfld=1,nfld sp(jfld,jc+RE) = sp_value(m,n,RE) sp(jfld,jc+IM) = sp_value(m,n,IM) enddo jc = jc+2 enddo enddo if( .not. field%contiguous() ) then call fckit_log%error("No support for gather if fields are not contiguous") return endif fieldg = spectral%create_field(atlas_real(c_double),global=.true.) call spectral%gather(field,fieldg) ! Check global field (gathered matches distributed field) call fieldg%data(sp) T = spectral%truncation() if( mpi%rank() == 0 ) then write(0,*) spectral%nb_spectral_coefficients_global() jc = 1 do m=0,T do n=m,T FCTEST_CHECK( jc <= spectral%nb_spectral_coefficients_global() - 1 ) do jfld=1,nfld FCTEST_CHECK_EQUAL( sp(jfld,jc+RE), sp_value(m,n,RE) ) FCTEST_CHECK_EQUAL( sp(jfld,jc+IM), sp_value(m,n,IM) ) enddo jc = jc+2 enddo enddo endif call field%final() call spectral%final() contains function sp_value(m,n,complex_component) real(c_double) :: sp_value integer :: m, n, complex_component if( complex_component == RE ) then sp_value = m*T+n else if( m == 0 ) then sp_value = 0 else sp_value = -m*T+n endif endif end function END_TEST END_TESTSUITE atlas-0.46.0/src/tests/trans/CMakeLists.txt0000664000175000017500000000634115160212070020707 0ustar alastairalastair# (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. if( HAVE_FCTEST ) if( atlas_HAVE_ECTRANS ) add_fctest( TARGET atlas_fctest_trans LINKER_LANGUAGE Fortran SOURCES fctest_trans.F90 LIBS atlas_f CONDITION eckit_HAVE_MPI AND ( transi_HAVE_MPI OR ectrans_HAVE_MPI ) AND MPI_SLOTS GREATER_EQUAL 4 MPI 4 ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) endif() if( atlas_HAVE_ECTRANS ) add_fctest( TARGET atlas_fctest_trans_invtrans_grad LINKER_LANGUAGE Fortran SOURCES fctest_trans_invtrans_grad.F90 LIBS atlas_f ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) endif() add_fctest( TARGET atlas_fctest_trans_unstructured LINKER_LANGUAGE Fortran SOURCES fctest_trans_unstructured.F90 LIBS atlas_f ) endif() ecbuild_add_test( TARGET atlas_test_trans MPI 4 SOURCES test_trans.cc CONDITION atlas_HAVE_ECTRANS AND eckit_HAVE_MPI AND ( transi_HAVE_MPI OR ectrans_HAVE_MPI ) AND MPI_SLOTS GREATER_EQUAL 4 LIBS atlas transi ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) ecbuild_add_test( TARGET atlas_test_trans_split_comm SOURCES test_trans_split_comm.cc LIBS atlas transi ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ATLAS_FPE=FE_OVERFLOW,FE_DIVBYZERO CONDITION atlas_HAVE_ECTRANS AND eckit_HAVE_MPI AND ectrans_HAVE_MPI AND ectrans_VERSION GREATER_EQUAL 1.8 AND MPI_SLOTS GREATER_EQUAL 4 MPI 4 ) ecbuild_add_test( TARGET atlas_test_trans_serial SOURCES test_trans.cc CONDITION atlas_HAVE_ECTRANS LIBS atlas transi ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) ecbuild_add_test( TARGET atlas_test_trans_invtrans_grad SOURCES test_trans_invtrans_grad.cc CONDITION atlas_HAVE_ECTRANS LIBS atlas transi ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) # Note: This duplication is needed because transi is a private library of atlas, # and this tests needs access to the transi include directories. # ToDo: Fix this inside the test code so that we don't directly need to include transi headers. if( atlas_HAVE_ECTRANS ) set( atlas_test_transgeneral_extra_libs transi ) endif() ecbuild_add_test( TARGET atlas_test_transgeneral_fftw SOURCES test_transgeneral.cc LIBS atlas ${atlas_test_transgeneral_extra_libs} ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ATLAS_TRACE_REPORT=1 CONDITION atlas_HAVE_FFTW ) ecbuild_add_test( TARGET atlas_test_transgeneral_pocketfft SOURCES test_transgeneral.cc LIBS atlas ${atlas_test_transgeneral_extra_libs} ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ATLAS_TRACE_REPORT=1 ATLAS_LINALG_FFT_BACKEND=pocketfft CONDITION atlas_HAVE_POCKETFFT ) ecbuild_add_test( TARGET atlas_test_trans_localcache SOURCES test_trans_localcache.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ATLAS_TRACE_REPORT=1 CONDITION atlas_HAVE_ATLAS_TRANS ) atlas-0.46.0/src/tests/output/0000775000175000017500000000000015160212070016354 5ustar alastairalastairatlas-0.46.0/src/tests/output/test_gmsh.cc0000664000175000017500000000245015160212070020661 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "atlas/mesh/Mesh.h" #include "atlas/output/Gmsh.h" #include "atlas/output/Output.h" #include "tests/AtlasTestEnvironment.h" #include "tests/TestMeshes.h" namespace atlas { namespace test { //----------------------------------------------------------------------------- CASE("test_gmsh_output_1") { Mesh mesh = test::generate_mesh(Grid("N32")); output::Gmsh gmsh("test_gmsh_output_1.msh"); gmsh.write(mesh); } CASE("test_gmsh_output_2") { Mesh mesh = test::generate_mesh(Grid("N32")); atlas::output::GmshFileStream file("test_gmsh_output_2.msh", "w"); Log::warning() << "TODO: Not yet implemented!!! ATLAS-254" << std::endl; // output::Gmsh gmsh( file ); // gmsh.write( mesh ); } //----------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/output/test_pointcloud_io.cc0000664000175000017500000003623215160212070022577 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include #include #include #include "eckit/types/FloatCompare.h" #include "atlas/array/Array.h" #include "atlas/array/MakeView.h" #include "atlas/field/Field.h" #include "atlas/field/FieldSet.h" #include "atlas/functionspace/FunctionSpace.h" #include "atlas/functionspace/NodeColumns.h" #include "atlas/grid/Grid.h" #include "atlas/grid/detail/grid/Unstructured.h" #include "atlas/library/config.h" #include "atlas/mesh/Mesh.h" #include "atlas/mesh/Nodes.h" #include "atlas/output/detail/PointCloudIO.h" #include "atlas/parallel/mpi/mpi.h" #include "tests/AtlasTestEnvironment.h" //---------------------------------------------------------------------------------------------------------------------- namespace atlas { namespace { using mdspan_xy = mdspan>; mdspan_xy make_mdspan(const atlas::Field& xy) { return mdspan_xy{xy.array().host_data(), xy.shape(0), 2 }; } } namespace test_arrays { const size_t nb_pts = 5; const size_t nb_fld = 2; const size_t nb_columns = 2 + nb_fld; const double lon[] = {-31.233, -28.717, -27.217, -25.750, -16.917}; const double lat[] = {39.467, 38.583, 38.483, 37.817, 32.650}; const double helper_f1[] = {1., 2., 3., 4., 5.}; const double helper_f2[] = {-0.1, -0.2, -0.3, -0.4, -0.5}; const double* fvalues[] = {helper_f1, helper_f2}; const char* fnames[] = {" f_1 ", "f 2 "}; } // namespace test_arrays namespace test_vectors { const size_t nb_pts = test_arrays::nb_pts; const size_t nb_fld = test_arrays::nb_fld; const size_t nb_columns = test_arrays::nb_columns; const std::vector lon(test_arrays::lon, test_arrays::lon + nb_pts); const std::vector lat(test_arrays::lat, test_arrays::lat + nb_pts); std::vector helper_f1(test_arrays::helper_f1, test_arrays::helper_f1 + nb_pts); std::vector helper_f2(test_arrays::helper_f2, test_arrays::helper_f2 + nb_pts); std::vector* helper_fvalues[] = {&helper_f1, &helper_f2}; const std::vector*> fvalues(helper_fvalues, helper_fvalues + nb_fld); const std::vector fnames(test_arrays::fnames, test_arrays::fnames + nb_fld); } // namespace test_vectors void test_write_file(const std::string& file_path, const size_t& nb_pts, const size_t& nb_columns) { REQUIRE(nb_pts > 0); std::ofstream f(file_path.c_str()); if (!f.is_open()) { throw eckit::CantOpenFile(file_path); } f << "PointCloudIO " << nb_pts << " " << nb_columns << " lon lat f_1 " "__f3 more resilience \n" "-31.233 39.467 1. -0.1\n" "-28.717 38.583 2. -0.2 even more " "resilience\n" "-27.217 38.483 3. -0.3\n" "-25.750 37.817 4. -0.4\n" "-16.917 32.650 5. -0.5\n"; } void test_write_file_bad(const std::string& file_path) { std::ofstream f(file_path.c_str()); if (!f.is_open()) { throw eckit::CantOpenFile(file_path); } f << '?'; } } // end anonymous namespace // ------------------------------------------------------------------ namespace atlas { namespace test { // ------------------------------------------------------------------ CASE("read_inexistent_file") { EXPECT_THROWS_AS(output::detail::PointCloudIO::read("pointcloud.txt_should_not_exist"), eckit::CantOpenFile); } CASE("read_badly_formatted_file") { test_write_file_bad("pointcloud.txt"); EXPECT_THROWS_AS(output::detail::PointCloudIO::read("pointcloud.txt"), eckit::Exception); } CASE("read_grid_sample_file") { // test sample file, header properly formatted (some fluff is present) test_write_file("pointcloud.txt", test_arrays::nb_pts, test_arrays::nb_columns); Log::info() << "pointcloud.txt created" << std::endl; Mesh mesh = output::detail::PointCloudIO::read("pointcloud.txt"); Log::info() << "Mesh created" << std::endl; Grid grid(new grid::detail::grid::Unstructured(make_mdspan(mesh.nodes().xy()))); EXPECT(grid); EXPECT(grid.size() == test_arrays::nb_pts); EXPECT(mesh.nodes().has_field("f_1") == true); EXPECT(mesh.nodes().has_field("f3") == true); } CASE("read_grid_sample_file_header_less_rows") { Log::info() << "Creating Mesh..." << std::endl; // test sample file with (wrong) header with less rows test_write_file("pointcloud.txt", test_arrays::nb_pts - 2, test_arrays::nb_columns); Log::info() << "Creating Mesh..." << std::endl; Mesh mesh = output::detail::PointCloudIO::read("pointcloud.txt"); Log::info() << "Creating Mesh...done" << std::endl; Grid grid(new grid::detail::grid::Unstructured(make_mdspan(mesh.nodes().xy()))); EXPECT(grid); EXPECT(grid.size() == test_arrays::nb_pts - 2); EXPECT(mesh.nodes().has_field("f_1") == true); EXPECT(mesh.nodes().has_field("f3") == true); } CASE("read_grid_sample_file_header_less_columns_1") { // test sample file with (wrong) header with one field less test_write_file("pointcloud.txt", test_arrays::nb_pts, test_arrays::nb_columns - 1); Mesh mesh = output::detail::PointCloudIO::read("pointcloud.txt"); Grid grid(new grid::detail::grid::Unstructured(make_mdspan(mesh.nodes().xy()))); EXPECT(grid); EXPECT(grid.size() == test_arrays::nb_pts); EXPECT(mesh.nodes().has_field("f_1") == true); EXPECT(mesh.nodes().has_field("f3") == false); } CASE("read_grid_sample_file_header_less_columns_2") { // test sample file with (wrong) header with no fields test_write_file("pointcloud.txt", test_arrays::nb_pts, test_arrays::nb_columns - test_arrays::nb_fld); Mesh mesh = output::detail::PointCloudIO::read("pointcloud.txt"); Grid grid(new grid::detail::grid::Unstructured(make_mdspan(mesh.nodes().xy()))); EXPECT(grid); EXPECT(grid.size() == test_arrays::nb_pts); EXPECT(mesh.nodes().has_field("f_1") == false); EXPECT(mesh.nodes().has_field("f3") == false); } CASE("write_array") { std::ifstream f; std::string signature, str_lon, str_lat, str_f1, str_f2; size_t nb_pts, nb_columns; output::detail::PointCloudIO::write("pointcloud.txt", test_arrays::nb_pts, test_arrays::lon, test_arrays::lat); f.open("pointcloud.txt"); EXPECT(f.is_open()); f >> signature >> nb_pts >> nb_columns >> str_lon >> str_lat >> str_f1 >> str_f2; f.close(); EXPECT(nb_pts == test_arrays::nb_pts); EXPECT(nb_columns == test_arrays::nb_columns - test_arrays::nb_fld); EXPECT(str_lon == "lon"); EXPECT(str_lat == "lat"); EXPECT(str_f1 != "f_1"); // (this column is not written) EXPECT(str_f2 != "f____2"); // (this column is not written) } CASE("write_array_less_rows") { std::ifstream f; std::string signature, str_lon, str_lat, str_f1, str_f2; size_t nb_pts, nb_columns; output::detail::PointCloudIO::write("pointcloud.txt", test_arrays::nb_pts - 1 /* deliberate */, test_arrays::lon, test_arrays::lat, test_arrays::nb_fld, test_arrays::fvalues, test_arrays::fnames); f.open("pointcloud.txt"); EXPECT(f.is_open()); f >> signature >> nb_pts >> nb_columns >> str_lon >> str_lat >> str_f1 >> str_f2; f.close(); EXPECT(nb_pts == test_arrays::nb_pts - 1); // (one row is not written) EXPECT(nb_columns == test_arrays::nb_columns); EXPECT(str_lon == "lon"); EXPECT(str_lat == "lat"); EXPECT(str_f1 == "f_1"); EXPECT(str_f2 == "f____2"); } CASE("write_array_less_columns") { std::ifstream f; std::string signature, str_lon, str_lat, str_f1, str_f2; size_t nb_pts, nb_columns; output::detail::PointCloudIO::write("pointcloud.txt", test_arrays::nb_pts, test_arrays::lon, test_arrays::lat, test_arrays::nb_fld - 1 /* deliberate */, test_arrays::fvalues, test_arrays::fnames); f.open("pointcloud.txt"); EXPECT(f.is_open()); f >> signature >> nb_pts >> nb_columns >> str_lon >> str_lat >> str_f1 >> str_f2; f.close(); EXPECT(nb_pts == test_arrays::nb_pts); EXPECT(nb_columns == test_arrays::nb_columns - 1); // (one column is not written) EXPECT(str_lon == "lon"); EXPECT(str_lat == "lat"); EXPECT(str_f1 == "f_1"); EXPECT(str_f2 != "f____2"); // (this column is not written) } CASE("write_vector_all_fields") { std::ifstream f; std::string signature, str_lon, str_lat, str_f1, str_f2; size_t nb_pts, nb_columns; output::detail::PointCloudIO::write("pointcloud.txt", test_vectors::lon, test_vectors::lat, test_vectors::fvalues, test_vectors::fnames); f.open("pointcloud.txt"); EXPECT(f.is_open()); f >> signature >> nb_pts >> nb_columns >> str_lon >> str_lat >> str_f1 >> str_f2; f.close(); EXPECT(nb_pts == test_vectors::nb_pts); EXPECT(nb_columns == test_vectors::nb_columns); EXPECT(str_lon == "lon"); EXPECT(str_lat == "lat"); EXPECT(str_f1 == "f_1"); EXPECT(str_f2 == "f____2"); } CASE("write_vector_no_fields") { std::ifstream f; std::string signature, str_lon, str_lat, str_f1, str_f2; size_t nb_pts, nb_columns; output::detail::PointCloudIO::write("pointcloud.txt", test_vectors::lon, test_vectors::lat); f.open("pointcloud.txt"); EXPECT(f.is_open()); f >> signature >> nb_pts >> nb_columns >> str_lon >> str_lat >> str_f1 >> str_f2; f.close(); EXPECT(nb_pts == test_vectors::nb_pts); EXPECT(nb_columns == test_vectors::nb_columns - test_vectors::nb_fld); EXPECT(str_lon == "lon"); EXPECT(str_lat == "lat"); EXPECT(str_f1 != "f_1"); // (this column is not written) EXPECT(str_f2 != "f____2"); // (this column is not written) } static double funny_formula(int x) { return ((double)(x)) * std::pow((double)-1., (int)(x)); } CASE("write_read_write_field") { // build suitable data structures do hold field name & values std::string field_name("my_super_field"); std::vector field_values(test_vectors::nb_pts, 0.); for (size_t i = 0; i < test_vectors::nb_pts; ++i) { field_values[i] = funny_formula(i); } // PART 1 // write field vector values as column in file "pointcloud.txt" Log::info() << "Part 1" << std::endl; std::ifstream f; std::string signature, str_lon, str_lat, str_f; size_t nb_pts, nb_columns; output::detail::PointCloudIO::write("pointcloud.txt", test_vectors::lon, test_vectors::lat, std::vector*>(1, &field_values), std::vector(1, field_name)); f.open("pointcloud.txt"); EXPECT(f.is_open()); f >> signature >> nb_pts >> nb_columns >> str_lon >> str_lat >> str_f; f.close(); EXPECT(nb_pts == test_vectors::nb_pts); EXPECT(nb_columns == 2 + 1); // (lon,lat,my_super_field) EXPECT(str_lon == "lon"); EXPECT(str_lat == "lat"); EXPECT(str_f == "my_super_field"); // PART 2 // read field vector from just-created file Log::info() << "Part 2" << std::endl; Mesh mesh = output::detail::PointCloudIO::read("pointcloud.txt"); Grid grid(new grid::detail::grid::Unstructured(make_mdspan(mesh.nodes().xy()))); EXPECT(grid); EXPECT(grid.size() == test_vectors::nb_pts); mesh::Nodes& nodes = mesh.nodes(); EXPECT(nodes.has_field("my_super_field") == true); EXPECT(nodes.has_field("_StRaNgE_FiElD_NaMe_") == false); // PART 3 // check field values to a very small tolerance (relative tol. 0.001%) Log::info() << "Part 3" << std::endl; Field& field(nodes.field("my_super_field")); EXPECT( /* data used to write file*/ test_vectors::nb_pts == /* data read from file*/ field.size()); array::ArrayView field_data = array::make_view(field); for (idx_t i = 0; i < field_data.size(); ++i) { EXPECT(eckit::types::is_approximately_equal(funny_formula(i), field_data(i), 0.001)); // 0.001% relative error EXPECT(eckit::types::is_approximately_equal(funny_formula(i), field_data(i), 0.001)); // 0.001% relative error } // PART 4 // write to file a Field (the just-read one), // a FieldSet, and // a Grid (should be exactly the same) Log::info() << "Part 4" << std::endl; FieldSet fieldset; EXPECT_NO_THROW(fieldset.add(field)); functionspace::NodeColumns functionspace(mesh); EXPECT_NO_THROW(output::detail::PointCloudIO::write("pointcloud_FieldSet.txt", fieldset, functionspace)); EXPECT_NO_THROW(output::detail::PointCloudIO::write("pointcloud_Grid.txt", mesh)); Mesh mesh_from_FieldSet = output::detail::PointCloudIO::read("pointcloud_FieldSet.txt"); Grid grid_from_FieldSet(new grid::detail::grid::Unstructured(make_mdspan(mesh_from_FieldSet.nodes().xy()))); Mesh mesh_from_Grid(output::detail::PointCloudIO::read("pointcloud_Grid.txt")); Grid grid_from_Grid(new grid::detail::grid::Unstructured(make_mdspan(mesh_from_Grid.nodes().xy()))); EXPECT(grid_from_FieldSet); EXPECT(grid_from_Grid); // PART 5 // compare reading of reference data to: // - grid_from_FieldSet, and // - grid_from_Grid (all different but equivalent writing methods) Log::info() << "Part 5" << std::endl; // (header section) EXPECT(grid_from_FieldSet.size() == test_arrays::nb_pts); EXPECT(mesh_from_FieldSet.nodes().has_field("my_super_field") == true); EXPECT(mesh_from_FieldSet.nodes().has_field("_StRaNgE_FiElD_NaMe_") == false); EXPECT(grid_from_Grid.size() == test_arrays::nb_pts); EXPECT(mesh_from_FieldSet.nodes().has_field("my_super_field") == true); EXPECT(mesh_from_FieldSet.nodes().has_field("_StRaNgE_FiElD_NaMe_") == false); // (data section: guarantee data are from different places, to make checks // useful) const Field& field_from_FieldSet(mesh_from_FieldSet.nodes().field("my_super_field")); const Field& field_from_Grid(mesh_from_FieldSet.nodes().field("my_super_field")); EXPECT(field.array().data() != field_from_FieldSet.array().data()); EXPECT(field.array().data() != field_from_Grid.array().data()); auto field_from_FieldSet_data = array::make_view(field_from_FieldSet); auto field_from_Grid_data = array::make_view(field_from_Grid); for (size_t i = 0; i < test_arrays::nb_pts; ++i) { EXPECT(eckit::types::is_approximately_equal(field_data(i), field_from_FieldSet_data(i), 0.001)); // 0.001% relative error EXPECT(eckit::types::is_approximately_equal(field_data(i), field_from_Grid_data(i), 0.001)); // ... } } //----------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/output/test_gmsh_read.cc0000664000175000017500000000225515160212070021657 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "atlas/mesh/Mesh.h" #include "atlas/output/Gmsh.h" #include "atlas/output/detail/GmshIO.h" #include "tests/AtlasTestEnvironment.h" namespace atlas { namespace test { //----------------------------------------------------------------------------- CASE("test_gmsh_read") { output::detail::GmshIO gmsh_reader; std::string file = eckit::Resource("--mesh", ""); if (file.empty()) { Log::error() << "Argument --mesh missing" << std::endl; } Mesh mesh = gmsh_reader.read(file); output::Gmsh gmsh("test_gmsh_read_output.msh"); gmsh.write(mesh); } //----------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/output/fctest_gmsh.F900000664000175000017500000000570715160212070021153 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. ! This File contains Unit Tests for testing the ! C++ / Fortran Interfaces to the Mesh Datastructure ! @author Willem Deconinck #include "fckit/fctest.h" ! ----------------------------------------------------------------------------- TESTSUITE(fctest_atlas_Gmsh) ! ----------------------------------------------------------------------------- TESTSUITE_INIT use atlas_module call atlas_library%initialise() END_TESTSUITE_INIT ! ----------------------------------------------------------------------------- TESTSUITE_FINALIZE use atlas_module call atlas_library%finalise() END_TESTSUITE_FINALIZE ! ----------------------------------------------------------------------------- TEST( test_gmsh ) use atlas_module implicit none type(atlas_StructuredGrid) :: grid type(atlas_MeshGenerator) :: meshgenerator type(atlas_Mesh) :: mesh type(atlas_functionspace_NodeColumns) :: functionspace_nodes type(atlas_mesh_Nodes) :: nodes type(atlas_Field) :: field type(atlas_FieldSet) :: fieldset type(atlas_Output) :: gmsh integer(4), pointer :: fdata(:,:) integer :: jlev call atlas_log%info("test_gmsh starting") grid = atlas_StructuredGrid("N24") meshgenerator = atlas_MeshGenerator() mesh = meshgenerator%generate(grid) call meshgenerator%final() gmsh = atlas_output_Gmsh("output_fctest_gmsh.msh","w",coordinates="xyz",gather=.false.,levels=[0,2]) call gmsh%write(mesh) functionspace_nodes = atlas_functionspace_NodeColumns(mesh,1) nodes = mesh%nodes() field = nodes%global_index() call gmsh%write(field,functionspace_nodes) field = nodes%remote_index() call gmsh%write(field,functionspace_nodes) fieldset = atlas_FieldSet() call fieldset%add( nodes%lonlat() ) call fieldset%add( nodes%partition() ) call gmsh%write(fieldset,functionspace_nodes) field = functionspace_nodes%create_field(name="leveled",kind=atlas_integer(4),levels=4); call field%data(fdata) do jlev=1,field%levels() fdata(jlev,:) = jlev enddo call gmsh%write(field) call gmsh%write(field) fieldset = atlas_FieldSet() field = functionspace_nodes%create_field(name="scal1",kind=atlas_integer(4),levels=4); call field%data(fdata) do jlev=1,field%levels() fdata(jlev,:) = jlev-1 enddo call fieldset%add(field) field = functionspace_nodes%create_field(name="scal2",kind=atlas_integer(4),levels=4); call field%data(fdata) do jlev=1,field%levels() fdata(jlev,:) = -(jlev-1) enddo call fieldset%add(field) call gmsh%write(fieldset) END_TEST ! ----------------------------------------------------------------------------- END_TESTSUITE atlas-0.46.0/src/tests/output/CMakeLists.txt0000664000175000017500000000207315160212070021116 0ustar alastairalastair# (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. ecbuild_add_test( TARGET atlas_test_gmsh SOURCES test_gmsh.cc ../TestMeshes.h LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) ecbuild_add_test( TARGET atlas_test_pointcloud_io SOURCES test_pointcloud_io.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) ecbuild_add_test( TARGET atlas_test_gmsh_read SOURCES test_gmsh_read.cc ARGS --mesh ${CMAKE_CURRENT_SOURCE_DIR}/../mesh/test_mesh_reorder_unstructured.msh LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) if( HAVE_FCTEST ) add_fctest( TARGET atlas_fctest_gmsh LINKER_LANGUAGE Fortran SOURCES fctest_gmsh.F90 LIBS atlas_f ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) endif() atlas-0.46.0/src/tests/export_tests/0000775000175000017500000000000015160212070017557 5ustar alastairalastairatlas-0.46.0/src/tests/export_tests/CMakeLists.txt0000664000175000017500000000126315160212070022321 0ustar alastairalastairif( HAVE_TESTS ) ecbuild_add_option( FEATURE EXPORT_TESTS DEFAULT OFF ) endif() ecbuild_add_test( TARGET atlas_test_build_hello_world COMMAND ${PROJECT_SOURCE_DIR}/doc/example-projects/build_hello_world.sh CONDITION atlas_HAVE_EXPORT_TESTS ENVIRONMENT atlas_DIR=${PROJECT_BINARY_DIR} CTEST_ARGS=-VV MAKE_ARGS=VERBOSE=1 ) ecbuild_add_test( TARGET atlas_test_build_hello_world_fortran COMMAND ${PROJECT_SOURCE_DIR}/doc/example-projects/build_hello_world_fortran.sh CONDITION atlas_HAVE_EXPORT_TESTS AND atlas_HAVE_FORTRAN ENVIRONMENT atlas_DIR=${PROJECT_BINARY_DIR} CTEST_ARGS=-VV MAKE_ARGS=VERBOSE=1 ) atlas-0.46.0/src/tests/parallel/0000775000175000017500000000000015160212070016610 5ustar alastairalastairatlas-0.46.0/src/tests/parallel/test_omp_copy.cc0000664000175000017500000000276615160212070022016 0ustar alastairalastair/* * (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 // generate, is_sorted #include // bind #include // mt19937 and uniform_int_distribution #include // vector #include "atlas/grid.h" #include "atlas/parallel/omp/copy.h" #include "atlas/util/Point.h" #include "atlas/util/vector.h" #include "tests/AtlasTestEnvironment.h" namespace atlas { namespace test { //----------------------------------------------------------------------------- CASE("test omp::copy") { Grid grid("O400"); atlas::vector points(grid.size()); omp::copy(grid.xy().begin(), grid.xy().end(), points.begin()); } CASE("test atlas::vector assign") { // atlas::vector assign uses omp::copy // It should reproduce exactly the same points as previous test case Grid grid("O400"); atlas::vector points; points.assign(grid.xy().begin(), grid.xy().end()); EXPECT(points.size() == grid.size()); } //----------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/parallel/test_sync.F900000664000175000017500000001002115160212070021075 0ustar alastairalastair! (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. ! =================================================================== ! test_sync program ! --------------------- ! This program tests MPI_ALLTOALLV communication, ! used for synchronisation ! =================================================================== program test_sync use common_module use parallel_module use datastruct implicit none integer, allocatable :: nb_nodes(:) integer, allocatable :: proc(:) integer, allocatable :: glb_idx(:) integer, allocatable :: master_glb_idx(:) real(kind=jprs), allocatable, target :: field(:), vectorfield(:,:) real(kind=jprs), pointer :: vectorfield_ptr(:,:) real(kind=jprs), allocatable :: glb_field(:) integer :: bounds(2) integer :: length integer :: parallel_bound = 1 type(Comm_type) :: comm type(HaloExchange_type) :: halo_exchange type(FunctionSpace_type) :: function_space call set_log_level(LOG_LEVEL_INFO) call parallel_init() ! MPI_INIT etc halo_exchange = new_HaloExchange() ! Create distribution with overlap regions if (nproc .eq. 3) then allocate( nb_nodes(3) ) nb_nodes = [ 5, 6, 7 ] length = nb_nodes(myproc+1) bounds = [ -1, length ] allocate( proc(length) ) allocate( glb_idx(length) ) allocate( master_glb_idx(length) ) allocate( field(length) ) allocate( vectorfield(2,length) ) select case (myproc) case (0) proc = [2,0,0,0,1] glb_idx = [9,1,2,3,4] field = [-1,1,2,3,-1] case (1) proc = [0,1,1,1,2,2] glb_idx = [3,4,5,6,7,8] field = [-1,4,5,6,-1,-1] case (2) proc = [1,1,2,2,2,0,0] glb_idx = [5,6,7,8,9,1,2] field = [-1,-1,7,8,9,-1,-1] end select master_glb_idx(:) = glb_idx(:) vectorfield(1,:) = field*10 vectorfield(2,:) = field*100 vectorfield_ptr => vectorfield ! Setup a communicator for synchronisation call comm%setup(proc,glb_idx) ! We can setup function_space for halo_exchange function_space = new_FunctionSpace("nodes","shape_func",length) call function_space%parallelise( proc, glb_idx, master_glb_idx ) ! Or we can setup a custom halo_exchange object !call halo_exchange%setup(proc, glb_idx, bounds, parallel_bound) ! Update the field values whose proc is not myproc ! This is the older fortran implementation !call comm%synchronise(field) !call comm%synchronise(vectorfield) ! Halo exchange through the function_space (We don't need to know nbvars) call function_space%halo_exchange(field) call function_space%halo_exchange(vectorfield) !call function_space%halo_exchange(vectorfield(:)) !call function_space%halo_exchange(vectorfield(:,2)) ! Halo exchange through the custom halo_exchange object !call halo_exchange%execute(field,1) !call halo_exchange%execute(vectorfield,2) !call halo_exchange%execute(vectorfield(:,1),1) !call halo_exchange%execute(vectorfield(:,2),1) ! Verify that update happened correctly write(log_str,*) myproc, ": field = ", field; call log_info() write(log_str,*) myproc, ": vectorfield(1,:) = ", vectorfield(1,:); call log_info() write(log_str,*) myproc, ": vectorfield(2,:) = ", vectorfield(2,:); call log_info() !call comm%gather(field, glb_field) !call set_log_proc(0) !write(log_str,*) myproc, ": glb_field = ", glb_field; call log_info() else call set_log_proc(0) call log_error("ERROR: Run this program with 3 tasks") end if call delete(halo_exchange) call parallel_finalise() ! MPI_FINALIZE end program test_sync ! OUTPUT: ! ! 0: field = 90 10 20 30 40 ! 1: field = 30 40 50 60 70 ! 2: field = 60 70 80 90 10 atlas-0.46.0/src/tests/parallel/test_haloexchange.cc0000664000175000017500000006312215160212070022610 0ustar alastairalastair/* * (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 "pluto/pluto.h" #include "atlas/array.h" #include "atlas/array/ArrayView.h" #include "atlas/array/MakeView.h" #include "atlas/library/config.h" #include "atlas/parallel/HaloExchange.h" #include "atlas/parallel/mpi/mpi.h" #include "tests/AtlasTestEnvironment.h" /// POD: Type to test using POD = double; namespace atlas { namespace test { //----------------------------------------------------------------------------- template std::vector vec(const T (&list)[N]) { return std::vector(list, list + N); } template size_t eval_idx(size_t pos, std::array& strides, FirstDim first) { return first * strides[pos]; } template size_t eval_idx(size_t pos, std::array& strides, FirstDim first, SecondDim second) { return first * strides[pos] + eval_idx(pos + 1, strides, second); } template size_t eval_idx(size_t pos, std::array& strides, FirstDim first, SecondDim second, ThirdDim third) { return first * strides[pos] + eval_idx(pos + 1, strides, second, third); } template struct validate_impl; template struct validate_impl { template static void apply(array::ArrayView& arrv, DATA_TYPE arr_c[], std::array& strides, Int... dims) { EXPECT(arrv(dims...) == arr_c[eval_idx((size_t)0, strides, dims...)]); } }; template struct validate_impl { template static void apply(array::ArrayView& arrv, DATA_TYPE arr_c[], std::array& strides, Int... dims) { for (idx_t cnt = 0; cnt < arrv.template shape(); ++cnt) { validate_impl::apply(arrv, arr_c, strides, dims..., cnt); } } }; template struct compute_strides; template <> struct compute_strides<0> { template static void apply(array::ArrayView& arrv, std::array& strides) {} }; template struct compute_strides { template static void apply(array::ArrayView& arrv, std::array& strides) { strides[Dim - 1] = strides[Dim] * arrv.template shape<(unsigned int)Dim>(); compute_strides::apply(arrv, strides); } }; template struct validate { static void apply(array::ArrayView& arrv, DATA_TYPE arr_c[]) { std::array strides; strides[Rank - 1] = 1; compute_strides::apply(arrv, strides); for (idx_t i = 0; i < arrv.template shape<0>(); ++i) { validate_impl::apply(arrv, arr_c, strides, i); } } }; struct Fixture { Fixture(bool _on_device = false): on_device(_on_device) { int nnodes_c[] = {5, 6, 7}; nb_nodes = vec(nnodes_c); N = nb_nodes[mpi::comm().rank()]; switch (mpi::comm().rank()) { case 0: { int part_c[] = {2, 0, 0, 0, 1}; part = vec(part_c); idx_t ridx_c[] = {4, 1, 2, 3, 1}; ridx = vec(ridx_c); POD gidx_c[] = {0, 1, 2, 3, 0}; gidx = vec(gidx_c); break; } case 1: { int part_c[] = {0, 1, 1, 1, 2, 2}; part = vec(part_c); idx_t ridx_c[] = {3, 1, 2, 3, 2, 3}; ridx = vec(ridx_c); POD gidx_c[] = {0, 4, 5, 6, 0, 0}; gidx = vec(gidx_c); break; } case 2: { int part_c[] = {1, 1, 2, 2, 2, 0, 0}; part = vec(part_c); idx_t ridx_c[] = {2, 3, 2, 3, 4, 1, 2}; ridx = vec(ridx_c); POD gidx_c[] = {0, 0, 7, 8, 9, 0, 0}; gidx = vec(gidx_c); break; } } halo_exchange.setup(part.data(), ridx.data(), 0, N); } parallel::HaloExchange halo_exchange; std::vector nb_nodes; std::vector part; std::vector ridx; std::vector gidx; int N; bool on_device; }; //----------------------------------------------------------------------------- void test_rank0_arrview(Fixture& f) { array::ArrayT arr(f.N); array::ArrayView arrv = array::make_host_view(arr); for (int j = 0; j < f.N; ++j) { arrv(j) = (size_t(f.part[j]) != mpi::comm().rank() ? 0 : f.gidx[j]); } if (f.on_device) { arr.updateDevice(); } f.halo_exchange.execute(arr, f.on_device); if( f.on_device ) { arr.updateHost(); } switch (mpi::comm().rank()) { case 0: { POD arr_c[] = {9, 1, 2, 3, 4}; validate::apply(arrv, arr_c); break; } case 1: { POD arr_c[] = {3, 4, 5, 6, 7, 8}; validate::apply(arrv, arr_c); break; } case 2: { POD arr_c[] = {5, 6, 7, 8, 9, 1, 2}; validate::apply(arrv, arr_c); break; } } } void test_rank1(Fixture& f) { array::ArrayT arr(f.N, 2); array::ArrayView arrv = array::make_host_view(arr); for (int j = 0; j < f.N; ++j) { arrv(j, 0) = (size_t(f.part[j]) != mpi::comm().rank() ? 0 : f.gidx[j] * 10); arrv(j, 1) = (size_t(f.part[j]) != mpi::comm().rank() ? 0 : f.gidx[j] * 100); } if (f.on_device) { arr.updateDevice(); } f.halo_exchange.execute(arr, f.on_device); if (f.on_device) { arr.updateHost(); } switch (mpi::comm().rank()) { case 0: { POD arr_c[] = {90, 900, 10, 100, 20, 200, 30, 300, 40, 400}; validate::apply(arrv, arr_c); break; } case 1: { POD arr_c[] = {30, 300, 40, 400, 50, 500, 60, 600, 70, 700, 80, 800}; validate::apply(arrv, arr_c); break; } case 2: { POD arr_c[] = {50, 500, 60, 600, 70, 700, 80, 800, 90, 900, 10, 100, 20, 200}; validate::apply(arrv, arr_c); break; } } } void test_rank1_strided_v1(Fixture& f) { // create a 2d field from the gidx data, with two components per grid point array::ArrayT arr_t(f.N, 2); array::ArrayView arrv_t = array::make_host_view(arr_t); for (int j = 0; j < f.N; ++j) { arrv_t(j, 0) = (size_t(f.part[j]) != mpi::comm().rank() ? 0 : f.gidx[j] * 10); arrv_t(j, 1) = (size_t(f.part[j]) != mpi::comm().rank() ? 0 : f.gidx[j] * 100); } // create a wrap array where we fake the strides in a way that the second // dimension // (number of components) contains only one component but the associated // stride is 2 // (i.e. we are only selecting and exchanging the first component of the // field) std::unique_ptr arr(array::Array::wrap( arrv_t.data(), array::ArraySpec { array::make_shape(f.N, 1), #if ATLAS_GRIDTOOLS_STORAGE_BACKEND_CUDA array::make_strides(32, 1) #else array::make_strides(2, 1) #endif })); if (f.on_device) { arr->updateDevice(); } f.halo_exchange.execute(*arr, f.on_device); if (f.on_device) { arr->updateHost(); } switch (mpi::comm().rank()) { case 0: { POD arr_c[] = {90, 0, 10, 100, 20, 200, 30, 300, 40, 0}; validate::apply(arrv_t, arr_c); break; } case 1: { POD arr_c[] = {30, 0, 40, 400, 50, 500, 60, 600, 70, 0, 80, 0}; validate::apply(arrv_t, arr_c); break; } case 2: { POD arr_c[] = {50, 0, 60, 0, 70, 700, 80, 800, 90, 900, 10, 0, 20, 0}; validate::apply(arrv_t, arr_c); break; } } } void test_rank1_strided_v2(Fixture& f) { // create a 2d field from the gidx data, with two components per grid point array::ArrayT arr_t(f.N, 2); array::ArrayView arrv_t = array::make_host_view(arr_t); for (int j = 0; j < f.N; ++j) { arrv_t(j, 0) = (size_t(f.part[j]) != mpi::comm().rank() ? 0 : f.gidx[j] * 10); arrv_t(j, 1) = (size_t(f.part[j]) != mpi::comm().rank() ? 0 : f.gidx[j] * 100); } // create a wrap array where we fake the strides in a way that the second // dimension // (number of components) contains only one component but the associated // stride is 2 // (i.e. we are only selecting and exchanging the first component of the // field) std::unique_ptr arr(array::Array::wrap( &(arrv_t(0, 1)), array::ArraySpec { array::make_shape(f.N, 1), #if ATLAS_GRIDTOOLS_STORAGE_BACKEND_CUDA array::make_strides(32, 1) #else array::make_strides(2, 1) #endif })); f.halo_exchange.execute(*arr, false); switch (mpi::comm().rank()) { case 0: { POD arr_c[] = {0, 900, 10, 100, 20, 200, 30, 300, 0, 400}; validate::apply(arrv_t, arr_c); break; } case 1: { POD arr_c[] = {0, 300, 40, 400, 50, 500, 60, 600, 0, 700, 0, 800}; validate::apply(arrv_t, arr_c); break; } case 2: { POD arr_c[] = {0, 500, 0, 600, 70, 700, 80, 800, 90, 900, 0, 100, 0, 200}; validate::apply(arrv_t, arr_c); break; } } } void test_rank2(Fixture& f) { array::ArrayT arr(f.N, 3, 2); array::ArrayView arrv = array::make_host_view(arr); for (int p = 0; p < f.N; ++p) { for (size_t i = 0; i < 3; ++i) { arrv((size_t)p, i, (size_t)0) = (size_t(f.part[p]) != mpi::comm().rank() ? 0 : -f.gidx[p] * std::pow(10, i)); arrv((size_t)p, i, (size_t)1) = (size_t(f.part[p]) != mpi::comm().rank() ? 0 : f.gidx[p] * std::pow(10, i)); } } if (f.on_device) { arr.updateDevice(); } f.halo_exchange.execute(arr, f.on_device); if (f.on_device) { arr.updateHost(); } switch (mpi::comm().rank()) { case 0: { POD arr_c[] = {-9, 9, -90, 90, -900, 900, -1, 1, -10, 10, -100, 100, -2, 2, -20, 20, -200, 200, -3, 3, -30, 30, -300, 300, -4, 4, -40, 40, -400, 400}; validate::apply(arrv, arr_c); break; } case 1: { POD arr_c[] = {-3, 3, -30, 30, -300, 300, -4, 4, -40, 40, -400, 400, -5, 5, -50, 50, -500, 500, -6, 6, -60, 60, -600, 600, -7, 7, -70, 70, -700, 700, -8, 8, -80, 80, -800, 800}; validate::apply(arrv, arr_c); break; } case 2: { POD arr_c[] = {-5, 5, -50, 50, -500, 500, -6, 6, -60, 60, -600, 600, -7, 7, -70, 70, -700, 700, -8, 8, -80, 80, -800, 800, -9, 9, -90, 90, -900, 900, -1, 1, -10, 10, -100, 100, -2, 2, -20, 20, -200, 200}; validate::apply(arrv, arr_c); break; } } } void test_rank2_l1(Fixture& f) { array::ArrayT arr_t(f.N, 3, 2); array::ArrayView arrv_t = array::make_host_view(arr_t); for (int p = 0; p < f.N; ++p) { for (size_t i = 0; i < 3; ++i) { arrv_t((size_t)p, i, (size_t)0) = (size_t(f.part[p]) != mpi::comm().rank() ? 0 : -f.gidx[p] * std::pow(10, i)); arrv_t((size_t)p, i, (size_t)1) = (size_t(f.part[p]) != mpi::comm().rank() ? 0 : f.gidx[p] * std::pow(10, i)); } } std::unique_ptr arr(array::Array::wrap( arrv_t.data(), array::ArraySpec { array::make_shape(f.N, 1, 2), #if ATLAS_GRIDTOOLS_STORAGE_BACKEND_CUDA array::make_strides(96, 32, 1) #else array::make_strides(6, 2, 1) #endif })); if (f.on_device) { arr->updateDevice(); } f.halo_exchange.execute(*arr, f.on_device); if (f.on_device) { arr->updateHost(); } switch (mpi::comm().rank()) { case 0: { POD arr_c[] = {-9, 9, 0, 0, 0, 0, // halo -1, 1, -10, 10, -100, 100, // core -2, 2, -20, 20, -200, 200, // core -3, 3, -30, 30, -300, 300, // core -4, 4, 0, 0, 0, 0}; // halo validate::apply(arrv_t, arr_c); break; } case 1: { POD arr_c[] = {-3, 3, 0, 0, 0, 0, // halo -4, 4, -40, 40, -400, 400, // core -5, 5, -50, 50, -500, 500, // core -6, 6, -60, 60, -600, 600, // core -7, 7, 0, 0, 0, 0, // halo -8, 8, 0, 0, 0, 0}; // halo validate::apply(arrv_t, arr_c); break; } case 2: { POD arr_c[] = {-5, 5, 0, 0, 0, 0, // halo -6, 6, 0, 0, 0, 0, // halo -7, 7, -70, 70, -700, 700, // core -8, 8, -80, 80, -800, 800, // core -9, 9, -90, 90, -900, 900, // core -1, 1, 0, 0, 0, 0, // halo -2, 2, 0, 0, 0, 0}; // halo validate::apply(arrv_t, arr_c); break; } } } void test_rank2_l2_v2(Fixture& f) { // Test rank 2 halo-exchange array::ArrayT arr_t(f.N, 3, 2); array::ArrayView arrv_t = array::make_host_view(arr_t); for (int p = 0; p < f.N; ++p) { for (size_t i = 0; i < 3; ++i) { arrv_t((size_t)p, i, (size_t)0) = (size_t(f.part[p]) != mpi::comm().rank() ? 0 : -f.gidx[p] * std::pow(10, i)); arrv_t((size_t)p, i, (size_t)1) = (size_t(f.part[p]) != mpi::comm().rank() ? 0 : f.gidx[p] * std::pow(10, i)); } } std::unique_ptr arr(array::Array::wrap( &arrv_t(0, 1, 1), array::ArraySpec { array::make_shape(f.N, 1, 1), #if ATLAS_GRIDTOOLS_STORAGE_BACKEND_CUDA array::make_strides(192, 32, 1) #else array::make_strides(6, 2, 1) #endif })); if (f.on_device) { arr->updateDevice(); } f.halo_exchange.execute(*arr, f.on_device); if (f.on_device) { arr->updateHost(); } switch (mpi::comm().rank()) { case 0: { POD arr_c[] = {0, 0, 0, 90, 0, 0, // halo -1, 1, -10, 10, -100, 100, // core -2, 2, -20, 20, -200, 200, // core -3, 3, -30, 30, -300, 300, // core 0, 0, 0, 40, 0, 0}; // halo validate::apply(arrv_t, arr_c); break; } case 1: { POD arr_c[] = {0, 0, 0, 30, 0, 0, // halo -4, 4, -40, 40, -400, 400, // core -5, 5, -50, 50, -500, 500, // core -6, 6, -60, 60, -600, 600, // core 0, 0, 0, 70, 0, 0, // halo 0, 0, 0, 80, 0, 0}; // halo validate::apply(arrv_t, arr_c); break; } case 2: { POD arr_c[] = {0, 0, 0, 50, 0, 0, // halo 0, 0, 0, 60, 0, 0, // halo -7, 7, -70, 70, -700, 700, // core -8, 8, -80, 80, -800, 800, // core -9, 9, -90, 90, -900, 900, // core 0, 0, 0, 10, 0, 0, // halo 0, 0, 0, 20, 0, 0}; // halo validate::apply(arrv_t, arr_c); break; } } } void test_rank2_v2(Fixture& f) { array::ArrayT arr_t(f.N, 3, 2); array::ArrayView arrv_t = array::make_view(arr_t); for (int p = 0; p < f.N; ++p) { for (size_t i = 0; i < 3; ++i) { arrv_t((size_t)p, i, (size_t)0) = (size_t(f.part[p]) != mpi::comm().rank() ? 0 : -f.gidx[p] * std::pow(10, i)); arrv_t((size_t)p, i, (size_t)1) = (size_t(f.part[p]) != mpi::comm().rank() ? 0 : f.gidx[p] * std::pow(10, i)); } } std::unique_ptr arr(array::Array::wrap( &arrv_t(0, 0, 1), array::ArraySpec { array::make_shape(f.N, 3, 1), #if ATLAS_GRIDTOOLS_STORAGE_BACKEND_CUDA array::make_strides(192, 32, 2) #else array::make_strides(6, 2, 2) #endif })); if (f.on_device) { arr->updateDevice(); } f.halo_exchange.execute(*arr, f.on_device); if (f.on_device) { arr->updateHost(); } switch (mpi::comm().rank()) { case 0: { POD arr_c[] = {0, 9, 0, 90, 0, 900, // halo -1, 1, -10, 10, -100, 100, // core -2, 2, -20, 20, -200, 200, // core -3, 3, -30, 30, -300, 300, // core 0, 4, 0, 40, 0, 400}; // halo validate::apply(arrv_t, arr_c); break; } case 1: { POD arr_c[] = {0, 3, 0, 30, 0, 300, // halo -4, 4, -40, 40, -400, 400, // core -5, 5, -50, 50, -500, 500, // core -6, 6, -60, 60, -600, 600, // core 0, 7, 0, 70, 0, 700, // halo 0, 8, 0, 80, 0, 800}; // halo validate::apply(arrv_t, arr_c); break; } case 2: { POD arr_c[] = {0, 5, 0, 50, 0, 500, // halo 0, 6, 0, 60, 0, 600, // halo -7, 7, -70, 70, -700, 700, // core -8, 8, -80, 80, -800, 800, // core -9, 9, -90, 90, -900, 900, // core 0, 1, 0, 10, 0, 100, // halo 0, 2, 0, 20, 0, 200}; // halo validate::apply(arrv_t, arr_c); break; } } } void test_rank0_wrap(Fixture& f) { std::unique_ptr arr(array::Array::wrap(f.gidx.data(), array::make_shape(f.N))); array::ArrayView arrv = array::make_view(*arr); if (f.on_device) { arr->updateDevice(); } f.halo_exchange.execute(*arr, f.on_device); if (f.on_device) { arr->updateHost(); } switch (mpi::comm().rank()) { case 0: { POD arr_c[] = {9, 1, 2, 3, 4}; validate::apply(arrv, arr_c); break; } case 1: { POD arr_c[] = {3, 4, 5, 6, 7, 8}; validate::apply(arrv, arr_c); break; } case 2: { POD arr_c[] = {5, 6, 7, 8, 9, 1, 2}; validate::apply(arrv, arr_c); break; } } } void test_rank1_paralleldim1(Fixture& f) { array::ArrayT arr(2, f.N); array::ArrayView arrv = array::make_view(arr); for (int j = 0; j < f.N; ++j) { arrv(0, j) = (size_t(f.part[j]) != mpi::comm().rank() ? 0 : f.gidx[j] * 10); arrv(1, j) = (size_t(f.part[j]) != mpi::comm().rank() ? 0 : f.gidx[j] * 100); } f.halo_exchange.execute(arr, false); switch (mpi::comm().rank()) { case 0: { POD arr_c[] = {90, 10, 20, 30, 40, 900, 100, 200, 300, 400}; // 90,900, 10,100, 20,200, 30,300, 40,400 }; validate::apply(arrv, arr_c); break; } case 1: { POD arr_c[] = {30, 40, 50, 60, 70, 80, 300, 400, 500, 600, 700, 800}; validate::apply(arrv, arr_c); break; } case 2: { POD arr_c[] = {50, 60, 70, 80, 90, 10, 20, 500, 600, 700, 800, 900, 100, 200}; validate::apply(arrv, arr_c); break; } } } void test_rank2_paralleldim2(Fixture& f) { array::ArrayT arr(3, f.N, 2); array::ArrayView arrv = array::make_view(arr); for (int p = 0; p < f.N; ++p) { for (size_t i = 0; i < 3; ++i) { arrv(i, (size_t)p, (size_t)0) = (size_t(f.part[p]) != mpi::comm().rank() ? 0 : -f.gidx[p] * std::pow(10, i)); arrv(i, (size_t)p, (size_t)1) = (size_t(f.part[p]) != mpi::comm().rank() ? 0 : f.gidx[p] * std::pow(10, i)); } } f.halo_exchange.execute>(arr, false); switch (mpi::comm().rank()) { case 0: { POD arr_c[] = {-9, 9, -1, 1, -2, 2, -3, 3, -4, 4, -90, 90, -10, 10, -20, 20, -30, 30, -40, 40, -900, 900, -100, 100, -200, 200, -300, 300, -400, 400}; validate::apply(arrv, arr_c); break; } case 1: { POD arr_c[] = {-3, 3, -4, 4, -5, 5, -6, 6, -7, 7, -8, 8, -30, 30, -40, 40, -50, 50, -60, 60, -70, 70, -80, 80, -300, 300, -400, 400, -500, 500, -600, 600, -700, 700, -800, 800}; validate::apply(arrv, arr_c); break; } case 2: { POD arr_c[] = {-5, 5, -6, 6, -7, 7, -8, 8, -9, 9, -1, 1, -2, 2, -50, 50, -60, 60, -70, 70, -80, 80, -90, 90, -10, 10, -20, 20, -500, 500, -600, 600, -700, 700, -800, 800, -900, 900, -100, 100, -200, 200}; validate::apply(arrv, arr_c); break; } } } void test_rank1_cinterface(Fixture& f) { array::ArrayT arr(f.N, 2); array::ArrayView arrv = array::make_host_view(arr); for (int j = 0; j < f.N; ++j) { arrv(j, 0) = (size_t(f.part[j]) != mpi::comm().rank() ? 0 : f.gidx[j] * 10); arrv(j, 1) = (size_t(f.part[j]) != mpi::comm().rank() ? 0 : f.gidx[j] * 100); } int shapes[2] = {(int)arrv.shape(0), (int)arrv.shape(1)}; int strides[2] = {(int)arrv.stride(0), (int)arrv.stride(1)}; atlas__HaloExchange__execute_strided_double(&(f.halo_exchange), arrv.data(), &(strides[1]), &(shapes[1]), 1); switch (mpi::comm().rank()) { case 0: { POD arr_c[] = {90, 900, 10, 100, 20, 200, 30, 300, 40, 400}; validate::apply(arrv, arr_c); break; } case 1: { POD arr_c[] = {30, 300, 40, 400, 50, 500, 60, 600, 70, 700, 80, 800}; validate::apply(arrv, arr_c); break; } case 2: { POD arr_c[] = {50, 500, 60, 600, 70, 700, 80, 800, 90, 900, 10, 100, 20, 200}; validate::apply(arrv, arr_c); break; } } } CASE("test_haloexchange") { Fixture f; SECTION("test_rank0_arrview") { test_rank0_arrview(f); } SECTION("test_rank1") { test_rank1(f); } SECTION("test_rank1_strided_v1") { test_rank1_strided_v1(f); } SECTION("test_rank1_strided_v2") { test_rank1_strided_v2(f); } SECTION("test_rank2") { test_rank2(f); } SECTION("test_rank2_l1") { test_rank2_l1(f); } SECTION("test_rank2_l2_v2") { test_rank2_l2_v2(f); } SECTION("test_rank2_v2") { test_rank2_v2(f); } SECTION("test_rank0_wrap") { test_rank0_wrap(f); } SECTION("test_rank1_paralleldim_1") { test_rank1_paralleldim1(f); } SECTION("test_rank2_paralleldim_2") { test_rank2_paralleldim2(f); } SECTION("test_rank1_cinterface") { test_rank1_cinterface(f); } } //----------------------------------------------------------------------------- #if ATLAS_HAVE_GPU CASE("test_haloexchange on device") { if (pluto::devices() == 0) { Log::warning() << "\"test_haloexchange on device skipped\": No devices available" << std::endl; return; } bool on_device = true; Fixture f(on_device); SECTION("test_rank0_arrview") { test_rank0_arrview(f); } SECTION("test_rank1") { test_rank1(f); } SECTION("test_rank2") { test_rank2(f); } SECTION("test_rank0_wrap") { test_rank0_wrap(f); } } #endif //----------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/parallel/test_collect.cc0000664000175000017500000001373715160212070021616 0ustar alastairalastair/* * (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/array.h" #include "atlas/parallel/Collect.h" #include "tests/AtlasTestEnvironment.h" /// POD: Type to test using POD = double; namespace atlas::test { //----------------------------------------------------------------------------- template std::vector vec(const T (&list)[N]) { return std::vector(list, list + N); } struct Fixture { /* Data: [0] : 9[2,0], 1[0,1], 2[0,2], 3[0,3], 4[1,1], 20[1,2] [1] : 3[0,3], 4[1,1], 5[1,2], 6[1,3], 7[2,1], 8[2,3] [2] : 5[1,2], 6[1,3], 7[2,2], 8[2,3], 9[2,4], 1[0,1], 2[0,2] 1[0,1] 2[0,2] 9[2,4] 3[0,3] 8[2,3] 4[1,1] 7[2,2] 5[1,2] 6[1,3] Receive gidx: [0] : 7[2,2], 5[1,2], 6[1,3] [1] : 9[2,4], 3[0,3], 8[2,3], 4[1,1] [2] : 1[0,1], 2[0,2] */ Fixture() { rank = static_cast(mpi::comm().rank()); comm_size = static_cast(mpi::comm().size()); int nnodes_c[] = {6, 6, 7}; nb_nodes = vec(nnodes_c); Nl = nb_nodes[rank]; int ridx_base = 0; switch (mpi::comm().rank()) { case 0: { //./----> extra ghost point with nonstandard gidx int part_c[] = {2, 0, 0, 0, 1, 2}; part = vec(part_c); idx_t ridx_c[] = {4, 1, 2, 3, 1, 3}; ridx = vec(ridx_c); gidx_t gidx_c[] = {9, 1, 2, 3, 4, 20}; gidx = vec(gidx_c); int recv_part_c[] = {2, 1, 1}; recv_part = vec(recv_part_c); idx_t recv_ridx_c[] = {2, 2, 3}; recv_ridx = vec(recv_ridx_c); break; } case 1: { int part_c[] = {0, 1, 1, 1, 2, 2}; part = vec(part_c); idx_t ridx_c[] = {3, 1, 2, 3, 2, 3}; ridx = vec(ridx_c); gidx_t gidx_c[] = {3, 4, 5, 6, 7, 8}; gidx = vec(gidx_c); int recv_part_c[] = {2, 0, 2, 1}; recv_part = vec(recv_part_c); idx_t recv_ridx_c[] = {4, 3, 3, 1}; recv_ridx = vec(recv_ridx_c); break; } case 2: { int part_c[] = {1, 1, 2, 2, 2, 0, 0}; part = vec(part_c); idx_t ridx_c[] = {2, 3, 2, 3, 4, 1, 2}; ridx = vec(ridx_c); gidx_t gidx_c[] = {5, 6, 7, 8, 9, 1, 2}; gidx = vec(gidx_c); int recv_part_c[] = {0, 0}; recv_part = vec(recv_part_c); idx_t recv_ridx_c[] = {1, 2}; recv_ridx = vec(recv_ridx_c); break; } } recv_size = recv_part.size(); collect.setup(recv_size, recv_part.data(), recv_ridx.data(), ridx_base); } parallel::Collect collect; std::vector nb_nodes; std::vector part; std::vector ridx; std::vector gidx; std::vector recv_part; std::vector recv_ridx; int recv_size; int ridx_base; int Nl; int root; int rank; int comm_size; }; //----------------------------------------------------------------------------- CASE("test_all_to_all rank 1") { Fixture f; array::ArrayT arr_loc(f.Nl); array::ArrayT arr_recv(f.recv_size); { auto loc = array::make_view(arr_loc); for (int j = 0; j < f.Nl; ++j) { loc[j] = f.gidx[j]; } } f.collect.execute(arr_loc, arr_recv); { auto recv = array::make_view(arr_recv); if (f.rank == 0) { EXPECT_EQ(recv(0), 7); EXPECT_EQ(recv(1), 5); EXPECT_EQ(recv(2), 6); } if (f.rank == 1) { EXPECT_EQ(recv(0), 9); EXPECT_EQ(recv(1), 3); EXPECT_EQ(recv(2), 8); EXPECT_EQ(recv(3), 4); } if (f.rank == 2) { EXPECT_EQ(recv(0), 1); EXPECT_EQ(recv(1), 2); } } } CASE("test_all_to_all rank 2") { Fixture f; array::ArrayT arr_loc(f.Nl, 2); array::ArrayT arr_recv(f.recv_size, 2); { auto loc = array::make_view(arr_loc); for (int j = 0; j < f.Nl; ++j) { loc(j,0) = f.gidx[j]; loc(j,1) = 10 * f.gidx[j]; } } f.collect.execute(arr_loc, arr_recv); { auto recv = array::make_view(arr_recv); if (f.rank == 0) { EXPECT_EQ(recv(0,0), 7); EXPECT_EQ(recv(0,1), 70); EXPECT_EQ(recv(1,0), 5); EXPECT_EQ(recv(1,1), 50); EXPECT_EQ(recv(2,0), 6); EXPECT_EQ(recv(2,1), 60); } if (f.rank == 1) { EXPECT_EQ(recv(0,0), 9); EXPECT_EQ(recv(0,1), 90); EXPECT_EQ(recv(1,0), 3); EXPECT_EQ(recv(1,1), 30); EXPECT_EQ(recv(2,0), 8); EXPECT_EQ(recv(2,1), 80); EXPECT_EQ(recv(3,0), 4); EXPECT_EQ(recv(3,1), 40); } if (f.rank == 2) { EXPECT_EQ(recv(0,0), 1); EXPECT_EQ(recv(0,1), 10); EXPECT_EQ(recv(1,0), 2); EXPECT_EQ(recv(1,1), 20); } } } //----------------------------------------------------------------------------- } // namespace atlas::test int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/parallel/test_haloexchange_adjoint.cc0000664000175000017500000015135215160212070024323 0ustar alastairalastair/* * (C) British Crown Copyright, 2020, Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include #include #include #include #include "atlas/array.h" #include "atlas/array/ArrayView.h" #include "atlas/array/MakeView.h" #include "atlas/library/config.h" #include "atlas/parallel/HaloExchange.h" #include "atlas/parallel/mpi/mpi.h" #include "tests/AtlasTestEnvironment.h" /// POD: Type to test using POD = double; namespace atlas { namespace test { template std::vector vec(const T (&list)[N]) { return std::vector(list, list + N); } template size_t eval_idx(size_t pos, std::array& strides, FirstDim first) { return static_cast(first) * strides[pos]; } template size_t eval_idx(size_t pos, std::array& strides, FirstDim first, SecondDim second) { return static_cast(first) * strides[pos] + eval_idx(pos + 1, strides, second); } template size_t eval_idx(size_t pos, std::array& strides, FirstDim first, SecondDim second, ThirdDim third) { return static_cast(first) * strides[pos] + eval_idx(pos + 1, strides, second, third); } template struct validate_impl; template struct validate_impl { template static void apply(array::ArrayView& arrv, DATA_TYPE arr_c[], std::array& strides, Int... dims) { EXPECT(arrv(dims...) == arr_c[eval_idx(static_cast(0), strides, dims...)]); } }; template struct validate_impl { template static void apply(array::ArrayView& arrv, DATA_TYPE arr_c[], std::array& strides, Int... dims) { for (idx_t cnt = 0; cnt < arrv.template shape(); ++cnt) { validate_impl::apply(arrv, arr_c, strides, dims..., cnt); } } }; template struct compute_strides; template <> struct compute_strides<0> { template static void apply(array::ArrayView& arrv, std::array(Rank)>& strides) {} }; template struct compute_strides { template static void apply(array::ArrayView& arrv, std::array(Rank)>& strides) { strides[Dim - 1] = strides[Dim] * static_cast(arrv.template shape(Dim)>()); compute_strides::apply(arrv, strides); } }; template struct validate { static void apply(array::ArrayView& arrv, DATA_TYPE arr_c[]) { std::array strides; strides[Rank - 1] = 1; compute_strides::apply(arrv, strides); for (idx_t i = 0; i < arrv.template shape<0>(); ++i) { validate_impl::apply(arrv, arr_c, strides, i); } } }; struct Fixture { int N; std::vector nb_nodes; std::vector part; std::vector ridx; std::vector gidx; bool on_device_; std::unique_ptr halo_exchange_std{new parallel::HaloExchange()}; Fixture(bool on_device): on_device_(on_device) { int nnodes_c[] = {5, 6, 7}; nb_nodes = vec(nnodes_c); N = nb_nodes[mpi::comm().rank()]; switch (mpi::comm().rank()) { case 0: { int part_c[] = {2, 0, 0, 0, 1}; part = vec(part_c); idx_t ridx_c[] = {4, 1, 2, 3, 1}; ridx = vec(ridx_c); POD gidx_c[] = {0, 1, 2, 3, 0}; gidx = vec(gidx_c); break; } case 1: { int part_c[] = {0, 1, 1, 1, 2, 2}; part = vec(part_c); idx_t ridx_c[] = {3, 1, 2, 3, 2, 3}; ridx = vec(ridx_c); POD gidx_c[] = {0, 4, 5, 6, 0, 0}; gidx = vec(gidx_c); break; } case 2: { int part_c[] = {1, 1, 2, 2, 2, 0, 0}; part = vec(part_c); idx_t ridx_c[] = {2, 3, 2, 3, 4, 1, 2}; ridx = vec(ridx_c); POD gidx_c[] = {0, 0, 7, 8, 9, 0, 0}; gidx = vec(gidx_c); break; } } halo_exchange_std->setup(part.data(), ridx.data(), 0, N); } }; //----------------------------------------------------------------------------- void test_rank0_arrview(Fixture& f) { array::ArrayT arr(f.N); array::ArrayView arrv = array::make_host_view(arr); switch (mpi::comm().rank()) { case 0: { POD arr_c[] = {90, 1, 2, 3, 40}; for (int j = 0; j < f.N; ++j) { arrv(j) = arr_c[j]; } break; } case 1: { POD arr_c[] = {30, 4, 5, 6, 70, 80}; for (int j = 0; j < f.N; ++j) { arrv(j) = arr_c[j]; } break; } case 2: { POD arr_c[] = {50, 60, 7, 8, 9, 10, 20}; for (int j = 0; j < f.N; ++j) { arrv(j) = arr_c[j]; } break; } } if (f.on_device_) { arr.syncHostDevice(); } f.halo_exchange_std->execute_adjoint(arr, f.on_device_); if (f.on_device_) { arr.syncHostDevice(); } switch (mpi::comm().rank()) { case 0: { POD arr_c[] = {0, 11, 22, 33, 0}; validate::apply(arrv, arr_c); break; } case 1: { POD arr_c[] = {0, 44, 55, 66, 0, 0}; validate::apply(arrv, arr_c); break; } case 2: { POD arr_c[] = {0, 0, 77, 88, 99, 0, 0}; validate::apply(arrv, arr_c); break; } } } //----------------------------------------------------------------------------- void adjoint_test(POD sum1, POD sum2, const std::string string_test) { POD tol(1e-8); atlas::mpi::comm().allReduceInPlace(sum1, eckit::mpi::sum()); atlas::mpi::comm().allReduceInPlace(sum2, eckit::mpi::sum()); std::cout << "Adjoint test " << string_test << " " << sum1 << " " << sum2 << std::endl; EXPECT(std::abs(sum1 - sum2) < tol); } //----------------------------------------------------------------------------- // The *_adj_tests perform the adjoint test of the form // < A x , A x> = < A^T A x , x > // where A is the halo exchange and A^T is the adjoint of the halo exchange // and < > are the inner products //----------------------------------------------------------------------------- void test_rank0_arrview_adj_test(Fixture& f) { array::ArrayT arr_init(f.N); array::ArrayT arr(f.N); array::ArrayView arrv_init = array::make_host_view(arr_init); array::ArrayView arrv = array::make_host_view(arr); for (std::size_t j = 0; j < static_cast(f.N); ++j) { arrv_init(j) = (static_cast(f.part[j]) != mpi::comm().rank() ? 0 : f.gidx[j]); arrv(j) = arrv_init(j); } if (f.on_device_) { arr.syncHostDevice(); } f.halo_exchange_std->execute(arr, f.on_device_); if (f.on_device_) { arr.syncHostDevice(); } // sum1 POD sum1(0); for (std::size_t j = 0; j < static_cast(f.N); ++j) { sum1 += arrv(j) * arrv(j); } if (f.on_device_) { arr.syncHostDevice(); } f.halo_exchange_std->execute_adjoint(arr, f.on_device_); if (f.on_device_) { arr.syncHostDevice(); } // sum2 POD sum2(0); for (std::size_t j = 0; j < static_cast(f.N); ++j) { sum2 += arrv_init(j) * arrv(j); } adjoint_test(sum1, sum2, "test_rank0_arrview_adj_test"); } void test_rank1(Fixture& f) { array::ArrayT arr(f.N, 2); array::ArrayView arrv = array::make_host_view(arr); switch (mpi::comm().rank()) { case 0: { POD arr_c[] = {9, 1, 2, 3, 4}; for (int j = 0; j < f.N; ++j) { arrv(j, 0) = arr_c[j] * 10; arrv(j, 1) = arr_c[j] * 100; } break; } case 1: { POD arr_c[] = {3, 4, 5, 6, 7, 8}; for (int j = 0; j < f.N; ++j) { arrv(j, 0) = arr_c[j] * 10; arrv(j, 1) = arr_c[j] * 100; } break; } case 2: { POD arr_c[] = {5, 6, 7, 8, 9, 1, 2}; for (int j = 0; j < f.N; ++j) { arrv(j, 0) = arr_c[j] * 10; arrv(j, 1) = arr_c[j] * 100; } break; } } if (f.on_device_) { arr.syncHostDevice(); } f.halo_exchange_std->execute_adjoint(arr, f.on_device_); if (f.on_device_) { arr.syncHostDevice(); } switch (mpi::comm().rank()) { case 0: { POD arr_c[] = {0, 0, 20, 200, 40, 400, 60, 600, 0, 0}; validate::apply(arrv, arr_c); break; } case 1: { POD arr_c[] = {0, 0, 80, 800, 100, 1000, 120, 1200, 0, 0, 0, 0}; validate::apply(arrv, arr_c); break; } case 2: { POD arr_c[] = {0, 0, 0, 0, 140, 1400, 160, 1600, 180, 1800, 0, 0, 0, 0}; validate::apply(arrv, arr_c); break; } } } void test_rank1_adj_test(Fixture& f) { array::ArrayT arr_init(f.N, 2); array::ArrayT arr(f.N, 2); array::ArrayView arrv_init = array::make_host_view(arr_init); array::ArrayView arrv = array::make_host_view(arr); for (std::size_t j = 0; j < static_cast(f.N); ++j) { arrv_init(j, 0ul) = (static_cast(f.part[j]) != mpi::comm().rank() ? 0 : f.gidx[j] * 10); arrv_init(j, 1ul) = (static_cast(f.part[j]) != mpi::comm().rank() ? 0 : f.gidx[j] * 100); arrv(j, 0ul) = arrv_init(j, 0ul); arrv(j, 1ul) = arrv_init(j, 1ul); } if (f.on_device_) { arr.syncHostDevice(); } f.halo_exchange_std->execute(arr, f.on_device_); if (f.on_device_) { arr.syncHostDevice(); } // sum1 POD sum1(0); for (std::size_t j = 0; j < static_cast(f.N); ++j) { for (std::size_t i = 0; i < 2; ++i) { sum1 += arrv(j, i) * arrv(j, i); } } if (f.on_device_) { arr.syncHostDevice(); } f.halo_exchange_std->execute_adjoint(arr, f.on_device_); if (f.on_device_) { arr.syncHostDevice(); } // sum2 POD sum2(0); for (std::size_t j = 0; j < static_cast(f.N); ++j) { for (std::size_t i = 0; i < 2; ++i) { sum2 += arrv_init(j, i) * arrv(j, i); } } adjoint_test(sum1, sum2, "test_rank1_adj_test"); } void test_rank1_strided_v1(Fixture& f) { // create a 2d field from the gidx data, with two components per grid point array::ArrayT arr_t(f.N, 2); array::ArrayView arrv_t = array::make_host_view(arr_t); switch (mpi::comm().rank()) { case 0: { POD arr_c[] = {9, 1, 2, 3, 4}; for (int j = 0; j < f.N; ++j) { arrv_t(j, 0) = arr_c[j] * 10; arrv_t(j, 1) = arr_c[j] * 100; } break; } case 1: { POD arr_c[] = {3, 4, 5, 6, 7, 8}; for (int j = 0; j < f.N; ++j) { arrv_t(j, 0) = arr_c[j] * 10; arrv_t(j, 1) = arr_c[j] * 100; } break; } case 2: { POD arr_c[] = {5, 6, 7, 8, 9, 1, 2}; for (int j = 0; j < f.N; ++j) { arrv_t(j, 0) = arr_c[j] * 10; arrv_t(j, 1) = arr_c[j] * 100; } break; } } // create a wrap array where we fake the strides in a way that the second // dimension // (number of components) contains only one component but the associated // stride is 2 // (i.e. we are only selecting and exchanging the first component of the // field) std::unique_ptr arr(array::Array::wrap( arrv_t.data(), array::ArraySpec { array::make_shape(f.N, 1), #if ATLAS_GRIDTOOLS_STORAGE_BACKEND_CUDA array::make_strides(32, 1) } #else array::make_strides(2, 1) } #endif )); arr->syncHostDevice(); f.halo_exchange_std->execute_adjoint(*arr, f.on_device_); arr->syncHostDevice(); switch (mpi::comm().rank()) { case 0: { POD arr_c[] = {0, 900, 20, 100, 40, 200, 60, 300, 0, 400}; validate::apply(arrv_t, arr_c); break; } case 1: { POD arr_c[] = {0, 300, 80, 400, 100, 500, 120, 600, 0, 700, 0, 800}; validate::apply(arrv_t, arr_c); break; } case 2: { POD arr_c[] = {0, 500, 0, 600, 140, 700, 160, 800, 180, 900, 0, 100, 0, 200}; validate::apply(arrv_t, arr_c); break; } } } void test_rank1_strided_v1_adj_test(Fixture& f) { // create a 2d field from the gidx data, with two components per grid point array::ArrayT arr_init_t(f.N, 2); array::ArrayView arrv_init_t = array::make_host_view(arr_init_t); array::ArrayT arr_t(f.N, 2); array::ArrayView arrv_t = array::make_host_view(arr_t); for (std::size_t j = 0; j < static_cast(f.N); ++j) { arrv_init_t(j, 0ul) = (static_cast(f.part[j]) != mpi::comm().rank() ? 0 : f.gidx[j] * 10); arrv_init_t(j, 1ul) = (static_cast(f.part[j]) != mpi::comm().rank() ? 0 : f.gidx[j] * 100); arrv_t(j, 0ul) = arrv_init_t(j, 0ul); arrv_t(j, 1ul) = arrv_init_t(j, 1ul); } // create a wrap array where we fake the strides in a way that the second // dimension // (number of components) contains only one component but the associated // stride is 2 // (i.e. we are only selecting and exchanging the first component of the // field) std::unique_ptr arr(array::Array::wrap( arrv_t.data(), array::ArraySpec { array::make_shape(f.N, 1), #if ATLAS_GRIDTOOLS_STORAGE_BACKEND_CUDA array::make_strides(32, 1) } #else array::make_strides(2, 1) } #endif )); if (f.on_device_) { arr->syncHostDevice(); } f.halo_exchange_std->execute(*arr, f.on_device_); if (f.on_device_) { arr->syncHostDevice(); } // sum1 POD sum1(0); for (std::size_t j = 0; j < static_cast(f.N); ++j) { for (std::size_t i = 0; i < 2; ++i) { sum1 += arrv_t(j, i) * arrv_t(j, i); } } if (f.on_device_) { arr->syncHostDevice(); } f.halo_exchange_std->execute_adjoint(*arr, f.on_device_); if (f.on_device_) { arr->syncHostDevice(); } // sum2 POD sum2(0); for (std::size_t j = 0; j < static_cast(f.N); ++j) { for (std::size_t i = 0; i < 2; ++i) { sum2 += arrv_init_t(j, i) * arrv_t(j, i); } } adjoint_test(sum1, sum2, "test_rank1_strided_v1_adj_test"); } void test_rank1_strided_v2(Fixture& f) { // create a 2d field from the gidx data, with two components per grid point array::ArrayT arr_t(f.N, 2); array::ArrayView arrv_t = array::make_host_view(arr_t); switch (mpi::comm().rank()) { case 0: { POD arr_c[] = {9, 1, 2, 3, 4}; for (int j = 0; j < f.N; ++j) { arrv_t(j, 0) = arr_c[j] * 10; arrv_t(j, 1) = arr_c[j] * 100; } break; } case 1: { POD arr_c[] = {3, 4, 5, 6, 7, 8}; for (int j = 0; j < f.N; ++j) { arrv_t(j, 0) = arr_c[j] * 10; arrv_t(j, 1) = arr_c[j] * 100; } break; } case 2: { POD arr_c[] = {5, 6, 7, 8, 9, 1, 2}; for (int j = 0; j < f.N; ++j) { arrv_t(j, 0) = arr_c[j] * 10; arrv_t(j, 1) = arr_c[j] * 100; } break; } } // create a wrap array where we fake the strides in a way that the second // dimension // (number of components) contains only one component but the associated // stride is 2 // (i.e. we are only selecting and exchanging the first component of the // field) std::unique_ptr arr(array::Array::wrap( &(arrv_t(0, 1)), array::ArraySpec { array::make_shape(f.N, 1), #if ATLAS_GRIDTOOLS_STORAGE_BACKEND_CUDA array::make_strides(32, 1) #else array::make_strides(2, 1) #endif })); if (f.on_device_) { arr->syncHostDevice(); } f.halo_exchange_std->execute_adjoint(*arr, false); switch (mpi::comm().rank()) { case 0: { POD arr_c[] = {90, 0, 10, 200, 20, 400, 30, 600, 40, 0}; validate::apply(arrv_t, arr_c); break; } case 1: { POD arr_c[] = {30, 0, 40, 800, 50, 1000, 60, 1200, 70, 0, 80, 0}; validate::apply(arrv_t, arr_c); break; } case 2: { POD arr_c[] = {50, 0, 60, 0, 70, 1400, 80, 1600, 90, 1800, 10, 0, 20, 0}; validate::apply(arrv_t, arr_c); break; } } } void test_rank1_strided_v2_adj_test(Fixture& f) { // create a 2d field from the gidx data, with two components per grid point array::ArrayT arr_init_t(f.N, 2); array::ArrayView arrv_init_t = array::make_host_view(arr_init_t); array::ArrayT arr_t(f.N, 2); array::ArrayView arrv_t = array::make_host_view(arr_t); for (std::size_t j = 0; j < static_cast(f.N); ++j) { arrv_init_t(j, 0ul) = (static_cast(f.part[j]) != mpi::comm().rank() ? 0 : f.gidx[j] * 10); arrv_init_t(j, 1ul) = (static_cast(f.part[j]) != mpi::comm().rank() ? 0 : f.gidx[j] * 100); arrv_t(j, 0ul) = arrv_init_t(j, 0ul); arrv_t(j, 1ul) = arrv_init_t(j, 1ul); } // create a wrap array where we fake the strides in a way that the second // dimension // (number of components) contains only one component but the associated // stride is 2 // (i.e. we are only selecting and exchanging the second component of the // field) std::unique_ptr arr(array::Array::wrap( &(arrv_t(0, 1)), array::ArraySpec { array::make_shape(f.N, 1), #if ATLAS_GRIDTOOLS_STORAGE_BACKEND_CUDA array::make_strides(32, 1) #else array::make_strides(2, 1) #endif })); if (f.on_device_) { arr->syncHostDevice(); } f.halo_exchange_std->execute(*arr, f.on_device_); if (f.on_device_) { arr->syncHostDevice(); } // sum1 POD sum1(0); for (std::size_t j = 0; j < static_cast(f.N); ++j) { for (std::size_t i = 0; i < 2; ++i) { sum1 += arrv_t(j, i) * arrv_t(j, i); } } if (f.on_device_) { arr->syncHostDevice(); } f.halo_exchange_std->execute_adjoint(*arr, f.on_device_); if (f.on_device_) { arr->syncHostDevice(); } // sum2 POD sum2(0); for (std::size_t j = 0; j < static_cast(f.N); ++j) { for (std::size_t i = 0; i < 2; ++i) { sum2 += arrv_init_t(j, i) * arrv_t(j, i); } } adjoint_test(sum1, sum2, "test_rank1_strided_v2_adj_test"); } void test_rank2(Fixture& f) { array::ArrayT arr(f.N, 3, 2); array::ArrayView arrv = array::make_host_view(arr); switch (mpi::comm().rank()) { case 0: { POD arr_c[] = {9, 1, 2, 3, 4}; for (std::size_t j = 0; j < static_cast(f.N); ++j) { for (std::size_t i = 0; i < 3; ++i) { for (std::size_t k = 0; k < 2; ++k) { arrv(j, i, k) = (2 * static_cast(k) - 1) * arr_c[j] * std::pow(10, i); } } } break; } case 1: { POD arr_c[] = {3, 4, 5, 6, 7, 8}; for (std::size_t j = 0; j < static_cast(f.N); ++j) { for (std::size_t i = 0; i < 3; ++i) { for (std::size_t k = 0; k < 2; ++k) { arrv(j, i, k) = (2 * static_cast(k) - 1) * arr_c[j] * std::pow(10, i); } } } break; } case 2: { POD arr_c[] = {5, 6, 7, 8, 9, 1, 2}; for (std::size_t j = 0; j < static_cast(f.N); ++j) { for (std::size_t i = 0; i < 3; ++i) { for (std::size_t k = 0; k < 2; ++k) { arrv(j, i, k) = (2 * static_cast(k) - 1) * arr_c[j] * std::pow(10, i); } } } break; } } if (f.on_device_) { arr.syncHostDevice(); } f.halo_exchange_std->execute_adjoint(arr, f.on_device_); if (f.on_device_) { arr.syncHostDevice(); } switch (mpi::comm().rank()) { case 0: { POD arr_c[] = {0, 0, 0, 0, 0, 0, -2, 2, -20, 20, -200, 200, -4, 4, -40, 40, -400, 400, -6, 6, -60, 60, -600, 600, 0, 0, 0, 0, 0, 0}; validate::apply(arrv, arr_c); break; } case 1: { POD arr_c[] = {0, 0, 0, 0, 0, 0, -8, 8, -80, 80, -800, 800, -10, 10, -100, 100, -1000, 1000, -12, 12, -120, 120, -1200, 1200, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; validate::apply(arrv, arr_c); break; } case 2: { POD arr_c[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -14, 14, -140, 140, -1400, 1400, -16, 16, -160, 160, -1600, 1600, -18, 18, -180, 180, -1800, 1800, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; validate::apply(arrv, arr_c); break; } } } void test_rank2_adj_test(Fixture& f) { array::ArrayT arr_init(f.N, 3, 2); array::ArrayView arrv_init = array::make_host_view(arr_init); array::ArrayT arr(f.N, 3, 2); array::ArrayView arrv = array::make_host_view(arr); for (size_t p = 0; p < static_cast(f.N); ++p) { for (size_t i = 0; i < 3; ++i) { arrv_init(p, i, static_cast(0)) = (static_cast(f.part[p]) != mpi::comm().rank() ? 0 : -f.gidx[p] * std::pow(10, i)); arrv_init(p, i, static_cast(1)) = (static_cast(f.part[p]) != mpi::comm().rank() ? 0 : f.gidx[p] * std::pow(10, i)); arrv(p, i, static_cast(0)) = arrv_init(p, i, static_cast(0)); arrv(p, i, static_cast(1)) = arrv_init(p, i, static_cast(1)); } } if (f.on_device_) { arr.syncHostDevice(); } f.halo_exchange_std->execute(arr, f.on_device_); // sum1 POD sum1(0); for (std::size_t p = 0; p < static_cast(f.N); ++p) { for (size_t i = 0; i < 3; ++i) { for (std::size_t k = 0; k < 2; ++k) { sum1 += arrv(p, i, k) * arrv(p, i, k); } } } if (f.on_device_) { arr.syncHostDevice(); } f.halo_exchange_std->execute_adjoint(arr, f.on_device_); if (f.on_device_) { arr.syncHostDevice(); } // sum2 POD sum2(0); for (std::size_t p = 0; p < static_cast(f.N); ++p) { for (size_t i = 0; i < 3; ++i) { for (std::size_t k = 0; k < 2; ++k) { sum2 += arrv_init(p, i, k) * arrv(p, i, k); } } } adjoint_test(sum1, sum2, "test_rank2_adj_test"); } void test_rank2_l1(Fixture& f) { array::ArrayT arr_t(f.N, 3, 2); array::ArrayView arrv_t = array::make_host_view(arr_t); switch (mpi::comm().rank()) { case 0: { POD arr_c[] = {9, 1, 2, 3, 4}; for (std::size_t j = 0; j < static_cast(f.N); ++j) { for (std::size_t i = 0; i < 3; ++i) { for (std::size_t k = 0; k < 2; ++k) { arrv_t(j, i, k) = (2 * static_cast(k) - 1) * arr_c[j] * std::pow(10, i); } } } break; } case 1: { POD arr_c[] = {3, 4, 5, 6, 7, 8}; for (std::size_t j = 0; j < static_cast(f.N); ++j) { for (std::size_t i = 0; i < 3; ++i) { for (std::size_t k = 0; k < 2; ++k) { arrv_t(j, i, k) = (2 * static_cast(k) - 1) * arr_c[j] * std::pow(10, i); } } } break; } case 2: { POD arr_c[] = {5, 6, 7, 8, 9, 1, 2}; for (std::size_t j = 0; j < static_cast(f.N); ++j) { for (std::size_t i = 0; i < 3; ++i) { for (std::size_t k = 0; k < 2; ++k) { arrv_t(j, i, k) = (2 * static_cast(k) - 1) * arr_c[j] * std::pow(10, i); } } } break; } } std::unique_ptr arr(array::Array::wrap( arrv_t.data(), array::ArraySpec { array::make_shape(f.N, 1, 2), #if ATLAS_GRIDTOOLS_STORAGE_BACKEND_CUDA array::make_strides(96, 32, 1) #else array::make_strides(6, 2, 1) #endif })); f.halo_exchange_std->execute_adjoint(*arr, false); switch (mpi::comm().rank()) { case 0: { POD arr_c[] = {0, 0, -90, 90, -900, 900, // halo -2, 2, -10, 10, -100, 100, // core -4, 4, -20, 20, -200, 200, // core -6, 6, -30, 30, -300, 300, // core 0, 0, -40, 40, -400, 400}; // halo validate::apply(arrv_t, arr_c); break; } case 1: { POD arr_c[] = {0, 0, -30, 30, -300, 300, // halo -8, 8, -40, 40, -400, 400, // core -10, 10, -50, 50, -500, 500, // core -12, 12, -60, 60, -600, 600, // core 0, 0, -70, 70, -700, 700, // halo 0, 0, -80, 80, -800, 800}; // halo validate::apply(arrv_t, arr_c); break; } case 2: { POD arr_c[] = {0, 0, -50, 50, -500, 500, // halo 0, 0, -60, 60, -600, 600, // halo -14, 14, -70, 70, -700, 700, // core -16, 16, -80, 80, -800, 800, // core -18, 18, -90, 90, -900, 900, // core 0, 0, -10, 10, -100, 100, // halo 0, 0, -20, 20, -200, 200}; // halo validate::apply(arrv_t, arr_c); break; } } } void test_rank2_l1_adj_test(Fixture& f) { array::ArrayT arr_init_t(f.N, 3, 2); array::ArrayView arrv_init_t = array::make_host_view(arr_init_t); array::ArrayT arr_t(f.N, 3, 2); array::ArrayView arrv_t = array::make_host_view(arr_t); for (std::size_t p = 0; p < static_cast(f.N); ++p) { for (std::size_t i = 0; i < 3; ++i) { arrv_init_t(p, i, static_cast(0)) = (static_cast(f.part[p]) != mpi::comm().rank() ? 0 : -f.gidx[p] * std::pow(10, i)); arrv_init_t(p, i, static_cast(1)) = (size_t(f.part[p]) != mpi::comm().rank() ? 0 : f.gidx[p] * std::pow(10, i)); arrv_t(p, i, static_cast(0)) = arrv_init_t(p, i, static_cast(0)); arrv_t(p, i, static_cast(1)) = arrv_init_t(p, i, static_cast(1)); } } std::unique_ptr arr(array::Array::wrap( arrv_t.data(), array::ArraySpec { array::make_shape(f.N, 1, 2), #if ATLAS_GRIDTOOLS_STORAGE_BACKEND_CUDA array::make_strides(96, 32, 1) #else array::make_strides(6, 2, 1) #endif })); f.halo_exchange_std->execute(*arr, false); // sum1 POD sum1(0); for (std::size_t p = 0; p < static_cast(f.N); ++p) { for (std::size_t i = 0; i < 3; ++i) { for (std::size_t k = 0; k < 2; ++k) { sum1 += arrv_t(p, i, k) * arrv_t(p, i, k); } } } f.halo_exchange_std->execute_adjoint(*arr, false); // sum2 POD sum2(0); for (std::size_t p = 0; p < static_cast(f.N); ++p) { for (std::size_t i = 0; i < 3; ++i) { for (std::size_t k = 0; k < 2; ++k) { sum2 += arrv_init_t(p, i, k) * arrv_t(p, i, k); } } } adjoint_test(sum1, sum2, "test_rank2_l1_adj_test"); } void test_rank2_l2_v2(Fixture& f) { #if ATLAS_GRIDTOOLS_STORAGE_BACKEND_HOST // Test rank 2 halo-exchange array::ArrayT arr_t(f.N, 3, 2); array::ArrayView arrv_t = array::make_host_view(arr_t); switch (mpi::comm().rank()) { case 0: { POD arr_c[] = {9, 1, 2, 3, 4}; for (std::size_t j = 0; j < static_cast(f.N); ++j) { for (std::size_t i = 0; i < 3; ++i) { for (std::size_t k = 0; k < 2; ++k) { arrv_t(j, i, k) = (2 * static_cast(k) - 1) * arr_c[j] * std::pow(10, i); } } } break; } case 1: { POD arr_c[] = {3, 4, 5, 6, 7, 8}; for (std::size_t j = 0; j < static_cast(f.N); ++j) { for (std::size_t i = 0; i < 3; ++i) { for (std::size_t k = 0; k < 2; ++k) { arrv_t(j, i, k) = (2 * static_cast(k) - 1) * arr_c[j] * std::pow(10, i); } } } break; } case 2: { POD arr_c[] = {5, 6, 7, 8, 9, 1, 2}; for (std::size_t j = 0; j < static_cast(f.N); ++j) { for (std::size_t i = 0; i < 3; ++i) { for (std::size_t k = 0; k < 2; ++k) { arrv_t(j, i, k) = (2 * static_cast(k) - 1) * arr_c[j] * std::pow(10, i); } } } break; } } /* for ( int p = 0; p < f.N; ++p ) { for ( size_t i = 0; i < 3; ++i ) { arrv_t( (size_t)p, i, (size_t)0 ) = ( size_t( f.part[p] ) != mpi::comm().rank() ? 0 : -f.gidx[p] * std::pow( 10, i ) ); arrv_t( (size_t)p, i, (size_t)1 ) = ( size_t( f.part[p] ) != mpi::comm().rank() ? 0 : f.gidx[p] * std::pow( 10, i ) ); } } */ std::unique_ptr arr(array::Array::wrap( &arrv_t(0, 1, 1), array::ArraySpec { array::make_shape(f.N, 1, 1), #if ATLAS_GRIDTOOLS_STORAGE_BACKEND_CUDA array::make_strides(192, 32, 1) #else array::make_strides(6, 2, 1) #endif })); f.halo_exchange_std->execute_adjoint(*arr, f.on_device_); switch (mpi::comm().rank()) { case 0: { POD arr_c[] = {-9, 9, -90, 0, -900, 900, // halo -1, 1, -10, 20, -100, 100, // core -2, 2, -20, 40, -200, 200, // core -3, 3, -30, 60, -300, 300, // core -4, 4, -40, 0, -400, 400}; // halo validate::apply(arrv_t, arr_c); break; } case 1: { POD arr_c[] = {-3, 3, -30, 0, -300, 300, // halo -4, 4, -40, 80, -400, 400, // core -5, 5, -50, 100, -500, 500, // core -6, 6, -60, 120, -600, 600, // core -7, 7, -70, 0, -700, 700 // halo - 8, 8, -80, 0, -800, 800}; // halo validate::apply(arrv_t, arr_c); break; } case 2: { POD arr_c[] = {-5, 5, -50, 0, -500, 500, // halo -6, 6, -60, 0, -600, 600, // halo -7, 7, -70, 140, -700, 700, // core -8, 8, -80, 160, -800, 800, // core -9, 9, -90, 180, -900, 900, // core -1, 1, -10, 0, -100, 100, // halo -2, 2, -20, 0, -200, 200}; // halo validate::apply(arrv_t, arr_c); break; } } #endif } void test_rank2_v2(Fixture& f) { #if ATLAS_GRIDTOOLS_STORAGE_BACKEND_HOST array::ArrayT arr_t(f.N, 3, 2); array::ArrayView arrv_t = array::make_view(arr_t); switch (mpi::comm().rank()) { case 0: { POD arr_c[] = {9, 1, 2, 3, 4}; for (std::size_t j = 0; j < static_cast(f.N); ++j) { for (std::size_t i = 0; i < 3; ++i) { for (std::size_t k = 0; k < 2; ++k) { arrv_t(j, i, k) = (2 * static_cast(k) - 1) * arr_c[j] * std::pow(10, i); } } } break; } case 1: { POD arr_c[] = {3, 4, 5, 6, 7, 8}; for (std::size_t j = 0; j < static_cast(f.N); ++j) { for (std::size_t i = 0; i < 3; ++i) { for (std::size_t k = 0; k < 2; ++k) { arrv_t(j, i, k) = (2 * static_cast(k) - 1) * arr_c[j] * std::pow(10, i); } } } break; } case 2: { POD arr_c[] = {5, 6, 7, 8, 9, 1, 2}; for (std::size_t j = 0; j < static_cast(f.N); ++j) { for (std::size_t i = 0; i < 3; ++i) { for (std::size_t k = 0; k < 2; ++k) { arrv_t(j, i, k) = (2 * static_cast(k) - 1) * arr_c[j] * std::pow(10, i); } } } break; } } /* for ( int p = 0; p < f.N; ++p ) { for ( size_t i = 0; i < 3; ++i ) { arrv_t( (size_t)p, i, (size_t)0 ) = ( size_t( f.part[p] ) != mpi::comm().rank() ? 0 : -f.gidx[p] * std::pow( 10, i ) ); arrv_t( (size_t)p, i, (size_t)1 ) = ( size_t( f.part[p] ) != mpi::comm().rank() ? 0 : f.gidx[p] * std::pow( 10, i ) ); } } */ std::unique_ptr arr(array::Array::wrap( &arrv_t(0, 0, 1), array::ArraySpec { array::make_shape(f.N, 3, 1), #if ATLAS_GRIDTOOLS_STORAGE_BACKEND_CUDA array::make_strides(192, 32, 2) #else array::make_strides(6, 2, 2) #endif })); f.halo_exchange_std->execute_adjoint(*arr, f.on_device_); switch (mpi::comm().rank()) { case 0: { POD arr_c[] = {-9, 0, -90, 0, -900, 0, // halo -1, 2, -10, 20, -100, 200, // core -2, 4, -20, 40, -200, 400, // core -3, 6, -30, 60, -300, 600, // core -4, 0, -40, 0, -400, 0}; // halo validate::apply(arrv_t, arr_c); break; } case 1: { POD arr_c[] = {-3, 0, -30, 0, -300, 0, // halo -4, 8, -40, 80, -400, 800, // core -5, 10, -50, 100, -500, 1000, // core -6, 12, -60, 120, -600, 1200, // core -7, 0, -70, 0, -700, 0, // halo -8, 0, -80, 0, -800, 0}; // halo validate::apply(arrv_t, arr_c); break; } case 2: { POD arr_c[] = {-5, 0, -50, 0, -500, 0, // halo -6, 0, -60, 0, -600, 0, // halo -7, 14, -70, 140, -700, 1400, // core -8, 16, -80, 160, -800, 1600, // core -9, 18, -90, 180, -900, 1800, // core -1, 0, -10, 0, -100, 0, // halo -2, 0, -20, 0, -200, 0}; // halo validate::apply(arrv_t, arr_c); break; } } #endif } void test_rank0_wrap(Fixture& f) { std::vector existing = f.gidx; std::unique_ptr arr(array::Array::wrap(existing.data(), array::make_shape(f.N))); array::ArrayView arrv = array::make_view(*arr); switch (mpi::comm().rank()) { case 0: { POD arr_c[] = {0, 2, 4, 6, 0}; for (int j = 0; j < f.N; ++j) { arrv(j) = arr_c[j]; } break; } case 1: { POD arr_c[] = {0, 8, 10, 12, 0, 0}; for (int j = 0; j < f.N; ++j) { arrv(j) = arr_c[j]; } break; } case 2: { POD arr_c[] = {0, 0, 14, 16, 18, 0, 0}; for (int j = 0; j < f.N; ++j) { arrv(j) = arr_c[j]; } break; } } if (f.on_device_) { arr->syncHostDevice(); } f.halo_exchange_std->execute_adjoint(*arr, f.on_device_); if (f.on_device_) { arr->syncHostDevice(); } switch (mpi::comm().rank()) { case 0: { POD arr_c[] = {0, 2, 4, 6, 0}; validate::apply(arrv, arr_c); break; } case 1: { POD arr_c[] = {0, 8, 10, 12, 0, 0}; validate::apply(arrv, arr_c); break; } case 2: { POD arr_c[] = {0, 0, 14, 16, 18, 0, 0}; validate::apply(arrv, arr_c); break; } } arr->deallocateDevice(); } void test_rank0_wrap_adj_test(Fixture& f) { std::unique_ptr arr(array::Array::wrap(f.gidx.data(), array::make_shape(f.N))); array::ArrayView arrv = array::make_view(*arr); // note we have to be VERY careful here // we can't do //std::unique_ptr arr_init( // array::Array::wrap( f.gidx.data(), array::make_shape( f.N ) ) ); //array::ArrayView arrv_init = // array::make_view( *arr_init ); // // as both pointers will end up sharing the same memory !! // // but instead we need to do; array::ArrayT arr_init(f.N); array::ArrayView arrv_init = array::make_host_view(arr_init); for (std::size_t j = 0; j < static_cast(f.N); ++j) { arrv_init(j) = arrv(j); } if (f.on_device_) { arr->syncHostDevice(); } f.halo_exchange_std->execute(*arr, f.on_device_); // sum1 POD sum1(0); for (std::size_t j = 0; j < static_cast(f.N); ++j) { sum1 += arrv(j) * arrv(j); } if (f.on_device_) { arr->syncHostDevice(); } f.halo_exchange_std->execute_adjoint(*arr, f.on_device_); if (f.on_device_) { arr->syncHostDevice(); } // sum2 POD sum2(0); for (std::size_t j = 0; j < static_cast(f.N); ++j) { sum2 += arrv_init(j) * arrv(j); } adjoint_test(sum1, sum2, "test_rank0_wrap_adj_test"); } void test_rank1_paralleldim1(Fixture& f) { array::ArrayT arr(2, f.N); array::ArrayView arrv = array::make_view(arr); switch (mpi::comm().rank()) { case 0: { POD arr_c[] = {9, 1, 2, 3, 4}; for (int j = 0; j < f.N; ++j) { arrv(0, j) = arr_c[j] * 10; arrv(1, j) = arr_c[j] * 100; } break; } case 1: { POD arr_c[] = {3, 4, 5, 6, 7, 8}; for (int j = 0; j < f.N; ++j) { arrv(0, j) = arr_c[j] * 10; arrv(1, j) = arr_c[j] * 100; } break; } case 2: { POD arr_c[] = {5, 6, 7, 8, 9, 1, 2}; for (int j = 0; j < f.N; ++j) { arrv(0, j) = arr_c[j] * 10; arrv(1, j) = arr_c[j] * 100; } break; } } f.halo_exchange_std->execute_adjoint(arr, false); switch (mpi::comm().rank()) { case 0: { POD arr_c[] = {0, 20, 40, 60, 0, 0, 200, 400, 600, 0}; // 90,900, 10,100, 20,200, 30,300, 40,400 }; validate::apply(arrv, arr_c); break; } case 1: { POD arr_c[] = {0, 80, 100, 120, 0, 0, 0, 800, 1000, 1200, 0, 0}; validate::apply(arrv, arr_c); break; } case 2: { POD arr_c[] = {0, 0, 140, 160, 180, 0, 0, 0, 0, 1400, 1600, 1800, 0, 0}; validate::apply(arrv, arr_c); break; } } } void test_rank1_paralleldim1_adj_test(Fixture& f) { array::ArrayT arr_init(2, f.N); array::ArrayView arrv_init = array::make_view(arr_init); array::ArrayT arr(2, f.N); array::ArrayView arrv = array::make_view(arr); for (std::size_t j = 0; j < static_cast(f.N); ++j) { arrv_init(0ul, j) = (static_cast(f.part[j]) != mpi::comm().rank() ? 0 : f.gidx[j] * 10); arrv_init(1ul, j) = (static_cast(f.part[j]) != mpi::comm().rank() ? 0 : f.gidx[j] * 100); arrv(0ul, j) = arrv_init(0ul, j); arrv(1ul, j) = arrv_init(1ul, j); } f.halo_exchange_std->execute(arr, false); // sum1 POD sum1(0); for (std::size_t p = 0; p < static_cast(f.N); ++p) { for (std::size_t i = 0; i < 2; ++i) { sum1 += arrv(i, p) * arrv(i, p); } } f.halo_exchange_std->execute_adjoint(arr, false); // sum2 POD sum2(0); for (std::size_t p = 0; p < static_cast(f.N); ++p) { for (std::size_t i = 0; i < 2; ++i) { sum2 += arrv_init(i, p) * arrv(i, p); } } adjoint_test(sum1, sum2, "test_rank1_paralleldim1_adj_test"); } void test_rank2_paralleldim2(Fixture& f) { array::ArrayT arr(3, f.N, 2); array::ArrayView arrv = array::make_view(arr); switch (mpi::comm().rank()) { case 0: { POD arr_c[] = {9, 1, 2, 3, 4}; for (std::size_t j = 0; j < static_cast(f.N); ++j) { for (std::size_t i = 0; i < 3; ++i) { for (std::size_t k = 0; k < 2; ++k) { arrv(i, j, k) = (2 * static_cast(k) - 1) * arr_c[j] * std::pow(10, i); } } } break; } case 1: { POD arr_c[] = {3, 4, 5, 6, 7, 8}; for (std::size_t j = 0; j < static_cast(f.N); ++j) { for (std::size_t i = 0; i < 3; ++i) { for (std::size_t k = 0; k < 2; ++k) { arrv(i, j, k) = (2 * static_cast(k) - 1) * arr_c[j] * std::pow(10, i); } } } break; } case 2: { POD arr_c[] = {5, 6, 7, 8, 9, 1, 2}; for (std::size_t j = 0; j < static_cast(f.N); ++j) { for (std::size_t i = 0; i < 3; ++i) { for (std::size_t k = 0; k < 2; ++k) { arrv(i, j, k) = (2 * static_cast(k) - 1) * arr_c[j] * std::pow(10, i); } } } break; } } f.halo_exchange_std->execute_adjoint>(arr, false); switch (mpi::comm().rank()) { case 0: { POD arr_c[] = {0, 0, -2, 2, -4, 4, -6, 6, 0, 0, 0, 0, -20, 20, -40, 40, -60, 60, 0, 0, 0, 0, -200, 200, -400, 400, -600, 600, 0, 0}; validate::apply(arrv, arr_c); break; } case 1: { POD arr_c[] = {0, 0, -8, 8, -10, 10, -12, 12, 0, 0, 0, 0, 0, 0, -80, 80, -100, 100, -120, 120, 0, 0, 0, 0, 0, 0, -800, 800, -1000, 1000, -1200, 1200, 0, 0, 0, 0}; validate::apply(arrv, arr_c); break; } case 2: { POD arr_c[] = {0, 0, 0, 0, -14, 14, -16, 16, -18, 18, 0, 0, 0, 0, 0, 0, 0, 0, -140, 140, -160, 160, -180, 180, 0, 0, 0, 0, 0, 0, 0, 0, -1400, 1400, -1600, 1600, -1800, 1800, 0, 0, 0, 0}; validate::apply(arrv, arr_c); break; } } } void test_rank2_paralleldim2_adj_test(Fixture& f) { array::ArrayT arr_init(3, f.N, 2); array::ArrayView arrv_init = array::make_view(arr_init); array::ArrayT arr(3, f.N, 2); array::ArrayView arrv = array::make_view(arr); for (size_t p = 0; p < static_cast(f.N); ++p) { for (size_t i = 0; i < 3; ++i) { arrv_init(i, p, static_cast(0)) = (size_t(f.part[p]) != mpi::comm().rank() ? 0 : -f.gidx[p] * std::pow(10, i)); arrv_init(i, p, static_cast(1)) = (size_t(f.part[p]) != mpi::comm().rank() ? 0 : f.gidx[p] * std::pow(10, i)); arrv(i, p, static_cast(0)) = arrv_init(i, p, static_cast(0)); arrv(i, p, static_cast(1)) = arrv_init(i, p, static_cast(1)); } } f.halo_exchange_std->execute>(arr, false); // sum1 POD sum1(0); for (std::size_t p = 0; p < static_cast(f.N); ++p) { for (std::size_t i = 0; i < 3; ++i) { for (std::size_t k = 0; k < 2; ++k) { sum1 += arrv(i, p, k) * arrv(i, p, k); } } } f.halo_exchange_std->execute_adjoint>(arr, false); // sum2 POD sum2(0); for (std::size_t p = 0; p < static_cast(f.N); ++p) { for (std::size_t i = 0; i < 3; ++i) { for (std::size_t k = 0; k < 2; ++k) { sum2 += arrv_init(i, p, k) * arrv(i, p, k); } } } adjoint_test(sum1, sum2, "test_rank2_paralleldim2_adj_test"); } void test_rank1_cinterface(Fixture& f) { #if ATLAS_GRIDTOOLS_STORAGE_BACKEND_HOST std::cout << "entering test_rank1_cinterface" << std::endl; array::ArrayT arr(f.N, 2); array::ArrayView arrv = array::make_host_view(arr); switch (mpi::comm().rank()) { case 0: { POD arr_c[] = {9, 1, 2, 3, 4}; for (int j = 0; j < f.N; ++j) { arrv(j, 0) = arr_c[j] * 10; arrv(j, 1) = arr_c[j] * 100; } break; } case 1: { POD arr_c[] = {3, 4, 5, 6, 7, 8}; for (int j = 0; j < f.N; ++j) { arrv(j, 0) = arr_c[j] * 10; arrv(j, 1) = arr_c[j] * 100; } break; } case 2: { POD arr_c[] = {5, 6, 7, 8, 9, 1, 2}; for (int j = 0; j < f.N; ++j) { arrv(j, 0) = arr_c[j] * 10; arrv(j, 1) = arr_c[j] * 100; } break; } } if (f.on_device_) { arr.syncHostDevice(); } int shapes[2] = {(int)arrv.shape(0), (int)arrv.shape(1)}; int strides[2] = {(int)arrv.stride(0), (int)arrv.stride(1)}; atlas__HaloExchange__execute_adjoint_strided_double(f.halo_exchange_std.get(), arrv.data(), &(strides[1]), &(shapes[1]), 1); switch (mpi::comm().rank()) { case 0: { POD arr_c[] = {0, 0, 20, 200, 40, 400, 60, 600, 0, 0}; validate::apply(arrv, arr_c); break; } case 1: { POD arr_c[] = {0, 0, 80, 800, 100, 1000, 120, 1200, 0, 0, 0, 0}; validate::apply(arrv, arr_c); break; } case 2: { POD arr_c[] = {0, 0, 0, 0, 140, 1400, 160, 1600, 180, 1800, 0, 0, 0, 0}; validate::apply(arrv, arr_c); break; } } #endif } CASE("test_haloexchange_adjoint") { Fixture f(false); SECTION("test_rank0_arrview") { test_rank0_arrview(f); } SECTION("test_rank0_arrview_adj_test") { test_rank0_arrview_adj_test(f); } SECTION("test_rank1") { test_rank1(f); } SECTION("test_rank1_adj_test") { test_rank1_adj_test(f); } SECTION("test_rank1_strided_v1") { test_rank1_strided_v1(f); } SECTION("test_rank1_strided_v1_adj_test") { test_rank1_strided_v1_adj_test(f); } SECTION("test_rank1_strided_v2") { test_rank1_strided_v2(f); } SECTION("test_rank1_strided_v2_adj_test") { test_rank1_strided_v2_adj_test(f); } SECTION("test_rank2") { test_rank2(f); } SECTION("test_rank2_adj_test") { test_rank2_adj_test(f); } SECTION("test_rank2_l1") { test_rank2_l1(f); } SECTION("test_rank2_l1_adj_test") { test_rank2_l1_adj_test(f); } /* NOT TESTED AS USES ATLAS_GRIDTOOLS_STORAGE_BACKEND_HOST SECTION( "test_rank2_l2_v2" ) { test_rank2_l2_v2( f ); } SECTION( "test_rank2_v2" ) { test_rank2_v2( f ); } */ SECTION("test_rank0_wrap") { test_rank0_wrap(f); } SECTION("test_rank0_wrap_adj_test") { test_rank0_wrap_adj_test(f); } SECTION("test_rank1_paralleldim_1") { test_rank1_paralleldim1(f); } SECTION("test_rank1_paralleldim_1_adj_test") { test_rank1_paralleldim1_adj_test(f); } SECTION("test_rank2_paralleldim_2") { test_rank2_paralleldim2(f); } SECTION("test_rank2_paralleldim_2_adj_test") { test_rank2_paralleldim2_adj_test(f); } /* NOT TESTED AS USES ATLAS_GRIDTOOLS_STORAGE_BACKEND_HOST SECTION( "test_rank1_cinterface" ) { test_rank1_cinterface( f ); } */ /* NOT IMPLEMENTED #if ATLAS_GRIDTOOLS_STORAGE_BACKEND_CUDA f.on_device_ = true; SECTION( "test_rank0_arrview" ) { test_rank0_arrview( f ); } SECTION( "test_rank1" ) { test_rank1( f ); } SECTION( "test_rank2" ) { test_rank2( f ); } SECTION( "test_rank0_wrap" ) { test_rank0_wrap( f ); } #endif */ } //----------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/parallel/test_mpi_scope.cc0000664000175000017500000000710615160212070022140 0ustar alastairalastair#include "atlas/parallel/mpi/mpi.h" #include "eckit/mpi/Comm.h" #include "tests/AtlasTestEnvironment.h" namespace atlas::test { CASE("test_mpi_scope") { mpi::comm().split(mpi::comm().rank(), "comm1"); mpi::comm().split(mpi::comm().rank(), "comm2"); eckit::mpi::setCommDefault("comm1"); EXPECT_EQ( mpi::comm().name(), "comm1" ); { // Use manual push/pop style scope management mpi::scope::push("self"); EXPECT_EQ( mpi::comm().name(), "self" ); mpi::scope::pop(); } EXPECT_EQ( mpi::comm().name(), "comm1" ); eckit::mpi::setCommDefault("comm2"); EXPECT_EQ( mpi::comm().name(), "comm2" ); { // Use RAII style scope management using atlas::mpi::Scope mpi::Scope scope("self"); EXPECT_EQ( mpi::comm().name(), "self" ); } EXPECT_EQ( mpi::comm().name(), "comm2" ); eckit::mpi::setCommDefault("comm1"); EXPECT_EQ( mpi::comm().name(), "comm1" ); { // Use RAII style scope management using atlas::mpi::Scope, now without explicitly specifying the comm name // We can edit at will manually the default comm within the scope, and it will be restored to "comm1" at the end of the scope mpi::Scope scope; EXPECT_EQ( mpi::comm().name(), "comm1" ); eckit::mpi::setCommDefault("self"); EXPECT_EQ( mpi::comm().name(), "self" ); } EXPECT_EQ( mpi::comm().name(), "comm1" ); { // Use RAII style scope management using atlas::mpi::Scope with Comm argument mpi::Scope scope(mpi::comm("self")); EXPECT_EQ( mpi::comm().name(), "self" ); } EXPECT_EQ( mpi::comm().name(), "comm1" ); eckit::mpi::setCommDefault("world"); } } // namespace atlas::test #if ATLAS_HAVE_MPI #define OMPI_SKIP_MPICXX 1 #include namespace atlas::test { CASE ("test_mpi_scope_with_int and deep nesting") { constexpr bool eckit_unregister_comm_not_available = not ATLAS_ECKIT_VERSION_AT_LEAST(2, 0, 0); MPI_Comm comm1, comm2; int rank, size; MPI_Comm_rank(MPI_COMM_WORLD, &rank); MPI_Comm_size(MPI_COMM_WORLD, &size); MPI_Comm_split(MPI_COMM_WORLD, rank%2, rank, &comm1); MPI_Comm_split(MPI_COMM_WORLD, rank%2, rank, &comm2); int comm1_int = MPI_Comm_c2f(comm1); int comm2_int = MPI_Comm_c2f(comm2); int world_int = MPI_Comm_c2f(MPI_COMM_WORLD); EXPECT_EQ( mpi::comm().name(), "world"); { mpi::Scope scope(comm1_int); EXPECT_EQ( mpi::comm().name(), "int." + std::to_string(comm1_int) ); { mpi::Scope scope2(comm2_int); EXPECT_EQ( mpi::comm().name(), "int." + std::to_string(comm2_int) ); { mpi::Scope scope3(comm1_int); EXPECT_EQ( mpi::comm().name(), "int." + std::to_string(comm1_int) ); { mpi::Scope scope4(world_int); EXPECT_EQ( mpi::comm().name(), "world"); } EXPECT_EQ( mpi::comm().name(), "int." + std::to_string(comm1_int) ); } EXPECT( mpi::has_comm("int." + std::to_string(comm1_int)) ); } EXPECT( not mpi::has_comm("int." + std::to_string(comm2_int)) || eckit_unregister_comm_not_available ); EXPECT_EQ( mpi::comm().name(), "int." + std::to_string(comm1_int) ); } EXPECT( not mpi::has_comm("int." + std::to_string(comm2_int)) || eckit_unregister_comm_not_available ); EXPECT_EQ( mpi::comm().name(), "world"); MPI_Comm_free(&comm1); MPI_Comm_free(&comm2); } } // namespace atlas::test #endif int main(int argc, char* argv[]) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/parallel/test_gather.cc0000664000175000017500000006623115160212070021440 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include #include #include #include "atlas/array.h" #include "atlas/array/ArrayView.h" #include "atlas/array/MakeView.h" #include "atlas/library/config.h" #include "atlas/parallel/GatherScatter.h" #include "atlas/parallel/mpi/mpi.h" #include "eckit/utils/Translator.h" #include "tests/AtlasTestEnvironment.h" /// POD: Type to test using POD = double; namespace atlas { namespace test { //----------------------------------------------------------------------------- template std::vector vec(const T (&list)[N]) { return std::vector(list, list + N); } struct Fixture { Fixture() { rank = static_cast(mpi::comm().rank()); comm_size = static_cast(mpi::comm().size()); int nnodes_c[] = {6, 6, 7}; nb_nodes = vec(nnodes_c); Nl = nb_nodes[rank]; switch (mpi::comm().rank()) { case 0: { //./----> extra ghost point with nonstandard gidx int part_c[] = {2, 0, 0, 0, 1, 2}; part = vec(part_c); idx_t ridx_c[] = {4, 1, 2, 3, 1, 3}; ridx = vec(ridx_c); gidx_t gidx_c[] = {9, 1, 2, 3, 4, 20}; gidx = vec(gidx_c); break; } case 1: { int part_c[] = {0, 1, 1, 1, 2, 2}; part = vec(part_c); idx_t ridx_c[] = {3, 1, 2, 3, 2, 3}; ridx = vec(ridx_c); gidx_t gidx_c[] = {3, 4, 5, 6, 7, 8}; gidx = vec(gidx_c); break; } case 2: { int part_c[] = {1, 1, 2, 2, 2, 0, 0}; part = vec(part_c); idx_t ridx_c[] = {2, 3, 2, 3, 4, 1, 2}; ridx = vec(ridx_c); gidx_t gidx_c[] = {5, 6, 7, 8, 9, 1, 2}; gidx = vec(gidx_c); break; } } gather_scatter.setup(part.data(), ridx.data(), 0, gidx.data(), Nl); } parallel::GatherScatter gather_scatter; std::vector nb_nodes; std::vector part; std::vector ridx; std::vector gidx; int Nl; int root; int rank; int comm_size; int Ng() { return rank == root ? gather_scatter.glb_dof() : 0; } }; //----------------------------------------------------------------------------- CASE("test_gather") { Fixture f; SECTION("test_gather_rank0") { for (f.root = 0; f.root < f.comm_size; ++f.root) { std::vector loc(f.Nl); std::vector glb(f.Ng()); for (int j = 0; j < f.Nl; ++j) { loc[j] = (idx_t(f.part[j]) != f.rank ? 0 : f.gidx[j] * 10); } idx_t strides[] = {1}; idx_t extents[] = {1}; f.gather_scatter.gather(loc.data(), strides, extents, 1, glb.data(), strides, extents, 1, f.root); if (f.rank == f.root) { POD glb_c[] = {10, 20, 30, 40, 50, 60, 70, 80, 90}; EXPECT(glb == eckit::testing::make_view(glb_c, glb_c + f.Ng())); } } } #if 1 SECTION("test_gather_rank1_deprecated") { for (f.root = 0; f.root < f.comm_size; ++f.root) { array::ArrayT loc(f.Nl, 2); array::ArrayT glb(f.Ng(), 2); array::ArrayT glb1(f.Ng(), 1); array::ArrayT glb2(f.Ng(), 1); array::ArrayView locv = array::make_view(loc); for (int j = 0; j < f.Nl; ++j) { locv(j, 0) = (size_t(f.part[j]) != mpi::comm().rank() ? 0 : f.gidx[j] * 10); locv(j, 1) = (size_t(f.part[j]) != mpi::comm().rank() ? 0 : f.gidx[j] * 100); } // Gather complete field { idx_t loc_strides[] = {loc.stride(0), loc.stride(1)}; idx_t loc_extents[] = {1, loc.shape(1)}; idx_t glb_strides[] = {glb.stride(0), glb.stride(1)}; idx_t glb_extents[] = {1, glb.shape(1)}; f.gather_scatter.gather(loc.data(), loc_strides, loc_extents, 2, glb.data(), glb_strides, glb_extents, 2, f.root); } if (f.rank == f.root) { auto glbv = array::make_view(glb); POD glb_c[] = {10, 100, 20, 200, 30, 300, 40, 400, 50, 500, 60, 600, 70, 700, 80, 800, 90, 900}; idx_t c(0); for (idx_t i = 0; i < glb.shape(0); ++i) { for (idx_t j = 0; j < glb.shape(1); ++j) { EXPECT(glbv(i, j) == glb_c[c++]); } } } // Gather only first component { idx_t loc_strides[] = {loc.stride(0), 2}; idx_t loc_extents[] = {1, 1}; idx_t glb_strides[] = {glb1.stride(0), glb1.stride(1)}; idx_t glb_extents[] = {1, glb1.shape(1)}; f.gather_scatter.gather(loc.data(), loc_strides, loc_extents, 2, glb1.data(), glb_strides, glb_extents, 2, f.root); } if (f.rank == f.root) { auto glbv = array::make_view(glb1); POD glb1_c[] = {10, 20, 30, 40, 50, 60, 70, 80, 90}; idx_t c(0); for (idx_t i = 0; i < glb1.shape(0); ++i) { for (idx_t j = 0; j < glb1.shape(1); ++j) { EXPECT(glbv(i, j) == glb1_c[c++]); } } } // Gather only second component { idx_t loc_strides[] = {loc.stride(0), 2}; idx_t loc_extents[] = {1, 1}; idx_t glb_strides[] = {glb2.stride(0), glb2.stride(1)}; idx_t glb_extents[] = {1, glb2.shape(1)}; f.gather_scatter.gather(loc.data() + 1, loc_strides, loc_extents, 1, glb2.data(), glb_strides, glb_extents, 1, f.root); } if (f.rank == f.root) { auto glbv = array::make_view(glb2); POD glb2_c[] = {100, 200, 300, 400, 500, 600, 700, 800, 900}; idx_t c(0); for (idx_t i = 0; i < glb2.shape(0); ++i) { for (idx_t j = 0; j < glb2.shape(1); ++j) { EXPECT(glbv(i, j) == glb2_c[c++]); } } } } } #endif SECTION("test_gather_rank1") { for (f.root = 0; f.root < f.comm_size; ++f.root) { array::ArrayT loc(f.Nl, 2); array::ArrayT glb(f.Ng(), 2); array::ArrayT glb1(f.Ng(), 1); array::ArrayT glb2(f.Ng(), 1); array::ArrayView locv = array::make_view(loc); for (int j = 0; j < f.Nl; ++j) { locv(j, 0) = (idx_t(f.part[j]) != f.rank ? 0 : f.gidx[j] * 10); locv(j, 1) = (idx_t(f.part[j]) != f.rank ? 0 : f.gidx[j] * 100); } // Gather complete field #if 0 { idx_t loc_strides[] = {2,1}; idx_t loc_extents[] = {idx_t(f.Nl), 2}; idx_t loc_rank = 2; idx_t loc_mpl_idxpos[] = {0}; idx_t loc_mpl_rank = 1; idx_t glb_strides[] = {2,1}; idx_t glb_extents[] = {idx_t(f.Ng()), 2}; idx_t glb_rank = 2; idx_t glb_mpl_idxpos[] = {0}; idx_t glb_mpl_rank = 1; parallel::detail::MPL_ArrayView lview(loc.data(),loc_strides,loc_extents,loc_rank,loc_mpl_idxpos,loc_mpl_rank); parallel::detail::MPL_ArrayView gview(glb.data(),glb_strides,glb_extents,glb_rank,glb_mpl_idxpos,glb_mpl_rank); EXPECT(lview.var_rank() == 1); EXPECT(lview.var_stride(0) == 1); EXPECT(lview.var_shape(0) == 2); EXPECT(gview.var_rank() == 1); EXPECT(gview.var_stride(0) == 1); EXPECT(gview.var_shape(0) == 2); EXPECT(lview.mpl_rank() == 1); EXPECT(lview.mpl_stride(0) == 2); EXPECT(lview.mpl_shape(0) == f.Nl); EXPECT(gview.mpl_rank() == 1); EXPECT(gview.mpl_stride(0) == 2); EXPECT(gview.mpl_shape(0) == f.Ng()); f.gather_scatter.gather( loc.data(), loc_strides, loc_extents, loc_rank, loc_mpl_idxpos, loc_mpl_rank, glb.data(), glb_strides, glb_extents, glb_rank, glb_mpl_idxpos, glb_mpl_rank, f.root ); if( mpi::comm().rank() == f.root ) { POD glb_c[] = { 10,100, 20,200, 30,300, 40,400, 50,500, 60,600, 70,700, 80,800, 90,900 }; EXPECT(make_view(glb.data(),glb.data()+2*f.Ng()) == make_view(glb_c,glb_c+2*f.Ng())); } } #endif // Gather only first component #if 0 { idx_t loc_strides[] = {2,2}; idx_t loc_extents[] = {idx_t(f.Nl), 1}; idx_t loc_rank = 2; idx_t loc_mpl_idxpos[] = {0}; idx_t loc_mpl_rank = 1; idx_t glb_strides[] = {1}; idx_t glb_extents[] = {idx_t(f.Ng())}; idx_t glb_rank = 1; idx_t glb_mpl_idxpos[] = {0}; idx_t glb_mpl_rank = 1; parallel::detail::MPL_ArrayView lview(loc.data(),loc_strides,loc_extents,loc_rank,loc_mpl_idxpos,loc_mpl_rank); EXPECT(lview.var_rank() == 1); EXPECT(lview.var_stride(0) == 2); EXPECT(lview.var_shape(0) == 1); EXPECT(lview.mpl_rank() == 1); EXPECT(lview.mpl_stride(0) == 2); EXPECT(lview.mpl_shape(0) == f.Nl); parallel::detail::MPL_ArrayView gview(glb1.data(),glb_strides,glb_extents,glb_rank,glb_mpl_idxpos,glb_mpl_rank); EXPECT(gview.var_rank() == 1); EXPECT(gview.var_stride(0) == 1); EXPECT(gview.var_shape(0) == 1); EXPECT(gview.mpl_rank() == 1); EXPECT(gview.mpl_stride(0) == 1); EXPECT(gview.mpl_shape(0) == f.Ng()); f.gather_scatter.gather( loc.data(), loc_strides, loc_extents, loc_rank, loc_mpl_idxpos, loc_mpl_rank, glb1.data(), glb_strides, glb_extents, glb_rank, glb_mpl_idxpos, glb_mpl_rank, f.root ); if( mpi::comm().rank() == f.root ) { POD glb1_c[] = { 10, 20, 30, 40, 50, 60, 70, 80, 90 }; EXPECT(make_view(glb1.data(),glb1.data()+f.Ng()) == make_view( glb1_c,glb1_c+f.Ng())); } } #endif // Gather only second component #if 0 { idx_t loc_strides[] = {2,2}; idx_t loc_extents[] = {idxt(f.Nl), 1}; idx_t loc_rank = 2; idx_t loc_mpl_idxpos[] = {0}; idx_t loc_mpl_rank = 1; idx_t glb_strides[] = {1}; idx_t glb_extents[] = {idx_t(f.Ng())}; idx_t glb_rank = 1; idx_t glb_mpl_idxpos[] = {0}; idx_t glb_mpl_rank = 1; f.gather_scatter.gather( loc.data()+1, loc_strides, loc_extents, loc_rank, loc_mpl_idxpos, loc_mpl_rank, glb2.data(), glb_strides, glb_extents, glb_rank, glb_mpl_idxpos, glb_mpl_rank, f.root ); } if( mpi::comm().rank() == f.root ) { POD glb2_c[] = { 100, 200, 300, 400, 500, 600, 700, 800, 900 }; EXPECT(make_view(glb2.data(),glb2.data()+f.Ng()) == make_view(glb2_c,glb2_c+f.Ng())); } #endif } } SECTION("test_gather_rank2") { for (f.root = 0; f.root < f.comm_size; ++f.root) { array::ArrayT loc(f.Nl, 3, 2); array::ArrayT glb(f.Ng(), 3, 2); array::ArrayT glbx1(f.Ng(), 3); array::ArrayT glbx2(f.Ng(), 3); array::ArrayT glb1x(f.Ng(), 2); array::ArrayT glb2x(f.Ng(), 2); array::ArrayT glb32(f.Ng()); array::ArrayView locv = array::make_view(loc); for (int p = 0; p < f.Nl; ++p) { for (int i = 0; i < 3; ++i) { locv(p, i, 0) = (size_t(f.part[p]) != mpi::comm().rank() ? 0 : -f.gidx[p] * std::pow(10, i)); locv(p, i, 1) = (size_t(f.part[p]) != mpi::comm().rank() ? 0 : f.gidx[p] * std::pow(10, i)); } } // Gather complete field #if 0 { idx_t loc_strides[] = {6,2,1}; idx_t loc_extents[] = {idx_t(f.Nl), 3, 2}; idx_t loc_rank = 3; idx_t loc_mpl_idxpos[] = {0}; idx_t loc_mpl_rank = 1; idx_t glb_strides[] = {6,2,1}; idx_t glb_extents[] = {idx_t(f.Ng()), 3, 2}; idx_t glb_rank = 3; idx_t glb_mpl_idxpos[] = {0}; idx_t glb_mpl_rank = 1; f.gather_scatter.gather( loc.data(), loc_strides, loc_extents, loc_rank, loc_mpl_idxpos, loc_mpl_rank, glb.data(), glb_strides, glb_extents, glb_rank, glb_mpl_idxpos, glb_mpl_rank, f.root ); } if( mpi::comm().rank() == f.root ) { POD glb_c[] = { -1,1, -10,10, -100,100, -2,2, -20,20, -200,200, -3,3, -30,30, -300,300, -4,4, -40,40, -400,400, -5,5, -50,50, -500,500, -6,6, -60,60, -600,600, -7,7, -70,70, -700,700, -8,8, -80,80, -800,800, -9,9, -90,90, -900,900 }; EXPECT(make_view(glb.data(),glb.data()+6*f.Ng()) == make_view(glb_c,glb_c+6*f.Ng())); } #endif // Gather var 1 #if 0 { idx_t loc_strides[] = {6,2,2}; idx_t loc_extents[] = {idx_t(f.Nl), 3, 1}; idx_t loc_rank = 3; idx_t loc_mpl_idxpos[] = {0}; idx_t loc_mpl_rank = 1; idx_t glb_strides[] = {6,1}; idx_t glb_extents[] = {idx_t(f.Ng()), 3}; idx_t glb_rank = 2; idx_t glb_mpl_idxpos[] = {0}; idx_t glb_mpl_rank = 1; f.gather_scatter.gather( &locv(0,0,0), loc_strides, loc_extents, loc_rank, loc_mpl_idxpos, loc_mpl_rank, glbx1.data(), glb_strides, glb_extents, glb_rank, glb_mpl_idxpos, glb_mpl_rank, f.root ); } if( mpi::comm().rank() == f.root ) { POD glb_c[] = { -1, -10, -100, -2, -20, -200, -3, -30, -300, -4, -40, -400, -5, -50, -500, -6, -60, -600, -7, -70, -700, -8, -80, -800, -9, -90, -900 }; EXPECT(make_view(glbx1.data(),glbx1.data()+3*f.Ng()) == make_view(glb_c,glb_c+3*f.Ng())); } #endif // Gather var 2 #if 0 { idx_t loc_strides[] = {6,2,2}; idx_t loc_extents[] = {idx_t(f.Nl), 3, 1}; idx_t loc_rank = 3; idx_t loc_mpl_idxpos[] = {0}; idx_t loc_mpl_rank = 1; idx_t glb_strides[] = {6,1}; idx_t glb_extents[] = {idx_t(f.Ng()), 3}; idx_t glb_rank = 2; idx_t glb_mpl_idxpos[] = {0}; idx_t glb_mpl_rank = 1; f.gather_scatter.gather( &locv(0,0,1), loc_strides, loc_extents, loc_rank, loc_mpl_idxpos, loc_mpl_rank, glbx2.data(), glb_strides, glb_extents, glb_rank, glb_mpl_idxpos, glb_mpl_rank, f.root ); } if( mpi::comm().rank() == f.root ) { POD glb_c[] = { 1, 10, 100, 2, 20, 200, 3, 30, 300, 4, 40, 400, 5, 50, 500, 6, 60, 600, 7, 70, 700, 8, 80, 800, 9, 90, 900 }; BOOST_CHECK_EQUAL_COLLECTIONS(glbx2.data(),glbx2.data()+3*f.Ng(), glb_c,glb_c+3*f.Ng()); } #endif // Gather lev 1 #if 0 { idx_t loc_strides[] = {6,6,1}; idx_t loc_extents[] = {idx_t(f.Nl), 1, 2}; idx_t loc_rank = 3; idx_t loc_mpl_idxpos[] = {0}; idx_t loc_mpl_rank = 1; idx_t glb_strides[] = {2,1}; idx_t glb_extents[] = {idx_t(f.Ng()), 2}; idx_t glb_rank = 2; idx_t glb_mpl_idxpos[] = {0}; idx_t glb_mpl_rank = 1; f.gather_scatter.gather( &locv(0,0,0), loc_strides, loc_extents, loc_rank, loc_mpl_idxpos, loc_mpl_rank, glb1x.data(), glb_strides, glb_extents, glb_rank, glb_mpl_idxpos, glb_mpl_rank, f.root ); } if( mpi::comm().rank() == f.root ) { POD glb_c[] = { -1,1, -2,2, -3,3, -4,4, -5,5, -6,6, -7,7, -8,8, -9,9, }; BOOST_CHECK_EQUAL_COLLECTIONS(glb1x.data(),glb1x.data()+2*f.Ng(), glb_c,glb_c+2*f.Ng()); } #endif // Gather lev 2 #if 0 { idx_t loc_strides[] = {6,6,1}; idx_t loc_extents[] = {idx_t(f.Nl), 1, 2}; idx_t loc_rank = 3; idx_t loc_mpl_idxpos[] = {0}; idx_t loc_mpl_rank = 1; idx_t glb_strides[] = {2,1}; idx_t glb_extents[] = {idx_t(f.Ng()), 2}; idx_t glb_rank = 2; idx_t glb_mpl_idxpos[] = {0}; idx_t glb_mpl_rank = 1; f.gather_scatter.gather( &locv(0,1,0), loc_strides, loc_extents, loc_rank, loc_mpl_idxpos, loc_mpl_rank, glb2x.data(), glb_strides, glb_extents, glb_rank, glb_mpl_idxpos, glb_mpl_rank, f.root ); } if( mpi::comm().rank() == f.root ) { POD glb_c[] = { -10,10, -20,20, -30,30, -40,40, -50,50, -60,60, -70,70, -80,80, -90,90, }; BOOST_CHECK_EQUAL_COLLECTIONS(glb2x.data(),glb2x.data()+2*f.Ng(), glb_c,glb_c+2*f.Ng()); } #endif // Gather lev 3 var 2 #if 0 { idx_t loc_strides[] = {6,6,2}; idx_t loc_extents[] = {idx_t(f.Nl), 1, 1}; idx_t loc_rank = 3; idx_t loc_mpl_idxpos[] = {0}; idx_t loc_mpl_rank = 1; idx_t glb_strides[] = {1}; idx_t glb_extents[] = {idx_t(f.Ng())}; idx_t glb_rank = 1; idx_t glb_mpl_idxpos[] = {0}; idx_t glb_mpl_rank = 1; f.gather_scatter.gather( &locv(0,2,1), loc_strides, loc_extents, loc_rank, loc_mpl_idxpos, loc_mpl_rank, glb32.data(), glb_strides, glb_extents, glb_rank, glb_mpl_idxpos, glb_mpl_rank, f.root ); } if( mpi::comm().rank() == f.root ) { POD glb_c[] = { 100, 200, 300, 400, 500, 600, 700, 800, 900 }; BOOST_CHECK_EQUAL_COLLECTIONS(glb32.data(),glb32.data()+f.Ng(), glb_c,glb_c+f.Ng()); } #endif } } SECTION("test_gather_rank0_ArrayView") { for (f.root = 0; f.root < f.comm_size; ++f.root) { array::ArrayT loc(f.Nl); array::ArrayT glb(f.Ng()); array::ArrayView locv = array::make_view(loc); array::ArrayView glbv = array::make_view(glb); for (int p = 0; p < f.Nl; ++p) { locv(p) = (size_t(f.part[p]) != mpi::comm().rank() ? 0 : f.gidx[p] * 10); } // Gather complete field { f.gather_scatter.gather(locv, glbv, f.root); } if (f.rank == f.root) { POD glb_c[] = {10, 20, 30, 40, 50, 60, 70, 80, 90}; for (idx_t n = 0; n < glb.shape(0); ++n) { EXPECT(glbv(n) == glb_c[n]); } } } } SECTION("test_gather_rank1_ArrayView") { for (f.root = 0; f.root < f.comm_size; ++f.root) { array::ArrayT loc(f.Nl, 2); array::ArrayT glb(f.Ng(), 2); array::ArrayView locv = array::make_view(loc); array::ArrayView glbv = array::make_view(glb); for (int p = 0; p < f.Nl; ++p) { locv(p, 0) = (size_t(f.part[p]) != mpi::comm().rank() ? 0 : -f.gidx[p] * 10); locv(p, 1) = (size_t(f.part[p]) != mpi::comm().rank() ? 0 : f.gidx[p] * 10); } // Gather complete field { f.gather_scatter.gather(locv, glbv, f.root); } if (f.rank == f.root) { POD glb_c[] = {-10, 10, -20, 20, -30, 30, -40, 40, -50, 50, -60, 60, -70, 70, -80, 80, -90, 90}; auto glbv = array::make_view(glb); idx_t c(0); for (idx_t i = 0; i < glb.shape(0); ++i) { for (idx_t j = 0; j < glb.shape(1); ++j) { EXPECT(glbv(i, j) == glb_c[c++]); } } } } } SECTION("test_gather_rank2_ArrayView") { for (f.root = 0; f.root < f.comm_size; ++f.root) { array::ArrayT loc(f.Nl, 3, 2); array::ArrayT glb(f.Ng(), 3, 2); array::ArrayView locv = array::make_view(loc); array::ArrayView glbv = array::make_view(glb); for (int p = 0; p < f.Nl; ++p) { for (int i = 0; i < 3; ++i) { locv(p, i, 0) = (size_t(f.part[p]) != mpi::comm().rank() ? 0 : -f.gidx[p] * std::pow(10, i)); locv(p, i, 1) = (size_t(f.part[p]) != mpi::comm().rank() ? 0 : f.gidx[p] * std::pow(10, i)); } } // Gather complete field { f.gather_scatter.gather(locv, glbv, f.root); } if (f.rank == f.root) { POD glb_c[] = {-1, 1, -10, 10, -100, 100, -2, 2, -20, 20, -200, 200, -3, 3, -30, 30, -300, 300, -4, 4, -40, 40, -400, 400, -5, 5, -50, 50, -500, 500, -6, 6, -60, 60, -600, 600, -7, 7, -70, 70, -700, 700, -8, 8, -80, 80, -800, 800, -9, 9, -90, 90, -900, 900}; idx_t c(0); for (idx_t i = 0; i < glb.shape(0); ++i) { for (idx_t j = 0; j < glb.shape(1); ++j) { for (idx_t k = 0; k < glb.shape(2); ++k) { EXPECT(glbv(i, j, k) == glb_c[c++]); } } } } } f.root = 0; } SECTION("test_scatter_rank2_ArrayView") { for (f.root = 0; f.root < f.comm_size; ++f.root) { array::ArrayT loc(f.Nl, 3, 2); array::ArrayT glb(f.Ng(), 3, 2); array::ArrayView locv = array::make_view(loc); array::ArrayView glbv = array::make_view(glb); if (f.rank == f.root) { POD glb_c[] = {-1, 1, -10, 10, -100, 100, -2, 2, -20, 20, -200, 200, -3, 3, -30, 30, -300, 300, -4, 4, -40, 40, -400, 400, -5, 5, -50, 50, -500, 500, -6, 6, -60, 60, -600, 600, -7, 7, -70, 70, -700, 700, -8, 8, -80, 80, -800, 800, -9, 9, -90, 90, -900, 900}; idx_t c(0); for (int i = 0; i < glb.shape(0); ++i) { for (int j = 0; j < glb.shape(1); ++j) { for (int k = 0; k < glb.shape(2); ++k) { glbv(i, j, k) = glb_c[c++]; } } } } POD nan = -1000.; locv.assign(nan); f.gather_scatter.scatter(glbv, locv, f.root); switch (mpi::comm().rank()) { case 0: { POD loc_c[] = {nan, nan, nan, nan, nan, nan, -1, 1, -10, 10, -100, 100, -2, 2, -20, 20, -200, 200, -3, 3, -30, 30, -300, 300, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan}; idx_t c(0); for (idx_t i = 0; i < loc.shape(0); ++i) { for (idx_t j = 0; j < loc.shape(1); ++j) { for (idx_t k = 0; k < loc.shape(2); ++k) { EXPECT(is_approximately_equal(locv(i, j, k), loc_c[c++])); } } } break; } case 1: { POD loc_c[] = {nan, nan, nan, nan, nan, nan, -4, 4, -40, 40, -400, 400, -5, 5, -50, 50, -500, 500, -6, 6, -60, 60, -600, 600, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan}; idx_t c(0); for (idx_t i = 0; i < loc.shape(0); ++i) { for (idx_t j = 0; j < loc.shape(1); ++j) { for (idx_t k = 0; k < loc.shape(2); ++k) { EXPECT(is_approximately_equal(locv(i, j, k), loc_c[c++])); } } } break; } case 2: { POD loc_c[] = {nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, -7, 7, -70, 70, -700, 700, -8, 8, -80, 80, -800, 800, -9, 9, -90, 90, -900, 900, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan, nan}; idx_t c(0); for (idx_t i = 0; i < loc.shape(0); ++i) { for (idx_t j = 0; j < loc.shape(1); ++j) { for (idx_t k = 0; k < loc.shape(2); ++k) { EXPECT(is_approximately_equal(locv(i, j, k), loc_c[c++])); } } } break; } } } } } //----------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/parallel/test_setcomm.cc0000664000175000017500000000370615160212070021633 0ustar alastairalastair#include "atlas/library/Library.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/util/Config.h" #include "eckit/mpi/Comm.h" #include "tests/AtlasTestEnvironment.h" #include #include namespace atlas { namespace test { CASE("test_setcomm") { printf(" eckit::mpi::comm () = 0x%p, eckit::mpi::comm ().size () = %8zu\n", &eckit::mpi::comm(), eckit::mpi::comm().size()); std::cout << eckit::mpi::comm().name() << std::endl; printf(" atlas::mpi::comm () = 0x%p, atlas::mpi::comm ().size () = %8zu\n", &atlas::mpi::comm(), atlas::mpi::comm().size()); std::cout << atlas::mpi::comm().name() << std::endl; EXPECT_EQ(eckit::mpi::comm().size(), atlas::mpi::comm().size()); int irank = eckit::mpi::comm().rank(); eckit::mpi::comm().split(irank, "SPLIT"); eckit::mpi::setCommDefault("SPLIT"); printf(" eckit::mpi::comm () = 0x%p, eckit::mpi::comm ().size () = %8zu\n", &eckit::mpi::comm(), eckit::mpi::comm().size()); std::cout << eckit::mpi::comm().name() << std::endl; printf(" atlas::mpi::comm () = 0x%p, atlas::mpi::comm ().size () = %8zu\n", &atlas::mpi::comm(), atlas::mpi::comm().size()); std::cout << atlas::mpi::comm().name() << std::endl; EXPECT_EQ(eckit::mpi::comm().size(), atlas::mpi::comm().size()); std::cout << "----- STOP -----" << std::endl; } CASE("test_split") { int irank = mpi::comm("world").rank(); auto& split_comm = mpi::comm("world").split(irank%2, "test_split"); EXPECT_EQ(mpi::comm("world").name(), "world"); EXPECT_EQ(split_comm.name(), "test_split"); if( mpi::comm("world").size()%2 == 0) { // even number EXPECT_EQ( split_comm.size() , mpi::comm("world").size()/2); } auto& split_comm_by_name = mpi::comm("test_split"); EXPECT_EQ( &split_comm, &split_comm_by_name); } } // namespace test } // namespace atlas int main(int argc, char* argv[]) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/parallel/test_omp_sort.cc0000664000175000017500000000613715160212070022027 0ustar alastairalastair/* * (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 // generate, is_sorted #include // bind #include // mt19937 and uniform_int_distribution #include // vector #include "atlas/parallel/omp/sort.h" #include "atlas/util/vector.h" #include "tests/AtlasTestEnvironment.h" namespace atlas { namespace test { atlas::vector create_random_data(int n) { std::random_device r; std::seed_seq seed{r(), r(), r(), r(), r(), r(), r(), r()}; std::mt19937 eng(seed); // a source of random data std::uniform_int_distribution dist; atlas::vector v(n); std::generate(v.begin(), v.end(), std::bind(dist, eng)); return v; } //----------------------------------------------------------------------------- CASE("test_sort_little") { // Because there is little work to be done, the underlying algorithm will not use OpenMP here. auto integers = create_random_data(20); SECTION("default order") { omp::sort(integers.begin(), integers.end()); EXPECT(std::is_sorted(integers.begin(), integers.end())); } SECTION("reverse order") { omp::sort(integers.begin(), integers.end(), [](const int& a, const int& b) { return b > a; }); EXPECT(std::is_sorted(integers.begin(), integers.end(), [](const int& a, const int& b) { return b > a; })); } } CASE("test_sort_large") { // There is enough work to use OpenMP underneith, if ATLAS_HAVE_OMP && OMP_NUM_THREADS > 1 auto integers = create_random_data(1000000); SECTION("default order") { omp::sort(integers.begin(), integers.end()); EXPECT(std::is_sorted(integers.begin(), integers.end())); } SECTION("reverse order") { omp::sort(integers.begin(), integers.end(), [](const int& a, const int& b) { return b > a; }); EXPECT(std::is_sorted(integers.begin(), integers.end(), [](const int& a, const int& b) { return b > a; })); } } CASE("test_merge_blocks") { // There is enough work to use OpenMP underneith, if ATLAS_HAVE_OMP && OMP_NUM_THREADS > 1 auto nb_blocks = 16; auto block_size = 100000; auto integers = create_random_data(nb_blocks * block_size); std::vector blocks_size(nb_blocks, block_size); auto end = integers.begin(); for (int b = 0; b < nb_blocks; ++b) { auto begin = end; end = begin + blocks_size[b]; omp::sort(begin, end); } omp::merge_blocks(integers.begin(), integers.end(), blocks_size.begin(), blocks_size.end()); EXPECT(std::is_sorted(integers.begin(), integers.end())); } //----------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/parallel/CMakeLists.txt0000664000175000017500000000423615160212070021355 0ustar alastairalastair# (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. ecbuild_add_test( TARGET atlas_test_haloexchange_adjoint MPI 3 CONDITION eckit_HAVE_MPI SOURCES test_haloexchange_adjoint.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) ecbuild_add_test( TARGET atlas_test_setcomm MPI 3 CONDITION eckit_HAVE_MPI SOURCES test_setcomm.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) ecbuild_add_test( TARGET atlas_test_haloexchange MPI 3 CONDITION eckit_HAVE_MPI SOURCES test_haloexchange.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) ecbuild_add_test( TARGET atlas_test_haloexchange_on_device MPI 3 CONDITION eckit_HAVE_MPI AND atlas_HAVE_GPU SOURCES test_haloexchange.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ATLAS_RUN_NGPUS=1 ) if( TEST atlas_test_haloexchange_on_device ) set_tests_properties ( atlas_test_haloexchange_on_device PROPERTIES LABELS "gpu") endif() ecbuild_add_test( TARGET atlas_test_gather MPI 3 CONDITION eckit_HAVE_MPI SOURCES test_gather.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) ecbuild_add_test( TARGET atlas_test_collect MPI 3 CONDITION eckit_HAVE_MPI SOURCES test_collect.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) ecbuild_add_test( TARGET atlas_test_omp_sort OMP 8 SOURCES test_omp_sort.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} CONDITION NOT SKIP_TEST_atlas_test_omp_sort ) ecbuild_add_test( TARGET atlas_test_omp_copy OMP 8 SOURCES test_omp_copy.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) ecbuild_add_test( TARGET atlas_test_mpi_scope MPI 1 CONDITION eckit_HAVE_MPI SOURCES test_mpi_scope.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) atlas-0.46.0/src/tests/mesh/0000775000175000017500000000000015160212070015750 5ustar alastairalastairatlas-0.46.0/src/tests/mesh/test_shapefunctions.cc0000664000175000017500000006320515160212070022355 0ustar alastairalastair/* * (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/util/Object.h" #include "atlas/util/ObjectHandle.h" // #include "tests/TestMeshes.h" #include "atlas/mpi/mpi.h" #include "atlas/util/Debug.h" // #include "atlas/Mesh.h" #include "atlas/array.h" #include "atlas/util/IndexView.h" // #include "atlas/actions/BuildParallelFields.h" // #include "atlas/actions/BuildPeriodicBoundaries.h" // #include "atlas/actions/BuildHalo.h" // #include "atlas/actions/BuildEdges.h" // #include "atlas/actions/BuildDualMesh.h" #include "eckit/maths/Matrix.h" #include "tests/AtlasTestEnvironment.h" using namespace eckit::maths; namespace atlas { namespace test { //----------------------------------------------------------------------------- template class Mononomial { public: Mononomial() {} Mononomial(double coeff_, int power_[NDIM]) { coeff = coeff_; std::copy(power_, power_ + NDIM, power); } Mononomial(double coeff_, int p1_) { coeff = coeff_; power[0] = p1_; } Mononomial(double coeff_, int p1_, int p2_) { coeff = coeff_; power[0] = p1_; power[1] = p2_; } Mononomial(double coeff_, int p1_, int p2_, int p3_) { coeff = coeff_; power[0] = p1_; power[1] = p2_; power[2] = p3_; } double eval(const double point[NDIM]) const { double val = coeff; for (size_t d = 0; d < NDIM; ++d) { val *= std::pow(point[d], power[d]); } return val; } double eval(const double& x) const { return coeff * std::pow(x, power[0]); } double eval(const double& x, const double& y) const { return coeff * std::pow(x, power[0]) * std::pow(y, power[1]); } double eval(const double& x, const double& y, const double& z) const { return coeff * std::pow(x, power[0]) * std::pow(y, power[1]) * std::pow(z, power[2]); } Mononomial deriv(size_t dim) const { double c = coeff * power[dim]; int p[NDIM]; std::copy(power, power + NDIM, p); p[dim] -= 1; return Mononomial(c, p); } double coeff; int power[NDIM]; }; template class Polynomial { public: typedef Mononomial monomial_type; public: Polynomial& add(const monomial_type& mononomial) { if (mononomial.coeff != 0) mononomials_.push_back(mononomial); return *this; } double eval(const double point[NDIM]) const { double val = 0; for (size_t n = 0; n < mononomials_.size(); ++n) { val += mononomials_[n].eval(point); } return val; } double eval(const double& x) const { double val = 0; for (size_t n = 0; n < mononomials_.size(); ++n) { val += mononomials_[n].eval(x); } return val; } double eval(const double& x, const double& y) const { double val = 0; for (size_t n = 0; n < mononomials_.size(); ++n) { val += mononomials_[n].eval(x, y); } return val; } double eval(const double& x, const double& y, const double& z) const { double val = 0; for (size_t n = 0; n < mononomials_.size(); ++n) { val += mononomials_[n].eval(x, y, z); } return val; } double operator()(const double point[NDIM]) const { return eval(point); } double operator()(const double& x) const { return eval(x); } double operator()(const double& x, const double& y) const { return eval(x, y); } double operator()(const double& x, const double& y, const double& z) const { return eval(x, y, z); } Polynomial deriv(size_t dim) const { Polynomial p; for (size_t i = 0; i < mononomials_.size(); ++i) p.add(mononomials_[i].deriv(dim)); return p; } std::vector grad() const { std::vector g; for (size_t d = 0; d < NDIM; ++d) g.push_back(deriv(d)); return g; } static Polynomial div(const std::vector& pvec) { Polynomial p; for (size_t d = 0; d < NDIM; ++d) p += pvec[d].deriv(d); return p; } static Polynomial curl_z(const std::vector& pvec) { return pvec[1].deriv(0) - pvec[0].deriv(1); } static std::vector curl(const std::vector& pvec) { std::vector p(3); if (NDIM == 2) { p[2] = pvec[1].deriv(0) - pvec[0].deriv(1); } if (NDIM == 3) { p[0] = pvec[2].deriv(1) - pvec[1].deriv(2); p[1] = pvec[0].deriv(2) - pvec[2].deriv(0); p[2] = pvec[1].deriv(0) - pvec[0].deriv(1); } return p; } Polynomial operator+(const Polynomial& p2) { Polynomial p = *this; p += p2; return p; } Polynomial operator-(const Polynomial& p2) { Polynomial p = *this; p -= p2; return p; } Polynomial& operator+=(const Polynomial& other) { return addition(other, +1.); } Polynomial& operator-=(const Polynomial& other) { return addition(other, -1.); } private: Polynomial& addition(const Polynomial& other, double sign = 1.) { std::vector matched(other.mononomials_.size(), false); for (size_t i = 0; i < mononomials_.size(); ++i) { for (size_t j = 0; j < other.mononomials_.size(); ++j) { if (!matched[j]) { bool match = true; for (size_t d = 0; d < NDIM; ++d) { if (mononomials_[i].power[d] != other.mononomials_[j].power[d]) { match = false; break; } } if (match) { matched[j] = true; mononomials_[i].coeff += sign * other.mononomials_[j].coeff; } } } } for (size_t j = 0; j < other.mononomials_.size(); ++j) { if (!matched[j]) { add(other.mononomials_[j]); } } return *this; } private: std::vector mononomials_; }; class ShapeFunction : public util::Object { public: ShapeFunction() {} virtual ~ShapeFunction() {} }; class ElementType : public util::Object { public: typedef util::ObjectHandle Ptr; typedef std::vector Vector; public: size_t N() const { return N_; } bool parametric() const { return parametric_; } const double* nodes() const { return nodes_.data(); } const ShapeFunction& shape_function() const { return *shape_function_; } protected: bool parametric_; size_t N_; std::vector nodes_; ShapeFunction::Ptr shape_function_; }; class Point : public ElementType { public: Point() { N_ = 1; parametric_ = false; shape_function_ = ShapeFunction::Ptr(new ShapeFunction); } }; class Polygon : public ElementType { public: Polygon(size_t max_nodes = 0) { parametric_ = false; N_ = max_nodes; } }; class QuadP1 : public ElementType { public: QuadP1() { parametric_ = true; N_ = 4; int nodes_init[] = {-1., -1., 1., -1., 1., 1., -1., 1.}; nodes_.assign(nodes_init, nodes_init + N_); shape_function_ = ShapeFunction::Ptr(new ShapeFunction); } }; class TriagP1 : public ElementType { public: TriagP1() { parametric_ = true; N_ = 3; int nodes_init[] = {0., 0., 1., 0., 0., 1.}; nodes_.assign(nodes_init, nodes_init + N_); shape_function_ = ShapeFunction::Ptr(new ShapeFunction); } }; class LineP0 : public ElementType { public: LineP0() { parametric_ = true; N_ = 1; double nodes_init[] = {0.}; nodes_.assign(nodes_init, nodes_init + N_); shape_function_ = ShapeFunction::Ptr(new ShapeFunction); } }; class LineP1 : public ElementType { public: LineP1() { parametric_ = true; N_ = 2; double nodes_init[] = {-1., 1.}; nodes_.assign(nodes_init, nodes_init + N_); shape_function_ = ShapeFunction::Ptr(new ShapeFunction); } }; class LineP2 : public ElementType { public: LineP2() { parametric_ = true; N_ = 3; double nodes_init[] = {-1., 1., 0.}; nodes_.assign(nodes_init, nodes_init + N_); shape_function_ = ShapeFunction::Ptr(new ShapeFunction); } }; class Structured1D : public ElementType { public: Structured1D(int N) { parametric_ = true; N_ = N; shape_function_ = ShapeFunction::Ptr(new ShapeFunction); } }; class Nodes : public util::Object { public: Nodes() { npts_ = 0; nlev_ = 0; nlon_ = 0; nlat_ = 0; nlev_ = 0; nproma_ = 0; nblk_ = 0; } size_t npts() const { return npts_; } size_t nlev() const { return nlev_; } size_t nproma() const { return nproma_; } size_t nblk() const { return nblk_; } size_t nlon() const { return nlon_; } size_t nlat() const { return nlat_; } private: size_t npts_; size_t nlev_; size_t nproma_; size_t nblk_; size_t nlon_; size_t nlat_; }; enum IndexType { IDX_NOTUSED = -100, IDX_NODE = -1, IDX_LEVEL = -2, IDX_BLK = -3, IDX_NPROMA = -4, IDX_VAR = -5 }; class NewFunctionSpace { public: NewFunctionSpace(): nproma_(0), nb_nodes_(0), nblk_(0) {} /// @brief Number of element types size_t nb_element_types() const { return nelem_per_type_.size(); } /// @brief Element type at index const ElementType& element_type(size_t idx) const { ATLAS_ASSERT(idx < element_types_.size()); return *element_types_[idx]; } /// @brief Number of elements for element type at given index size_t nb_elements_in_element_type(size_t idx) const { ATLAS_ASSERT(idx < nelem_per_type_.size()); return nelem_per_type_[idx]; } /// @brief Number of elements for element type at given index size_t nb_elements() const { return nb_elements_; } /// @brief Number of owned elements for element type at given index size_t nb_owned_elements() const { return nb_owned_elements_; } /// @brief Number of nodes in this function space size_t nb_nodes() const { return nb_nodes_; } /// @brief Number of nodes in this function space size_t nb_owned_nodes() const { return nb_owned_nodes_; } /// @brief Number of levels in this function space size_t nb_levels() const { return nlev_; } /// @brief Number of blocks (tiles) in this function space size_t nblk() const { return nblk_; } /// @brief Number of nodes within one block in this function space size_t nproma() const { return nproma_; } /// @brief Maximum number of points per element type size_t N_max() const { return N_max_; } /// @brief Minimum number of points per element type size_t N_min() const { return N_min_; } /// @brief Set the total number of nodes in FunctionSpace void set_nb_nodes(int nb_nodes) { nb_nodes_ = nb_nodes; if (nproma_ == 0) nproma_ = 1; ATLAS_ASSERT(nb_nodes_ % nproma_ == 0); nblk_ = nb_nodes_ / nproma_; } /// @brief Set the total number of nodes in FunctionSpace void set_nproma(int nproma) { nproma_ = nproma; nblk_ = 0; if (nb_nodes_ != 0) { ATLAS_ASSERT(nb_nodes_ % nproma_ == 0); nblk_ = nb_nodes_ / nproma_; } } /// @brief Set the number of levels in FunctionSpace void set_nb_levels(int nb_levels) { nlev_ = nb_levels; } /// @brief Add elements const ElementType& add_elements(const std::string& element_type, int nelem) { if (element_type == "Polygon") element_types_.push_back(ElementType::Ptr(new Polygon())); if (element_type == "QuadP1") element_types_.push_back(ElementType::Ptr(new QuadP1)); if (element_type == "TriagP1") element_types_.push_back(ElementType::Ptr(new TriagP1)); if (element_type == "LineP0") element_types_.push_back(ElementType::Ptr(new LineP0)); if (element_type == "LineP1") element_types_.push_back(ElementType::Ptr(new LineP1)); if (element_type == "LineP2") element_types_.push_back(ElementType::Ptr(new LineP2)); index_[element_type] = nelem_per_type_.size(); nelem_per_type_.push_back(nelem); const ElementType& elem = *element_types_.back(); if (nelem_per_type_.size() == 1) { N_max_ = elem.N(); N_min_ = elem.N(); nb_elements_ = nelem; } else { N_max_ = std::max(N_max_, elem.N()); N_min_ = std::min(N_min_, elem.N()); nb_elements_ += nelem; } return elem; } /// @brief Set the nodes that define the element void set_nodes(const Nodes& nodes) { nodes_ = &nodes; } template atlas::array::ArrayT create_field(int idx1 = IDX_NOTUSED, int idx2 = IDX_NOTUSED, int idx3 = IDX_NOTUSED, int idx4 = IDX_NOTUSED, int idx5 = IDX_NOTUSED, int idx6 = IDX_NOTUSED, int idx7 = IDX_NOTUSED) { atlas::array::ArrayShape shape; shape.reserve(7); if (idx1 != IDX_NOTUSED) shape.push_back(range(idx1)); if (idx2 != IDX_NOTUSED) shape.push_back(range(idx2)); if (idx3 != IDX_NOTUSED) shape.push_back(range(idx3)); if (idx4 != IDX_NOTUSED) shape.push_back(range(idx4)); if (idx5 != IDX_NOTUSED) shape.push_back(range(idx5)); if (idx6 != IDX_NOTUSED) shape.push_back(range(idx6)); if (idx7 != IDX_NOTUSED) shape.push_back(range(idx7)); atlas::array::ArrayT field(shape); return field; } private: int range(int idx_type) const { switch (idx_type) { case IDX_NODE: return nb_nodes(); case IDX_LEVEL: return nb_levels(); case IDX_BLK: return nblk(); case IDX_NPROMA: return nproma(); default: if (idx_type >= 0) return idx_type; } throw_Exception("idx_type not recognized"); return 0; } private: /// @brief Lookup of element_type index by name std::map index_; /// @brief Reference to nodes Nodes const* nodes_; /// @brief Element types ElementType::Vector element_types_; /// @brief Number of elements per element type std::vector nelem_per_type_; /// @brief Total number of elements size_t nb_elements_; /// @brief Maximum number of points per element type size_t N_max_; /// @brief Minimum number of points per element type size_t N_min_; /// @brief Total number of nodes size_t nb_nodes_; /// @brief Number of vertical levels size_t nlev_; /// @brief Number of nodes in one blk size_t nproma_; /// @brief Number of blocks in domain size_t nblk_; size_t nb_owned_nodes_; size_t nb_owned_elements_; }; class Column : public ElementType // Really is a Elementtype in 3D { public: Column(const ElementType::Ptr& horizontal, const ElementType::Ptr& vertical): horizontal_(horizontal), vertical_(vertical) { shape_function_ = ShapeFunction::Ptr(new ShapeFunction); } size_t N() const { return npts() * nlev(); } size_t npts() const { return horizontal_->N(); } size_t nlev() const { return vertical_->N(); } protected: ElementType::Ptr horizontal_; ElementType::Ptr vertical_; }; template std::vector> polynomial_basis(int order, double* points, int npts) { std::vector> basis(npts); Matrix vandermonde(npts, npts); Matrix coefficients(npts, npts); Matrix powers(npts, NDIM); Matrix::Proxy pts(points, npts, NDIM); if (NDIM == 1) { size_t n(0); for (size_t k = 0; k <= order; ++k) { powers(n, 0) = k; ++n; } for (n = 0; n < npts; ++n) { double x = pts(n, 0); for (size_t p = 0; p < npts; ++p) { double x_p = (powers(p, 0) == 0. ? 1. : std::pow(x, powers(p, 0))); vandermonde(n, p) = x_p; } } } else if (NDIM == 2) { size_t n(0); for (size_t l = 0; l <= order; ++l) { for (size_t k = 0; k <= order; ++k) { powers(n, 0) = k; powers(n, 1) = l; ++n; } } for (n = 0; n < npts; ++n) { double x = pts(n, 0); double y = pts(n, 1); for (size_t p = 0; p < npts; ++p) { double x_p = (powers(p, 0) == 0. ? 1. : std::pow(x, powers(p, 0))); double y_p = (powers(p, 1) == 0. ? 1. : std::pow(y, powers(p, 1))); vandermonde(n, p) = x_p * y_p; } } } std::cout << "pts = \n" << pts << std::endl; std::cout << "powers = \n" << powers << std::endl; std::cout << "vandermonde = \n" << vandermonde << std::endl; coefficients = vandermonde.inverse().transpose(); std::cout << "coefficients = \n" << coefficients << std::endl; int pwr[3]; for (size_t n = 0; n < npts; ++n) { for (size_t i = 0; i < npts; ++i) { for (size_t d = 0; d < NDIM; ++d) pwr[d] = powers(i, d); basis[n].add(Mononomial(coefficients(n, i), pwr)); } } return basis; } template IndexView make_IndexView(atlas::array::ArrayT& array, NewFunctionSpace& elements, int element_type_index) { IndexView view; size_t offset = 0; size_t strides[2]; size_t shape[2]; ATLAS_ASSERT(element_type_index < elements.nb_element_types()); ATLAS_ASSERT(array.shape(1) == elements.N_max()); for (int i = 0; i < element_type_index; ++i) { offset += elements.nb_elements_in_element_type(i) * elements.N_max(); } const ElementType& elem_type = elements.element_type(element_type_index); strides[0] = array.shape(1); strides[1] = 1; shape[0] = elements.nb_elements_in_element_type(element_type_index); shape[1] = elem_type.N(); return IndexView(array.data() + offset, strides, shape); } class Connectivity : public IndexView { Connectivity(NewFunctionSpace& elements, int element_type_index) {} }; //----------------------------------------------------------------------------- CASE("test_functionspace") { ElementType::Ptr point(new Point); ElementType::Ptr quad(new QuadP1); ElementType::Ptr levels(new Structured1D(100)); Column quad_column(quad, levels); Column point_column(point, levels); // std::cout << quad->N() << std::endl; // std::cout << levels->N() << std::endl; DEBUG_VAR(quad_column.N()); DEBUG_VAR(quad_column.nlev()); DEBUG_VAR(quad_column.npts()); DEBUG_VAR(point_column.N()); DEBUG_VAR(point_column.nlev()); DEBUG_VAR(point_column.npts()); Nodes nodes; NewFunctionSpace fs; fs.set_nb_nodes(8); fs.set_nproma(4); fs.set_nb_levels(100); const ElementType& triags = fs.add_elements("TriagP1", 2); EXPECT(fs.nb_element_types() == 1); EXPECT(fs.N_max() == 3); EXPECT(fs.N_min() == 3); EXPECT(fs.nb_elements() == 2); const ElementType& quads = fs.add_elements("QuadP1", 2); EXPECT(fs.nb_element_types() == 2); EXPECT(fs.N_max() == 4); EXPECT(fs.N_min() == 3); EXPECT(fs.nb_elements() == 4); /// Allocate array for all connectivity across all elements atlas::array::ArrayT element_node_connectivity(fs.nb_elements(), fs.N_max()); /// Access the data across all elements atlas::IndexView elem_connectivity(element_node_connectivity); // --- Triangle 1 --- elem_connectivity(0, 0) = 1; elem_connectivity(0, 1) = 2; elem_connectivity(0, 2) = 3; elem_connectivity(0, 3) = -1; // --- Triangle 2--- elem_connectivity(1, 0) = 11; elem_connectivity(1, 1) = 12; elem_connectivity(1, 2) = 13; elem_connectivity(1, 3) = -1; // --- Quad 1 --- elem_connectivity(2, 0) = 21; elem_connectivity(2, 1) = 22; elem_connectivity(2, 2) = 23; elem_connectivity(2, 3) = 24; // --- Quad 2 --- elem_connectivity(3, 0) = 31; elem_connectivity(3, 1) = 32; elem_connectivity(3, 2) = 33; elem_connectivity(3, 3) = 34; /// Access the data atlas::IndexView triag_node_connectivity = make_IndexView(element_node_connectivity, fs, 0); EXPECT(triag_node_connectivity.shape(0) == 2); EXPECT(triag_node_connectivity.shape(1) == 3); EXPECT(triag_node_connectivity(0, 0) == 1); EXPECT(triag_node_connectivity(0, 1) == 2); EXPECT(triag_node_connectivity(0, 2) == 3); EXPECT(triag_node_connectivity(1, 0) == 11); EXPECT(triag_node_connectivity(1, 1) == 12); EXPECT(triag_node_connectivity(1, 2) == 13); BOOST_CHECK_THROW(triag_node_connectivity(0, 3), eckit::OutOfRange); // should fail (OUT OF RANGE) atlas::IndexView quad_node_connectivity = make_IndexView(element_node_connectivity, fs, 1); EXPECT( quad_node_connectivity.shape(0 == 2 ); EXPECT( quad_node_connectivity.shape(1 == 4 ); EXPECT( quad_node_connectivity(0,0 == 21 ); EXPECT( quad_node_connectivity(0,1 == 22 ); EXPECT( quad_node_connectivity(0,2 == 23 ); EXPECT( quad_node_connectivity(0,3 == 24 ); EXPECT( quad_node_connectivity(1,0 == 31 ); EXPECT( quad_node_connectivity(1,1 == 32 ); EXPECT( quad_node_connectivity(1,2 == 33 ); EXPECT( quad_node_connectivity(1,3 == 34 ); NewFunctionSpace edges; // edges.set_nodes( nodes ); edges.add_elements("LineP0",11); atlas::array::ArrayT press_ifs(fs.nproma(),fs.nb_levels(),nodes.nblk()); atlas::array::ArrayT press = fs.create_field(IDX_LEVEL,IDX_NODE); EXPECT( press.size() == fs.nb_levels()*fs.nb_nodes() ); atlas::array::ArrayT wind_uv = fs.create_field(2,IDX_LEVEL,IDX_NODE); EXPECT( wind_uv.size() == 2*fs.nb_levels()*fs.nb_nodes() ); EXPECT( fs.nproma() == 4); EXPECT( fs.nblk() == 2); EXPECT( fs.nb_nodes() == 8); atlas::array::ArrayT wind_uv_ifs = fs.create_field(IDX_NPROMA,IDX_LEVEL,2,IDX_BLK); EXPECT( wind_uv_ifs.size() == 2*fs.nb_levels()*fs.nb_nodes() ); EXPECT( wind_uv_ifs.rank() == 4); EXPECT( wind_uv_ifs.shape(0) == fs.nproma() ); EXPECT( wind_uv_ifs.shape(1) == fs.nb_levels() ); EXPECT( wind_uv_ifs.shape(2) == 2 ); EXPECT( wind_uv_ifs.shape(3) == fs.nblk() ); } CASE("test_polynomial") { typedef Polynomial<2> polynomial_type; typedef polynomial_type::monomial_type monomial_type; polynomial_type p; p.add(monomial_type(3., 0, 0)); p.add(monomial_type(1., 1, 0)); p.add(monomial_type(1., 2, 0)); DEBUG_VAR(p(2, 2)); polynomial_type dpdx = p.deriv(0); DEBUG_VAR(dpdx(2, 2)); polynomial_type dpdy = p.deriv(1); DEBUG_VAR(dpdy(2, 2)); std::vector grad = p.grad(); std::vector pvec(2, p); polynomial_type div = polynomial_type::div(pvec); DEBUG_VAR(div(2, 2)); DEBUG_VAR(polynomial_type::curl_z(grad)(2, 2)); typedef std::vector> PolynomialBasis1D; typedef std::vector> PolynomialBasis2D; Matrix m; // EXPECT( m.data_ == NULL ); // EXPECT( m.nr_ == 0 ); // EXPECT( m.nc_ == 0 ); m.resize(2, 3); EXPECT(m.size() == 6); EXPECT(m.rows() == 2); EXPECT(m.cols() == 3); m(0, 0) = 0; m(0, 1) = 2; m(0, 2) = 4; m(1, 0) = 1; m(1, 1) = 3; m(1, 2) = 5; m *= 10; Matrix b = m + m; std::cout << "m = \n" << b << std::endl; double line_pts[] = {-1., 1.}; int line_npts = 2; PolynomialBasis1D line_basis = polynomial_basis<1>(1, line_pts, line_npts); for (size_t n = 0; n < line_npts; ++n) DEBUG(n << ": " << line_basis[n](-1.) << " " << line_basis[n](+1.)); double quad_pts[] = {-1, 1., 1., -1, -1., -1., 1., 1.}; int quad_npts = 4; PolynomialBasis2D quad_basis = polynomial_basis<2>(1, quad_pts, quad_npts); for (size_t n = 0; n < quad_npts; ++n) DEBUG(n << ": " << quad_basis[n](-1., -1.) << " " << quad_basis[n](1., -1.) << " " << quad_basis[n](1., 1.) << " " << quad_basis[n](-1., 1.)); RowVector vr(3); vr(0) = 0; vr(1) = 1; vr(2) = 2; ColVector vc(3); vc(0) = 0; vc(1) = 1; vc(2) = 2; std::cout << vr << "\n" << vc << "\n\n" << vr * vc << std::endl; } //----------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/mesh/test_mesh_reorder.cc0000664000175000017500000007304715160212070022007 0ustar alastairalastair/* * (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 (size_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); } atlas-0.46.0/src/tests/mesh/test_distmesh.cc0000664000175000017500000001076115160212070021143 0ustar alastairalastair/* * (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/types/FloatCompare.h" #include "atlas/array/ArrayView.h" #include "atlas/array/MakeView.h" #include "atlas/grid/Grid.h" #include "atlas/mesh/IsGhostNode.h" #include "atlas/mesh/Mesh.h" #include "atlas/mesh/Nodes.h" #include "atlas/mesh/actions/BuildDualMesh.h" #include "atlas/mesh/actions/BuildEdges.h" #include "atlas/mesh/actions/BuildHalo.h" #include "atlas/mesh/actions/BuildParallelFields.h" #include "atlas/mesh/actions/BuildPeriodicBoundaries.h" #include "atlas/mesh/actions/WriteLoadBalanceReport.h" #include "atlas/output/Gmsh.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/runtime/Log.h" #include "atlas/util/CoordinateEnums.h" #include "tests/AtlasTestEnvironment.h" #include "tests/TestMeshes.h" using namespace atlas; using namespace atlas::output; using namespace atlas::meshgenerator; using namespace atlas::util; namespace atlas { namespace test { //----------------------------------------------------------------------------- double dual_volume(Mesh& mesh) { mesh::Nodes& nodes = mesh.nodes(); mesh::IsGhostNode is_ghost_node(nodes); idx_t nb_nodes = nodes.size(); array::ArrayView dual_volumes = array::make_view(nodes.field("dual_volumes")); double area = 0; for (idx_t node = 0; node < nb_nodes; ++node) { if (!is_ghost_node(node)) { area += dual_volumes(node); } } ATLAS_TRACE_MPI(ALLREDUCE) { mpi::comm().allReduceInPlace(area, eckit::mpi::sum()); } return area; } //----------------------------------------------------------------------------- CASE("test_distribute_t63") { // Every task builds full mesh // meshgenerator::StructuredMeshGenerator generate( util::Config // ("nb_parts",1) // ("part",0) ); StructuredMeshGenerator generate(util::Config("partitioner", "equal_regions")); // long lon[] = {4,6,8,8,8}; // test::TestGrid grid(5,lon); // GG grid(120,60); Grid grid("N16"); Mesh m(generate(grid)); // actions::distribute_mesh(m); mesh::actions::build_parallel_fields(m); mesh::actions::build_periodic_boundaries(m); mesh::actions::build_halo(m, 1); // mesh::actions::renumber_nodes_glb_idx(m.nodes()); mesh::actions::build_edges(m); Gmsh("dist.msh", util::Config("ghost", true)).write(m); mesh::actions::build_edges_parallel_fields(m); mesh::actions::build_median_dual_mesh(m); double computed_dual_volume = test::dual_volume(m); EXPECT(eckit::types::is_approximately_equal(computed_dual_volume, 360. * 180., 0.0001)); double difference = 360. * 180. - computed_dual_volume; if (difference > 1e-8) { std::cout << "difference = " << difference << std::endl; } Gmsh("dist.msh").write(m); mesh::actions::write_load_balance_report(m, "load_balance.dat"); Mesh& mesh1 = m; EXPECT(mesh1.nodes().size() == m.nodes().size()); const array::ArrayView part = array::make_view(m.nodes().partition()); const array::ArrayView ghost = array::make_view(m.nodes().ghost()); const array::ArrayView flags = array::make_view(m.nodes().flags()); Log::info() << "partition = [ "; for (idx_t jnode = 0; jnode < part.size(); ++jnode) { Log::info() << part(jnode) << " "; } Log::info() << "]" << std::endl; Log::info() << "ghost = [ "; for (idx_t jnode = 0; jnode < part.size(); ++jnode) { Log::info() << ghost(jnode) << " "; } Log::info() << "]" << std::endl; Log::info() << "flags = [ "; for (idx_t jnode = 0; jnode < part.size(); ++jnode) { Log::info() << mesh::Nodes::Topology::check(flags(jnode), mesh::Nodes::Topology::GHOST) << " "; EXPECT(mesh::Nodes::Topology::check(flags(jnode), mesh::Nodes::Topology::GHOST) == ghost(jnode)); } Log::info() << "]" << std::endl; } //----------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/mesh/fctest_elements.F900000664000175000017500000002221715160212070021420 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. ! This File contains Unit Tests for testing the ! C++ / Fortran Interfaces to the Elements ! @author Willem Deconinck #include "fckit/fctest.h" ! ----------------------------------------------------------------------------- TESTSUITE(fctest_atlas_elements) TESTSUITE_INIT() use atlas_module call atlas_library%initialise() END_TESTSUITE_INIT TESTSUITE_FINALISE() use atlas_module call atlas_library%finalise() END_TESTSUITE_FINALIZE ! ----------------------------------------------------------------------------- TEST( test_elementtype ) use atlas_HybridElements_module use atlas_ElementType_module use atlas_Elements_module implicit none type(atlas_ElementType) :: triag, quad, line write(*,*) "test_elementtype" triag = atlas_Triangle() quad = atlas_Quadrilateral() line = atlas_Line() END_TEST ! ----------------------------------------------------------------------------- TEST( test_hybridelements ) use atlas_mesh_Cells_module use atlas_ElementType_module use atlas_Elements_module use atlas_connectivity_module use atlas_field_module use atlas_kinds_module implicit none type(atlas_mesh_Cells) :: cells type(atlas_MultiBlockConnectivity) :: node_connectivity type(atlas_Field) :: field, field2 type(atlas_Elements) :: elements type(atlas_ElementType) :: element_type integer(ATLAS_KIND_IDX), pointer :: data(:,:) integer(ATLAS_KIND_IDX) :: jfield write(*,*) "test_hybridelements starting" cells = atlas_mesh_Cells() FCTEST_CHECK_EQUAL( cells%owners(), 1 ) FCTEST_CHECK_EQUAL( cells%size(), 0 ) node_connectivity = cells%node_connectivity() FCTEST_CHECK_EQUAL( node_connectivity%owners(), 2 ) FCTEST_CHECK_EQUAL( node_connectivity%rows(), 0 ) FCTEST_CHECK( cells%has_field("glb_idx") ) FCTEST_CHECK( cells%has_field("partition") ) FCTEST_CHECK( cells%has_field("remote_idx") ) FCTEST_CHECK( cells%has_field("halo") ) field = cells%field("partition") FCTEST_CHECK_EQUAL( field%owners(), 2 ) FCTEST_CHECK_EQUAL( field%name(), "partition" ) field2 = cells%partition() FCTEST_CHECK_EQUAL( field2%name(), "partition" ) FCTEST_CHECK_EQUAL( field%owners(), 3 ) field2 = cells%global_index() FCTEST_CHECK_EQUAL( field2%name(), "glb_idx" ) FCTEST_CHECK_EQUAL( field%owners(), 2 ) field2 = cells%remote_index() FCTEST_CHECK_EQUAL( field2%name(), "remote_idx" ) FCTEST_CHECK_EQUAL( field%owners(), 2 ) field2 = cells%halo() FCTEST_CHECK_EQUAL( field2%name(), "halo" ) FCTEST_CHECK_EQUAL( field%owners(), 2 ) call field2%final() do jfield=1,cells%nb_fields() field = cells%field(jfield) write(0,*) field%name() FCTEST_CHECK_EQUAL( field%owners(), 2 ) enddo call cells%add( atlas_Triangle(), 5 , & & [ 1, 2 ,3, & & 4, 5, 6, & & 7, 8, 9, & & 10, 11, 12, & & 13, 14, 15 ] ) FCTEST_CHECK_EQUAL( cells%size(), 5 ) FCTEST_CHECK_EQUAL( cells%nb_types(), 1 ) FCTEST_CHECK_EQUAL( node_connectivity%rows(), 5 ) FCTEST_CHECK_EQUAL( node_connectivity%maxcols(), 3 ) call node_connectivity%data(data) FCTEST_CHECK_EQUAL( data(1,1) , 1 ) FCTEST_CHECK_EQUAL( data(2,1) , 2 ) FCTEST_CHECK_EQUAL( data(3,1) , 3 ) FCTEST_CHECK_EQUAL( data(1,2) , 4 ) FCTEST_CHECK_EQUAL( data(2,2) , 5 ) FCTEST_CHECK_EQUAL( data(3,2) , 6 ) FCTEST_CHECK_EQUAL( data(1,3) , 7 ) FCTEST_CHECK_EQUAL( data(2,3) , 8 ) FCTEST_CHECK_EQUAL( data(3,3) , 9 ) FCTEST_CHECK_EQUAL( data(1,4) , 10 ) FCTEST_CHECK_EQUAL( data(2,4) , 11 ) FCTEST_CHECK_EQUAL( data(3,4) , 12 ) FCTEST_CHECK_EQUAL( data(1,5) , 13 ) FCTEST_CHECK_EQUAL( data(2,5) , 14 ) FCTEST_CHECK_EQUAL( data(3,5) , 15 ) call cells%add( atlas_Quadrilateral(), 2 , & & [ 16, 17, 18, 19, & & 20, 21, 22, 23 ] ) FCTEST_CHECK_EQUAL( cells%nb_types(), 2 ) FCTEST_CHECK_EQUAL( cells%size(), 7 ) FCTEST_CHECK_EQUAL( node_connectivity%rows(), 7 ) FCTEST_CHECK_EQUAL( node_connectivity%mincols(), 3 ) FCTEST_CHECK_EQUAL( node_connectivity%maxcols(), 4 ) elements = cells%elements(1) FCTEST_CHECK_EQUAL( elements%owners(), 2 ) FCTEST_CHECK_EQUAL( elements%size(), 5 ) element_type = elements%element_type() ! Todo: Check this aborts, ! because data has become irregular ! call node_connectivity%data(data) call node_connectivity%padded_data(data) FCTEST_CHECK_EQUAL( data(1,6) , 16 ) FCTEST_CHECK_EQUAL( data(2,6) , 17 ) FCTEST_CHECK_EQUAL( data(3,6) , 18 ) FCTEST_CHECK_EQUAL( data(4,6) , 19 ) FCTEST_CHECK_EQUAL( data(1,7) , 20 ) FCTEST_CHECK_EQUAL( data(2,7) , 21 ) FCTEST_CHECK_EQUAL( data(3,7) , 22 ) FCTEST_CHECK_EQUAL( data(4,7) , 23 ) field = atlas_Field("p",atlas_real(8),[int(cells%size())]) call cells%add(field) call cells%final() FCTEST_CHECK_EQUAL( node_connectivity%owners(), 1 ) FCTEST_CHECK_EQUAL( field%owners(), 1 ) call node_connectivity%final() call field%final() END_TEST ! ----------------------------------------------------------------------------- TEST( test_elements ) use atlas_mesh_Cells_module use atlas_ElementType_module use atlas_Elements_module use atlas_connectivity_module use atlas_field_module use atlas_kinds_module use, intrinsic :: iso_c_binding implicit none type(atlas_mesh_Cells) :: cells type(atlas_BlockConnectivity) :: node_connectivity type(atlas_Elements) :: elements type(atlas_ElementType) :: element_type integer(ATLAS_KIND_IDX), pointer :: data(:,:) write(*,*) "test_elements starting" cells = atlas_mesh_Cells() call cells%add( atlas_Triangle(), 5 , & & [ 1, 2 ,3, & & 4, 5, 6, & & 7, 8, 9, & & 10, 11, 12, & & 13, 14, 15 ] ) call cells%add( atlas_Quadrilateral(), 2 , & & [ 16, 17, 18, 19, & & 20, 21, 22, 23 ] ) elements = cells%elements(1) FCTEST_CHECK_EQUAL( elements%begin(), 1 ) FCTEST_CHECK_EQUAL( elements%end(), 5 ) element_type = elements%element_type() FCTEST_CHECK_EQUAL( element_type%owners(), 2 ) node_connectivity = elements%node_connectivity() !FCTEST_CHECK_EQUAL( node_connectivity%owners(), 2 ) FCTEST_CHECK_EQUAL( element_type%nb_nodes(), 3 ) FCTEST_CHECK_EQUAL( element_type%nb_edges(), 3 ) FCTEST_CHECK_EQUAL( element_type%name(), "Triangle" ) FCTEST_CHECK( element_type%parametric() ) call node_connectivity%data(data) FCTEST_CHECK_EQUAL( data(1,1) , 1 ) FCTEST_CHECK_EQUAL( data(2,1) , 2 ) FCTEST_CHECK_EQUAL( data(3,1) , 3 ) FCTEST_CHECK_EQUAL( data(1,2) , 4 ) FCTEST_CHECK_EQUAL( data(2,2) , 5 ) FCTEST_CHECK_EQUAL( data(3,2) , 6 ) FCTEST_CHECK_EQUAL( data(1,3) , 7 ) FCTEST_CHECK_EQUAL( data(2,3) , 8 ) FCTEST_CHECK_EQUAL( data(3,3) , 9 ) FCTEST_CHECK_EQUAL( data(1,4) , 10 ) FCTEST_CHECK_EQUAL( data(2,4) , 11 ) FCTEST_CHECK_EQUAL( data(3,4) , 12 ) FCTEST_CHECK_EQUAL( data(1,5) , 13 ) FCTEST_CHECK_EQUAL( data(2,5) , 14 ) FCTEST_CHECK_EQUAL( data(3,5) , 15 ) elements = cells%elements(2) FCTEST_CHECK_EQUAL( elements%begin(), 6 ) FCTEST_CHECK_EQUAL( elements%end(), 7 ) element_type = elements%element_type() FCTEST_CHECK_EQUAL( element_type%owners(), 2 ) FCTEST_CHECK_EQUAL( element_type%nb_nodes(), 4 ) FCTEST_CHECK_EQUAL( element_type%nb_edges(), 4 ) FCTEST_CHECK_EQUAL( element_type%name(), "Quadrilateral" ) FCTEST_CHECK( element_type%parametric() ) node_connectivity = elements%node_connectivity() !FCTEST_CHECK_EQUAL( node_connectivity%owners(), 2 ) call node_connectivity%data(data) FCTEST_CHECK_EQUAL( data(1,1) , 16 ) FCTEST_CHECK_EQUAL( data(2,1) , 17 ) FCTEST_CHECK_EQUAL( data(3,1) , 18 ) FCTEST_CHECK_EQUAL( data(4,1) , 19 ) FCTEST_CHECK_EQUAL( data(1,2) , 20 ) FCTEST_CHECK_EQUAL( data(2,2) , 21 ) FCTEST_CHECK_EQUAL( data(3,2) , 22 ) FCTEST_CHECK_EQUAL( data(4,2) , 23 ) !-------------------------- ! Add elements to triangles elements = cells%elements(1) call elements%add(2) FCTEST_CHECK_EQUAL( elements%begin(), 1 ) FCTEST_CHECK_EQUAL( elements%end(), 7 ) node_connectivity = elements%node_connectivity() call node_connectivity%data(data) FCTEST_CHECK_EQUAL( node_connectivity%missing_value(), 0 ) FCTEST_CHECK_EQUAL( data(1,6) , node_connectivity%missing_value() ) FCTEST_CHECK_EQUAL( data(2,6) , node_connectivity%missing_value() ) FCTEST_CHECK_EQUAL( data(3,6) , node_connectivity%missing_value() ) FCTEST_CHECK_EQUAL( data(1,7) , node_connectivity%missing_value() ) FCTEST_CHECK_EQUAL( data(2,7) , node_connectivity%missing_value() ) FCTEST_CHECK_EQUAL( data(3,7) , node_connectivity%missing_value() ) elements = cells%elements(2) FCTEST_CHECK_EQUAL( elements%begin(), 8 ) FCTEST_CHECK_EQUAL( elements%end(), 9 ) call elements%final() call cells%final() call node_connectivity%final() call element_type%final() END_TEST ! ----------------------------------------------------------------------------- END_TESTSUITE atlas-0.46.0/src/tests/mesh/test_pentagon_element.cc0000664000175000017500000001014115160212070022637 0ustar alastairalastair/* * (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/functionspace.h" #include "atlas/interpolation.h" #include "atlas/mesh.h" #include "atlas/option.h" #include "atlas/output/Gmsh.h" #include "atlas/output/Output.h" #include "tests/AtlasTestEnvironment.h" #include "tests/TestMeshes.h" namespace atlas { namespace test { //----------------------------------------------------------------------------- CASE("test_pentagon_like_healpix") { Mesh mesh; // 1 2 3 4 5 // +------+------+------+------+ // | 0 | 1 | 2 | 3 | // +6 +7 +8 +9 +10 // \ / \ / \ / \ / // + + + + // 11 12 13 14 auto points = std::vector{ {0., 90.}, // 1 {90, 90.}, // 2 {180., 90.}, // 3 {270., 90.}, // 4 {360., 90.}, // 5 {0., 80.}, // 6 {90, 80.}, // 7 {180., 80.}, // 8 {270., 80.}, // 9 {360., 80.}, // 10 {45., 70.}, // 11 {135., 70.}, // 12 {225., 70.}, // 13 {315., 70.}, // 14 }; // nodes { mesh.nodes().resize(14); auto xy = array::make_view(mesh.nodes().xy()); auto lonlat = array::make_view(mesh.nodes().lonlat()); auto nodes_glb_idx = array::make_view(mesh.nodes().global_index()); for (idx_t i = 0; i < mesh.nodes().size(); ++i) { lonlat(i, LON) = points[i][LON]; lonlat(i, LAT) = points[i][LAT]; nodes_glb_idx(i) = i + 1; } xy.assign(lonlat); } // elements { mesh.cells().add(mesh::ElementType::create("Pentagon"), 4); auto connect = [&](idx_t cell, std::array nodes) { mesh.cells().node_connectivity().block(0).set(cell, nodes.data()); }; connect(0, {0, 5, 10, 6, 1}); connect(1, {1, 6, 11, 7, 2}); connect(2, {2, 7, 12, 8, 3}); connect(3, {3, 8, 13, 9, 4}); auto cell_glb_idx = array::make_view(mesh.cells().global_index()); for (idx_t i = 0; i < mesh.cells().size(); ++i) { cell_glb_idx(i) = i + 1; } } { output::Gmsh gmsh("test_pentagon_xyz.msh", util::Config("coordinates", "xyz")); gmsh.write(mesh); } { output::Gmsh gmsh("test_pentagon_lonlat.msh", util::Config("coordinates", "lonlat")); gmsh.write(mesh); } auto src_fs = functionspace::NodeColumns(mesh); auto tgt_fs = functionspace::PointCloud({{45, 80}, {0, 89}, {180, 89}, {170, 89}}); Interpolation interpolation(option::type("finite-element"), src_fs, tgt_fs); auto src_field = src_fs.createField(); auto tgt_field = tgt_fs.createField(); auto src_lonlat = array::make_view(src_fs.lonlat()); auto src = array::make_view(src_field); for (auto i = 0; i < src_fs.size(); ++i) { src(i) = std::cos(src_lonlat(i, LAT)) * std::sin(src_lonlat(i, LON)); } interpolation.execute(src_field, tgt_field); auto tgt_lonlat = array::make_view(tgt_fs.lonlat()); auto tgt = array::make_view(tgt_field); for (auto i = 0; i < tgt_fs.size(); ++i) { double analytical = std::cos(tgt_lonlat(i, LAT)) * std::sin(tgt_lonlat(i, LON)); Log::info() << i << " " << PointLonLat{tgt_lonlat(i, LON), tgt_lonlat(i, LAT)} << " : interpolated=" << tgt(i) << "; expected=" << analytical << std::endl; } } //----------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/mesh/test_cgal_mesh_gen_from_points.cc0000664000175000017500000000242515160212070024513 0ustar alastairalastair/* * (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 "atlas/grid/Grid.h" #include "atlas/library.h" #include "atlas/library/config.h" #include "atlas/mesh/Mesh.h" #include "atlas/meshgenerator.h" #include "atlas/output/Gmsh.h" using namespace atlas; using namespace atlas::grid; using namespace atlas::meshgenerator; using namespace atlas::output; //---------------------------------------------------------------------------------------------------------------------- #define NLATS 64 #define NLONG 128 int main(int argc, char** argv) { atlas::initialize(argc, argv); Grid grid("L33x11"); // Build a mesh from grid MeshGenerator generate("delaunay"); Mesh mesh = generate(grid); Gmsh gmsh("earth.msh", util::Config("coordinates", "xyz")); gmsh.write(mesh); atlas::finalize(); atlas::mpi::finalize(); return 0; } atlas-0.46.0/src/tests/mesh/test_mesh_build_edges.cc0000664000175000017500000006422515160212070022611 0ustar alastairalastair/* * (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/grid/Grid.h" #include "atlas/library/config.h" #include "atlas/mesh/HybridElements.h" #include "atlas/mesh/Mesh.h" #include "atlas/mesh/actions/BuildEdges.h" #include "atlas/mesh/detail/AccumulateFacets.h" #include "atlas/meshgenerator.h" #include "atlas/option.h" #include "atlas/util/Unique.h" #include "tests/AtlasTestEnvironment.h" using namespace atlas::mesh; using namespace atlas::util; namespace atlas { namespace test { //----------------------------------------------------------------------------- CASE("test_accumulate_facets") { Grid grid("O2"); StructuredMeshGenerator generator(Config("angle", 29.0)("triangulate", false)("ghost_at_end", false)); Mesh mesh = generator.generate(grid); // storage for edge-to-node-connectivity shape=(nb_edges,2) std::vector edge_nodes_data; // storage for edge-to-cell-connectivity shape=(nb_edges,2) std::vector edge_to_cell_data; idx_t nb_edges; idx_t nb_inner_edges; idx_t missing_value; // Accumulate facets of cells ( edges in 2D ) mesh::detail::accumulate_facets(mesh.cells(), mesh.nodes(), edge_nodes_data, edge_to_cell_data, nb_edges, nb_inner_edges, missing_value); idx_t edge_nodes_check[] = { 0, 21, 21, 22, 22, 1, 1, 0, 22, 23, 23, 2, 2, 1, 3, 25, 25, 26, 26, 4, 4, 3, 26, 27, 27, 5, 5, 4, 27, 28, 28, 6, 6, 5, 28, 29, 29, 7, 7, 6, 8, 31, 31, 32, 32, 9, 9, 8, 32, 33, 33, 10, 10, 9, 33, 34, 34, 11, 11, 10, 34, 35, 35, 12, 12, 11, 13, 37, 37, 38, 38, 14, 14, 13, 38, 39, 39, 15, 15, 14, 39, 40, 40, 16, 16, 15, 40, 41, 41, 17, 17, 16, 18, 43, 43, 44, 44, 19, 19, 18, 44, 45, 45, 20, 20, 19, 21, 46, 46, 47, 47, 22, 47, 48, 48, 23, 48, 49, 49, 24, 24, 23, 49, 50, 50, 25, 25, 24, 50, 51, 51, 26, 51, 52, 52, 27, 52, 53, 53, 28, 53, 54, 54, 29, 54, 55, 55, 30, 30, 29, 55, 56, 56, 31, 31, 30, 56, 57, 57, 32, 57, 58, 58, 33, 58, 59, 59, 34, 59, 60, 60, 35, 60, 61, 61, 36, 36, 35, 61, 62, 62, 37, 37, 36, 62, 63, 63, 38, 63, 64, 64, 39, 64, 65, 65, 40, 65, 66, 66, 41, 66, 67, 67, 42, 42, 41, 67, 68, 68, 43, 43, 42, 68, 69, 69, 44, 69, 70, 70, 45, 46, 71, 71, 72, 72, 47, 72, 73, 73, 48, 50, 74, 74, 75, 75, 51, 75, 76, 76, 52, 76, 77, 77, 53, 77, 78, 78, 54, 56, 79, 79, 80, 80, 57, 80, 81, 81, 58, 81, 82, 82, 59, 82, 83, 83, 60, 62, 84, 84, 85, 85, 63, 85, 86, 86, 64, 86, 87, 87, 65, 87, 88, 88, 66, 68, 89, 89, 90, 90, 69, 90, 91, 91, 70, 24, 2, 24, 3, 3, 2, 30, 7, 30, 8, 8, 7, 36, 12, 36, 13, 13, 12, 42, 17, 42, 18, 18, 17, 73, 49, 73, 74, 74, 49, 78, 55, 78, 79, 79, 55, 83, 61, 83, 84, 84, 61, 88, 67, 88, 89, 89, 67}; EXPECT(edge_nodes_data == eckit::testing::make_view(edge_nodes_check, edge_nodes_check + 2 * nb_edges)); idx_t edge_to_cell_check[] = {0, missing_value, 0, 16, 0, 1, 0, missing_value, 1, 17, 1, 56, 1, missing_value, 2, 58, 2, 20, 2, 3, 2, missing_value, 3, 21, 3, 4, 3, missing_value, 4, 22, 4, 5, 4, missing_value, 5, 23, 5, 59, 5, missing_value, 6, 61, 6, 26, 6, 7, 6, missing_value, 7, 27, 7, 8, 7, missing_value, 8, 28, 8, 9, 8, missing_value, 9, 29, 9, 62, 9, missing_value, 10, 64, 10, 32, 10, 11, 10, missing_value, 11, 33, 11, 12, 11, missing_value, 12, 34, 12, 13, 12, missing_value, 13, 35, 13, 65, 13, missing_value, 14, 67, 14, 38, 14, 15, 14, missing_value, 15, 39, 15, missing_value, 15, missing_value, 16, missing_value, 16, 40, 16, 17, 17, 41, 17, 18, 18, 68, 18, 19, 18, 56, 19, 70, 19, 20, 19, 58, 20, 42, 20, 21, 21, 43, 21, 22, 22, 44, 22, 23, 23, 45, 23, 24, 24, 71, 24, 25, 24, 59, 25, 73, 25, 26, 25, 61, 26, 46, 26, 27, 27, 47, 27, 28, 28, 48, 28, 29, 29, 49, 29, 30, 30, 74, 30, 31, 30, 62, 31, 76, 31, 32, 31, 64, 32, 50, 32, 33, 33, 51, 33, 34, 34, 52, 34, 35, 35, 53, 35, 36, 36, 77, 36, 37, 36, 65, 37, 79, 37, 38, 37, 67, 38, 54, 38, 39, 39, 55, 39, missing_value, 40, missing_value, 40, missing_value, 40, 41, 41, missing_value, 41, 68, 42, 70, 42, missing_value, 42, 43, 43, missing_value, 43, 44, 44, missing_value, 44, 45, 45, missing_value, 45, 71, 46, 73, 46, missing_value, 46, 47, 47, missing_value, 47, 48, 48, missing_value, 48, 49, 49, missing_value, 49, 74, 50, 76, 50, missing_value, 50, 51, 51, missing_value, 51, 52, 52, missing_value, 52, 53, 53, missing_value, 53, 77, 54, 79, 54, missing_value, 54, 55, 55, missing_value, 55, missing_value, 56, 57, 57, 58, 57, missing_value, 59, 60, 60, 61, 60, missing_value, 62, 63, 63, 64, 63, missing_value, 65, 66, 66, 67, 66, missing_value, 68, 69, 69, missing_value, 69, 70, 71, 72, 72, missing_value, 72, 73, 74, 75, 75, missing_value, 75, 76, 77, 78, 78, missing_value, 78, 79}; EXPECT(edge_to_cell_data == eckit::testing::make_view(edge_to_cell_check, edge_to_cell_check + 2 * nb_edges)); } CASE("test_build_edges") { idx_t missing_value = -1; Grid grid("O2"); StructuredMeshGenerator generator(Config("angle", 29.0)("triangulate", false)("ghost_at_end", false)); Mesh mesh = generator.generate(grid); // Accumulate facets of cells ( edges in 2D ) mesh::actions::build_edges(mesh, option::pole_edges(false)); std::vector edge_nodes_check{ 0, 21, 21, 22, 22, 1, 1, 0, 22, 23, 23, 2, 2, 1, 3, 25, 25, 26, 26, 4, 4, 3, 26, 27, 27, 5, 5, 4, 27, 28, 28, 6, 6, 5, 28, 29, 29, 7, 7, 6, 8, 31, 31, 32, 32, 9, 9, 8, 32, 33, 33, 10, 10, 9, 33, 34, 34, 11, 11, 10, 34, 35, 35, 12, 12, 11, 13, 37, 37, 38, 38, 14, 14, 13, 38, 39, 39, 15, 15, 14, 39, 40, 40, 16, 16, 15, 40, 41, 41, 17, 17, 16, 18, 43, 43, 44, 44, 19, 19, 18, 44, 45, 45, 20, 20, 19, 21, 46, 46, 47, 47, 22, 47, 48, 48, 23, 48, 49, 49, 24, 24, 23, 49, 50, 50, 25, 25, 24, 50, 51, 51, 26, 51, 52, 52, 27, 52, 53, 53, 28, 53, 54, 54, 29, 54, 55, 55, 30, 30, 29, 55, 56, 56, 31, 31, 30, 56, 57, 57, 32, 57, 58, 58, 33, 58, 59, 59, 34, 59, 60, 60, 35, 60, 61, 61, 36, 36, 35, 61, 62, 62, 37, 37, 36, 62, 63, 63, 38, 63, 64, 64, 39, 64, 65, 65, 40, 65, 66, 66, 41, 66, 67, 67, 42, 42, 41, 67, 68, 68, 43, 43, 42, 68, 69, 69, 44, 69, 70, 70, 45, 46, 71, 71, 72, 72, 47, 72, 73, 73, 48, 50, 74, 74, 75, 75, 51, 75, 76, 76, 52, 76, 77, 77, 53, 77, 78, 78, 54, 56, 79, 79, 80, 80, 57, 80, 81, 81, 58, 81, 82, 82, 59, 82, 83, 83, 60, 62, 84, 84, 85, 85, 63, 85, 86, 86, 64, 86, 87, 87, 65, 87, 88, 88, 66, 68, 89, 89, 90, 90, 69, 90, 91, 91, 70, 24, 2, 24, 3, 3, 2, 30, 7, 30, 8, 8, 7, 36, 12, 36, 13, 13, 12, 42, 17, 42, 18, 18, 17, 73, 49, 73, 74, 74, 49, 78, 55, 78, 79, 79, 55, 83, 61, 83, 84, 84, 61, 88, 67, 88, 89, 89, 67}; { const mesh::HybridElements::Connectivity& edge_node_connectivity = mesh.edges().node_connectivity(); EXPECT(mesh.projection().units() == "degrees"); const util::UniqueLonLat compute_uid(mesh); EXPECT_EQ(mesh.edges().size(), edge_nodes_check.size() / 2); for (idx_t jedge = 0; jedge < mesh.edges().size(); ++jedge) { if (compute_uid(edge_nodes_check[2 * jedge + 0]) < compute_uid(edge_nodes_check[2 * jedge + 1])) { EXPECT_EQ(edge_nodes_check[2 * jedge + 0], edge_node_connectivity(jedge, 0)); EXPECT_EQ(edge_nodes_check[2 * jedge + 1], edge_node_connectivity(jedge, 1)); } else { EXPECT_EQ(edge_nodes_check[2 * jedge + 0], edge_node_connectivity(jedge, 1)); EXPECT_EQ(edge_nodes_check[2 * jedge + 1], edge_node_connectivity(jedge, 0)); } } } std::vector edge_to_cell_check{0, missing_value, 0, 16, 0, 1, 0, missing_value, 1, 17, 1, 56, 1, missing_value, 2, 58, 2, 20, 2, 3, 2, missing_value, 3, 21, 3, 4, 3, missing_value, 4, 22, 4, 5, 4, missing_value, 5, 23, 5, 59, 5, missing_value, 6, 61, 6, 26, 6, 7, 6, missing_value, 7, 27, 7, 8, 7, missing_value, 8, 28, 8, 9, 8, missing_value, 9, 29, 9, 62, 9, missing_value, 10, 64, 10, 32, 10, 11, 10, missing_value, 11, 33, 11, 12, 11, missing_value, 12, 34, 12, 13, 12, missing_value, 13, 35, 13, 65, 13, missing_value, 14, 67, 14, 38, 14, 15, 14, missing_value, 15, 39, 15, missing_value, 15, missing_value, 16, missing_value, 16, 40, 16, 17, 17, 41, 17, 18, 18, 68, 18, 19, 18, 56, 19, 70, 19, 20, 19, 58, 20, 42, 20, 21, 21, 43, 21, 22, 22, 44, 22, 23, 23, 45, 23, 24, 24, 71, 24, 25, 24, 59, 25, 73, 25, 26, 25, 61, 26, 46, 26, 27, 27, 47, 27, 28, 28, 48, 28, 29, 29, 49, 29, 30, 30, 74, 30, 31, 30, 62, 31, 76, 31, 32, 31, 64, 32, 50, 32, 33, 33, 51, 33, 34, 34, 52, 34, 35, 35, 53, 35, 36, 36, 77, 36, 37, 36, 65, 37, 79, 37, 38, 37, 67, 38, 54, 38, 39, 39, 55, 39, missing_value, 40, missing_value, 40, missing_value, 40, 41, 41, missing_value, 41, 68, 42, 70, 42, missing_value, 42, 43, 43, missing_value, 43, 44, 44, missing_value, 44, 45, 45, missing_value, 45, 71, 46, 73, 46, missing_value, 46, 47, 47, missing_value, 47, 48, 48, missing_value, 48, 49, 49, missing_value, 49, 74, 50, 76, 50, missing_value, 50, 51, 51, missing_value, 51, 52, 52, missing_value, 52, 53, 53, missing_value, 53, 77, 54, 79, 54, missing_value, 54, 55, 55, missing_value, 55, missing_value, 56, 57, 57, 58, 57, missing_value, 59, 60, 60, 61, 60, missing_value, 62, 63, 63, 64, 63, missing_value, 65, 66, 66, 67, 66, missing_value, 68, 69, 69, missing_value, 69, 70, 71, 72, 72, missing_value, 72, 73, 74, 75, 75, missing_value, 75, 76, 77, 78, 78, missing_value, 78, 79}; { const mesh::HybridElements::Connectivity& cell_node_connectivity = mesh.cells().node_connectivity(); const mesh::HybridElements::Connectivity& edge_cell_connectivity = mesh.edges().cell_connectivity(); const util::UniqueLonLat compute_uid(mesh); EXPECT_EQ(mesh.edges().size(), edge_to_cell_check.size() / 2); for (idx_t jedge = 0; jedge < mesh.edges().size(); ++jedge) { idx_t e1 = edge_to_cell_check[2 * jedge + 0]; idx_t e2 = edge_to_cell_check[2 * jedge + 1]; if (e2 == edge_cell_connectivity.missing_value() || compute_uid(cell_node_connectivity.row(e1)) < compute_uid(cell_node_connectivity.row(e2))) { EXPECT_EQ(edge_to_cell_check[2 * jedge + 0], edge_cell_connectivity(jedge, 0)); EXPECT_EQ(edge_to_cell_check[2 * jedge + 1], edge_cell_connectivity(jedge, 1)); } else { std::cout << "jedge " << jedge << std::endl; EXPECT_EQ(edge_to_cell_check[2 * jedge + 0], edge_cell_connectivity(jedge, 1)); EXPECT_EQ(edge_to_cell_check[2 * jedge + 1], edge_cell_connectivity(jedge, 0)); } } } { const MultiBlockConnectivity& elem_edge_connectivity = mesh.cells().edge_connectivity(); for (idx_t jelem = 0; jelem < mesh.cells().size(); ++jelem) { std::cout << std::setw(3) << jelem << " : "; for (idx_t jedge = 0; jedge < elem_edge_connectivity.cols(jelem); ++jedge) { std::cout << std::setw(3) << elem_edge_connectivity(jelem, jedge) << " "; } std::cout << std::endl; } } } CASE("test_build_edges_triangles_only") { Grid grid("O2"); StructuredMeshGenerator generator(Config("angle", 29.0)("triangulate", true)("ghost_at_end", false)); Mesh mesh = generator.generate(grid); // Accumulate facets of cells ( edges in 2D ) mesh::actions::build_edges(mesh, option::pole_edges(false)); { const MultiBlockConnectivity& elem_edge_connectivity = mesh.cells().edge_connectivity(); const MultiBlockConnectivity& elem_node_connectivity = mesh.cells().node_connectivity(); for (idx_t jelem = 0; jelem < mesh.cells().size(); ++jelem) { std::cout << "elem" << std::setw(3) << jelem << " : edges ( "; for (idx_t jedge = 0; jedge < elem_edge_connectivity.cols(jelem); ++jedge) { std::cout << std::setw(3) << elem_edge_connectivity(jelem, jedge) << " "; } std::cout << ") | nodes ( "; for (idx_t jnode = 0; jnode < elem_node_connectivity.cols(jelem); ++jnode) { std::cout << std::setw(3) << elem_node_connectivity(jelem, jnode) << " "; } std::cout << ")" << std::endl; } } std::cout << "( if you see all -1 entries, those are patch elements at the pole )" << std::endl; } //----------------------------------------------------------------------------- CASE("test_pole_edge_default") { auto pole_edges = [](const Grid& grid) { auto mesh = StructuredMeshGenerator().generate(grid); mesh::actions::build_edges(mesh); return mesh.edges().metadata().getBool("pole_edges"); }; EXPECT(pole_edges(Grid("L10x11")) == false); EXPECT(pole_edges(Grid("F4")) == true); EXPECT(pole_edges(Grid("S4")) == true); EXPECT(pole_edges(Grid("Slat4")) == true); EXPECT(pole_edges(Grid("Slon4")) == false); EXPECT(pole_edges(Grid(Config("type", "regional")("nx", 35)("ny", 25)("north", -10)("south", -50)("east", 170)( "west", 100))) == false); } //----------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/mesh/test_elements.cc0000664000175000017500000003563015160212070021141 0ustar alastairalastair/* * (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/field/Field.h" #include "atlas/grid/Grid.h" #include "atlas/library/config.h" #include "atlas/mesh/Connectivity.h" #include "atlas/mesh/ElementType.h" #include "atlas/mesh/Elements.h" #include "atlas/mesh/Mesh.h" #include "atlas/mesh/Nodes.h" #include "atlas/meshgenerator.h" #include "atlas/runtime/Log.h" #include "tests/AtlasTestEnvironment.h" using namespace atlas::mesh; namespace atlas { namespace test { // ------------------------------------------------------------------ CASE("hybrid_elements") { HybridElements hybrid_elements; idx_t triangle_nodes[] = {1, 5, 3, 1, 5, 2}; idx_t triags_type_idx = hybrid_elements.add(ElementType::create("Triangle"), 2, triangle_nodes); EXPECT(triags_type_idx == 0); hybrid_elements.add(Field("surface", array::make_datatype(), array::make_shape(hybrid_elements.size()))); std::vector quad_nodes(4); quad_nodes[0] = 0; quad_nodes[1] = 1; quad_nodes[2] = 2; quad_nodes[3] = 3; idx_t quads_type_idx = hybrid_elements.add(ElementType::create("Quadrilateral"), 1, quad_nodes); EXPECT(quads_type_idx == 1); { const HybridElements::Connectivity& connectivity = hybrid_elements.node_connectivity(); for (idx_t e = 0; e < hybrid_elements.size(); ++e) { eckit::Log::info() << e << std::endl; eckit::Log::info() << " " << hybrid_elements.name(e) << std::endl; eckit::Log::info() << " nb_nodes = " << hybrid_elements.nb_nodes(e) << std::endl; eckit::Log::info() << " nb_edges = " << hybrid_elements.nb_edges(e) << std::endl; eckit::Log::info() << " nodes = [ "; for (idx_t n = 0; n < hybrid_elements.nb_nodes(e); ++n) { eckit::Log::info() << connectivity(e, n) << " "; } eckit::Log::info() << "]" << std::endl; } EXPECT(connectivity(0, 0) == 1); EXPECT(connectivity(0, 1) == 5); EXPECT(connectivity(0, 2) == 3); EXPECT(connectivity(1, 0) == 1); EXPECT(connectivity(1, 1) == 5); EXPECT(connectivity(1, 2) == 2); EXPECT(connectivity(2, 0) == 0); EXPECT(connectivity(2, 1) == 1); EXPECT(connectivity(2, 2) == 2); EXPECT(connectivity(2, 3) == 3); } eckit::Log::info() << std::endl; idx_t quad0[4] = {9, 8, 7, 6}; { for (idx_t t = 0; t < hybrid_elements.nb_types(); ++t) { Elements& elements = hybrid_elements.elements(t); const BlockConnectivity& block_connectivity = elements.node_connectivity(); if (t == 0) { EXPECT(block_connectivity(0, 0) == 1); EXPECT(block_connectivity(0, 1) == 5); EXPECT(block_connectivity(0, 2) == 3); EXPECT(block_connectivity(1, 0) == 1); EXPECT(block_connectivity(1, 1) == 5); EXPECT(block_connectivity(1, 2) == 2); } if (t == 1) { EXPECT(block_connectivity(0, 0) == 0); EXPECT(block_connectivity(0, 1) == 1); EXPECT(block_connectivity(0, 2) == 2); EXPECT(block_connectivity(0, 3) == 3); } elements.node_connectivity().set(0, quad0); // const_cast(block_connectivity).set(0,quad0); // elements.set_node_connectivity(0,quad0); if (t == 0) { EXPECT(block_connectivity(0, 0) == 9); EXPECT(block_connectivity(0, 1) == 8); EXPECT(block_connectivity(0, 2) == 7); EXPECT(block_connectivity(1, 0) == 1); EXPECT(block_connectivity(1, 1) == 5); EXPECT(block_connectivity(1, 2) == 2); } if (t == 1) { EXPECT(block_connectivity(0, 0) == 9); EXPECT(block_connectivity(0, 1) == 8); EXPECT(block_connectivity(0, 2) == 7); EXPECT(block_connectivity(0, 3) == 6); } eckit::Log::info() << "name = " << elements.name() << std::endl; eckit::Log::info() << "nb_elements = " << elements.size() << std::endl; const BlockConnectivity& connectivity = elements.node_connectivity(); for (idx_t e = 0; e < elements.size(); ++e) { eckit::Log::info() << " nodes = [ "; for (idx_t n = 0; n < elements.nb_nodes(); ++n) { eckit::Log::info() << connectivity(e, n) << " "; } eckit::Log::info() << "]" << std::endl; } } } idx_t nb_elements = 3; EXPECT(hybrid_elements.size() == nb_elements); EXPECT(hybrid_elements.global_index().size() == nb_elements); EXPECT(hybrid_elements.partition().size() == nb_elements); EXPECT(hybrid_elements.remote_index().size() == nb_elements); EXPECT(hybrid_elements.field("surface").size() == nb_elements); EXPECT(hybrid_elements.elements(0).begin() == 0); EXPECT(hybrid_elements.elements(0).end() == 2); EXPECT(hybrid_elements.elements(1).begin() == 2); EXPECT(hybrid_elements.elements(1).end() == 3); // EXPECT( hybrid_elements.elements(0).type_idx() == 0 ); // EXPECT( hybrid_elements.elements(1).type_idx() == 1 ); } CASE("elements") { eckit::Log::info() << "\nelements" << std::endl; idx_t triangle_nodes[] = {1, 5, 3, 1, 5, 2}; idx_t triag1[3] = {9, 8, 7}; Elements elements(ElementType::create("Triangle"), 2, triangle_nodes); EXPECT(elements.begin() == 0); EXPECT(elements.end() == 2); // EXPECT( elements.type_idx() == 0); elements.node_connectivity().set(0, triag1); eckit::Log::info() << "name = " << elements.name() << std::endl; eckit::Log::info() << "nb_elements = " << elements.size() << std::endl; const BlockConnectivity& connectivity = elements.node_connectivity(); for (idx_t e = 0; e < elements.size(); ++e) { eckit::Log::info() << " nodes = [ "; for (idx_t n = 0; n < elements.nb_nodes(); ++n) { eckit::Log::info() << connectivity(e, n) << " "; } eckit::Log::info() << "]" << std::endl; } // This will create a new structure with a copy of elements HybridElements hybrid_elements; hybrid_elements.add(elements); { for (idx_t t = 0; t < hybrid_elements.nb_types(); ++t) { Elements& elements = hybrid_elements.elements(t); elements.node_connectivity().set(0, triag1); eckit::Log::info() << "name = " << elements.name() << std::endl; eckit::Log::info() << "nb_elements = " << elements.size() << std::endl; const BlockConnectivity& connectivity = elements.node_connectivity(); for (idx_t e = 0; e < elements.size(); ++e) { eckit::Log::info() << " nodes = [ "; for (idx_t n = 0; n < elements.nb_nodes(); ++n) { eckit::Log::info() << connectivity(e, n) << " "; } eckit::Log::info() << "]" << std::endl; } } } } CASE("hybrid_connectivity") { eckit::Log::info() << "\nhybrid_connectivity" << std::endl; idx_t triangle_nodes[] = {1, 5, 3, 1, 5, 2}; MultiBlockConnectivity hybrid_connectivity; hybrid_connectivity.add(2, 3, triangle_nodes); for (idx_t e = 0; e < hybrid_connectivity.rows(); ++e) { eckit::Log::info() << " cols = [ "; for (idx_t n = 0; n < hybrid_connectivity.cols(e); ++n) { eckit::Log::info() << hybrid_connectivity(e, n) << " "; } eckit::Log::info() << "]" << std::endl; } idx_t quad_nodes[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}; hybrid_connectivity.add(3, 4, quad_nodes); for (idx_t e = 0; e < hybrid_connectivity.rows(); ++e) { eckit::Log::info() << " cols = [ "; for (idx_t n = 0; n < hybrid_connectivity.cols(e); ++n) { eckit::Log::info() << hybrid_connectivity(e, n) << " "; } eckit::Log::info() << "]" << std::endl; } for (idx_t b = 0; b < hybrid_connectivity.blocks(); ++b) { const BlockConnectivity& block = hybrid_connectivity.block(b); for (idx_t r = 0; r < block.rows(); ++r) { eckit::Log::info() << " cols = [ "; for (idx_t c = 0; c < block.cols(); ++c) { eckit::Log::info() << block(r, c) << " "; } eckit::Log::info() << "]" << std::endl; } } } CASE("block_connectivity") { eckit::Log::info() << "\nblock_connectivity" << std::endl; idx_t triangle_nodes[] = {1, 5, 3, 1, 5, 2}; BlockConnectivity block; block.add(2, 3, triangle_nodes); block.add(2, 3, triangle_nodes); for (idx_t r = 0; r < block.rows(); ++r) { eckit::Log::info() << " cols = [ "; for (idx_t c = 0; c < block.cols(); ++c) { eckit::Log::info() << block(r, c) << " "; } eckit::Log::info() << "]" << std::endl; } } CASE("zero_elements") { HybridElements hybrid_elements; idx_t* nodes = nullptr; hybrid_elements.add(ElementType::create("Triangle"), 0, nodes); hybrid_elements.add(ElementType::create("Quadrilateral"), 0, nodes); EXPECT(hybrid_elements.size() == 0); EXPECT(hybrid_elements.nb_types() == 2); EXPECT(hybrid_elements.elements(0).size() == 0); EXPECT(hybrid_elements.elements(1).size() == 0); } CASE("irregularconnectivity_insert") { IrregularConnectivity connectivity; idx_t c[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; connectivity.add(3, 4, c); idx_t c2[] = {13, 14, 15, 16, 17, 18}; connectivity.insert(1, 2, 3, c2); connectivity.insert(2, 1, 5); idx_t iregular_c[] = {2, 3, 4, 1}; connectivity.insert(5, 4, iregular_c); idx_t values[] = {1, 2, 3, 4, 13, 14, 15, -1, -1, -1, -1, -1, 16, 17, 18, 5, 6, 7, 8, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 9, 10, 11, 12}; idx_t counts[] = {4, 3, 5, 3, 4, 2, 3, 4, 1, 4}; idx_t n(0); idx_t r(0); for (idx_t jrow = 0; jrow < connectivity.rows(); ++jrow) { EXPECT(connectivity.cols(jrow) == counts[r++]); for (idx_t jcol = 0; jcol < connectivity.cols(jrow); ++jcol) { EXPECT(connectivity(jrow, jcol) == values[n++]); } } } CASE("multiblockconnectivity_insert") { MultiBlockConnectivity connectivity; idx_t c1[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; connectivity.add(3, 4, c1); idx_t c2[] = {13, 14, 15, 16, 17, 18}; connectivity.add(2, 3, c2); idx_t c1i[] = {19, 20, 21, 22}; idx_t c2i[] = {23, 24, 25, 26, 27, 28}; EXPECT(connectivity.block(0).rows() == 3); EXPECT(connectivity.block(1).rows() == 2); EXPECT(connectivity.block(0).cols() == 4); EXPECT(connectivity.block(1).cols() == 3); std::cout << "block 0" << std::endl; for (idx_t jrow = 0; jrow < connectivity.block(0).rows(); ++jrow) { for (idx_t jcol = 0; jcol < connectivity.block(0).cols(); ++jcol) { std::cout << connectivity.block(0)(jrow, jcol) << " "; } std::cout << std::endl; } connectivity.insert(1, 1, 4, c1i); connectivity.insert(5, 2, 3, c2i); EXPECT(connectivity.block(0).rows() == 4); EXPECT(connectivity.block(1).rows() == 4); std::cout << "\nfull\n"; for (idx_t jrow = 0; jrow < connectivity.rows(); ++jrow) { for (idx_t jcol = 0; jcol < connectivity.cols(jrow); ++jcol) { std::cout << connectivity(jrow, jcol) << " "; } std::cout << std::endl; } idx_t values[] = {1, 2, 3, 4, 19, 20, 21, 22, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 23, 24, 25, 26, 27, 28, 16, 17, 18}; idx_t counts[] = {4, 4, 4, 4, 3, 3, 3, 3}; idx_t n(0); idx_t r(0); for (idx_t jrow = 0; jrow < connectivity.rows(); ++jrow) { EXPECT(connectivity.cols(jrow) == counts[r++]); for (idx_t jcol = 0; jcol < connectivity.cols(jrow); ++jcol) { EXPECT(connectivity(jrow, jcol) == values[n++]); } } } CASE("cells_insert") { Log::info() << "\n\n\ncells_insert \n============ \n\n" << std::endl; HybridElements cells; idx_t c1[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; cells.add(ElementType::create("Quadrilateral"), 3, c1); idx_t c2[] = {13, 14, 15, 16, 17, 18}; cells.add(ElementType::create("Triangle"), 2, c2); EXPECT(cells.elements(0).size() == 3); EXPECT(cells.elements(1).size() == 2); EXPECT(cells.size() == 5); EXPECT(cells.elements(0).node_connectivity().rows() == 3); EXPECT(cells.elements(1).node_connectivity().rows() == 2); Log::info() << "Update elements(0)" << std::endl; idx_t pos0 = cells.elements(0).add(3); Log::info() << "Update elements(1)" << std::endl; idx_t pos1 = cells.elements(1).add(2); EXPECT(pos0 == 3); EXPECT(pos1 == 2); EXPECT(cells.elements(0).size() == 6); EXPECT(cells.elements(1).size() == 4); EXPECT(cells.size() == 10); EXPECT(cells.node_connectivity().block(0).rows() == 6); EXPECT(cells.elements(0).node_connectivity().rows() == 6); EXPECT(cells.elements(1).node_connectivity().rows() == 4); const BlockConnectivity& conn1 = cells.elements(0).node_connectivity(); const BlockConnectivity& conn2 = cells.elements(1).node_connectivity(); std::cout << "\nconn1\n"; for (idx_t jrow = 0; jrow < conn1.rows(); ++jrow) { for (idx_t jcol = 0; jcol < conn1.cols(); ++jcol) { std::cout << conn1(jrow, jcol) << " "; } std::cout << std::endl; } std::cout << "\nconn2\n"; for (idx_t jrow = 0; jrow < conn2.rows(); ++jrow) { for (idx_t jcol = 0; jcol < conn2.cols(); ++jcol) { std::cout << conn2(jrow, jcol) << " "; } std::cout << std::endl; } } CASE("cells_add_add") { HybridElements cells; cells.add(ElementType::create("Quadrilateral"), 3); cells.add(ElementType::create("Triangle"), 2); HybridElements::Connectivity& conn = cells.node_connectivity(); idx_t nodes[] = {0, 1, 2, 3}; conn.set(0, nodes); EXPECT(conn(0, 0) == 0); EXPECT(conn(0, 1) == 1); EXPECT(conn(0, 2) == 2); EXPECT(conn(0, 3) == 3); EXPECT(cells.elements(0).node_connectivity()(0, 0) == 0); EXPECT(cells.elements(0).node_connectivity()(0, 1) == 1); EXPECT(cells.elements(0).node_connectivity()(0, 2) == 2); EXPECT(cells.elements(0).node_connectivity()(0, 3) == 3); } //----------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/mesh/fctest_mesh_triangular_mesh_builder.F900000664000175000017500000001042215160212070025505 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. ! This File contains Unit Tests for testing the ! C++ / Fortran Interfaces to the Mesh Datastructure ! @author Willem Deconinck #include "fckit/fctest.h" ! ----------------------------------------------------------------------------- TESTSUITE(fctest_atlas_TriangularMeshBuilder) ! ----------------------------------------------------------------------------- TESTSUITE_INIT use atlas_module call atlas_library%initialise() END_TESTSUITE_INIT ! ----------------------------------------------------------------------------- TESTSUITE_FINALIZE use atlas_module call atlas_library%finalise() END_TESTSUITE_FINALIZE ! ----------------------------------------------------------------------------- TEST( test_mesbuilder ) use, intrinsic :: iso_c_binding use atlas_module implicit none type(atlas_Mesh) :: mesh integer :: nb_nodes integer :: nb_triags integer(ATLAS_KIND_GIDX), allocatable :: node_global_index(:) integer(ATLAS_KIND_GIDX), allocatable :: triag_global_index(:) integer(ATLAS_KIND_GIDX), allocatable :: triag_nodes_global_index(:,:) real(c_double), allocatable :: xy_in(:,:), lonlat_in(:,:) integer(ATLAS_KIND_GIDX) :: global_index_base = 0 ! small mesh ! ! 1 ---- 5 ------- 6 ! | 3 / 4 \ 1 / 2 | ! 2 ------ 3 ----- 4 ! nb_nodes = 6 nb_triags = 4 allocate(node_global_index(nb_nodes)) allocate(xy_in(2,nb_nodes)) allocate(lonlat_in(2,nb_nodes)) allocate(triag_global_index(nb_triags)) allocate(triag_nodes_global_index(3,nb_triags)) node_global_index = [6, 5, 4, 3, 2, 1] lonlat_in = reshape([& 15.0, 5.0,& ! point 6 5.0, 5.0,& ! point 5 15.0, 0.0,& ! point 4 10.0, 0.0,& ! point 3 0.0, 0.0,& ! point 2 0.0, 5.0 & ! point 1 ], shape(lonlat_in)) xy_in(:,:) = lonlat_in / 10._c_double triag_global_index = [1, 2, 3, 4] triag_nodes_global_index = reshape([3,6,5 , 3,4,6, 2,5,1, 2,3,5], shape(triag_nodes_global_index)) ! Change base node_global_index = node_global_index - 1 + global_index_base triag_global_index = triag_global_index - 1 + global_index_base triag_nodes_global_index = triag_nodes_global_index - 1 + global_index_base ! Manually build mesh BLOCK type(atlas_TriangularMeshBuilder) :: meshbuilder meshbuilder = atlas_TriangularMeshBuilder() mesh = meshbuilder%build(nb_nodes, node_global_index, xy_in(1,:), xy_in(2,:), lonlat_in(1,:), lonlat_in(2,:), & nb_triags, triag_global_index, triag_nodes_global_index, & global_index_base) call meshbuilder%final() END BLOCK ! Mesh should created now. Verify some fields BLOCK type(atlas_mesh_Nodes) :: nodes real(c_double), pointer :: xy(:,:) real(c_double), pointer :: lonlat(:,:) integer(ATLAS_KIND_GIDX), pointer :: global_index(:) type(atlas_Field) :: field_xy type(atlas_Field) :: field_lonlat type(atlas_Field) :: field_global_index integer :: jnode integer :: nb_nodes nodes = mesh%nodes() nb_nodes = nodes%size() FCTEST_CHECK_EQUAL( nb_nodes, 6 ) field_xy = nodes%xy() field_lonlat = nodes%lonlat() field_global_index = nodes%global_index() call field_xy%data(xy) call field_lonlat%data(lonlat) call field_global_index%data(global_index) do jnode=1,nb_nodes FCTEST_CHECK_EQUAL( xy(:,jnode), xy_in(:,jnode) ) FCTEST_CHECK_EQUAL( lonlat(:,jnode), lonlat_in(:,jnode) ) FCTEST_CHECK_EQUAL( global_index(jnode), node_global_index(jnode) - global_index_base + 1 ) enddo call field_xy%final() call field_lonlat%final() call field_global_index%final() call nodes%final() END BLOCK ! Output mesh BLOCK type(atlas_Output) :: gmsh gmsh = atlas_output_Gmsh("out.msh",coordinates="lonlat") call gmsh%write(mesh) call gmsh%final() END BLOCK call mesh%final() END_TEST ! ----------------------------------------------------------------------------- END_TESTSUITE atlas-0.46.0/src/tests/mesh/test_healpixmeshgen.cc0000664000175000017500000002031515160212070022320 0ustar alastairalastair/* * (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/grid.h" #include "atlas/mesh/Mesh.h" #include "atlas/mesh/Nodes.h" #include "atlas/meshgenerator.h" #include "atlas/meshgenerator/detail/HealpixMeshGenerator.h" #include "atlas/output/Gmsh.h" #include "atlas/util/PolygonXY.h" #include "tests/AtlasTestEnvironment.h" using namespace atlas::output; using namespace atlas::meshgenerator; using namespace atlas::grid; namespace atlas { namespace test { //----------------------------------------------------------------------------- std::vector to_points(std::vector& lon, std::vector& lat) { ATLAS_ASSERT(lon.size() == lat.size()); std::vector points(lon.size()); for (size_t j = 0; j < points.size(); ++j) { points[j] = {lon[j], lat[j]}; } return points; } CASE("test_check_healpix_points") { SECTION("H1") { Grid grid("H1"); EXPECT_EQ(grid.size(), 12); std::vector lat = {41.810315, 41.810315, 41.810315, 41.810315, 0., 0., 0., 0., -41.810315, -41.810315, -41.810315, -41.810315}; std::vector lon = {45., 135., 225., 315., 0., 90., 180., 270., 45., 135., 225., 315.}; auto points = to_points(lon, lat); int i = 0; for (auto& p : grid.lonlat()) { EXPECT_APPROX_EQ(p, points[i], 1.e-6); i++; } } SECTION("H2") { Grid grid("H2"); EXPECT_EQ(grid.size(), 48); std::vector lat = {66.443536, 66.443536, 66.443536, 66.443536, 41.810315, 41.810315, 41.810315, 41.810315, 41.810315, 41.810315, 41.810315, 41.810315, 19.471221, 19.471221, 19.471221, 19.471221, 19.471221, 19.471221, 19.471221, 19.471221, 0., 0., 0., 0., 0., 0., 0., 0., -19.471221, -19.471221, -19.471221, -19.471221, -19.471221, -19.471221, -19.471221, -19.471221, -41.810315, -41.810315, -41.810315, -41.810315, -41.810315, -41.810315, -41.810315, -41.810315, -66.443536, -66.443536, -66.443536, -66.443536}; std::vector lon = {45., 135., 225., 315., 22.5, 67.5, 112.5, 157.5, 202.5, 247.5, 292.5, 337.5, 0., 45., 90., 135., 180., 225., 270., 315., 22.5, 67.5, 112.5, 157.5, 202.5, 247.5, 292.5, 337.5, 0., 45., 90., 135., 180., 225., 270., 315., 22.5, 67.5, 112.5, 157.5, 202.5, 247.5, 292.5, 337.5, 45., 135., 225., 315.}; auto points = to_points(lon, lat); int i = 0; for (auto& p : grid.lonlat()) { EXPECT_APPROX_EQ(p, points[i], 1.e-6); i++; } } SECTION("H3") { Grid grid("H3"); std::vector lat = { 74.357529, 74.357529, 74.357529, 74.357529, 58.413662, 58.413662, 58.413662, 58.413662, 58.413662, 58.413662, 58.413662, 58.413662, 41.810315, 41.810315, 41.810315, 41.810315, 41.810315, 41.810315, 41.810315, 41.810315, 41.810315, 41.810315, 41.810315, 41.810315, 26.3878, 26.3878, 26.3878, 26.3878, 26.3878, 26.3878, 26.3878, 26.3878, 26.3878, 26.3878, 26.3878, 26.3878, 12.839588, 12.839588, 12.839588, 12.839588, 12.839588, 12.839588, 12.839588, 12.839588, 12.839588, 12.839588, 12.839588, 12.839588, 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., -12.839588, -12.839588, -12.839588, -12.839588, -12.839588, -12.839588, -12.839588, -12.839588, -12.839588, -12.839588, -12.839588, -12.839588, -26.3878, -26.3878, -26.3878, -26.3878, -26.3878, -26.3878, -26.3878, -26.3878, -26.3878, -26.3878, -26.3878, -26.3878, -41.810315, -41.810315, -41.810315, -41.810315, -41.810315, -41.810315, -41.810315, -41.810315, -41.810315, -41.810315, -41.810315, -41.810315, -58.413662, -58.413662, -58.413662, -58.413662, -58.413662, -58.413662, -58.413662, -58.413662, -74.357529, -74.357529, -74.357529, -74.357529}; std::vector lon = { 45., 135., 225., 315., 22.5, 67.5, 112.5, 157.5, 202.5, 247.5, 292.5, 337.5, 15., 45., 75., 105., 135., 165., 195., 225., 255., 285., 315., 345., 0., 30., 60., 90., 120., 150., 180., 210., 240., 270., 300., 330., 15., 45., 75., 105., 135., 165., 195., 225., 255., 285., 315., 345., 0., 30., 60., 90., 120., 150., 180., 210., 240., 270., 300., 330., 15., 45., 75., 105., 135., 165., 195., 225., 255., 285., 315., 345., 0., 30., 60., 90., 120., 150., 180., 210., 240., 270., 300., 330., 15., 45., 75., 105., 135., 165., 195., 225., 255., 285., 315., 345., 22.5, 67.5, 112.5, 157.5, 202.5, 247.5, 292.5, 337.5, 45., 135., 225., 315.}; auto points = to_points(lon, lat); int i = 0; for (auto& p : grid.lonlat()) { EXPECT_APPROX_EQ(p, points[i], 1.e-6); i++; } } } CASE("test_create_healpix_mesh") { util::Config opts; HealpixMeshGenerator generate(opts); Mesh m = generate(Grid("H16")); Grid grid = m.grid(); EXPECT(grid); Log::info() << grid.spec() << "\n"; Log::info() << grid.size() << "\n"; Gmsh("out_3d.msh", util::Config("coordinates", "xyz")).write(m); Gmsh("out_ll.msh", util::Config("coordinates", "lonlat")).write(m); } //----------------------------------------------------------------------------- CASE("construction by config") { EXPECT(Grid(util::Config("type", "healpix")("N", 3)) == Grid("H3")); } CASE("construction by integer") { EXPECT(HealpixGrid(3) == Grid("H3")); } CASE("construction by integer and ordering") { EXPECT(HealpixGrid(3, "ring") == Grid("H3")); } CASE("failing construction by integer and unsupported ordering") { EXPECT_THROWS(HealpixGrid grid(3, "nested")); } CASE("failing construction by config with unsupported ordering") { EXPECT_THROWS(Grid grid(util::Config("name", "H3")("ordering", "nested"))); } CASE("failing construction by config with unsupported ordering") { EXPECT_THROWS(Grid grid(util::Config("type", "healpix")("N", 3)("ordering", "nested"))); } //----------------------------------------------------------------------------- CASE("matching mesh partitioner") { auto grid = Grid{"H8"}; auto mesh = HealpixMeshGenerator{}.generate(grid); auto match = MatchingPartitioner{mesh}; auto& polygons = mesh.polygons(); [[maybe_unused]] static bool do_once = [&]() { for (idx_t i = 0; i < polygons.size(); ++i) { auto poly = util::PolygonXY{polygons[i]}; Log::info() << "polygon[" << i << "]:\n"; for (idx_t j = 0; j < poly.size(); ++j) { Log::info() << " " << std::setw(5) << std::left << j << std::setprecision(16) << poly[j] << std::endl; } } return true; }(); SECTION("H8 -> O64") { EXPECT_NO_THROW(match.partition(Grid{"O64"})); } SECTION("H8 -> L32x17") { EXPECT_NO_THROW(match.partition(Grid{"L32x17"})); } SECTION("H8 -> S32x17") { EXPECT_NO_THROW(match.partition(Grid{"S32x17"})); } SECTION("H8 -> F32") { EXPECT_NO_THROW(match.partition(Grid{"F32"})); } SECTION("H8 -> L64x33 (west=-180)") { EXPECT_NO_THROW(match.partition(Grid{"L64x33", GlobalDomain(-180.)})); } } } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/mesh/fctest_meshgen.F900000664000175000017500000001321715160212070021232 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. ! This File contains Unit Tests for testing the ! C++ / Fortran Interfaces to the Mesh Datastructure ! @author Willem Deconinck #include "fckit/fctest.h" ! ----------------------------------------------------------------------------- TESTSUITE(fctest_atlas_Meshgen) ! ----------------------------------------------------------------------------- TESTSUITE_INIT use atlas_module call atlas_library%initialise() END_TESTSUITE_INIT ! ----------------------------------------------------------------------------- TESTSUITE_FINALIZE use atlas_module call atlas_library%finalise() END_TESTSUITE_FINALIZE ! ----------------------------------------------------------------------------- !DISABLE( from_constructor ) ! use atlas_module ! implicit none ! type(atlas_StructuredGrid) :: grid ! type(atlas_Mesh) :: mesh ! type(atlas_MeshGenerator) :: meshgenerator ! type(atlas_Output) :: gmsh ! integer, parameter :: nlat = 7 ! real(c_double) :: lats(nlat) ! real(c_double) :: lon_min(nlat) ! integer(c_int) :: pl(nlat) ! lats = [ 90., 60., 30., 0., -30., -60., -90. ] ! pl = [ 2, 4, 8, 12, 8, 4, 2 ] ! lon_min = [ 0., 15., 0., 30., 0., 15., 0. ] ! grid = atlas_grid_CustomStructured(lats,pl,lon_min) ! meshgenerator = atlas_MeshGenerator() ! mesh = meshgenerator%generate(grid) ! gmsh = atlas_output_Gmsh("test_custom_structured1.msh") ! call gmsh%write(mesh) ! call meshgenerator%final() ! call grid%final() ! call mesh%final() !END_DISABLE !DISABLE( from_config ) ! use atlas_module ! implicit none ! type(atlas_Config) :: conf ! type(atlas_StructuredGrid) :: grid ! type(atlas_Mesh) :: mesh ! type(atlas_MeshGenerator) :: meshgenerator ! type(atlas_Output) :: gmsh ! conf = atlas_Config( ) ! call conf%set("Implementationype","custom_structured") ! call conf%set("nlat",7) ! call conf%set("latitudes",[ 90., 60., 30., 0., -30., -60., -90. ]) ! call conf%set("pl",[ 2, 4, 8, 12, 8, 4, 2 ]) ! call conf%set("lon_min",[ 0., 15., 0., 30., 0., 15., 0. ]) ! grid = atlas_StructuredGrid(conf) ! meshgenerator = atlas_MeshGenerator() ! mesh = meshgenerator%generate(grid) ! gmsh = atlas_output_Gmsh("test_custom_structured2.msh") ! call gmsh%write(mesh) ! call meshgenerator%final() ! call grid%final() ! call mesh%final() ! call conf%final() !END_DISABLE !DISABLE( from_json_file ) ! use atlas_module ! implicit none ! type(atlas_Config) :: conf ! type(atlas_StructuredGrid) :: grid ! type(atlas_Mesh) :: mesh ! type(atlas_MeshGenerator) :: meshgenerator ! type(atlas_Output) :: gmsh ! ! Write a json file ! OPEN (UNIT=9 , FILE="custom.json", STATUS='REPLACE') ! write(9,'(A)') & ! & '{' & ! & // ' "Implementationype" : "custom_structured",' & ! & // ' "nlat" : 7,' & ! & // ' "latitudes" : [ 90, 60, 30, 0, -30, -60, -90 ],' & ! & // ' "pl" : [ 2, 4, 8, 12, 8, 4, 2 ],' & ! & // ' "lon_min" : [ 0, 15, 0, 30, 0, 15, 0 ]' & ! & // '}' ! CLOSE(9) ! conf = atlas_Config( atlas_PathName("custom.json") ) ! grid = atlas_StructuredGrid(conf) ! meshgenerator = atlas_MeshGenerator() ! mesh = meshgenerator%generate(grid) ! gmsh = atlas_output_Gmsh("test_custom_structured3.msh") ! call gmsh%write(mesh) ! call meshgenerator%final() ! call grid%final() ! call mesh%final() ! call conf%final() !END_DISABLE TEST( test_meshgen ) use, intrinsic :: iso_c_binding use atlas_module implicit none type(atlas_StructuredGrid) :: grid type(atlas_MeshGenerator) :: meshgenerator type(atlas_Mesh) :: mesh type(atlas_mesh_Nodes) :: nodes type(atlas_mesh_Edges) :: edges type(atlas_Field) :: field type(atlas_functionspace_NodeColumns) :: functionspace_nodes type(atlas_functionspace_EdgeColumns) :: functionspace_edges type(atlas_HaloExchange) :: halo_exchange type(atlas_Output) :: gmsh integer(ATLAS_KIND_IDX), pointer :: ridx(:) real(c_double), pointer :: arr1d(:), arr2d(:,:) integer :: i, nnodes, nghost write(*,*) "test_meshgen starting" grid = atlas_StructuredGrid("N24") ! grid = atlas_grid_ShiftedLat(40,20) meshgenerator = atlas_MeshGenerator() mesh = meshgenerator%generate(grid) nodes = mesh%nodes() call meshgenerator%final() functionspace_edges = atlas_functionspace_EdgeColumns(mesh,halo=1) functionspace_nodes = atlas_functionspace_NodeColumns(mesh,halo=1) call atlas_build_median_dual_mesh(mesh) nnodes = nodes%size() FCTEST_CHECK_EQUAL( nnodes, 3672 ) field = nodes%field("remote_idx") call field%data(ridx) nghost = 0 do i=1,nnodes if( ridx(i) /= i ) nghost = nghost + 1 enddo write(0,*) "nghost =",nghost FCTEST_CHECK_EQUAL( nghost, 144 ) call atlas_log%info( nodes%str() ) field = nodes%field("dual_volumes") call field%data(arr1d) call field%final() functionspace_nodes = atlas_functionspace_NodeColumns(mesh,1) halo_exchange = functionspace_nodes%get_halo_exchange() call halo_exchange%execute(arr1d) edges = mesh%edges() field = edges%field("dual_normals") call field%data(arr2d) call field%final() gmsh = atlas_output_Gmsh("testf2.msh",ghost=.true.) call gmsh%write(mesh) call atlas_write_load_balance_report(mesh,"N24_loadbalance.dat") END_TEST ! ----------------------------------------------------------------------------- END_TESTSUITE atlas-0.46.0/src/tests/mesh/test_connectivity_kernel.hic0000664000175000017500000001056115160212070023555 0ustar alastairalastair/* * (C) Copyright 1996-2016 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 "pluto/pluto.h" #include "atlas/mesh/Connectivity.h" #include "tests/AtlasTestEnvironment.h" template class managed { public: managed() : alloc_(pluto::managed_resource()) { data_ = alloc_.allocate(1); } ~managed() { alloc_.deallocate(data_, 1); } const T* data() const { return data_; } T* data() { return data_; } const T& value() const { return *data_; } T& value() { return *data_; } private: T* data_; pluto::allocator alloc_; }; using namespace atlas::mesh; namespace atlas { namespace test { #if ATLAS_HAVE_FORTRAN #define IN_FORTRAN -1 #else #define IN_FORTRAN #endif __global__ void kernel_block(BlockConnectivityImpl conn, bool* result) { *result = true; *result &= (conn.rows() == 2); *result &= (conn.cols() == 5); *result &= ((conn)(0,2) == 9 ); *result &= ((conn)(0,4) == 356 ); *result &= ((conn)(1,1) == 3 ); } __global__ void kernel_irr(IrregularConnectivityImpl conn, bool* result) { *result = true; *result &= (conn.rows()== 2); *result &= (conn.cols(0) == 3); *result &= (conn.cols(1) == 3); *result &= (conn.mincols() == 3); *result &= (conn.maxcols() == 3); *result &= (conn(0,0) == 1 IN_FORTRAN); *result &= (conn(0,1) == 3 IN_FORTRAN); *result &= (conn(0,2) == 4 IN_FORTRAN); } __global__ void kernel_multiblock(MultiBlockConnectivityImpl conn, bool* result) { *result = true; *result &= (conn.blocks()== 1); *result &= (conn.rows()== 2); *result &= (conn.cols(0) == 3); *result &= (conn.cols(1) == 3); *result &= (conn.mincols() == 3); *result &= (conn.maxcols() == 3); *result &= (conn(0,0) == 1 IN_FORTRAN); *result &= (conn(0,1) == 3 IN_FORTRAN); *result &= (conn(0,2) == 4 IN_FORTRAN); BlockConnectivity& ablock = conn.block(0); *result &= (ablock(0,0) == 1 IN_FORTRAN); *result &= (ablock(0,1) == 3 IN_FORTRAN); *result &= (ablock(0,2) == 4 IN_FORTRAN); } CASE( "test_block_connectivity" ) { BlockConnectivity conn; managed result; idx_t vals2[12] = {2,3,9,34,356,86,3,24,84,45,2,2}; conn.add(2,5, vals2); EXPECT(conn(0,0) == 2); EXPECT(conn(0,1) == 3); EXPECT(conn(0,2) == 9); EXPECT(conn(0,3) == 34); EXPECT(conn(0,4) == 356); EXPECT(conn(1,0) == 86); EXPECT(conn(1,1) == 3); EXPECT(conn(1,2) == 24); EXPECT(conn(1,3) == 84); EXPECT(conn(1,4) == 45); kernel_block<<<1,1>>>(conn, result.data()); pluto::wait(); EXPECT( result.value() == true ); // copy back, although not strickly needed since the gpu copy does not modify values, // but for the sake of testing it EXPECT((conn)(0,4) == 356 ); } CASE( "test_irregular_connectivity" ) { IrregularConnectivity conn("mesh"); EXPECT(conn.rows() == 0); EXPECT(conn.maxcols() ==0); constexpr idx_t vals[6] = {1,3,4,3,7,8}; bool from_fortran = true; conn.add(2, 3, vals, from_fortran); EXPECT(conn(0,0) == 1 IN_FORTRAN); managed result; kernel_irr<<<1,1>>>(conn, result.data()); pluto::wait(); EXPECT( result.value() == true ); // copy back, although not strickly needed since the gpu copy does not modify values, // but for the sake of testing it EXPECT(conn(0,1) == 3 IN_FORTRAN); } CASE( "test_multiblock_connectivity" ) { MultiBlockConnectivity conn("mesh"); EXPECT(conn.rows() == 0); EXPECT(conn.maxcols() == 0); constexpr idx_t vals[6] = {1,3,4,3,7,8}; bool from_fortran = true; conn.add(2, 3, vals, from_fortran); EXPECT(conn.block(0)(0,0) == 1 IN_FORTRAN); managed result; kernel_multiblock<<<1,1>>>(conn, result.data()); pluto::wait(); EXPECT( result.value() == true ); // copy back, although not strickly needed since the gpu copy does not modify values, // but for the sake of testing it EXPECT(conn.block(0)(0,0) == 1 IN_FORTRAN); } } } int main(int argc, char **argv) { return atlas::test::run( argc, argv ); } atlas-0.46.0/src/tests/mesh/test_mesh_reorder_unstructured.msh0000664000175000017500000004754615160212070025045 0ustar alastairalastair$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 atlas-0.46.0/src/tests/mesh/test_halo.cc0000664000175000017500000003222615160212070020246 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include #include #include #include "eckit/types/FloatCompare.h" #include "atlas/array.h" #include "atlas/array/ArrayView.h" #include "atlas/array/IndexView.h" #include "atlas/library/config.h" #include "atlas/mesh/IsGhostNode.h" #include "atlas/mesh/Mesh.h" #include "atlas/mesh/Nodes.h" #include "atlas/mesh/actions/BuildDualMesh.h" #include "atlas/mesh/actions/BuildEdges.h" #include "atlas/mesh/actions/BuildHalo.h" #include "atlas/mesh/actions/BuildParallelFields.h" #include "atlas/mesh/actions/BuildPeriodicBoundaries.h" #include "atlas/output/Gmsh.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/util/CoordinateEnums.h" #include "atlas/util/MicroDeg.h" #include "atlas/util/Unique.h" #include "tests/AtlasTestEnvironment.h" #include "tests/TestMeshes.h" using namespace atlas::output; using namespace atlas::util; using namespace atlas::meshgenerator; namespace atlas { namespace test { double dual_volume(Mesh& mesh) { mesh::Nodes& nodes = mesh.nodes(); mesh::IsGhostNode is_ghost_node(nodes); int nb_nodes = nodes.size(); array::ArrayView dual_volumes = array::make_view(nodes.field("dual_volumes")); double area = 0; for (int node = 0; node < nb_nodes; ++node) { if (!is_ghost_node(node)) { area += dual_volumes(node); } } ATLAS_TRACE_MPI(ALLREDUCE) { mpi::comm().allReduceInPlace(area, eckit::mpi::sum()); } return area; } #if 0 CASE( "test_small" ) { int nlat = 5; int lon[5] = {10, 12, 14, 16, 16}; Mesh = test::generate_mesh(nlat, lon); mesh::actions::build_parallel_fields(*m); mesh::actions::build_periodic_boundaries(*m); mesh::actions::build_halo(*m,2); if( mpi::comm().size() == 5 ) { IndexView ridx ( m->nodes().remote_index() ); array::array::ArrayView gidx ( m->nodes().global_index() ); switch( mpi::comm().rank() ) // with 5 tasks { case 0: EXPECT( ridx(9) == 9 ); EXPECT( gidx(9) == 10 ); EXPECT( ridx(30) == 9 ); EXPECT( gidx(30) == 875430066 ); // hashed unique idx break; } } else { if( mpi::comm().rank() == 0 ) std::cout << "skipping tests with 5 mpi tasks!" << std::endl; } mesh::actions::build_edges(*m); mesh::actions::build_median_dual_mesh(*m); EXPECT( eckit::types::is_approximately_equal( test::dual_volume(*m), 2.*M_PI*M_PI, 1e-6 )); std::stringstream filename; filename << "small_halo_p" << mpi::comm().rank() << ".msh"; Gmsh(filename.str()).write(*m); } #endif #if 1 CASE("test_custom") { // Mesh m = test::generate_mesh( T63() ); Mesh m = test::generate_mesh({10, 12, 14, 16, 16, 16, 16, 14, 12, 10}); mesh::actions::build_nodes_parallel_fields(m.nodes()); mesh::actions::build_periodic_boundaries(m); mesh::actions::build_halo(m, 1); std::stringstream filename; filename << "custom.msh"; Gmsh(filename.str(), util::Config("ghost", true)).write(m); // EXPECT( eckit::types::is_approximately_equal( test::dual_volume(m), // 2.*M_PI*M_PI, 1e-6 )); auto lonlat = array::make_view(m.nodes().lonlat()); #if ATLAS_BITS_GLOBAL == 64 std::vector check; switch (mpi::comm().rank()) { case 0: check = {607990293346953216, 607990293382953216, 607990293418953216, 607990293454953216, 607990293490953216, 607990293526953216, 607990293562953216, 607990293598953216, 607990293634953216, 607990293670953216, 644481443595331584, 644481443625331584, 644481443655331584, 644481443685331584, 644481443715331584, 644481443745331584, 644481443775331584, 644481443805331584, 644481443835331584, 644481443865331584, 644481443895331584, 644481443925331584, 681187136050079744, 681187136075794030, 681187136101508315, 681187136127222601, 681187136152936887, 681187136178651173, 607990293706953216, 644481443955331584, 681187136204365458, 681187136230079744, 681187136255794030, 681187136281508315, 681187136307222601, 681187136332936887, 681187136358651173, 681187136384365458, 717939789677242368, 717939789699742368, 717939789722242368, 717939789744742368, 717939789767242368, 717939789789742368, 717939789812242368, 754708008265885696, 754708008288385696, 754708008310885696, 754708008333385696, 754708008355885696, 754708008378385696, 754708008400885696, 717939789834742368, 717939789857242368, 717939789879742368, 717939789902242368, 754708008423385696, 681187136410079744, 717939789924742368, 717939789947242368, 717939789969742368, 717939789992242368, 717939790014742368, 607990293310953216, 644481443565331584, 681187136024365458, 717939789654742368, 754708008243385696, 607990293742953216, 644481443985331584, 681187136435794030}; break; case 1: check = {717939789677242368, 717939789699742368, 717939789722242368, 717939789744742368, 717939789767242368, 717939789789742368, 754708008265885696, 754708008288385696, 754708008310885696, 754708008333385696, 754708008355885696, 754708008378385696, 791480221174114304, 791480221196614304, 791480221219114304, 791480221241614304, 791480221264114304, 828248439762757632, 828248439785257632, 828248439807757632, 828248439830257632, 828248439852757632, 865001093389920256, 865001093415634542, 865001093441348827, 865001093467063113, 865001093492777399, 717939789812242368, 754708008400885696, 791480221286614304, 828248439875257632, 865001093518491685, 901706785844668416, 901706785874668416, 901706785904668416, 901706785934668416, 901706785964668416, 681187136050079744, 681187136075794030, 681187136101508315, 681187136127222601, 681187136152936887, 681187136178651173, 681187136204365458, 717939789834742368, 754708008423385696, 791480221309114304, 791480221331614304, 828248439897757632, 865001093544205970, 901706785994668416, 938197936093046784, 938197936129046784, 938197936165046784, 938197936201046784, 938197936237046784, 717939789654742368, 754708008243385696, 791480221151614304, 828248439740257632, 865001093364205970, 901706785814668416}; break; case 2: check = {681187136204365458, 681187136230079744, 681187136255794030, 717939789812242368, 717939789834742368, 717939789857242368, 717939789879742368, 717939789902242368, 754708008400885696, 754708008423385696, 754708008445885696, 754708008468385696, 754708008490885696, 791480221286614304, 791480221309114304, 791480221331614304, 791480221354114304, 791480221376614304, 828248439875257632, 828248439897757632, 828248439920257632, 828248439942757632, 828248439965257632, 865001093518491685, 865001093544205970, 865001093569920256, 865001093595634542, 681187136281508315, 717939789924742368, 754708008378385696, 754708008513385696, 791480221399114304, 828248439987757632, 865001093492777399, 865001093621348827, 901706785964668416, 901706785994668416, 901706786024668416, 901706786054668416, 644481443745331584, 644481443775331584, 644481443805331584, 644481443835331584, 681187136178651173, 681187136307222601, 717939789789742368, 717939789767242368, 754708008355885696, 791480221264114304, 828248439852757632, 865001093467063113, 901706785934668416, 717939789947242368, 754708008535885696, 791480221421614304, 791480221444114304, 828248440010257632, 865001093647063113, 901706786084668416, 938197936201046784, 938197936237046784, 938197936273046784, 938197936309046784}; break; case 3: check = {681187136281508315, 681187136307222601, 681187136332936887, 681187136358651173, 681187136384365458, 717939789924742368, 717939789947242368, 717939789969742368, 717939789992242368, 717939790014742368, 754708008513385696, 754708008535885696, 754708008558385696, 754708008580885696, 754708008603385696, 791480221399114304, 791480221421614304, 791480221444114304, 791480221466614304, 791480221489114304, 791480221511614304, 828248439987757632, 828248440010257632, 828248440032757632, 828248440055257632, 828248440077757632, 828248440100257632, 644481443955331584, 681187136410079744, 717939789902242368, 717939790037242368, 754708008490885696, 754708008625885696, 791480221534114304, 828248440122757632, 865001093621348827, 865001093647063113, 865001093672777399, 865001093698491685, 865001093724205970, 865001093749920256, 607990293706953216, 644481443805331584, 644481443835331584, 644481443865331584, 644481443895331584, 644481443925331584, 681187136255794030, 717939789879742368, 754708008468385696, 791480221376614304, 828248439965257632, 865001093595634542, 901706786054668416, 901706786084668416, 901706786114668416, 901706786144668416, 901706786174668416, 901706786204668416, 644481443985331584, 681187136435794030, 717939790059742368, 754708008648385696, 791480221556614304, 828248440145257632, 865001093775634542}; break; case 4: check = {865001093621348827, 865001093647063113, 865001093672777399, 865001093698491685, 865001093724205970, 901706785844668416, 901706785874668416, 901706785904668416, 901706785934668416, 901706785964668416, 901706785994668416, 901706786024668416, 901706786054668416, 901706786084668416, 901706786114668416, 901706786144668416, 901706786174668416, 938197936093046784, 938197936129046784, 938197936165046784, 938197936201046784, 938197936237046784, 938197936273046784, 938197936309046784, 938197936345046784, 938197936381046784, 938197936417046784, 865001093749920256, 901706786204668416, 938197936453046784, 865001093389920256, 865001093415634542, 865001093441348827, 865001093467063113, 865001093492777399, 865001093518491685, 828248439987757632, 865001093544205970, 865001093569920256, 865001093595634542, 828248440010257632, 828248440032757632, 828248440055257632, 828248440077757632, 828248440100257632, 828248440122757632, 865001093364205970, 901706785814668416, 938197936057046784, 828248440145257632, 865001093775634542, 901706786234668416, 938197936489046784}; break; default: check.clear(); } std::vector uid(m.nodes().size()); for (idx_t j = 0; j < m.nodes().size(); ++j) { uid[j] = util::unique_lonlat(lonlat(j, 0), lonlat(j, 1)); } if (check.size() && mpi::comm().size() == 5) { EXPECT_EQ(uid.size(), check.size()); if (uid != check) { for (idx_t i = 0; i < uid.size(); ++i) { if (uid[i] != check[i]) { Log::warning() << "uid[" << i << "] != check[" << i << "] : " << uid[i] << " expected to be " << check[i] << " point = " << std::setprecision(7) << std::fixed << PointLonLat{lonlat(i, LON), lonlat(i, LAT)} << " microdeg(lon) = " << microdeg(lonlat(i, LON)) << std::endl; } } Log::info() << "uid = { "; for (idx_t i = 0; i < uid.size(); ++i) { Log::info() << uid[i] << std::string(i < uid.size() - 1 ? ", " : " "); } Log::info() << "};" << std::endl; } EXPECT(uid == check); } #endif // FunctionSpace& edges = m.function_space("edges"); // array::array::ArrayView dual_volumes ( nodes.field( // "dual_volumes" ) ); // array::array::ArrayView dual_normals ( edges.field( // "dual_normals" ) ); // std::string checksum; // checksum = nodes.checksum()->execute(dual_volumes); // DEBUG("dual_volumes checksum "<execute(dual_normals); // DEBUG("dual_normals checksum "< array(10, 2); Log::info() << "array.footprint = " << eckit::Bytes(array.footprint()) << std::endl; Field field("field", array::make_datatype(), array::make_shape(10, 2)); Log::info() << "field.footprint = " << eckit::Bytes(field.footprint()) << std::endl; Grid grid(griduid()); MeshGenerator meshgen("structured"); Mesh mesh = meshgen.generate(grid); Log::info() << "Footprint for mesh generated from grid " << grid.name() << std::endl; Log::info() << "mesh.footprint = " << eckit::Bytes(mesh.footprint()) << std::endl; Log::info() << " .nodes.footprint = " << eckit::Bytes(mesh.nodes().footprint()) << std::endl; for (idx_t f = 0; f < mesh.nodes().nb_fields(); ++f) { Log::info() << " ." + mesh.nodes().field(f).name() + ".footprint = " << eckit::Bytes(mesh.nodes().field(f).footprint()) << std::endl; } Log::info() << " .cells.footprint = " << eckit::Bytes(mesh.cells().footprint()) << std::endl; for (idx_t f = 0; f < mesh.cells().nb_fields(); ++f) { Log::info() << " ." + mesh.cells().field(f).name() + ".footprint = " << eckit::Bytes(mesh.cells().field(f).footprint()) << std::endl; } Log::info() << " .node_connectivity.footprint = " << eckit::Bytes(mesh.cells().node_connectivity().footprint()) << std::endl; Log::info() << " .edge_connectivity.footprint = " << eckit::Bytes(mesh.cells().edge_connectivity().footprint()) << std::endl; Log::info() << " .cell_connectivity.footprint = " << eckit::Bytes(mesh.cells().cell_connectivity().footprint()) << std::endl; } //----------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/mesh/fctest_connectivity.F900000664000175000017500000002052115160212070022316 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. ! This File contains Unit Tests for testing the ! C++ / Fortran Interfaces to the Connectivity ! @author Willem Deconinck #include "fckit/fctest.h" ! ----------------------------------------------------------------------------- TESTSUITE(fctest_atlas_Connectivity) ! ----------------------------------------------------------------------------- TESTSUITE_INIT() use fckit_module call fckit_main%init() END_TESTSUITE_INIT TEST( test_connectivity ) #if 1 use fckit_module use atlas_connectivity_module use atlas_kinds_module use, intrinsic :: iso_c_binding implicit none type(atlas_Connectivity) :: connectivity integer(ATLAS_KIND_IDX), pointer :: padded(:,:), row(:), data(:,:) integer(ATLAS_KIND_IDX), pointer :: cols(:) integer(c_int) :: ncols call fckit_log%info("test_connectivity starting") connectivity = atlas_Connectivity("hybrid") FCTEST_CHECK_EQUAL( connectivity%owners(), 1 ) FCTEST_CHECK_EQUAL(connectivity%name(),"hybrid") FCTEST_CHECK_EQUAL(connectivity%rows(),0) FCTEST_CHECK_EQUAL(connectivity%missing_value(),0) call connectivity%add(2,4, & & [ 1, 2, 3, 4, & & 5, 6, 7, 8 ] ) FCTEST_CHECK_EQUAL(connectivity%mincols(),4) FCTEST_CHECK_EQUAL(connectivity%maxcols(),4) FCTEST_CHECK_EQUAL(connectivity%rows(), 2) call connectivity%data(data,ncols) FCTEST_CHECK_EQUAL(ncols,4) FCTEST_CHECK_EQUAL(data(1,1), 1) FCTEST_CHECK_EQUAL(data(2,1), 2) FCTEST_CHECK_EQUAL(data(3,1), 3) FCTEST_CHECK_EQUAL(data(4,1), 4) FCTEST_CHECK_EQUAL(data(1,2), 5) FCTEST_CHECK_EQUAL(data(2,2), 6) FCTEST_CHECK_EQUAL(data(3,2), 7) FCTEST_CHECK_EQUAL(data(4,2), 8) call connectivity%add(2,3, & & [ 9, 10, 11, & & 12, 13, 14 ] ) FCTEST_CHECK_EQUAL(connectivity%mincols(),3) FCTEST_CHECK_EQUAL(connectivity%maxcols(),4) FCTEST_CHECK_EQUAL(connectivity%rows(), 4) call connectivity%add(2,4) call connectivity%add(2,3) !============= Functional access =============! FCTEST_CHECK_EQUAL(connectivity%value(1,1), 1) FCTEST_CHECK_EQUAL(connectivity%value(2,1), 2) FCTEST_CHECK_EQUAL(connectivity%value(3,1), 3) FCTEST_CHECK_EQUAL(connectivity%value(4,1), 4) FCTEST_CHECK_EQUAL(connectivity%value(1,2), 5) FCTEST_CHECK_EQUAL(connectivity%value(2,2), 6) FCTEST_CHECK_EQUAL(connectivity%value(3,2), 7) FCTEST_CHECK_EQUAL(connectivity%value(4,2), 8) FCTEST_CHECK_EQUAL(connectivity%value(1,3), 9) FCTEST_CHECK_EQUAL(connectivity%value(2,3), 10) FCTEST_CHECK_EQUAL(connectivity%value(3,3), 11) FCTEST_CHECK_EQUAL(connectivity%value(1,4), 12) FCTEST_CHECK_EQUAL(connectivity%value(2,4), 13) FCTEST_CHECK_EQUAL(connectivity%value(3,4), 14) !============= Padded Data pointer access =============! ! Todo: Check this aborts, ! because data has become irregular ! call connectivity%data(data,ncols) call connectivity%padded_data(padded,cols) FCTEST_CHECK_EQUAL(padded(1,1), 1) FCTEST_CHECK_EQUAL(padded(2,1), 2) FCTEST_CHECK_EQUAL(padded(3,1), 3) FCTEST_CHECK_EQUAL(padded(4,1), 4) FCTEST_CHECK_EQUAL(padded(1,2), 5) FCTEST_CHECK_EQUAL(padded(2,2), 6) FCTEST_CHECK_EQUAL(padded(3,2), 7) FCTEST_CHECK_EQUAL(padded(4,2), 8) FCTEST_CHECK_EQUAL(padded(1,3), 9) FCTEST_CHECK_EQUAL(padded(2,3), 10) FCTEST_CHECK_EQUAL(padded(3,3), 11) FCTEST_CHECK_EQUAL(padded(1,4), 12) FCTEST_CHECK_EQUAL(padded(2,4), 13) FCTEST_CHECK_EQUAL(padded(3,4), 14) !============= Structural access =============! FCTEST_CHECK_EQUAL(connectivity%access%row(1)%col(1), 1) FCTEST_CHECK_EQUAL(connectivity%access%row(1)%col(2), 2) FCTEST_CHECK_EQUAL(connectivity%access%row(1)%col(3), 3) FCTEST_CHECK_EQUAL(connectivity%access%row(1)%col(4), 4) FCTEST_CHECK_EQUAL(connectivity%access%row(2)%col(1), 5) FCTEST_CHECK_EQUAL(connectivity%access%row(2)%col(2), 6) FCTEST_CHECK_EQUAL(connectivity%access%row(2)%col(3), 7) FCTEST_CHECK_EQUAL(connectivity%access%row(2)%col(4), 8) FCTEST_CHECK_EQUAL(connectivity%access%row(3)%col(1), 9) FCTEST_CHECK_EQUAL(connectivity%access%row(3)%col(2), 10) FCTEST_CHECK_EQUAL(connectivity%access%row(3)%col(3), 11) FCTEST_CHECK_EQUAL(connectivity%access%row(4)%col(1), 12) FCTEST_CHECK_EQUAL(connectivity%access%row(4)%col(2), 13) FCTEST_CHECK_EQUAL(connectivity%access%row(4)%col(3), 14) !============= Row access =============! call connectivity%row(1, row,ncols) FCTEST_CHECK_EQUAL(ncols,4) FCTEST_CHECK_EQUAL(row(1),1) FCTEST_CHECK_EQUAL(row(2),2) FCTEST_CHECK_EQUAL(row(3),3) FCTEST_CHECK_EQUAL(row(4),4) call connectivity%row(2, row,ncols) FCTEST_CHECK_EQUAL(ncols,4) FCTEST_CHECK_EQUAL(row(1),5) FCTEST_CHECK_EQUAL(row(2),6) FCTEST_CHECK_EQUAL(row(3),7) FCTEST_CHECK_EQUAL(row(4),8) call connectivity%row(3, row,ncols) FCTEST_CHECK_EQUAL(ncols,3) FCTEST_CHECK_EQUAL(row(1),9) FCTEST_CHECK_EQUAL(row(2),10) FCTEST_CHECK_EQUAL(row(3),11) call connectivity%row(4, row,ncols) FCTEST_CHECK_EQUAL(ncols,3) FCTEST_CHECK_EQUAL(row(1),12) FCTEST_CHECK_EQUAL(row(2),13) FCTEST_CHECK_EQUAL(row(3),14) call connectivity%final() #endif END_TEST ! ----------------------------------------------------------------------------- TEST( test_multiblockconnectivity ) use fckit_module use atlas_connectivity_module use atlas_kinds_module use, intrinsic :: iso_c_binding implicit none type(atlas_MultiBlockConnectivity) :: multiblock type(atlas_BlockConnectivity) :: block integer(ATLAS_KIND_IDX), pointer :: data(:,:), padded(:,:) integer(ATLAS_KIND_IDX), pointer :: cols(:) type(atlas_Connectivity) :: base call fckit_log%info("test_multiblockconnectivity starting") multiblock = atlas_MultiBlockConnectivity() FCTEST_CHECK_EQUAL( multiblock%owners(), 1 ) FCTEST_CHECK_EQUAL(multiblock%name(),"") FCTEST_CHECK_EQUAL(multiblock%rows(),0) FCTEST_CHECK_EQUAL(multiblock%blocks(),0) call multiblock%add(2,4, & & [ 1, 2, 3, 4, & & 5, 6, 7, 8 ] ) FCTEST_CHECK_EQUAL(multiblock%mincols(),4) FCTEST_CHECK_EQUAL(multiblock%maxcols(),4) FCTEST_CHECK_EQUAL(multiblock%rows(), 2) FCTEST_CHECK_EQUAL(multiblock%blocks(), 1) call multiblock%add(2,3, & & [ 9, 10, 11, & & 12, 13, 14 ] ) FCTEST_CHECK_EQUAL(multiblock%mincols(),3) FCTEST_CHECK_EQUAL(multiblock%maxcols(),4) FCTEST_CHECK_EQUAL(multiblock%blocks(), 2) block = multiblock%block(1_ATLAS_KIND_IDX) !FCTEST_CHECK_EQUAL( block%owners(), 2 ) FCTEST_CHECK_EQUAL( block%rows(), 2 ) FCTEST_CHECK_EQUAL( block%cols(), 4 ) call block%data(data) FCTEST_CHECK_EQUAL(data(1,1), 1) FCTEST_CHECK_EQUAL(data(2,1), 2) FCTEST_CHECK_EQUAL(data(3,1), 3) FCTEST_CHECK_EQUAL(data(4,1), 4) FCTEST_CHECK_EQUAL(data(1,2), 5) FCTEST_CHECK_EQUAL(data(2,2), 6) FCTEST_CHECK_EQUAL(data(3,2), 7) FCTEST_CHECK_EQUAL(data(4,2), 8) block = multiblock%block(2_ATLAS_KIND_IDX) !FCTEST_CHECK_EQUAL( block%owners(), 2 ) FCTEST_CHECK_EQUAL( block%rows(), 2 ) FCTEST_CHECK_EQUAL( block%cols(), 3 ) call block%data(data) FCTEST_CHECK_EQUAL(data(1,1), 9) FCTEST_CHECK_EQUAL(data(2,1), 10) FCTEST_CHECK_EQUAL(data(3,1), 11) FCTEST_CHECK_EQUAL(data(1,2), 12) FCTEST_CHECK_EQUAL(data(2,2), 13) FCTEST_CHECK_EQUAL(data(3,2), 14) call block%final() FCTEST_CHECK_EQUAL( multiblock%owners(), 1 ) base = multiblock FCTEST_CHECK_EQUAL( base%owners(), 2 ) FCTEST_CHECK_EQUAL( multiblock%owners(), 2 ) call base%padded_data(padded,cols) FCTEST_CHECK_EQUAL(padded(1,1), 1) FCTEST_CHECK_EQUAL(padded(2,1), 2) FCTEST_CHECK_EQUAL(padded(3,1), 3) FCTEST_CHECK_EQUAL(padded(4,1), 4) FCTEST_CHECK_EQUAL(padded(1,2), 5) FCTEST_CHECK_EQUAL(padded(2,2), 6) FCTEST_CHECK_EQUAL(padded(3,2), 7) FCTEST_CHECK_EQUAL(padded(4,2), 8) FCTEST_CHECK_EQUAL(padded(1,3), 9) FCTEST_CHECK_EQUAL(padded(2,3), 10) FCTEST_CHECK_EQUAL(padded(3,3), 11) FCTEST_CHECK_EQUAL(padded(1,4), 12) FCTEST_CHECK_EQUAL(padded(2,4), 13) FCTEST_CHECK_EQUAL(padded(3,4), 14) call multiblock%final() FCTEST_CHECK_EQUAL( base%owners(), 1 ) call base%final() END_TEST ! ----------------------------------------------------------------------------- END_TESTSUITE atlas-0.46.0/src/tests/mesh/test_mesh_node2cell.cc0000664000175000017500000005421015160212070022203 0ustar alastairalastair/* * (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/grid/Grid.h" #include "atlas/library/config.h" #include "atlas/mesh/HybridElements.h" #include "atlas/mesh/Mesh.h" #include "atlas/mesh/actions/BuildHalo.h" #include "atlas/mesh/actions/BuildNode2CellConnectivity.h" #include "atlas/mesh/actions/BuildParallelFields.h" #include "atlas/mesh/actions/BuildPeriodicBoundaries.h" #include "atlas/mesh/detail/AccumulateFacets.h" #include "atlas/meshgenerator.h" #include "atlas/option.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/util/Unique.h" #include "tests/AtlasTestEnvironment.h" using namespace atlas::mesh; using namespace atlas::util; namespace atlas { namespace test { //----------------------------------------------------------------------------- /** print_check() * This routine is used to print code that can be copy-pasted to check results */ #if 0 void print_check( Mesh& mesh ) { std::ostream& out = std::cout; auto cell_glb_idx = array::make_view( mesh.cells().global_index() ); idx_t size{0}; for ( idx_t jnode = 0; jnode < mesh.nodes().size(); ++jnode ) { for ( idx_t jcell = 0; jcell < mesh.nodes().cell_connectivity().cols( jnode ); ++jcell ) { ++size; } } for ( size_t p = 0; p < mpi::comm().size(); ++p ) { mpi::comm().barrier(); if ( mpi::comm().rank() == p ) { out << newl << "if( mpi::comm().rank() == " << mpi::comm().rank() << " && mpi::comm().size() == " << mpi::comm().size() << ") {" << newl; out << "auto node_cell_connectivity_check = std::array { "; for ( idx_t jnode = 0; jnode < mesh.nodes().size(); ++jnode ) { for ( idx_t jcell = 0; jcell < mesh.nodes().cell_connectivity().cols( jnode ); ++jcell ) { idx_t icell = mesh.nodes().cell_connectivity()( jnode, jcell ); out << cell_glb_idx( icell ) << ", "; } } out << " };" << newl; out << "check_node_cell_connectivity( mesh, node_cell_connectivity_check );" << newl; out << "}" << std::endl; } } } #endif //----------------------------------------------------------------------------- // Routine that checks if the connectivity-table matches a given array "check" template void check_node_cell_connectivity(Mesh& mesh, const T& check) { auto cell_glb_idx = array::make_view(mesh.cells().global_index()); size_t c{0}; for (idx_t jnode = 0; jnode < mesh.nodes().size(); ++jnode) { for (idx_t jcell = 0; jcell < mesh.nodes().cell_connectivity().cols(jnode); ++jcell) { idx_t icell = mesh.nodes().cell_connectivity()(jnode, jcell); EXPECT(c < check.size()); EXPECT(cell_glb_idx(icell) == check[c++]); } } EXPECT(c == check.size()); } //----------------------------------------------------------------------------- struct PrettyPrintNodeCellConnectivity { const Mesh& mesh; PrettyPrintNodeCellConnectivity(const Mesh& _mesh): mesh{_mesh} {} void print(std::ostream& out) const { auto node_glb_idx = array::make_view(mesh.nodes().global_index()); auto cell_glb_idx = array::make_view(mesh.cells().global_index()); for (idx_t jnode = 0; jnode < mesh.nodes().size(); ++jnode) { out << node_glb_idx(jnode) << " : "; for (idx_t jcell = 0; jcell < mesh.nodes().cell_connectivity().cols(jnode); ++jcell) { idx_t icell = mesh.nodes().cell_connectivity()(jnode, jcell); out << cell_glb_idx(icell) << " "; } out << '\n'; } } friend std::ostream& operator<<(std::ostream& out, const PrettyPrintNodeCellConnectivity& p) { p.print(out); return out; } }; //----------------------------------------------------------------------------- CASE("test_node2cell") { // Create mesh Grid grid("O2"); StructuredMeshGenerator generator; Mesh mesh = generator.generate(grid); // Create node to cell connectivity within mesh mesh::actions::build_node_to_cell_connectivity(mesh); Log::info() << "Connectivity without halo :\n" << PrettyPrintNodeCellConnectivity{mesh} << std::endl; // Following command generates (prints) code between braces following //print_check( mesh ); // mpi-size 1 { if (mpi::comm().rank() == 0 && mpi::comm().size() == 1) { auto node_cell_connectivity_check = std::array{ 113, 26, 25, 113, 115, 114, 26, 28, 27, 115, 117, 116, 28, 30, 29, 117, 119, 118, 30, 32, 31, 119, 121, 120, 32, 34, 33, 121, 123, 122, 34, 37, 35, 36, 123, 125, 124, 37, 39, 38, 125, 127, 126, 39, 41, 40, 127, 129, 128, 41, 43, 42, 129, 130, 43, 45, 44, 130, 45, 48, 46, 47, 129, 130, 128, 48, 50, 49, 127, 128, 126, 50, 52, 51, 125, 126, 124, 52, 54, 53, 123, 124, 122, 54, 56, 55, 121, 122, 120, 56, 59, 57, 58, 119, 120, 118, 59, 61, 60, 117, 118, 116, 61, 63, 62, 115, 116, 114, 63, 65, 64, 113, 114, 65, 67, 66, 25, 1, 26, 25, 27, 1, 2, 28, 27, 29, 2, 3, 30, 29, 31, 3, 4, 32, 31, 33, 4, 5, 34, 33, 35, 5, 6, 35, 36, 6, 7, 37, 36, 38, 7, 8, 39, 38, 40, 8, 9, 41, 40, 42, 9, 10, 43, 42, 44, 10, 11, 45, 44, 46, 11, 12, 46, 47, 12, 13, 48, 47, 49, 13, 14, 50, 49, 51, 14, 15, 52, 51, 53, 15, 16, 54, 53, 55, 16, 17, 56, 55, 57, 17, 18, 57, 58, 18, 19, 59, 58, 60, 19, 20, 61, 60, 62, 20, 21, 63, 62, 64, 21, 22, 65, 64, 66, 22, 23, 67, 66, 68, 23, 24, 1, 69, 1, 2, 69, 71, 70, 2, 3, 71, 73, 72, 3, 4, 73, 75, 74, 4, 5, 75, 77, 76, 5, 6, 77, 79, 78, 6, 7, 79, 80, 7, 8, 80, 82, 81, 8, 9, 82, 84, 83, 9, 10, 84, 86, 85, 10, 11, 86, 88, 87, 11, 12, 88, 90, 89, 12, 13, 90, 91, 13, 14, 91, 93, 92, 14, 15, 93, 95, 94, 15, 16, 95, 97, 96, 16, 17, 97, 99, 98, 17, 18, 99, 101, 100, 18, 19, 101, 102, 19, 20, 102, 104, 103, 20, 21, 104, 106, 105, 21, 22, 106, 108, 107, 22, 23, 108, 110, 109, 23, 24, 110, 112, 111, 69, 70, 131, 71, 70, 72, 131, 133, 132, 73, 72, 74, 133, 135, 134, 75, 74, 76, 135, 137, 136, 77, 76, 78, 137, 139, 138, 79, 80, 78, 81, 139, 141, 140, 82, 81, 83, 141, 143, 142, 84, 83, 85, 143, 145, 144, 86, 85, 87, 145, 147, 146, 88, 87, 89, 147, 148, 90, 91, 89, 92, 148, 93, 92, 94, 147, 148, 146, 95, 94, 96, 145, 146, 144, 97, 96, 98, 143, 144, 142, 99, 98, 100, 141, 142, 140, 101, 102, 100, 103, 139, 140, 138, 104, 103, 105, 137, 138, 136, 106, 105, 107, 135, 136, 134, 108, 107, 109, 133, 134, 132, 110, 109, 111, 131, 132, 67, 68, 68, 24, 24, 112, 112, 111, }; check_node_cell_connectivity(mesh, node_cell_connectivity_check); } } // mpi-size 4 { if (mpi::comm().rank() == 0 && mpi::comm().size() == 4) { auto node_cell_connectivity_check = std::array{ 45, 3, 2, 45, 47, 46, 3, 5, 4, 47, 49, 48, 5, 7, 6, 49, 51, 50, 7, 9, 8, 51, 53, 52, 9, 11, 10, 53, 55, 54, 11, 14, 12, 13, 55, 57, 56, 14, 16, 15, 57, 59, 58, 16, 18, 17, 59, 61, 60, 18, 20, 19, 61, 62, 20, 22, 21, 62, 22, 25, 23, 24, 61, 62, 60, 25, 27, 26, 59, 60, 58, 27, 29, 28, 57, 58, 56, 29, 31, 30, 55, 56, 54, 31, 33, 32, 53, 54, 52, 33, 36, 34, 35, 51, 52, 50, 36, 38, 37, 49, 50, 48, 38, 40, 39, 47, 48, 46, 40, 42, 41, 45, 46, 42, 44, 43, 2, 1, 3, 2, 4, 1, 44, 5, 4, 6, 7, 6, 8, 9, 8, 10, 11, 10, 12, 12, 13, 14, 13, 15, 16, 15, 17, 18, 17, 19, 20, 19, 21, 22, 21, 23, 23, 24, 25, 24, 26, 27, 26, 28, 29, 28, 30, 31, 30, 32, 33, 32, 34, 34, 35, 36, 35, 37, 38, 37, 39, 40, 39, 41, 42, 41, 43, 44, 43, 1, 1, }; check_node_cell_connectivity(mesh, node_cell_connectivity_check); } if (mpi::comm().rank() == 1 && mpi::comm().size() == 4) { auto node_cell_connectivity_check = std::array{ 63, 64, 64, 65, 65, 66, 66, 67, 67, 68, 68, 69, 69, 70, 70, 71, 71, 72, 72, 73, 63, 63, 64, 64, 65, 65, 66, 66, 67, 67, 68, 68, 69, 69, 70, 70, 71, 71, 72, 72, 73, 63, 73, 73, }; check_node_cell_connectivity(mesh, node_cell_connectivity_check); } if (mpi::comm().rank() == 2 && mpi::comm().size() == 4) { auto node_cell_connectivity_check = std::array{ 74, 74, 75, 75, 76, 76, 77, 77, 78, 78, 79, 79, 80, 80, 81, 81, 82, 82, 83, 83, 84, 86, 84, 85, 74, 74, 75, 75, 76, 76, 77, 77, 78, 78, 79, 79, 80, 80, 81, 81, 82, 82, 83, 86, 86, 85, 83, 84, 84, 85, 85, }; check_node_cell_connectivity(mesh, node_cell_connectivity_check); } if (mpi::comm().rank() == 3 && mpi::comm().size() == 4) { auto node_cell_connectivity_check = std::array{ 126, 128, 127, 128, 130, 129, 87, 88, 131, 89, 88, 90, 131, 133, 132, 91, 90, 92, 133, 135, 134, 93, 92, 94, 135, 137, 136, 95, 94, 96, 137, 139, 138, 97, 98, 96, 99, 139, 141, 140, 100, 99, 101, 141, 143, 142, 102, 101, 103, 143, 145, 144, 104, 103, 105, 145, 147, 146, 106, 105, 107, 147, 148, 108, 109, 107, 110, 148, 111, 110, 112, 147, 148, 146, 113, 112, 114, 145, 146, 144, 115, 114, 116, 143, 144, 142, 117, 116, 118, 141, 142, 140, 119, 120, 118, 121, 139, 140, 138, 122, 121, 123, 137, 138, 136, 124, 123, 125, 135, 136, 134, 126, 125, 127, 133, 134, 132, 128, 127, 129, 131, 132, 87, 87, 89, 88, 89, 91, 90, 91, 93, 92, 93, 95, 94, 95, 97, 96, 97, 98, 98, 100, 99, 100, 102, 101, 102, 104, 103, 104, 106, 105, 106, 108, 107, 108, 109, 109, 111, 110, 111, 113, 112, 113, 115, 114, 115, 117, 116, 117, 119, 118, 119, 120, 120, 122, 121, 122, 124, 123, 124, 126, 125, 130, 130, 129, }; check_node_cell_connectivity(mesh, node_cell_connectivity_check); } } } //----------------------------------------------------------------------------- CASE("test_node2cell_with_halo") { Grid grid("O2"); StructuredMeshGenerator generator; Mesh mesh = generator.generate(grid); mesh::actions::build_parallel_fields(mesh); mesh::actions::build_periodic_boundaries(mesh); mesh::actions::build_halo(mesh, 1); mesh::actions::build_node_to_cell_connectivity(mesh); Log::info() << "Connectivity with halo :\n" << PrettyPrintNodeCellConnectivity{mesh} << std::endl; // Following command generates (prints) code between braces following // print_check( mesh ); // mpi-size 1 { if (mpi::comm().rank() == 0 && mpi::comm().size() == 1) { auto node_cell_connectivity_check = std::array{ 113, 149, 26, 151, 25, 113, 115, 114, 26, 28, 27, 115, 117, 116, 28, 30, 29, 117, 119, 118, 30, 32, 31, 119, 121, 120, 32, 34, 33, 121, 123, 122, 34, 37, 35, 36, 123, 125, 124, 37, 39, 38, 125, 127, 126, 39, 41, 40, 127, 129, 128, 41, 43, 42, 129, 130, 43, 45, 44, 130, 45, 48, 46, 47, 129, 130, 128, 48, 50, 49, 127, 128, 126, 50, 52, 51, 125, 126, 124, 52, 54, 53, 123, 124, 122, 54, 56, 55, 121, 122, 120, 56, 59, 57, 58, 119, 120, 118, 59, 61, 60, 117, 118, 116, 61, 63, 62, 115, 116, 114, 63, 65, 64, 113, 114, 65, 67, 66, 151, 25, 153, 1, 26, 25, 27, 1, 2, 28, 27, 29, 2, 3, 30, 29, 31, 3, 4, 32, 31, 33, 4, 5, 34, 33, 35, 5, 6, 35, 36, 6, 7, 37, 36, 38, 7, 8, 39, 38, 40, 8, 9, 41, 40, 42, 9, 10, 43, 42, 44, 10, 11, 45, 44, 46, 11, 12, 46, 47, 12, 13, 48, 47, 49, 13, 14, 50, 49, 51, 14, 15, 52, 51, 53, 15, 16, 54, 53, 55, 16, 17, 56, 55, 57, 17, 18, 57, 58, 18, 19, 59, 58, 60, 19, 20, 61, 60, 62, 20, 21, 63, 62, 64, 21, 22, 65, 64, 66, 22, 23, 67, 66, 68, 23, 24, 153, 1, 155, 69, 1, 2, 69, 71, 70, 2, 3, 71, 73, 72, 3, 4, 73, 75, 74, 4, 5, 75, 77, 76, 5, 6, 77, 79, 78, 6, 7, 79, 80, 7, 8, 80, 82, 81, 8, 9, 82, 84, 83, 9, 10, 84, 86, 85, 10, 11, 86, 88, 87, 11, 12, 88, 90, 89, 12, 13, 90, 91, 13, 14, 91, 93, 92, 14, 15, 93, 95, 94, 15, 16, 95, 97, 96, 16, 17, 97, 99, 98, 17, 18, 99, 101, 100, 18, 19, 101, 102, 19, 20, 102, 104, 103, 20, 21, 104, 106, 105, 21, 22, 106, 108, 107, 22, 23, 108, 110, 109, 23, 24, 110, 112, 111, 155, 69, 157, 70, 131, 71, 70, 72, 131, 133, 132, 73, 72, 74, 133, 135, 134, 75, 74, 76, 135, 137, 136, 77, 76, 78, 137, 139, 138, 79, 80, 78, 81, 139, 141, 140, 82, 81, 83, 141, 143, 142, 84, 83, 85, 143, 145, 144, 86, 85, 87, 145, 147, 146, 88, 87, 89, 147, 148, 90, 91, 89, 92, 148, 93, 92, 94, 147, 148, 146, 95, 94, 96, 145, 146, 144, 97, 96, 98, 143, 144, 142, 99, 98, 100, 141, 142, 140, 101, 102, 100, 103, 139, 140, 138, 104, 103, 105, 137, 138, 136, 106, 105, 107, 135, 136, 134, 108, 107, 109, 133, 134, 132, 110, 109, 111, 131, 132, 67, 150, 68, 152, 68, 152, 24, 154, 24, 154, 112, 156, 112, 156, 111, 158, 149, 149, 151, 153, 153, 155, 157, 157, 150, 150, 152, 154, 154, 156, 158, 158, }; check_node_cell_connectivity(mesh, node_cell_connectivity_check); } } // mpi-size 4 { if (mpi::comm().rank() == 0 && mpi::comm().size() == 4) { auto node_cell_connectivity_check = std::array{ 45, 149, 3, 151, 2, 45, 47, 46, 3, 5, 4, 47, 49, 48, 5, 7, 6, 49, 51, 50, 7, 9, 8, 51, 53, 52, 9, 11, 10, 53, 55, 54, 11, 14, 12, 13, 55, 57, 56, 14, 16, 15, 57, 59, 58, 16, 18, 17, 59, 61, 60, 18, 20, 19, 61, 62, 20, 22, 21, 62, 22, 25, 23, 24, 61, 62, 60, 25, 27, 26, 59, 60, 58, 27, 29, 28, 57, 58, 56, 29, 31, 30, 55, 56, 54, 31, 33, 32, 53, 54, 52, 33, 36, 34, 35, 51, 52, 50, 36, 38, 37, 49, 50, 48, 38, 40, 39, 47, 48, 46, 40, 42, 41, 45, 46, 42, 44, 43, 151, 2, 153, 1, 3, 2, 4, 1, 63, 44, 150, 86, 152, 5, 4, 6, 63, 64, 7, 6, 8, 64, 65, 9, 8, 10, 65, 66, 11, 10, 12, 66, 67, 12, 13, 67, 68, 14, 13, 15, 68, 69, 16, 15, 17, 69, 70, 18, 17, 19, 70, 71, 20, 19, 21, 71, 72, 22, 21, 23, 72, 73, 23, 24, 73, 74, 25, 24, 26, 74, 75, 27, 26, 28, 75, 76, 29, 28, 30, 76, 77, 31, 30, 32, 77, 78, 33, 32, 34, 78, 79, 34, 35, 79, 80, 36, 35, 37, 80, 81, 38, 37, 39, 81, 82, 40, 39, 41, 82, 83, 42, 41, 43, 83, 84, 44, 43, 86, 84, 85, 153, 1, 155, 87, 1, 63, 87, 89, 88, 63, 64, 89, 64, 65, 65, 66, 66, 67, 67, 68, 68, 69, 69, 70, 70, 71, 71, 72, 72, 73, 73, 74, 86, 152, 85, 74, 75, 75, 76, 76, 77, 77, 78, 78, 79, 79, 80, 80, 81, 81, 82, 82, 83, 83, 84, 84, 85, 85, 155, 87, 88, 89, 88, 149, 149, 151, 153, 153, 155, 150, 150, 152, }; check_node_cell_connectivity(mesh, node_cell_connectivity_check); } if (mpi::comm().rank() == 1 && mpi::comm().size() == 4) { auto node_cell_connectivity_check = std::array{ 5, 4, 6, 63, 64, 7, 6, 8, 64, 65, 9, 8, 10, 65, 66, 11, 10, 12, 66, 67, 12, 13, 67, 68, 14, 13, 15, 68, 69, 16, 15, 17, 69, 70, 18, 17, 19, 70, 71, 20, 19, 21, 71, 72, 22, 21, 23, 72, 73, 1, 87, 1, 63, 87, 89, 88, 63, 64, 89, 91, 90, 64, 65, 91, 93, 92, 65, 66, 93, 95, 94, 66, 67, 95, 97, 96, 67, 68, 97, 98, 68, 69, 98, 100, 99, 69, 70, 100, 102, 101, 70, 71, 102, 104, 103, 71, 72, 104, 106, 105, 72, 73, 106, 108, 107, 3, 2, 4, 1, 63, 23, 24, 73, 74, 73, 74, 108, 109, 3, 2, 3, 5, 4, 5, 7, 6, 7, 9, 8, 9, 11, 10, 11, 14, 12, 13, 14, 16, 15, 16, 18, 17, 18, 20, 19, 20, 22, 21, 22, 23, 24, 2, 1, 24, 74, 74, 109, 87, 88, 89, 88, 90, 91, 90, 92, 93, 92, 94, 95, 94, 96, 97, 98, 96, 99, 100, 99, 101, 102, 101, 103, 104, 103, 105, 106, 105, 107, 108, 109, 107, }; check_node_cell_connectivity(mesh, node_cell_connectivity_check); } if (mpi::comm().rank() == 2 && mpi::comm().size() == 4) { auto node_cell_connectivity_check = std::array{ 23, 24, 73, 74, 25, 24, 26, 74, 75, 27, 26, 28, 75, 76, 29, 28, 30, 76, 77, 31, 30, 32, 77, 78, 33, 32, 34, 78, 79, 34, 35, 79, 80, 36, 35, 37, 80, 81, 38, 37, 39, 81, 82, 40, 39, 41, 82, 83, 42, 41, 43, 83, 84, 44, 43, 86, 84, 85, 73, 74, 108, 109, 74, 75, 109, 111, 110, 75, 76, 111, 113, 112, 76, 77, 113, 115, 114, 77, 78, 115, 117, 116, 78, 79, 117, 119, 118, 79, 80, 119, 120, 80, 81, 120, 122, 121, 81, 82, 122, 124, 123, 82, 83, 124, 126, 125, 44, 150, 86, 152, 86, 152, 85, 154, 83, 84, 126, 128, 127, 84, 85, 128, 130, 129, 85, 154, 130, 156, 25, 23, 24, 25, 27, 26, 27, 29, 28, 29, 31, 30, 31, 33, 32, 33, 36, 34, 35, 36, 38, 37, 38, 40, 39, 40, 42, 41, 42, 44, 43, 23, 73, 73, 108, 108, 109, 110, 111, 110, 112, 113, 112, 114, 115, 114, 116, 117, 116, 118, 119, 120, 118, 121, 122, 121, 123, 124, 123, 125, 126, 125, 127, 128, 127, 129, 130, 156, 129, 150, 150, 152, 154, 154, 156, }; check_node_cell_connectivity(mesh, node_cell_connectivity_check); } if (mpi::comm().rank() == 3 && mpi::comm().size() == 4) { auto node_cell_connectivity_check = std::array{ 83, 84, 126, 128, 127, 84, 85, 128, 130, 129, 155, 87, 157, 88, 131, 89, 88, 90, 131, 133, 132, 91, 90, 92, 133, 135, 134, 93, 92, 94, 135, 137, 136, 95, 94, 96, 137, 139, 138, 97, 98, 96, 99, 139, 141, 140, 100, 99, 101, 141, 143, 142, 102, 101, 103, 143, 145, 144, 104, 103, 105, 145, 147, 146, 106, 105, 107, 147, 148, 108, 109, 107, 110, 148, 111, 110, 112, 147, 148, 146, 113, 112, 114, 145, 146, 144, 115, 114, 116, 143, 144, 142, 117, 116, 118, 141, 142, 140, 119, 120, 118, 121, 139, 140, 138, 122, 121, 123, 137, 138, 136, 124, 123, 125, 135, 136, 134, 126, 125, 127, 133, 134, 132, 128, 127, 129, 131, 132, 153, 1, 155, 87, 1, 63, 87, 89, 88, 63, 64, 89, 91, 90, 64, 65, 91, 93, 92, 65, 66, 93, 95, 94, 66, 67, 95, 97, 96, 67, 68, 97, 98, 68, 69, 98, 100, 99, 69, 70, 100, 102, 101, 70, 71, 102, 104, 103, 71, 72, 104, 106, 105, 72, 73, 106, 108, 107, 73, 74, 108, 109, 74, 75, 109, 111, 110, 75, 76, 111, 113, 112, 76, 77, 113, 115, 114, 77, 78, 115, 117, 116, 78, 79, 117, 119, 118, 79, 80, 119, 120, 80, 81, 120, 122, 121, 81, 82, 122, 124, 123, 82, 83, 124, 126, 125, 85, 154, 130, 156, 130, 156, 129, 158, 153, 1, 1, 63, 63, 64, 64, 65, 65, 66, 66, 67, 67, 68, 68, 69, 69, 70, 70, 71, 71, 72, 72, 73, 73, 74, 74, 75, 75, 76, 76, 77, 77, 78, 78, 79, 79, 80, 80, 81, 81, 82, 82, 83, 83, 84, 84, 85, 85, 154, 153, 153, 155, 157, 157, 154, 154, 156, 158, 158, }; check_node_cell_connectivity(mesh, node_cell_connectivity_check); } } } CASE("test_node2cell_AtlasGrids") { std::vector gridnames{"N16", "O2", "F2", "L2", "H2"}; for (auto& gridname : gridnames) { SECTION(gridname) { auto grid = Grid(gridname); Mesh mesh = MeshGenerator(grid.meshgenerator()).generate(grid); mesh::actions::build_parallel_fields(mesh); mesh::actions::build_periodic_boundaries(mesh); mesh::actions::build_halo(mesh, 1); mesh::actions::build_node_to_cell_connectivity(mesh); for (idx_t jnode = 0; jnode < mesh.nodes().size(); ++jnode) { EXPECT(mesh.nodes().cell_connectivity().cols(jnode) > 0); } for (idx_t jcell = 0; jcell < mesh.cells().size(); ++jcell) { EXPECT(mesh.cells().node_connectivity().cols(jcell) > 0); } } } } //----------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/mesh/test_meshgen_splitcomm.cc0000664000175000017500000001677615160212070023054 0ustar alastairalastair/* * (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/grid.h" #include "atlas/mesh/Mesh.h" #include "atlas/meshgenerator.h" #include "atlas/output/Gmsh.h" #include "atlas/grid/Partitioner.h" #include "tests/AtlasTestEnvironment.h" namespace atlas { namespace test { //----------------------------------------------------------------------------- namespace option { struct mpi_comm : public util::Config { mpi_comm(std::string_view name) { set("mpi_comm",std::string(name)); } }; } int color() { static int c = mpi::comm("world").rank()%2; return c; } Grid grid() { static Grid g (color() == 0 ? "O32" : "N32" ); return g; } Grid grid_A() { return grid(); } Grid grid_B() { static Grid g (color() == 0 ? "F64" : "O64" ); return g; } Grid grid_CS() { static Grid g (color() == 0 ? "CS-LFR-C-8" : "CS-LFR-C-16" ); return g; } Grid grid_Regular() { static Grid g (color() == 0 ? "S8" : "L16" ); return g; } Grid grid_healpix() { static Grid g (color() == 0 ? "H8" : "H16" ); return g; } struct Fixture { Fixture() { mpi::comm().split(color(),"split"); } ~Fixture() { if (eckit::mpi::hasComm("split")) { eckit::mpi::deleteComm("split"); } } }; CASE("Partitioners") { Fixture fixture; SECTION("default") { auto partitioner = grid::Partitioner("equal_regions"); EXPECT_EQ(partitioner.mpi_comm(),mpi::comm().name()); auto distribution = partitioner.partition(Grid("O8")); EXPECT_EQ(distribution.nb_partitions(), mpi::comm().size()); } SECTION("split") { auto partitioner = grid::Partitioner("equal_regions", option::mpi_comm("split")); EXPECT_EQ(partitioner.mpi_comm(),"split"); auto distribution = partitioner.partition(Grid("O8")); EXPECT_EQ(distribution.nb_partitions(), mpi::comm("split").size()); } } CASE("StructuredMeshGenerator") { Fixture fixture; MeshGenerator meshgen{"structured", option::mpi_comm("split")}; Mesh mesh = meshgen.generate(grid()); EXPECT_EQUAL(mesh.nb_parts(),mpi::comm("split").size()); EXPECT_EQUAL(mesh.part(),mpi::comm("split").rank()); EXPECT_EQUAL(mesh.mpi_comm(),"split"); EXPECT_EQUAL(mpi::comm().name(),"world"); output::Gmsh gmsh(grid().name()+"_1.msh"); gmsh.write(mesh); // partitioning graph and polygon output EXPECT_NO_THROW(mesh.partitionGraph()); EXPECT_NO_THROW(mesh.polygons()); mesh.polygon().outputPythonScript(grid().name()+"_polygons_1.py"); } CASE("RegularMeshGenerator") { Fixture fixture; MeshGenerator meshgen{"regular", option::mpi_comm("split")}; Mesh mesh = meshgen.generate(grid_Regular()); EXPECT_EQUAL(mesh.nb_parts(),mpi::comm("split").size()); EXPECT_EQUAL(mesh.part(),mpi::comm("split").rank()); EXPECT_EQUAL(mesh.mpi_comm(),"split"); EXPECT_EQUAL(mpi::comm().name(),"world"); output::Gmsh gmsh(grid().name()+"_regular.msh"); gmsh.write(mesh); // partitioning graph and polygon output EXPECT_NO_THROW(mesh.partitionGraph()); EXPECT_NO_THROW(mesh.polygons()); mesh.polygon().outputPythonScript(grid().name()+"_regular_polygons_1.py"); } CASE("HealpixMeshGenerator") { Fixture fixture; MeshGenerator meshgen{"healpix", option::mpi_comm("split")}; Mesh mesh = meshgen.generate(grid_healpix()); EXPECT_EQUAL(mesh.nb_parts(),mpi::comm("split").size()); EXPECT_EQUAL(mesh.part(),mpi::comm("split").rank()); EXPECT_EQUAL(mesh.mpi_comm(),"split"); EXPECT_EQUAL(mpi::comm().name(),"world"); output::Gmsh gmsh(grid().name()+"_regular.msh"); gmsh.write(mesh); // partitioning graph and polygon output EXPECT_NO_THROW(mesh.partitionGraph()); EXPECT_NO_THROW(mesh.polygons()); mesh.polygon().outputPythonScript(grid().name()+"_regular_polygons_1.py"); } CASE("DelaunayMeshGenerator") { if( ATLAS_HAVE_TESSELATION ) { Fixture fixture; MeshGenerator meshgen{"delaunay", option::mpi_comm("split")}; Mesh mesh = meshgen.generate(grid_CS()); EXPECT_EQUAL(mesh.nb_parts(),mpi::comm("split").size()); EXPECT_EQUAL(mesh.part(),mpi::comm("split").rank()); EXPECT_EQUAL(mesh.mpi_comm(),"split"); EXPECT_EQUAL(mpi::comm().name(),"world"); output::Gmsh gmsh(grid_CS().name()+"_delaunay.msh",util::Config("coordinates","xyz")); gmsh.write(mesh); } else { Log::warning() << "Not testing DelaunayMeshGenerator as TESSELATION feature is OFF" << std::endl; } } CASE("CubedSphereDualMeshGenerator") { Fixture fixture; MeshGenerator meshgen{"cubedsphere_dual", option::mpi_comm("split")}; Mesh mesh = meshgen.generate(grid_CS()); EXPECT_EQUAL(mesh.nb_parts(),mpi::comm("split").size()); EXPECT_EQUAL(mesh.part(),mpi::comm("split").rank()); EXPECT_EQUAL(mesh.mpi_comm(),"split"); EXPECT_EQUAL(mpi::comm().name(),"world"); output::Gmsh gmsh(grid_CS().name()+"_1.msh"); gmsh.write(mesh); } CASE("CubedSphereMeshGenerator") { Fixture fixture; MeshGenerator meshgen{"cubedsphere", option::mpi_comm("split")}; Mesh mesh = meshgen.generate(grid_CS()); EXPECT_EQUAL(mesh.nb_parts(),mpi::comm("split").size()); EXPECT_EQUAL(mesh.part(),mpi::comm("split").rank()); EXPECT_EQUAL(mesh.mpi_comm(),"split"); EXPECT_EQUAL(mpi::comm().name(),"world"); output::Gmsh gmsh(grid_CS().name()+"_2.msh"); gmsh.write(mesh); } CASE("Mesh constructor") { Fixture fixture; auto mesh = Mesh(grid(), option::mpi_comm("split")); EXPECT_EQUAL(mesh.nb_parts(),mpi::comm("split").size()); EXPECT_EQUAL(mesh.part(),mpi::comm("split").rank()); EXPECT_EQUAL(mesh.mpi_comm(),"split"); EXPECT_EQUAL(mpi::comm().name(),"world"); output::Gmsh gmsh(grid().name()+"_2.msh"); gmsh.write(mesh); // partitioning graph and polygon output EXPECT_NO_THROW(mesh.partitionGraph()); EXPECT_NO_THROW(mesh.polygons()); mesh.polygon().outputPythonScript(grid().name()+"_polygons_2.py"); } CASE("Mesh constructor with partitioner") { Fixture fixture; auto partitioner = grid::Partitioner("equal_regions", option::mpi_comm("split")); auto mesh = Mesh(grid(), partitioner); EXPECT_EQUAL(mesh.nb_parts(),mpi::comm("split").size()); EXPECT_EQUAL(mesh.part(),mpi::comm("split").rank()); EXPECT_EQUAL(mesh.mpi_comm(),"split"); EXPECT_EQUAL(mpi::comm().name(),"world"); output::Gmsh gmsh(grid().name()+"_2.msh"); gmsh.write(mesh); // partitioning graph and polygon output EXPECT_NO_THROW(mesh.partitionGraph()); EXPECT_NO_THROW(mesh.polygons()); mesh.polygon().outputPythonScript(grid().name()+"_polygons_2.py"); } CASE("MatchingPartitioner") { Fixture fixture; auto mesh_A = Mesh(grid_A(), option::mpi_comm("split")); auto mesh_B = Mesh(grid_B(), grid::MatchingPartitioner(mesh_A), option::mpi_comm("split")); output::Gmsh gmsh_B(grid_B().name()+"_3.msh"); gmsh_B.write(mesh_B); // partitioning graph and polygon output EXPECT_NO_THROW(mesh_B.partitionGraph()); EXPECT_NO_THROW(mesh_B.polygons()); mesh_B.polygon().outputPythonScript(grid_B().name()+"_polygons_3.py"); } } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/mesh/helper_mesh_builder.h0000664000175000017500000000744215160212070022131 0ustar alastairalastair/* * (C) Copyright 2023 UCAR. * * 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. */ #include #include #include #include "atlas/array/MakeView.h" #include "atlas/mesh/Elements.h" #include "atlas/mesh/HybridElements.h" #include "atlas/mesh/Mesh.h" #include "atlas/mesh/Nodes.h" namespace atlas { namespace test { namespace helper { void check_mesh_nodes_and_cells(const Mesh& mesh, const std::vector& lons, const std::vector& lats, const std::vector& ghosts, const std::vector& global_indices, const std::vector& remote_indices, const idx_t remote_index_base, const std::vector& partitions, const std::vector>& tri_boundary_nodes, const std::vector& tri_global_indices, const std::vector>& quad_boundary_nodes, const std::vector& quad_global_indices) { const auto mesh_xy = array::make_view(mesh.nodes().xy()); const auto mesh_lonlat = array::make_view(mesh.nodes().lonlat()); const auto mesh_ghost = array::make_view(mesh.nodes().ghost()); const auto mesh_gidx = array::make_view(mesh.nodes().global_index()); const auto mesh_ridx = array::make_indexview(mesh.nodes().remote_index()); const auto mesh_partition = array::make_view(mesh.nodes().partition()); const auto mesh_halo = array::make_view(mesh.nodes().halo()); EXPECT(mesh.nodes().size() == lons.size()); for (size_t i = 0; i < mesh.nodes().size(); ++i) { EXPECT(mesh_xy(i, 0) == lons[i]); EXPECT(mesh_xy(i, 1) == lats[i]); EXPECT(mesh_lonlat(i, 0) == lons[i]); EXPECT(mesh_lonlat(i, 1) == lats[i]); EXPECT(mesh_ghost(i) == ghosts[i]); EXPECT(mesh_gidx(i) == global_indices[i]); EXPECT(mesh_ridx(i) == remote_indices[i] - remote_index_base); EXPECT(mesh_partition(i) == partitions[i]); EXPECT(mesh_halo(i) == 0.); // Don't expect (or test) any node-to-cell connectivities } EXPECT(mesh.cells().nb_types() == 2); EXPECT(mesh.cells().size() == tri_boundary_nodes.size() + quad_boundary_nodes.size()); const auto position_of = [&global_indices](const gidx_t idx) { const auto& it = std::find(global_indices.begin(), global_indices.end(), idx); ATLAS_ASSERT(it != global_indices.end()); return std::distance(global_indices.begin(), it); }; // Check triangle cell-to-node connectivities EXPECT(mesh.cells().elements(0).size() == tri_boundary_nodes.size()); EXPECT(mesh.cells().elements(0).nb_nodes() == 3); for (size_t tri = 0; tri < mesh.cells().elements(0).size(); ++tri) { for (size_t node = 0; node < mesh.cells().elements(0).nb_nodes(); ++node) { EXPECT(mesh.cells().elements(0).node_connectivity()(tri, node) == position_of(tri_boundary_nodes[tri][node])); } } // Check quad cell-to-node connectivities EXPECT(mesh.cells().elements(1).size() == quad_boundary_nodes.size()); EXPECT(mesh.cells().elements(1).nb_nodes() == 4); for (size_t quad = 0; quad < mesh.cells().elements(1).size(); ++quad) { for (size_t node = 0; node < mesh.cells().elements(1).nb_nodes(); ++node) { EXPECT(mesh.cells().elements(1).node_connectivity()(quad, node) == position_of(quad_boundary_nodes[quad][node])); } } } } // namespace helper } // namespace test } // namespace atlas atlas-0.46.0/src/tests/mesh/test_ll.cc0000664000175000017500000000204015160212070017721 0ustar alastairalastair/* * (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/grid/Grid.h" #include "atlas/mesh/Mesh.h" #include "atlas/meshgenerator.h" #include "atlas/output/Gmsh.h" #include "atlas/parallel/mpi/mpi.h" #include "tests/AtlasTestEnvironment.h" namespace atlas { namespace test { //----------------------------------------------------------------------------- CASE("test_ll_meshgen_one_part") { Grid g("L5"); Mesh m = StructuredMeshGenerator().generate(g); output::Gmsh("L5.msh").write(m); } //----------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/mesh/test_mesh_triangular_mesh_builder.cc0000664000175000017500000000460215160212070025226 0ustar alastairalastair/* * (C) Copyright 2023 UCAR. * * 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. */ #include #include #include #include "atlas/array/MakeView.h" #include "atlas/grid.h" #include "atlas/mesh/Elements.h" #include "atlas/mesh/HybridElements.h" #include "atlas/mesh/Mesh.h" #include "atlas/mesh/MeshBuilder.h" #include "atlas/mesh/Nodes.h" #include "atlas/util/Config.h" #include "atlas/output/Gmsh.h" #include "tests/AtlasTestEnvironment.h" using namespace atlas::mesh; //#include "atlas/output/Gmsh.h" //using namespace atlas::output; namespace atlas { namespace test { //----------------------------------------------------------------------------- CASE("test_tiny_mesh") { // small regional grid whose cell-centers are connected as (global nodes and cells): // // 1 ---- 5 ----- 6 // | 3 / 4 | 1 /2 | // 2 ----- 3 ---- 4 // gidx_t global_index_base = 1; size_t nb_nodes = 6; std::vector lon{{0.0, 0.0, 10.0, 15.0, 5.0, 15.0}}; std::vector lat{{5.0, 0.0, 0.0, 0.0, 5.0, 5.0}}; std::vector x(nb_nodes); std::vector y(nb_nodes); for (size_t j=0; j global_index(6); std::iota(global_index.begin(), global_index.end(), global_index_base); // triangles size_t nb_triags = 4; std::vector> triag_nodes_global = {{{3, 6, 5}}, {{3, 4, 6}}, {{2, 5, 1}}, {{2, 3, 5}}}; std::vector triag_global_index = {1, 2, 3, 4}; const TriangularMeshBuilder mesh_builder{}; constexpr size_t stride_1 = 1; const Mesh mesh = mesh_builder(nb_nodes, global_index.data(), x.data(), y.data(), stride_1, stride_1, lon.data(), lat.data(), stride_1, stride_1, nb_triags, triag_global_index.data(), triag_nodes_global.data()->data(), global_index_base); output::Gmsh gmsh("out.msh", util::Config("coordinates", "xy")); gmsh.write(mesh); } //----------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/mesh/test_mesh_reorder_unstructured.geo0000664000175000017500000000037315160212070025013 0ustar alastairalastaircl__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}; atlas-0.46.0/src/tests/mesh/test_cubedsphere_meshgen.cc0000664000175000017500000005004115160212070023315 0ustar alastairalastair/* * (C) Crown Copyright 2021 Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ #include "atlas/array/MakeView.h" #include "atlas/field/FieldSet.h" #include "atlas/functionspace/CellColumns.h" #include "atlas/functionspace/NodeColumns.h" #include "atlas/functionspace/StructuredColumns.h" #include "atlas/grid.h" #include "atlas/grid/CubedSphereGrid.h" #include "atlas/grid/Partitioner.h" #include "atlas/grid/Tiles.h" #include "atlas/grid/detail/partitioner/CubedSpherePartitioner.h" #include "atlas/interpolation.h" #include "atlas/mesh.h" #include "atlas/meshgenerator.h" #include "atlas/meshgenerator/detail/cubedsphere/CubedSphereUtility.h" #include "atlas/option.h" #include "atlas/output/Gmsh.h" #include "atlas/util/CoordinateEnums.h" #include "atlas/util/function/VortexRollup.h" #include "tests/AtlasTestEnvironment.h" #include "eckit/mpi/Operation.h" namespace atlas { namespace test { CASE("cubedsphere_mesh_jacobian_test") { using namespace meshgenerator::detail::cubedsphere; // Set grid an N2 grid with a halo size of 1. const auto grid = atlas::Grid("CS-LFR-C-2"); // Set Jacobian const auto jacobian = NeighbourJacobian(CubedSphereGrid(grid)); // Set vectors of known good outputs. const auto xyLocalKgoVec = std::vector{ {0, -90}, {45, -90}, {90, -90}, {-45, -45}, {135, -45}, {-45, 0}, {135, 0}, {-45, 45}, {135, 45}, {0, 90}, {45, 90}, {90, 90}, {90, -90}, {135, -90}, {180, -90}, {45, -45}, {225, -45}, {45, 0}, {225, 0}, {45, 45}, {225, 45}, {90, 90}, {135, 90}, {180, 90}, {315, -45}, {315, 0}, {315, 45}, {270, -90}, {270, 90}, {225, -90}, {225, 90}, {180, -90}, {180, 90}, {135, -45}, {135, 0}, {135, 45}, {405, -45}, {405, 0}, {405, 45}, {360, -90}, {360, 90}, {315, -90}, {315, 90}, {270, -90}, {270, 90}, {225, -45}, {225, 0}, {225, 45}, {0, 0}, {45, 0}, {90, 0}, {-45, 45}, {135, 45}, {-45, 90}, {135, 90}, {-45, 135}, {135, 135}, {0, 180}, {45, 180}, {90, 180}, {-45, -45}, {-45, -90}, {-45, -135}, {0, 0}, {0, -180}, {45, 0}, {45, -180}, {90, 0}, {90, -180}, {135, -45}, {135, -90}, {135, -135}}; const auto xyGlobalKgoVec = std::vector{ {315, -45}, {45, -90}, {135, -45}, {315, -45}, {135, -45}, {315, 0}, {135, 0}, {0, 90}, {90, 90}, {0, 90}, {45, 90}, {90, 90}, {45, -45}, {45, -90}, {225, -45}, {45, -45}, {225, -45}, {45, 0}, {225, 0}, {45, 45}, {45, 135}, {45, 45}, {45, 90}, {45, 135}, {315, -45}, {315, 0}, {0, 90}, {315, -45}, {0, 90}, {45, -90}, {45, 90}, {135, -45}, {90, 90}, {135, -45}, {135, 0}, {90, 90}, {45, -45}, {45, 0}, {45, 45}, {45, -45}, {45, 45}, {45, -90}, {45, 90}, {225, -45}, {45, 135}, {225, -45}, {225, 0}, {45, 135}, {0, 0}, {45, 0}, {90, 0}, {0, 0}, {90, 0}, {315, 0}, {135, 0}, {270, 0}, {180, 0}, {270, 0}, {225, 0}, {180, 0}, {0, 0}, {315, 0}, {270, 0}, {0, 0}, {270, 0}, {45, 0}, {225, 0}, {90, 0}, {180, 0}, {90, 0}, {135, 0}, {180, 0}}; const auto ijGlobalKgoVec = std::vector{ {0, 1}, {1, 1}, {1, 0}, {0, 1}, {1, 0}, {1, 1}, {1, 1}, {0, 1}, {2, 1}, {0, 1}, {1, 1}, {2, 1}, {1, 0}, {1, 1}, {0, 1}, {1, 0}, {0, 1}, {1, 1}, {1, 1}, {1, 0}, {1, 2}, {1, 0}, {1, 1}, {1, 2}, {0, 1}, {1, 1}, {0, 1}, {0, 1}, {0, 1}, {1, 1}, {1, 1}, {1, 0}, {2, 1}, {1, 0}, {1, 1}, {2, 1}, {1, 0}, {1, 1}, {1, 0}, {1, 0}, {1, 0}, {1, 1}, {1, 1}, {0, 1}, {1, 2}, {0, 1}, {1, 1}, {1, 2}, {0, 1}, {1, 1}, {0, 1}, {0, 1}, {0, 1}, {1, 1}, {1, 1}, {1, 2}, {1, 2}, {1, 2}, {1, 1}, {1, 2}, {0, 1}, {1, 1}, {1, 2}, {0, 1}, {1, 2}, {1, 1}, {1, 1}, {0, 1}, {1, 2}, {0, 1}, {1, 1}, {1, 2}}; const auto tKgoVec = std::vector{3, 5, 1, 3, 1, 3, 1, 4, 4, 4, 4, 4, 0, 5, 2, 0, 2, 0, 2, 4, 4, 4, 4, 4, 3, 3, 4, 3, 4, 5, 4, 1, 4, 1, 1, 4, 0, 0, 4, 0, 4, 5, 4, 2, 4, 2, 2, 4, 0, 0, 1, 0, 1, 3, 1, 3, 2, 3, 2, 2, 0, 3, 3, 0, 3, 0, 2, 1, 2, 1, 1, 2}; // Set kgo iterators. auto xyLocalKgoIt = xyLocalKgoVec.cbegin(); auto xyGlobalKgoIt = xyGlobalKgoVec.cbegin(); auto ijGlobalKgoIt = ijGlobalKgoVec.cbegin(); auto tKgoIt = tKgoVec.cbegin(); // Play around with some grids. for (idx_t t = 0; t < 6; ++t) { for (idx_t j = -1; j < 4; ++j) { for (idx_t i = -1; i < 4; ++i) { // Set ij object. const auto ij = PointIJ(i, j); // Only look at halo values. if (!jacobian.ijCross(ij)) { continue; } if (jacobian.ijInterior(ij)) { continue; } // Get known good outputs. const auto xyLocalKgo = *xyLocalKgoIt++; const auto xyGlobalKgo = *xyGlobalKgoIt++; const auto ijGlobalKgo = *ijGlobalKgoIt++; const auto tKgo = *tKgoIt++; // Test known good output. const auto xyLocal = jacobian.xy(ij, t); const auto txyGlobal = jacobian.xyLocalToGlobal(xyLocal, t); const auto tijGlobal = jacobian.ijLocalToGlobal(ij, t); EXPECT(xyLocal == xyLocalKgo); EXPECT(txyGlobal.xy() == xyGlobalKgo); EXPECT(txyGlobal.t() == tKgo); EXPECT(tijGlobal.ij() == ijGlobalKgo); EXPECT(tijGlobal.t() == tKgo); // Check xy and ij transforms are consistent. EXPECT(jacobian.ij(txyGlobal.xy(), txyGlobal.t()) == jacobian.ijLocalToGlobal(ij, t).ij()); EXPECT(jacobian.xy(tijGlobal.ij(), tijGlobal.t()) == jacobian.xyLocalToGlobal(xyLocal, t).xy()); } } } } void testHaloExchange(const std::string& gridStr, const std::string& partitionerStr, idx_t halo, bool output = true) { // Set grid. const auto grid = Grid(gridStr); // Set mesh config. const auto meshConfig = util::Config("partitioner", partitionerStr) | util::Config("halo", halo); // Set mesh generator. const auto meshGen = MeshGenerator("cubedsphere", meshConfig); // Set mesh const auto mesh = meshGen.generate(grid); // Set function space. const auto nodeColumns = functionspace::NodeColumns(mesh); const auto cellColumns = functionspace::CellColumns(mesh); // --------------------------------------------------------------------------- // Test node columns halo exchange. // --------------------------------------------------------------------------- Log::info() << "Starting node columns test." << std::endl; // make a test field. auto testField1 = nodeColumns.createField(util::Config("name", "test field (node columns)")); // Make some field views. auto testView1 = array::make_view(testField1); auto lonLatView = array::make_view(nodeColumns.lonlat()); auto ghostView = array::make_view(nodeColumns.ghost()); // Set non-ghost values. idx_t testFuncCallCount = 0; for (idx_t i = 0; i < nodeColumns.size(); ++i) { // Stop once we've reached ghost points. if (ghostView(i)) { break; } testView1(i) = 1.0 + util::function::vortex_rollup(lonLatView(i, LON), lonLatView(i, LAT), 1.0); ++testFuncCallCount; } // Make sure that some of the field values were ghosts. if (halo > 0) { EXPECT(testFuncCallCount < nodeColumns.size()); } nodeColumns.haloExchange(testField1); // Check all values after halo exchange. double maxError = 0; for (idx_t i = 0; i < nodeColumns.size(); ++i) { const double testVal = 1.0 + util::function::vortex_rollup(lonLatView(i, LON), lonLatView(i, LAT), 1.0); maxError = std::max(maxError, std::abs(testView1(i) - testVal)); } mpi::comm().allReduceInPlace(maxError, eckit::mpi::Operation::Code::MAX); Log::info() << "Test field max error (NodeColumns) : " << maxError << std::scientific << std::endl; EXPECT(maxError < 1e-12); Log::info() << "Passed node columns test." << std::endl; // --------------------------------------------------------------------------- // Test cell columns halo exchange. // --------------------------------------------------------------------------- Log::info() << "Starting cell columns test." << std::endl; // make a test field. auto testField2 = cellColumns.createField(util::Config("name", "test field (cell columns)")); // Make some field views. auto testView2 = array::make_view(testField2); auto haloView = array::make_view(cellColumns.mesh().cells().halo()); lonLatView = array::make_view(cellColumns.mesh().cells().field("lonlat")); // Set non-halo values. testFuncCallCount = 0; for (idx_t i = 0; i < cellColumns.size(); ++i) { if (haloView(i)) { break; } testView2(i) = 1.0 + util::function::vortex_rollup(lonLatView(i, LON), lonLatView(i, LAT), 1.0); ++testFuncCallCount; } // Make sure that some of the field values were ghosts. if (halo > 0) { EXPECT(testFuncCallCount < cellColumns.size()); } cellColumns.haloExchange(testField2); // Check all values after halo exchange. maxError = 0; for (idx_t i = 0; i < cellColumns.size(); ++i) { // Test field and test function should be the same. const double testVal = 1.0 + util::function::vortex_rollup(lonLatView(i, LON), lonLatView(i, LAT), 1.0); maxError = std::max(maxError, std::abs(testView2(i) - testVal)); } mpi::comm().allReduceInPlace(maxError, eckit::mpi::Operation::Code::MAX); Log::info() << "Test field max error (CellColumns) : " << maxError << std::scientific << std::endl; EXPECT(maxError < 1e-12); Log::info() << "Passed cell columns test." << std::endl; if (output) { // Make a field set of useful diagnostic quantities. auto nodeFields = atlas::FieldSet{}; nodeFields.add(mesh.nodes().xy()); nodeFields.add(mesh.nodes().lonlat()); nodeFields.add(mesh.nodes().ghost()); nodeFields.add(mesh.nodes().halo()); nodeFields.add(mesh.nodes().remote_index()); nodeFields.add(mesh.nodes().partition()); nodeFields.add(mesh.nodes().global_index()); nodeFields.add(mesh.nodes().field("tij")); nodeFields.add(testField1); auto cellFields = FieldSet{}; cellFields.add(mesh.cells().halo()); cellFields.add(mesh.cells().remote_index()); cellFields.add(mesh.cells().partition()); cellFields.add(mesh.cells().global_index()); cellFields.add(mesh.cells().field("tij")); cellFields.add(mesh.cells().field("xy")); cellFields.add(mesh.cells().field("lonlat")); cellFields.add(testField2); // Set gmsh config. const auto gmshConfigXy = util::Config("coordinates", "xy") | util::Config("ghost", true); const auto gmshConfigXyz = util::Config("coordinates", "xyz") | util::Config("ghost", true); // Set gmsh objects. const auto fileStr = gridStr + "_" + partitionerStr + "_halo" + std::to_string(halo); // Node columns output. auto gmshXy = output::Gmsh(fileStr + "_NC_xy.msh", gmshConfigXy); auto gmshXyz = output::Gmsh(fileStr + "_NC_xyz.msh", gmshConfigXyz); gmshXy.write(mesh); gmshXy.write(nodeFields, nodeColumns); gmshXyz.write(mesh); gmshXyz.write(nodeFields, nodeColumns); // Cell columns output. gmshXy = output::Gmsh(fileStr + "_CC_xy.msh", gmshConfigXy); gmshXyz = output::Gmsh(fileStr + "_CC_xyz.msh", gmshConfigXyz); gmshXy.write(mesh); gmshXy.write(cellFields, cellColumns); gmshXyz.write(mesh); gmshXyz.write(cellFields, cellColumns); } } CASE("cubedsphere_mesh_test") { SECTION("N12, halo = 0") { testHaloExchange("CS-LFR-C-12", "equal_regions", 0); testHaloExchange("CS-LFR-C-12", "cubedsphere", 0); } SECTION("N12, halo = 1") { testHaloExchange("CS-LFR-C-12", "equal_regions", 1); testHaloExchange("CS-LFR-C-12", "cubedsphere", 1); } SECTION("N12, halo = 3") { testHaloExchange("CS-LFR-C-12", "equal_regions", 3); testHaloExchange("CS-LFR-C-12", "cubedsphere", 3); } SECTION("Prime number mesh (N17)") { testHaloExchange("CS-LFR-C-17", "equal_regions", 1); testHaloExchange("CS-LFR-C-17", "cubedsphere", 1); } } CASE("cubedsphere_dual_mesh_test") { SECTION("NodeColumns") { // This test generates a test field on a structured gird, then interpolates // it to a cubed-sphere dual mesh field. // The following functionality is tested: // * Generation of a cubed-sphere dual mesh. // * Partitioning of cubed-sphere dual mesh with a matching mesh partitioner. // * Interpolation from a StructuredColumns source to a cubed sphere dual mesh (NodeColumns) target. // Set number of levels for test field. const idx_t nLevels = 5; // Make a grid, mesh and functionspace for structured source grid. const auto sourceGrid = Grid("O96"); const auto sourceDistribution = grid::Distribution(sourceGrid, grid::Partitioner("equal_bands")); const auto sourceMesh = MeshGenerator("structured").generate(sourceGrid, sourceDistribution); const auto sourceFunctionSpace = functionspace::StructuredColumns( sourceGrid, sourceDistribution, util::Config("halo", 1) | util::Config("periodic_points", true)); // Make and set a source field. auto sourceField = sourceFunctionSpace.createField(util::Config("name", "source field") | util::Config("levels", nLevels)); auto sourceView = array::make_view(sourceField); const auto sourceLonLatView = array::make_view(sourceFunctionSpace.lonlat()); for (idx_t i = 0; i < sourceView.shape(0); ++i) { for (idx_t j = 0; j < sourceView.shape(1); ++j) { sourceView(i, j) = 1.0 + util::function::vortex_rollup(sourceLonLatView(i, LON), sourceLonLatView(i, LAT), static_cast(j) / (nLevels - 1)); } } // Set matching mesh partitioner. const auto targetPartitioner = grid::MatchingPartitioner(sourceMesh); // Set target grid, mesh and functionspace. const auto targetGrid = Grid("CS-LFR-C-24"); const auto targetMesh = MeshGenerator("cubedsphere_dual", util::Config("halo", 3)).generate(targetGrid, targetPartitioner); const auto targetFunctionSpace = functionspace::NodeColumns(targetMesh); auto targetField = targetFunctionSpace.createField(util::Config("name", "targetField") | util::Config("levels", 5)); // Perform interpolation. auto scheme = util::Config("type", "structured-bilinear") | util::Config("halo", 1); auto interp = Interpolation(scheme, sourceFunctionSpace, targetFunctionSpace); interp.execute(sourceField, targetField); targetField.haloExchange(); // Test accuracy of interpolation. auto targetView = array::make_view(targetField); const auto targetLonLatView = array::make_view(targetFunctionSpace.lonlat()); double maxError = 0.; for (idx_t i = 0; i < targetView.shape(0); ++i) { for (idx_t j = 0; j < targetView.shape(1); ++j) { double referenceVal = 1.0 + util::function::vortex_rollup(targetLonLatView(i, LON), targetLonLatView(i, LAT), static_cast(j) / (nLevels - 1)); double relativeError = std::abs((targetView(i, j) - referenceVal) / referenceVal); maxError = std::max(maxError, relativeError); } } mpi::comm().allReduceInPlace(maxError, eckit::mpi::Operation::Code::MAX); Log::info() << "Max interpolation error = " + std::to_string(maxError) << std::endl; // Maximum interpolation error should be less than 1%. EXPECT(maxError < 0.01); // gmsh output. const auto gmshConfigXy = util::Config("coordinates", "xy") | util::Config("ghost", true) | util::Config("info", true); const auto gmshConfigXyz = util::Config("coordinates", "xyz") | util::Config("ghost", true) | util::Config("info", true); auto gmshXy = output::Gmsh("dual_nodes_xy.msh", gmshConfigXy); auto gmshXyz = output::Gmsh("dual_nodes_xyz.msh", gmshConfigXyz); gmshXy.write(targetMesh); gmshXyz.write(targetMesh); gmshXy.write(targetField, targetFunctionSpace); gmshXyz.write(targetField, targetFunctionSpace); } SECTION("CellColumns") { // Simple test to check halo exchange and output mesh. // Set number of levels for test field. const idx_t nLevels = 5; // Set grid, mesh and functionspace. const auto grid = Grid("CS-LFR-C-24"); const auto mesh = MeshGenerator("cubedsphere_dual", util::Config("halo", 3)).generate(grid); const auto functionSpace = functionspace::CellColumns(mesh); auto field = functionSpace.createField(util::Config("name", "targetField") | util::Config("levels", nLevels)); // Make views. auto fieldView = array::make_view(field); const auto lonLatView = array::make_view(functionSpace.lonlat()); const auto remoteIdxView = array::make_indexview(functionSpace.cells().remote_index()); const auto partView = array::make_view(functionSpace.cells().partition()); // Set test field on owned cells. for (idx_t i = 0; i < fieldView.shape(0); ++i) { // Break once we've got to ghost cells (cells mirrored from ghost nodes). if (partView(i) != mpi::rank() || remoteIdxView(i) != i) { break; } for (idx_t j = 0; j < fieldView.shape(1); ++j) { fieldView(i, j) = 1.0 + util::function::vortex_rollup(lonLatView(i, LON), lonLatView(i, LAT), static_cast(j) / (nLevels - 1)); } } // Perform halo exchange. field.haloExchange(); // Check test field on *all* cells. for (idx_t i = 0; i < fieldView.shape(0); ++i) { for (idx_t j = 0; j < fieldView.shape(1); ++j) { EXPECT_APPROX_EQ(fieldView(i, j), 1.0 + util::function::vortex_rollup(lonLatView(i, LON), lonLatView(i, LAT), static_cast(j) / (nLevels - 1))); } } // gmsh output. auto fieldSet = FieldSet{field}; fieldSet.add(mesh.cells().halo()); const auto gmshConfigXy = util::Config("coordinates", "xy") | util::Config("ghost", true) | util::Config("info", true); const auto gmshConfigXyz = util::Config("coordinates", "xyz") | util::Config("ghost", true) | util::Config("info", true); auto gmshXy = output::Gmsh("dual_cells_xy.msh", gmshConfigXy); auto gmshXyz = output::Gmsh("dual_cells_xyz.msh", gmshConfigXyz); gmshXy.write(mesh); gmshXyz.write(mesh); gmshXy.write(fieldSet, functionSpace); gmshXyz.write(fieldSet, functionSpace); } } } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/mesh/test_rgg.cc0000664000175000017500000004220315160212070020076 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include #include #include #include "eckit/types/FloatCompare.h" #include "atlas/array/ArrayView.h" #include "atlas/array/MakeView.h" #include "atlas/field/Field.h" #include "atlas/grid.h" #include "atlas/grid/detail/partitioner/EqualRegionsPartitioner.h" #include "atlas/grid/detail/spacing/gaussian/Latitudes.h" #include "atlas/library/config.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/BuildParallelFields.h" #include "atlas/meshgenerator.h" #include "atlas/output/Gmsh.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/runtime/Log.h" #include "atlas/util/Config.h" #include "atlas/util/CoordinateEnums.h" #include "atlas/util/Metadata.h" #include "tests/AtlasTestEnvironment.h" namespace atlas { namespace grid { namespace spacing { namespace gaussian { void compute_gaussian_quadrature_npole_equator(const size_t N, double lats[], double weights[]); } } // namespace spacing } // namespace grid } // namespace atlas #define DISABLE if (0) #define ENABLE if (1) namespace atlas { namespace test { //----------------------------------------------------------------------------- static ReducedGaussianGrid debug_grid() { return {6, 10, 18, 22, 22, 22, 22, 18, 10, 6}; } static StructuredGrid minimal_grid(int N, long lon[]) { std::vector nx(2 * N); for (long j = 0; j < N; ++j) { nx[j] = lon[j]; nx[nx.size() - 1 - j] = nx[j]; } return ReducedGaussianGrid(nx); } double compute_lonlat_area(Mesh& mesh) { mesh::Nodes& nodes = mesh.nodes(); mesh::Elements& quads = mesh.cells().elements(0); mesh::Elements& triags = mesh.cells().elements(1); array::ArrayView lonlat = array::make_view(nodes.lonlat()); const mesh::BlockConnectivity& quad_nodes = quads.node_connectivity(); const mesh::BlockConnectivity& triag_nodes = triags.node_connectivity(); double area = 0; for (idx_t e = 0; e < quads.size(); ++e) { idx_t n0 = quad_nodes(e, 0); idx_t n1 = quad_nodes(e, 1); idx_t n2 = quad_nodes(e, 2); idx_t n3 = quad_nodes(e, 3); double x0 = lonlat(n0, LON), x1 = lonlat(n1, LON), x2 = lonlat(n2, LON), x3 = lonlat(n3, LON); double y0 = lonlat(n0, LAT), y1 = lonlat(n1, LAT), y2 = lonlat(n2, LAT), y3 = lonlat(n3, LAT); area += std::abs(x0 * (y1 - y2) + x1 * (y2 - y0) + x2 * (y0 - y1)) * 0.5; area += std::abs(x2 * (y3 - y0) + x3 * (y0 - y2) + x0 * (y2 - y3)) * 0.5; } for (idx_t e = 0; e < triags.size(); ++e) { idx_t n0 = triag_nodes(e, 0); idx_t n1 = triag_nodes(e, 1); idx_t n2 = triag_nodes(e, 2); double x0 = lonlat(n0, LON), x1 = lonlat(n1, LON), x2 = lonlat(n2, LON); double y0 = lonlat(n0, LAT), y1 = lonlat(n1, LAT), y2 = lonlat(n2, LAT); area += std::abs(x0 * (y1 - y2) + x1 * (y2 - y0) + x2 * (y0 - y1)) * 0.5; } return area; } //----------------------------------------------------------------------------- CASE("test_eq_caps") { std::vector n_regions; std::vector s_cap; grid::detail::partitioner::eq_caps(6, n_regions, s_cap); EXPECT(n_regions.size() == 3); EXPECT(n_regions[0] == 1); EXPECT(n_regions[1] == 4); EXPECT(n_regions[2] == 1); grid::detail::partitioner::eq_caps(10, n_regions, s_cap); EXPECT(n_regions.size() == 4); EXPECT(n_regions[0] == 1); EXPECT(n_regions[1] == 4); EXPECT(n_regions[2] == 4); EXPECT(n_regions[3] == 1); } CASE("test_partitioner") { Grid g("S4x2"); // 12 partitions { grid::detail::partitioner::EqualRegionsPartitioner partitioner(12); EXPECT(partitioner.nb_bands() == 4); EXPECT(partitioner.nb_regions(0) == 1); EXPECT(partitioner.nb_regions(1) == 5); EXPECT(partitioner.nb_regions(2) == 5); EXPECT(partitioner.nb_regions(3) == 1); } // 24 partitions { grid::detail::partitioner::EqualRegionsPartitioner partitioner(24); EXPECT(partitioner.nb_bands() == 5); EXPECT(partitioner.nb_regions(0) == 1); EXPECT(partitioner.nb_regions(1) == 6); EXPECT(partitioner.nb_regions(2) == 10); EXPECT(partitioner.nb_regions(3) == 6); EXPECT(partitioner.nb_regions(4) == 1); } // 48 partitions { grid::detail::partitioner::EqualRegionsPartitioner partitioner(48); EXPECT(partitioner.nb_bands() == 7); EXPECT(partitioner.nb_regions(0) == 1); EXPECT(partitioner.nb_regions(1) == 6); EXPECT(partitioner.nb_regions(2) == 11); EXPECT(partitioner.nb_regions(3) == 12); EXPECT(partitioner.nb_regions(4) == 11); EXPECT(partitioner.nb_regions(5) == 6); EXPECT(partitioner.nb_regions(6) == 1); } // 96 partitions { grid::detail::partitioner::EqualRegionsPartitioner partitioner(96); EXPECT(partitioner.nb_bands() == 10); EXPECT(partitioner.nb_regions(0) == 1); EXPECT(partitioner.nb_regions(1) == 6); EXPECT(partitioner.nb_regions(2) == 11); EXPECT(partitioner.nb_regions(3) == 14); EXPECT(partitioner.nb_regions(4) == 16); EXPECT(partitioner.nb_regions(5) == 16); EXPECT(partitioner.nb_regions(6) == 14); EXPECT(partitioner.nb_regions(7) == 11); EXPECT(partitioner.nb_regions(8) == 6); EXPECT(partitioner.nb_regions(9) == 1); } } CASE("test_gaussian_latitudes") { std::vector factory_latitudes; std::vector computed_latitudes; std::vector computed_weights; size_t size_test_N = 19; size_t test_N[] = {16, 24, 32, 48, 64, 80, 96, 128, 160, 200, 256, 320, 400, 512, 576, 640, 800, 1024, 1280, 1600, 2000, 4000, 8000}; for (size_t i = 0; i < size_test_N; ++i) { size_t N = test_N[i]; Log::info() << "Testing gaussian latitude " << N << std::endl; factory_latitudes.resize(N); computed_latitudes.resize(N); computed_weights.resize(N); // grid::gaussian::latitudes::gaussian_latitudes_npole_equator (N, // factory_latitudes.data()); // grid::gaussian::latitudes::compute_gaussian_quadrature_npole_equator(N, // computed_latitudes.data(), computed_weights.data()); grid::spacing::gaussian::gaussian_latitudes_npole_equator(N, factory_latitudes.data()); grid::spacing::gaussian::compute_gaussian_quadrature_npole_equator(N, computed_latitudes.data(), computed_weights.data()); double wsum = 0; for (size_t i = 0; i < N; ++i) { EXPECT(eckit::types::is_approximately_equal(computed_latitudes[i], factory_latitudes[i], 0.0000001)); wsum += computed_weights[i]; } EXPECT(eckit::types::is_approximately_equal(wsum * 2., 1., 0.0000001)); } } CASE("test_rgg_meshgen_one_part") { Mesh m; util::Config default_opts; default_opts.set("nb_parts", 1); default_opts.set("part", 0); // generate.options.set("nb_parts",1); // generate.options.set("part", 0); DISABLE { // This is all valid for meshes generated with MINIMAL NB TRIAGS ENABLE { StructuredMeshGenerator generate(default_opts("3d", true)("include_pole", false)); m = generate(atlas::test::debug_grid()); EXPECT(m.nodes().size() == 156); EXPECT(m.cells().elements(0).size() == 134); EXPECT(m.cells().elements(1).size() == 32); // EXPECT( m.nodes().metadata().get("nb_owned") == 156 ); // EXPECT( m.function_space("quads" // ).metadata().get("nb_owned") == 134 ); // EXPECT( // m.function_space("triags").metadata().get("nb_owned") == // 32 ); } ENABLE { StructuredMeshGenerator generate(default_opts("3d", false)("include_pole", false)); m = generate(atlas::test::debug_grid()); EXPECT(m.nodes().size() == 166); EXPECT(m.cells().elements(0).size() == 134); EXPECT(m.cells().elements(1).size() == 32); // EXPECT( m.nodes().metadata().get("nb_owned") == 166 ); // EXPECT( m.function_space("quads" // ).metadata().get("nb_owned") == 134 ); // EXPECT( // m.function_space("triags").metadata().get("nb_owned") == // 32 ); } ENABLE { StructuredMeshGenerator generate(default_opts("3d", true)("include_pole", true)); m = generate(atlas::test::debug_grid()); EXPECT(m.nodes().size() == 158); EXPECT(m.cells().elements(0).size() == 134); EXPECT(m.cells().elements(1).size() == 44); // EXPECT( m.nodes().metadata().get("nb_owned") == 158 ); // EXPECT( m.function_space("quads" // ).metadata().get("nb_owned") == 134 ); // EXPECT( // m.function_space("triags").metadata().get("nb_owned") == // 44 ); } Mesh mesh; ENABLE { StructuredMeshGenerator generate(default_opts("3d", false)("include_pole", false)); int nlat = 2; long lon[] = {4, 6}; mesh = generate(test::minimal_grid(nlat, lon)); EXPECT(mesh.nodes().size() == 24); EXPECT(mesh.cells().elements(0).size() == 14); EXPECT(mesh.cells().elements(1).size() == 4); double max_lat = test::minimal_grid(nlat, lon).y().front(); EXPECT( eckit::types::is_approximately_equal(test::compute_lonlat_area(mesh), 2. * M_PI * 2. * max_lat, 1e-8)); output::Gmsh("minimal2.msh").write(mesh); } // 3 latitudes ENABLE { StructuredMeshGenerator generate(default_opts("3d", false)("include_pole", false)); int nlat = 3; long lon[] = {4, 6, 8}; mesh = generate(test::minimal_grid(nlat, lon)); EXPECT(mesh.nodes().size() == 42); EXPECT(mesh.cells().elements(0).size() == 28); EXPECT(mesh.cells().elements(1).size() == 8); output::Gmsh("minimal3.msh").write(mesh); } // 4 latitudes ENABLE { StructuredMeshGenerator generate(default_opts("3d", false)("include_pole", false)); int nlat = 4; long lon[] = {4, 6, 8, 10}; mesh = generate(test::minimal_grid(nlat, lon)); EXPECT(mesh.nodes().size() == 64); EXPECT(mesh.cells().elements(0).size() == 46); EXPECT(mesh.cells().elements(1).size() == 12); output::Gmsh("minimal4.msh").write(mesh); } // 5 latitudes WIP ENABLE { StructuredMeshGenerator generate(default_opts("3d", false)("include_pole", false)); int nlat = 5; long lon[] = {6, 10, 18, 22, 22}; mesh = generate(test::minimal_grid(nlat, lon)); EXPECT(mesh.nodes().size() == 166); EXPECT(mesh.cells().elements(0).size() == 134); EXPECT(mesh.cells().elements(1).size() == 32); output::Gmsh("minimal5.msh").write(mesh); } } } CASE("test_rgg_meshgen_many_parts") { EXPECT(grid::detail::partitioner::PartitionerFactory::has("equal_regions")); int nb_parts = 20; // Alternative grid for debugging // int nlat=10; // long lon[] = { 10, 10, 10, 10, 10, 10, 10, 10, 10, 10 }; // test::MinimalGrid grid(nlat,lon); StructuredGrid grid = Grid("N32"); // RegularGrid grid(128,64); /* std::cout << grid.spec() << std::endl; for (int jlat=0;jlat<2*nlat; jlat++) { std::cout << grid.lon(jlat,0) << ", ... , " << "," << grid.lon(jlat,9) << grid.lon(jlat,10) << std::endl; } ASSERT(0); */ double max_lat = grid.y().front(); double check_area = 360. * 2. * max_lat; double area = 0; int nodes[] = {313, 332, 336, 338, 334, 337, 348, 359, 360, 361, 360, 360, 359, 370, 321, 334, 338, 335, 348, 315}; int quads[] = {243, 290, 293, 294, 291, 293, 310, 320, 321, 321, 320, 321, 320, 331, 278, 291, 294, 293, 305, 245}; int triags[] = {42, 13, 12, 13, 12, 14, 0, 1, 0, 1, 1, 0, 1, 0, 14, 12, 13, 11, 14, 42}; int nb_owned = 0; std::vector all_owned(grid.size(), -1); for (int p = 0; p < nb_parts; ++p) { ATLAS_DEBUG_VAR(p); StructuredMeshGenerator generate(util::Config("partitioner", "equal_regions")("nb_parts", nb_parts)("part", p)( "include_pole", false)("3d", false)); ATLAS_DEBUG_HERE(); Mesh m = generate(grid); ATLAS_DEBUG_HERE(); m.metadata().set("part", p); Log::info() << "generated grid " << p << std::endl; array::ArrayView part = array::make_view(m.nodes().partition()); array::ArrayView gidx = array::make_view(m.nodes().global_index()); area += test::compute_lonlat_area(m); ATLAS_DEBUG_HERE(); DISABLE { // This is all valid for meshes generated with MINIMAL NB TRIAGS if (nb_parts == 20) { EXPECT(m.nodes().size() == nodes[p]); EXPECT(m.cells().elements(0).size() == quads[p]); EXPECT(m.cells().elements(1).size() == triags[p]); } } ATLAS_DEBUG_HERE(); output::Gmsh("T63.msh").write(m); mesh::Nodes& nodes = m.nodes(); idx_t nb_nodes = nodes.size(); // Test if all nodes are connected { std::vector node_elem_connections(nb_nodes, 0); const mesh::HybridElements::Connectivity& cell_node_connectivity = m.cells().node_connectivity(); for (idx_t jelem = 0; jelem < static_cast(m.cells().size()); ++jelem) { for (idx_t jnode = 0; jnode < cell_node_connectivity.cols(jelem); ++jnode) { node_elem_connections[cell_node_connectivity(jelem, jnode)]++; } } for (idx_t jnode = 0; jnode < nb_nodes; ++jnode) { if (node_elem_connections[jnode] == 0) { std::stringstream ss; ss << "part " << p << ": node_gid " << gidx(jnode) << " is not connected to any element."; DISABLE { Log::error() << ss.str() << std::endl; } } } } // Test if all nodes are owned for (idx_t n = 0; n < nb_nodes; ++n) { if (gidx(n) <= grid.size()) { if (part(n) == p) { ++nb_owned; if (all_owned[gidx(n) - 1] != -1) { std::cout << "node " << gidx(n) << " already visited by " << all_owned[gidx(n) - 1] << std::endl; } EXPECT(all_owned[gidx(n) - 1] == -1); all_owned[gidx(n) - 1] = part(n); } } } } for (size_t gid = 1; gid <= all_owned.size(); ++gid) { if (all_owned[gid - 1] == -1) { Log::error() << "node " << gid << " is not owned by anyone" << std::endl; } } EXPECT_EQ(nb_owned, grid.size()); EXPECT_APPROX_EQ(area, check_area, 1e-9); } CASE("test_meshgen_ghost_at_end") { ATLAS_DEBUG_HERE(); Grid grid("O8"); atlas::util::Config cfg; cfg.set("part", 1); cfg.set("nb_parts", 8); StructuredMeshGenerator meshgenerator(cfg); Mesh mesh = meshgenerator.generate(grid); const auto part = array::make_view(mesh.nodes().partition()); const auto ghost = array::make_view(mesh.nodes().ghost()); const auto flags = array::make_view(mesh.nodes().flags()); Log::info() << "partition = [ "; for (idx_t jnode = 0; jnode < part.size(); ++jnode) { Log::info() << part(jnode) << " "; } Log::info() << "]" << std::endl; Log::info() << "ghost = [ "; for (idx_t jnode = 0; jnode < part.size(); ++jnode) { Log::info() << ghost(jnode) << " "; } Log::info() << "]" << std::endl; Log::info() << "flags = [ "; for (idx_t jnode = 0; jnode < part.size(); ++jnode) { Log::info() << mesh::Nodes::Topology::check(flags(jnode), mesh::Nodes::Topology::GHOST) << " "; EXPECT(mesh::Nodes::Topology::check(flags(jnode), mesh::Nodes::Topology::GHOST) == ghost(jnode)); } Log::info() << "]" << std::endl; } //----------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/mesh/test_connectivity.cc0000664000175000017500000003073715160212070022046 0ustar alastairalastair/* * (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/library/defines.h" #include "atlas/mesh/Connectivity.h" #include "atlas/runtime/Log.h" #include "atlas/runtime/Trace.h" #include "tests/AtlasTestEnvironment.h" using namespace atlas::mesh; namespace atlas { namespace test { //----------------------------------------------------------------------------- #if ATLAS_HAVE_FORTRAN #define FORTRAN_BASE 1 #define FROM_FORTRAN -1 #define TO_FORTRAN +1 #define IN_FORTRAN -1 #else #define FORTRAN_BASE 0 #define FROM_FORTRAN #define TO_FORTRAN #define IN_FORTRAN #endif CASE("test_irregular_connectivity") { IrregularConnectivity conn("mesh"); EXPECT(conn.rows() == 0); EXPECT(conn.maxcols() == 0); constexpr idx_t vals[4] = {2, 3, 5, 6}; conn.add(1, 4, vals, /* fortran-array = */ false); EXPECT(conn.rows() == 1); EXPECT(conn.cols(0) == 4); EXPECT(conn.mincols() == 4); EXPECT(conn.maxcols() == 4); EXPECT(conn(0, 0) == 2); EXPECT(conn(0, 1) == 3); EXPECT(conn(0, 2) == 5); EXPECT(conn(0, 3) == 6); EXPECT(conn.row(0)(0) == 2); EXPECT(conn.row(0)(1) == 3); EXPECT(conn.row(0)(2) == 5); EXPECT(conn.row(0)(3) == 6); constexpr idx_t vals2[6] = {1, 3, 4, 3, 7, 8}; conn.add(2, 3, vals2, /* fortran-array = */ true); conn.dump(Log::info()); Log::info() << std::endl; EXPECT(conn.rows() == 3); EXPECT(conn.cols(1) == 3); EXPECT(conn.cols(2) == 3); EXPECT(conn.mincols() == 3); EXPECT(conn.maxcols() == 4); EXPECT(conn(1, 0) == 1 IN_FORTRAN); EXPECT(conn(1, 1) == 3 IN_FORTRAN); EXPECT(conn(1, 2) == 4 IN_FORTRAN); EXPECT(conn.row(2)(0) == 3 IN_FORTRAN); EXPECT(conn.row(2)(1) == 7 IN_FORTRAN); EXPECT(conn.row(2)(2) == 8 IN_FORTRAN); conn.set(1, 1, 9 IN_FORTRAN); EXPECT(conn(1, 0) == 1 IN_FORTRAN); EXPECT(conn(1, 1) == 9 IN_FORTRAN); EXPECT(conn(1, 2) == 4 IN_FORTRAN); constexpr idx_t vals3[3] = {6 IN_FORTRAN, 7 IN_FORTRAN, 5 IN_FORTRAN}; conn.set(2, vals3); EXPECT(conn(2, 0) == 6 IN_FORTRAN); EXPECT(conn(2, 1) == 7 IN_FORTRAN); EXPECT(conn(2, 2) == 5 IN_FORTRAN); constexpr idx_t vals4[8] = {2, 11, 51, 12, 4, 13, 55, 78}; conn.insert(1, 2, 4, vals4, /* fortran-array = */ false); EXPECT(conn.mincols() == 3); EXPECT(conn.maxcols() == 4); EXPECT(conn.rows() == 5); EXPECT(conn.cols(0) == 4); EXPECT(conn.cols(1) == 4); EXPECT(conn.cols(2) == 4); EXPECT(conn.cols(3) == 3); EXPECT(conn.cols(4) == 3); EXPECT(conn(1, 0) == 2); EXPECT(conn(1, 1) == 11); EXPECT(conn(1, 2) == 51); EXPECT(conn(1, 3) == 12); EXPECT(conn(2, 0) == 4); EXPECT(conn(2, 1) == 13); EXPECT(conn(2, 2) == 55); EXPECT(conn(2, 3) == 78); EXPECT(conn(3, 0) == 1 IN_FORTRAN); EXPECT(conn(3, 1) == 9 IN_FORTRAN); EXPECT(conn(3, 2) == 4 IN_FORTRAN); constexpr idx_t vals5[2] = {3, 6}; conn.insert(3, 1, 2, vals5, true); EXPECT(conn.mincols() == 2); EXPECT(conn.maxcols() == 4); EXPECT(conn.rows() == 6); EXPECT(conn.cols(3) == 2); EXPECT(conn.cols(4) == 3); EXPECT(conn(3, 0) == 3 IN_FORTRAN); EXPECT(conn(3, 1) == 6 IN_FORTRAN); EXPECT(conn(4, 0) == 1 IN_FORTRAN); EXPECT(conn(4, 1) == 9 IN_FORTRAN); EXPECT(conn(4, 2) == 4 IN_FORTRAN); // insert 3 rows with 1 column conn.insert(4, 3, 1); EXPECT(conn.rows() == 9); EXPECT(conn.cols(4) == 1); EXPECT(conn.cols(5) == 1); EXPECT(conn.mincols() == 1); EXPECT(conn.maxcols() == 4); EXPECT(conn(7, 0) == 1 IN_FORTRAN); EXPECT(conn(7, 1) == 9 IN_FORTRAN); EXPECT(conn(7, 2) == 4 IN_FORTRAN); constexpr idx_t cols[3] = {3, 7, 1}; EXPECT(conn.cols(2) == 4); // insert in position 2, 3 rows with cols[3] number of columns conn.insert(2, 3, cols); EXPECT(conn.mincols() == 1); EXPECT(conn.maxcols() == 7); EXPECT(conn.rows() == 12); EXPECT(conn.cols(2) == 3); EXPECT(conn.cols(3) == 7); EXPECT(conn.cols(4) == 1); EXPECT(conn.cols(5) == 4); EXPECT(conn(5, 0) == 4); EXPECT(conn(5, 1) == 13); EXPECT(conn(5, 2) == 55); EXPECT(conn(5, 3) == 78); } CASE("test_irregular_insert") { IrregularConnectivity conn("mesh"); EXPECT(conn.rows() == 0); EXPECT(conn.maxcols() == 0); constexpr idx_t vals[4] = {2, 3, 5, 6}; conn.insert(0, 1, 4, vals, false); EXPECT(conn.rows() == 1); EXPECT(conn.cols(0) == 4); EXPECT(conn.mincols() == 4); EXPECT(conn.maxcols() == 4); EXPECT(conn(0, 0) == 2); EXPECT(conn(0, 1) == 3); EXPECT(conn(0, 2) == 5); EXPECT(conn(0, 3) == 6); EXPECT(conn.row(0)(0) == 2); EXPECT(conn.row(0)(1) == 3); EXPECT(conn.row(0)(2) == 5); EXPECT(conn.row(0)(3) == 6); } CASE("test_block_connectivity") { idx_t vals[15] = {3, 7, 1, 4, 5, 6, 4, 56, 8, 4, 1, 3, 76, 4, 3}; BlockConnectivity conn(3, 5, vals); EXPECT(conn.rows() == 3); EXPECT(conn.cols() == 5); EXPECT(conn(0, 2) == 1); EXPECT(conn(1, 1) == 4); EXPECT(conn(2, 2) == 76); } CASE("test_block_connectivity_add") { BlockConnectivity conn; idx_t vals2[10] = {2, 3, 9, 34, 356, 86, 3, 24, 84, 45}; conn.add(2, 5, vals2); EXPECT(conn.rows() == 2); EXPECT(conn.cols() == 5); EXPECT(conn(0, 2) == 9); EXPECT(conn(0, 4) == 356); EXPECT(conn(1, 1) == 3); } CASE("test_block_connectivity_empty_add") { BlockConnectivity conn; EXPECT(conn.rows() == 0); EXPECT(conn.cols() == 0); idx_t vals2[12] = {2, 3, 9, 34, 356, 86, 3, 24, 84, 45, 2, 2}; conn.add(2, 5, vals2); EXPECT(conn.rows() == 2); EXPECT(conn.cols() == 5); EXPECT(conn(0, 2) == 9); EXPECT(conn(0, 4) == 356); EXPECT(conn(1, 1) == 3); } #if 0 CASE("test_multi_block_connectivity_default") { idx_t values[22] = { 1, 3, 4, 2, 3, 4, 4, 5, 6, 7, 23, 54, 6, 9, 11, 12, 13, 14, 17, 18, 21, 24}; idx_t displ[7] = {0, 3, 6, 10, 14, 18, 20}; idx_t counts[7] = {3, 3, 4, 4, 4, 2, 2}; idx_t block_displ[3] = {0, 2, 5}; idx_t block_cols[3] = {3, 4, 2}; MultiBlockConnectivity mbc(values, 7, displ, counts, 3, block_displ, block_cols); EXPECT(mbc(0, 2) == 4 ); EXPECT(mbc(1, 1) == 3 ); EXPECT(mbc(2, 2) == 6 ); EXPECT(mbc(3, 3) == 9 ); EXPECT(mbc(4, 0) == 11 ); EXPECT(mbc(5, 0) == 17 ); EXPECT(mbc(6, 1) == 24 ); EXPECT(mbc(0, 1, 2) == 4 ); EXPECT(mbc(1, 1, 0) == 23 ); EXPECT(mbc(2, 1, 1) == 24 ); } #endif CASE("test_multi_block_connectivity_add") { MultiBlockConnectivity mbc("mbc"); idx_t vals[6] = {1, 3, 4, 2, 3, 4}; mbc.add(2, 3, vals, false); EXPECT(mbc(0, 2) == 4); EXPECT(mbc(1, 1) == 3); EXPECT(mbc(0, 1, 2) == 4); idx_t vals2[12]{4, 5, 6, 7, 23, 54, 6, 9, 11, 12, 13, 14}; mbc.add(3, 4, vals2, false); EXPECT(mbc(2, 2) == 6); EXPECT(mbc(3, 3) == 9); EXPECT(mbc(4, 0) == 11); idx_t vals3[4]{17, 18, 21, 24}; mbc.add(2, 2, vals3, false); EXPECT(mbc(5, 0) == 17); EXPECT(mbc(6, 1) == 24); EXPECT(mbc(0, 1, 2) == 4); EXPECT(mbc(1, 1, 0) == 23); EXPECT(mbc(2, 1, 1) == 24); } CASE("test_multi_block_connectivity_add_block") { Log::info() << "\n\n\ntest_multi_block_connectivity_add_block\n" << std::endl; MultiBlockConnectivity mbc("mbc"); EXPECT(mbc.blocks() == 0); { BlockConnectivity conn1(3, 5, {3, 7, 1, 4, 5, 6, 4, 56, 8, 4, 1, 3, 76, 4, 3}); EXPECT(conn1.owns()); EXPECT(conn1(0, 2) == 1); EXPECT(conn1(1, 1) == 4); EXPECT(conn1(2, 2) == 76); mbc.add(conn1); } EXPECT(mbc.blocks() == 1); EXPECT(mbc(0, 2) == 1); EXPECT(mbc(1, 1) == 4); EXPECT(mbc(2, 2) == 76); EXPECT(mbc(0, 0, 2) == 1); EXPECT(mbc(0, 1, 1) == 4); EXPECT(mbc(0, 2, 2) == 76); { BlockConnectivity conn2(3, 2, {31, 71, 61, 41, 11, 31}); EXPECT(conn2.owns()); EXPECT(conn2(0, 0) == 31); EXPECT(conn2(1, 1) == 41); EXPECT(conn2(2, 0) == 11); mbc.add(conn2); } EXPECT(mbc.blocks() == 2); EXPECT(mbc(0, 2) == 1); EXPECT(mbc(1, 1) == 4); EXPECT(mbc(2, 2) == 76); EXPECT(mbc(3, 0) == 31); EXPECT(mbc(4, 1) == 41); EXPECT(mbc(5, 0) == 11); EXPECT(mbc(0, 0, 2) == 1); EXPECT(mbc(0, 1, 1) == 4); EXPECT(mbc(0, 2, 2) == 76); EXPECT(mbc(1, 0, 0) == 31); EXPECT(mbc(1, 1, 1) == 41); EXPECT(mbc(1, 2, 0) == 11); const BlockConnectivity& b0 = mbc.block(0); EXPECT(b0(0, 2) == 1); EXPECT(b0(1, 1) == 4); EXPECT(b0(2, 2) == 76); const BlockConnectivity& b1 = mbc.block(1); EXPECT(b1(0, 0) == 31); EXPECT(b1(1, 1) == 41); EXPECT(b1(2, 0) == 11); MultiBlockConnectivity& ic = mbc; EXPECT(ic(0, 2) == 1); EXPECT(ic(1, 1) == 4); EXPECT(ic(2, 2) == 76); EXPECT(ic(3, 0) == 31); EXPECT(ic(4, 1) == 41); EXPECT(ic(5, 0) == 11); ic.set(5, 0, 12); EXPECT(b1(2, 0) == 12); } CASE("test_multi_block_connectivity_add_block_em") { MultiBlockConnectivity mbc("mbc"); EXPECT(mbc.blocks() == 0); { idx_t vals[15]{3, 7, 1, 4, 5, 6, 4, 56, 8, 4, 1, 3, 76, 4, 3}; BlockConnectivity conn(3, 5, vals); mbc.add(conn); } EXPECT(mbc.blocks() == 1); EXPECT(mbc(0, 2) == 1); EXPECT(mbc(1, 1) == 4); EXPECT(mbc(2, 2) == 76); EXPECT(mbc(0, 0, 2) == 1); EXPECT(mbc(0, 1, 1) == 4); EXPECT(mbc(0, 2, 2) == 76); { idx_t vals5[6]{4, 75, 65, 45, 51, 35}; BlockConnectivity conn2(3, 2, vals5); mbc.add(conn2); } EXPECT(mbc.blocks() == 2); EXPECT(mbc(3, 1) == 75); EXPECT(mbc(4, 1) == 45); EXPECT(mbc(5, 0) == 51); EXPECT(mbc(1, 0, 1) == 75); EXPECT(mbc(1, 1, 1) == 45); EXPECT(mbc(1, 2, 0) == 51); } CASE("test_multi_block_connectivity_insert") { MultiBlockConnectivity mbc("mbc"); EXPECT(mbc.blocks() == 0); mbc.add(BlockConnectivity(3, 5, {3, 7, 1, 4, 5, 6, 4, 56, 8, 4, 1, 3, 76, 4, 3})); EXPECT(mbc(0, 2) == 1); EXPECT(mbc(1, 1) == 4); EXPECT(mbc(2, 2) == 76); EXPECT(mbc(0, 0, 2) == 1); EXPECT(mbc(0, 1, 1) == 4); EXPECT(mbc(0, 2, 2) == 76); EXPECT(mbc.block(0).rows() == 3); { idx_t vals[10]{31, 71, 61, 41, 42, 11, 31, 33, 54, 56}; mbc.insert(0, 2, 5, vals); } /* idx_t check[] = { 31,71,61,41,42, 11,31,33,54,56, 3,7,1,4,5, 6,4,56,8,4, 1,3,76,4,3 }; */ EXPECT(mbc.block(0).rows() == 5); EXPECT(mbc(0, 2) == 61); EXPECT(mbc(1, 1) == 31); EXPECT(mbc(2, 2) == 1); EXPECT(mbc(3, 1) == 4); EXPECT(mbc(4, 2) == 76); EXPECT(mbc(0, 2, 2) == 1); EXPECT(mbc(0, 3, 1) == 4); EXPECT(mbc(0, 4, 2) == 76); mbc.add(BlockConnectivity(3, 2, {4, 75, 65, 45, 51, 35})); /* idx_t check[] = { 31,71,61,41,42, 11,31,33,54,56, 3,7,1,4,5, 6,4,56,8,4, 1,3,76,4,3, 4,75, 65,45, 51,35}; */ EXPECT(mbc.block(0).rows() == 5); EXPECT(mbc.block(1).rows() == 3); EXPECT(mbc(0, 2, 2) == 1); EXPECT(mbc(0, 3, 1) == 4); EXPECT(mbc(0, 4, 2) == 76); EXPECT(mbc(5, 1) == 75); EXPECT(mbc(7, 0) == 51); EXPECT(mbc(1, 0, 1) == 75); EXPECT(mbc(1, 2, 0) == 51); { idx_t vals[10]{19, 72, 62, 42, 43, 12, 32, 34, 55, 57}; mbc.insert(5, 2, 5, vals); } EXPECT(mbc.rows() == 10); EXPECT(mbc.block(0).rows() == 7); EXPECT(mbc.block(1).rows() == 3); EXPECT(mbc.blocks() == 2); /* idx_t check[] = { 31,71,61,41,42, 11,31,33,54,56, 3,7,1,4,5, 6,4,56,8,4, 1,3,76,4,3, 19,72,62,42,43, // inserted 12,32,34,55,57, // inserted 4,75, 65,45, 51,35}; */ EXPECT(mbc.block(0)(0, 0) == 31); EXPECT(mbc(0, 0, 0) == 31); EXPECT(mbc(0, 0) == 31); EXPECT(mbc.block(0)(5, 0) == 19); EXPECT(mbc(0, 5, 0) == 19); EXPECT(mbc(5, 0) == 19); EXPECT(mbc.block(1)(0, 0) == 4); EXPECT(mbc(1, 0, 0) == 4); EXPECT(mbc(7, 0) == 4); { idx_t vals[] = {67, 38, 89, 2, 5, 7}; EXPECT_THROWS_AS(mbc.insert(2, 2, 3, vals), eckit::AssertionFailed); } } //----------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/mesh/test_mesh_builder_parallel.cc0000664000175000017500000003023415160212070023636 0ustar alastairalastair/* * (C) Copyright 2023 UCAR. * * 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. */ #include #include #include #include #include "atlas/array/MakeView.h" #include "atlas/mesh/Elements.h" #include "atlas/mesh/HybridElements.h" #include "atlas/mesh/Mesh.h" #include "atlas/mesh/MeshBuilder.h" #include "atlas/mesh/Nodes.h" #include "atlas/parallel/mpi/mpi.h" #include "tests/AtlasTestEnvironment.h" #include "tests/mesh/helper_mesh_builder.h" using namespace atlas::mesh; //#include "atlas/output/Gmsh.h" //using namespace atlas::output; namespace atlas { namespace test { //----------------------------------------------------------------------------- template mdspan> make_mdspan(std::vector& v) { return mdspan>{v.data(), v.size()}; } template mdspan> make_mdspan(std::vector>& v) { return mdspan>{reinterpret_cast(v.data()), v.size()}; } //----------------------------------------------------------------------------- CASE("test_cs_c2_mesh_parallel") { ATLAS_ASSERT(mpi::comm().size() == 6); const int rank = mpi::comm().rank(); constexpr gidx_t global_index_base = 1; // Coordinates of the C2 LFRic cubed-sphere grid: grid("CS-LFR-2"); const std::vector global_lons = {337.5, 22.5, 337.5, 22.5, // +x 67.5, 112.5, 67.5, 112.5, // +y 202.5, 202.5, 157.5, 157.5, // -x 292.5, 292.5, 247.5, 247.5, // -y 315, 45, 225, 135, // +z 315, 225, 45, 135}; // -z const std::vector global_lats = {-20.941, -20.941, 20.941, 20.941, // +x -20.941, -20.941, 20.941, 20.941, // +y -20.941, 20.941, -20.941, 20.941, // -x -20.941, 20.941, -20.941, 20.941, // -y 59.6388, 59.6388, 59.6388, 59.6388, // +z -59.6388, -59.6388, -59.6388, -59.6388}; // -z const std::vector global_partitions = {0, 0, 0, 0, // +x 1, 1, 1, 1, // +y 2, 2, 2, 2, // -x 3, 3, 3, 3, // -y 4, 4, 4, 4, // +z 5, 5, 5, 5}; // -z const std::vector local_indices = {0, 1, 2, 3, // +x 0, 1, 2, 3, // +y 0, 1, 2, 3, // -x 0, 1, 2, 3, // -y 0, 1, 2, 3, // +z 0, 1, 2, 3}; // -z std::vector global_indices; std::vector> tri_boundary_nodes{}; std::vector tri_global_indices{}; std::vector> quad_boundary_nodes{}; std::vector quad_global_indices{}; if (rank == 0) { // global indices for points and cells are 1-based global_indices = {1, 2, 3, 4, 5, 7, 17, 18}; tri_boundary_nodes.push_back({{18, 4, 7}}); tri_global_indices = {2}; quad_boundary_nodes.push_back({{1, 2, 4, 3}}); quad_boundary_nodes.push_back({{2, 5, 7, 4}}); quad_boundary_nodes.push_back({{3, 4, 18, 17}}); quad_global_indices = {9, 15, 22}; } else if (rank == 1) { global_indices = {5, 6, 7, 8, 11, 12, 18, 20}; tri_boundary_nodes.push_back({{20, 8, 12}}); tri_global_indices = {3}; quad_boundary_nodes.push_back({{5, 6, 8, 7}}); quad_boundary_nodes.push_back({{6, 11, 12, 8}}); quad_boundary_nodes.push_back({{7, 8, 20, 18}}); quad_global_indices = {10, 16, 19}; } else if (rank == 2) { global_indices = {9, 10, 11, 12, 15, 16, 22, 24}; tri_boundary_nodes.push_back({{22, 15, 9}}); tri_global_indices = {8}; quad_boundary_nodes.push_back({{11, 9, 10, 12}}); quad_boundary_nodes.push_back({{9, 15, 16, 10}}); quad_boundary_nodes.push_back({{24, 22, 9, 11}}); quad_global_indices = {11, 17, 24}; } else if (rank == 3) { global_indices = {1, 3, 13, 14, 15, 16, 21, 22}; tri_boundary_nodes.push_back({{21, 1, 13}}); tri_global_indices = {5}; quad_boundary_nodes.push_back({{15, 13, 14, 16}}); quad_boundary_nodes.push_back({{13, 1, 3, 14}}); quad_boundary_nodes.push_back({{22, 21, 13, 15}}); quad_global_indices = {12, 18, 25}; } else if (rank == 4) { global_indices = {3, 10, 12, 14, 16, 17, 18, 19, 20}; tri_boundary_nodes.push_back({{17, 14, 3}}); tri_boundary_nodes.push_back({{19, 10, 16}}); tri_global_indices = {1, 4}; quad_boundary_nodes.push_back({{17, 18, 20, 19}}); quad_boundary_nodes.push_back({{12, 10, 19, 20}}); quad_boundary_nodes.push_back({{16, 14, 17, 19}}); quad_global_indices = {13, 20, 21}; } else { // rank == 5 global_indices = {1, 2, 5, 6, 11, 21, 22, 23, 24}; tri_boundary_nodes.push_back({{23, 5, 2}}); tri_boundary_nodes.push_back({{24, 11, 6}}); tri_global_indices = {6, 7}; quad_boundary_nodes.push_back({{21, 22, 24, 23}}); quad_boundary_nodes.push_back({{23, 24, 6, 5}}); quad_boundary_nodes.push_back({{21, 23, 2, 1}}); quad_global_indices = {14, 23, 26}; } // Compute (local subset of) {lons,lats,ghosts,partitions} from (local subset of) global indices std::vector lons; std::transform(global_indices.begin(), global_indices.end(), std::back_inserter(lons), [&global_lons](const gidx_t idx) { return global_lons[idx - 1]; }); std::vector lats; std::transform(global_indices.begin(), global_indices.end(), std::back_inserter(lats), [&global_lats](const gidx_t idx) { return global_lats[idx - 1]; }); std::vector ghosts; std::transform( global_indices.begin(), global_indices.end(), std::back_inserter(ghosts), [&global_partitions, &rank](const gidx_t idx) { return static_cast(global_partitions[idx - 1] != rank); }); const idx_t remote_index_base = 0; // 0-based indexing used in local_indices above std::vector remote_indices; std::transform(global_indices.begin(), global_indices.end(), std::back_inserter(remote_indices), [&local_indices](const gidx_t idx) { return static_cast(local_indices[idx - 1]); }); std::vector partitions; std::transform(global_indices.begin(), global_indices.end(), std::back_inserter(partitions), [&global_partitions](const gidx_t idx) { return global_partitions[idx - 1]; }); const MeshBuilder mesh_builder{}; SECTION("Build Mesh without a Grid") { const Mesh mesh = mesh_builder( make_mdspan(global_indices), make_mdspan(lons), make_mdspan(lats), make_mdspan(lons), make_mdspan(lats), make_mdspan(ghosts), make_mdspan(partitions), make_mdspan(remote_indices), remote_index_base, make_mdspan(tri_global_indices), make_mdspan(tri_boundary_nodes), make_mdspan(quad_global_indices), make_mdspan(quad_boundary_nodes), global_index_base); // const Mesh mesh = mesh_builder(lons, lats, ghosts, global_indices, remote_indices, remote_index_base, partitions, // tri_boundary_nodes, tri_global_indices, quad_boundary_nodes, quad_global_indices); helper::check_mesh_nodes_and_cells(mesh, lons, lats, ghosts, global_indices, remote_indices, remote_index_base, partitions, tri_boundary_nodes, tri_global_indices, quad_boundary_nodes, quad_global_indices); EXPECT(!mesh.grid()); //Gmsh gmsh("out.msh", util::Config("coordinates", "xyz")); //gmsh.write(mesh); } SECTION("Build Mesh with an UnstructuredGrid from config") { const size_t nb_owned_nodes = std::count(ghosts.begin(), ghosts.end(), 0); std::vector owned_lonlats(2 * nb_owned_nodes); int counter = 0; for (size_t i = 0; i < lons.size(); ++i) { if (ghosts[i] == 0) { owned_lonlats[counter] = lons[i]; counter++; owned_lonlats[counter] = lats[i]; counter++; } } size_t nb_nodes_global = 0; mpi::comm().allReduce(nb_owned_nodes, nb_nodes_global, eckit::mpi::sum()); std::vector global_lonlats(2 * nb_nodes_global); eckit::mpi::Buffer buffer(mpi::comm().size()); mpi::comm().allGatherv(owned_lonlats.begin(), owned_lonlats.end(), buffer); global_lonlats = std::move(buffer.buffer); util::Config config{}; config.set("grid.type", "unstructured"); config.set("grid.xy", global_lonlats); config.set("validate", true); // const Mesh mesh = mesh_builder(lons, lats, ghosts, global_indices, remote_indices, remote_index_base, partitions, // tri_boundary_nodes, tri_global_indices, quad_boundary_nodes, quad_global_indices, // config); const Mesh mesh = mesh_builder( make_mdspan(global_indices), make_mdspan(lons), make_mdspan(lats), make_mdspan(lons), make_mdspan(lats), make_mdspan(ghosts), make_mdspan(partitions), make_mdspan(remote_indices), remote_index_base, make_mdspan(tri_global_indices), make_mdspan(tri_boundary_nodes), make_mdspan(quad_global_indices), make_mdspan(quad_boundary_nodes), global_index_base, config); helper::check_mesh_nodes_and_cells(mesh, lons, lats, ghosts, global_indices, remote_indices, remote_index_base, partitions, tri_boundary_nodes, tri_global_indices, quad_boundary_nodes, quad_global_indices); EXPECT(mesh.grid()); EXPECT(mesh.grid().type() == "unstructured"); } SECTION("Build Mesh with an UnstructuredGrid, with Grid assembled from MeshBuilder arguments") { util::Config config{}; config.set("grid.type", "unstructured"); // const Mesh mesh = mesh_builder(lons, lats, ghosts, global_indices, remote_indices, remote_index_base, partitions, // tri_boundary_nodes, tri_global_indices, quad_boundary_nodes, quad_global_indices, // config); const Mesh mesh = mesh_builder( make_mdspan(global_indices), make_mdspan(lons), make_mdspan(lats), make_mdspan(lons), make_mdspan(lats), make_mdspan(ghosts), make_mdspan(partitions), make_mdspan(remote_indices), remote_index_base, make_mdspan(tri_global_indices), make_mdspan(tri_boundary_nodes), make_mdspan(quad_global_indices), make_mdspan(quad_boundary_nodes), global_index_base, config); helper::check_mesh_nodes_and_cells(mesh, lons, lats, ghosts, global_indices, remote_indices, remote_index_base, partitions, tri_boundary_nodes, tri_global_indices, quad_boundary_nodes, quad_global_indices); EXPECT(mesh.grid()); EXPECT(mesh.grid().type() == "unstructured"); } } //----------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/mesh/fctest_mesh.F900000664000175000017500000000402315160212070020533 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. ! This File contains Unit Tests for testing the ! C++ / Fortran Interfaces to the Mesh Datastructure ! @author Willem Deconinck #include "fckit/fctest.h" ! ----------------------------------------------------------------------------- module fcta_Mesh_fixture use atlas_module use, intrinsic :: iso_c_binding implicit none end module fcta_Mesh_fixture ! ----------------------------------------------------------------------------- TESTSUITE_WITH_FIXTURE(fctest_atlas_Mesh,fcta_Mesh_fixture) ! ----------------------------------------------------------------------------- TESTSUITE_INIT call atlas_library%initialise() END_TESTSUITE_INIT ! ----------------------------------------------------------------------------- TESTSUITE_FINALIZE call atlas_library%finalise() END_TESTSUITE_FINALIZE ! ----------------------------------------------------------------------------- !!! WARNING ! THIS IS DEPRECATED AND SHOULD NOT BE USED AS EXAMPLE !!!! TEST( test_mesh_nodes ) implicit none type(atlas_Mesh) :: mesh type(atlas_mesh_Nodes) :: nodes integer(c_int) :: nb_nodes write(*,*) "test_function_space starting" mesh = atlas_Mesh() nodes = mesh%nodes() nb_nodes = nodes%size() FCTEST_CHECK_EQUAL( nb_nodes, 0 ) FCTEST_CHECK_EQUAL( nodes%size() , 0 ) FCTEST_CHECK( nodes%has_field("partition") ) FCTEST_CHECK( nodes%has_field("remote_idx") ) call nodes%resize(10) nb_nodes = nodes%size() FCTEST_CHECK_EQUAL( nb_nodes, 10 ) FCTEST_CHECK_EQUAL( nodes%size() , 10 ) call atlas_log%info( nodes%str() ) call mesh%final() call nodes%final() END_TEST ! ----------------------------------------------------------------------------- END_TESTSUITE atlas-0.46.0/src/tests/mesh/test_mesh_builder.cc0000664000175000017500000003000615160212070021757 0ustar alastairalastair/* * (C) Copyright 2023 UCAR. * * 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. */ #include #include #include #include "atlas/array/MakeView.h" #include "atlas/grid.h" #include "atlas/mesh/Elements.h" #include "atlas/mesh/HybridElements.h" #include "atlas/mesh/Mesh.h" #include "atlas/mesh/MeshBuilder.h" #include "atlas/mesh/Nodes.h" #include "atlas/util/Config.h" #include "tests/AtlasTestEnvironment.h" #include "tests/mesh/helper_mesh_builder.h" using namespace atlas::mesh; //#include "atlas/output/Gmsh.h" //using namespace atlas::output; namespace atlas { namespace test { //----------------------------------------------------------------------------- template mdspan> make_mdspan(std::vector& v) { return mdspan>{v.data(), v.size()}; } template mdspan> make_mdspan(std::vector>& v) { return mdspan>{reinterpret_cast(v.data()), v.size()}; } //----------------------------------------------------------------------------- CASE("test_tiny_mesh") { // small regional grid whose cell-centers are connected as (global nodes and cells): // // 1 - 5 ----- 6 // | 3 \ 1 /2| // 2 ----- 3 - 4 // constexpr size_t nb_nodes = 6; std::vector lons{{0.0, 0.0, 10.0, 15.0, 5.0, 15.0}}; std::vector lats{{5.0, 0.0, 0.0, 0.0, 5.0, 5.0}}; std::vector ghosts(nb_nodes, 0); // all points owned std::vector global_indices(nb_nodes); std::iota(global_indices.begin(), global_indices.end(), 1); // 1-based numbering const idx_t remote_index_base = 0; // 0-based numbering std::vector remote_indices(nb_nodes); std::iota(remote_indices.begin(), remote_indices.end(), remote_index_base); std::vector partitions(nb_nodes, 0); // all points on proc 0 // triangles std::vector> tri_boundary_nodes = {{{3, 6, 5}}, {{3, 4, 6}}}; std::vector tri_global_indices = {1, 2}; // quads std::vector> quad_boundary_nodes = {{{1, 2, 3, 5}}}; std::vector quad_global_indices = {3}; gidx_t global_index_base = 1; const MeshBuilder mesh_builder{}; // const Mesh mesh = mesh_builder(lons, lats, ghosts, global_indices, remote_indices, remote_index_base, partitions, // tri_boundary_nodes, tri_global_indices, quad_boundary_nodes, quad_global_indices); const Mesh mesh = mesh_builder( make_mdspan(global_indices), make_mdspan(lons), make_mdspan(lats), make_mdspan(lons), make_mdspan(lats), make_mdspan(ghosts), make_mdspan(partitions), make_mdspan(remote_indices), remote_index_base, make_mdspan(tri_global_indices), make_mdspan(tri_boundary_nodes), make_mdspan(quad_global_indices), make_mdspan(quad_boundary_nodes), global_index_base); helper::check_mesh_nodes_and_cells(mesh, lons, lats, ghosts, global_indices, remote_indices, remote_index_base, partitions, tri_boundary_nodes, tri_global_indices, quad_boundary_nodes, quad_global_indices); //Gmsh gmsh("out.msh", util::Config("coordinates", "xyz")); //gmsh.write(mesh); } CASE("test_cs_c2_mesh_serial") { // coordinates of C2 lfric cubed-sphere grid: grid("CS-LFR-2"); std::vector lons = {337.5, 22.5, 337.5, 22.5, // +x 67.5, 112.5, 67.5, 112.5, // +y 202.5, 202.5, 157.5, 157.5, // -x 292.5, 292.5, 247.5, 247.5, // -y 315, 45, 225, 135, // +z 315, 225, 45, 135}; // -z std::vector lats = {-20.941, -20.941, 20.941, 20.941, // +x -20.941, -20.941, 20.941, 20.941, // +y -20.941, 20.941, -20.941, 20.941, // -x -20.941, 20.941, -20.941, 20.941, // -y 59.6388, 59.6388, 59.6388, 59.6388, // +z -59.6388, -59.6388, -59.6388, -59.6388}; // -z gidx_t global_index_base = 1; std::vector ghosts(24, 0); std::vector global_indices(24); std::iota(global_indices.begin(), global_indices.end(), global_index_base); const idx_t remote_index_base = 1; // test with 1-based numbering std::vector remote_indices(24); std::iota(remote_indices.begin(), remote_indices.end(), remote_index_base); std::vector partitions(24, 0); // triangles std::vector> tri_boundary_nodes = {//corners {{17, 14, 3}}, {{18, 4, 7}}, {{20, 8, 12}}, {{19, 10, 16}}, {{21, 1, 13}}, {{23, 5, 2}}, {{24, 11, 6}}, {{22, 15, 9}}}; std::vector tri_global_indices(8); std::iota(tri_global_indices.begin(), tri_global_indices.end(), global_index_base); // quads std::vector> quad_boundary_nodes = {// faces {{1, 2, 4, 3}}, {{5, 6, 8, 7}}, {{11, 9, 10, 12}}, {{15, 13, 14, 16}}, {{17, 18, 20, 19}}, {{21, 22, 24, 23}}, // edges between faces {{2, 5, 7, 4}}, {{6, 11, 12, 8}}, {{9, 15, 16, 10}}, {{13, 1, 3, 14}}, {{7, 8, 20, 18}}, {{12, 10, 19, 20}}, {{16, 14, 17, 19}}, {{3, 4, 18, 17}}, {{23, 24, 6, 5}}, {{24, 22, 9, 11}}, {{22, 21, 13, 15}}, {{21, 23, 2, 1}}}; std::vector quad_global_indices(18); std::iota(quad_global_indices.begin(), quad_global_indices.end(), global_index_base + tri_global_indices.size()); const MeshBuilder mesh_builder{}; SECTION("Build Mesh without a Grid") { const Mesh mesh = mesh_builder( make_mdspan(global_indices), make_mdspan(lons), make_mdspan(lats), make_mdspan(lons), make_mdspan(lats), make_mdspan(ghosts), make_mdspan(partitions), make_mdspan(remote_indices), remote_index_base, make_mdspan(tri_global_indices), make_mdspan(tri_boundary_nodes), make_mdspan(quad_global_indices), make_mdspan(quad_boundary_nodes), global_index_base); helper::check_mesh_nodes_and_cells(mesh, lons, lats, ghosts, global_indices, remote_indices, remote_index_base, partitions, tri_boundary_nodes, tri_global_indices, quad_boundary_nodes, quad_global_indices); EXPECT(!mesh.grid()); //Gmsh gmsh("out.msh", util::Config("coordinates", "xyz")); //gmsh.write(mesh); } SECTION("Build Mesh with an UnstructuredGrid from config") { std::vector lonlats(2 * lons.size()); int counter = 0; for (size_t i = 0; i < lons.size(); ++i) { lonlats[counter] = lons[i]; counter++; lonlats[counter] = lats[i]; counter++; } util::Config config{}; config.set("grid.type", "unstructured"); config.set("grid.xy", lonlats); config.set("validate", true); const Mesh mesh = mesh_builder( make_mdspan(global_indices), make_mdspan(lons), make_mdspan(lats), make_mdspan(lons), make_mdspan(lats), make_mdspan(ghosts), make_mdspan(partitions), make_mdspan(remote_indices), remote_index_base, make_mdspan(tri_global_indices), make_mdspan(tri_boundary_nodes), make_mdspan(quad_global_indices), make_mdspan(quad_boundary_nodes), global_index_base, config); helper::check_mesh_nodes_and_cells(mesh, lons, lats, ghosts, global_indices, remote_indices, remote_index_base, partitions, tri_boundary_nodes, tri_global_indices, quad_boundary_nodes, quad_global_indices); EXPECT(mesh.grid()); EXPECT(mesh.grid().type() == "unstructured"); } SECTION("Build Mesh with an UnstructuredGrid, with Grid assembled from MeshBuilder arguments") { util::Config config{}; config.set("grid.type", "unstructured"); const Mesh mesh = mesh_builder( make_mdspan(global_indices), make_mdspan(lons), make_mdspan(lats), make_mdspan(lons), make_mdspan(lats), make_mdspan(ghosts), make_mdspan(partitions), make_mdspan(remote_indices), remote_index_base, make_mdspan(tri_global_indices), make_mdspan(tri_boundary_nodes), make_mdspan(quad_global_indices), make_mdspan(quad_boundary_nodes), global_index_base, config); helper::check_mesh_nodes_and_cells(mesh, lons, lats, ghosts, global_indices, remote_indices, remote_index_base, partitions, tri_boundary_nodes, tri_global_indices, quad_boundary_nodes, quad_global_indices); EXPECT(mesh.grid()); EXPECT(mesh.grid().type() == "unstructured"); } SECTION("Build Mesh with a CubedSphereGrid from config") { // but can't validate this util::Config config{}; config.set("grid.name", "CS-LFR-2"); const Mesh mesh = mesh_builder( make_mdspan(global_indices), make_mdspan(lons), make_mdspan(lats), make_mdspan(lons), make_mdspan(lats), make_mdspan(ghosts), make_mdspan(partitions), make_mdspan(remote_indices), remote_index_base, make_mdspan(tri_global_indices), make_mdspan(tri_boundary_nodes), make_mdspan(quad_global_indices), make_mdspan(quad_boundary_nodes), global_index_base, config); helper::check_mesh_nodes_and_cells(mesh, lons, lats, ghosts, global_indices, remote_indices, remote_index_base, partitions, tri_boundary_nodes, tri_global_indices, quad_boundary_nodes, quad_global_indices); EXPECT(mesh.grid()); EXPECT(mesh.grid().type() == "cubedsphere"); } } //----------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/mesh/CMakeLists.txt0000664000175000017500000001214215160212070020510 0ustar alastairalastair# (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. if( HAVE_FCTEST ) add_fctest( TARGET atlas_fctest_mesh LINKER_LANGUAGE Fortran CONDITION NOT atlas_fctest_mesh_DISABLED SOURCES fctest_mesh.F90 LIBS atlas_f ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) add_fctest( TARGET atlas_fctest_meshgen LINKER_LANGUAGE Fortran CONDITION ON SOURCES fctest_meshgen.F90 LIBS atlas_f ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) add_fctest( TARGET atlas_fctest_mesh_triangular_mesh_builder LINKER_LANGUAGE Fortran CONDITION ON SOURCES fctest_mesh_triangular_mesh_builder.F90 LIBS atlas_f ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) add_fctest( TARGET atlas_fctest_connectivity LINKER_LANGUAGE Fortran SOURCES fctest_connectivity.F90 LIBS atlas_f ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) add_fctest( TARGET atlas_fctest_elements LINKER_LANGUAGE Fortran SOURCES fctest_elements.F90 LIBS atlas_f ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) endif() ecbuild_add_test( TARGET atlas_test_parfields MPI 2 CONDITION eckit_HAVE_MPI SOURCES test_parfields.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) ecbuild_add_test( TARGET atlas_test_halo MPI 5 CONDITION eckit_HAVE_MPI AND MPI_SLOTS GREATER_EQUAL 5 SOURCES test_halo.cc ../TestMeshes.h LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) ecbuild_add_test( TARGET atlas_test_distmesh MPI 5 CONDITION eckit_HAVE_MPI AND MPI_SLOTS GREATER_EQUAL 5 SOURCES test_distmesh.cc ../TestMeshes.h LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) ecbuild_add_test( TARGET atlas_test_cgal_mesh_gen_from_points SOURCES test_cgal_mesh_gen_from_points.cc CONDITION atlas_HAVE_TESSELATION LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) ecbuild_add_test( TARGET atlas_test_mesh_build_edges SOURCES test_mesh_build_edges.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) ecbuild_add_test( TARGET atlas_test_mesh_node2cell MPI 4 CONDITION eckit_HAVE_MPI AND MPI_SLOTS GREATER_EQUAL 4 SOURCES test_mesh_node2cell.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) ecbuild_add_test( TARGET atlas_test_meshgen_splitcomm MPI 4 CONDITION eckit_HAVE_MPI AND MPI_SLOTS GREATER_EQUAL 4 SOURCES test_meshgen_splitcomm.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) foreach( test connectivity stream_connectivity elements ll meshgen3d rgg ) ecbuild_add_test( TARGET atlas_test_${test} SOURCES test_${test}.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) endforeach() ecbuild_add_test( TARGET atlas_test_healpixmeshgen SOURCES test_healpixmeshgen.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) ecbuild_add_test( TARGET atlas_test_cubedsphere_meshgen MPI 8 CONDITION eckit_HAVE_MPI AND MPI_SLOTS GREATER_EQUAL 8 AND atlas_HAVE_ATLAS_INTERPOLATION SOURCES test_cubedsphere_meshgen.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) if( TEST atlas_test_cubedsphere_meshgen ) set_tests_properties( atlas_test_cubedsphere_meshgen PROPERTIES TIMEOUT 60 ) endif() ecbuild_add_test( TARGET atlas_test_mesh_builder SOURCES test_mesh_builder.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) ecbuild_add_test( TARGET atlas_test_mesh_builder_parallel MPI 6 CONDITION eckit_HAVE_MPI AND MPI_SLOTS GREATER_EQUAL 6 SOURCES test_mesh_builder_parallel.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) ecbuild_add_test( TARGET atlas_test_mesh_triangular_mesh_builder SOURCES test_mesh_triangular_mesh_builder.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) ecbuild_add_executable( TARGET atlas_test_mesh_reorder SOURCES test_mesh_reorder.cc LIBS atlas NOINSTALL ) 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} ) foreach( test footprint ) ecbuild_add_test( TARGET atlas_test_${test} SOURCES test_${test}.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) endforeach() atlas_add_hic_test( TARGET atlas_test_connectivity_kernel SOURCES test_connectivity_kernel.hic LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) ecbuild_add_test( TARGET atlas_test_pentagon_element SOURCES test_pentagon_element.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} CONDITION atlas_HAVE_ATLAS_INTERPOLATION ) atlas-0.46.0/src/tests/mesh/test_stream_connectivity.cc0000664000175000017500000001116115160212070023407 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "eckit/io/Buffer.h" #include "eckit/serialisation/ResizableMemoryStream.h" #include "atlas/mesh/Connectivity.h" #include "tests/AtlasTestEnvironment.h" using namespace atlas::mesh; namespace atlas { namespace test { //----------------------------------------------------------------------------- CASE("test_stream_irregular_connectivity") { eckit::Buffer b{0}; eckit::ResizableMemoryStream s{b}; // Create stream { IrregularConnectivity conn("bla"); constexpr idx_t vals[4] = {2, 3, 5, 6}; conn.add(1, 4, vals, /* fortran-array = */ false); constexpr idx_t vals2[6] = {1, 3, 4, 3, 7, 8}; conn.add(2, 3, vals2, /* fortran-array = */ false); s << conn; } s.rewind(); // Read from stream { IrregularConnectivity conn(s); EXPECT(conn.rows() == 3); EXPECT(conn.cols(0) == 4); EXPECT(conn.cols(1) == 3); EXPECT(conn.cols(2) == 3); EXPECT(conn(0, 0) == 2); EXPECT(conn(0, 1) == 3); EXPECT(conn(0, 2) == 5); EXPECT(conn(0, 3) == 6); EXPECT(conn(1, 0) == 1); EXPECT(conn(1, 1) == 3); EXPECT(conn(1, 2) == 4); EXPECT(conn(2, 0) == 3); EXPECT(conn(2, 1) == 7); EXPECT(conn(2, 2) == 8); EXPECT(conn.name() == "bla"); } s.rewind(); // Read from stream { IrregularConnectivity conn; s >> conn; EXPECT(conn.rows() == 3); EXPECT(conn.cols(0) == 4); EXPECT(conn.cols(1) == 3); EXPECT(conn.cols(2) == 3); EXPECT(conn(0, 0) == 2); EXPECT(conn(0, 1) == 3); EXPECT(conn(0, 2) == 5); EXPECT(conn(0, 3) == 6); EXPECT(conn(1, 0) == 1); EXPECT(conn(1, 1) == 3); EXPECT(conn(1, 2) == 4); EXPECT(conn(2, 0) == 3); EXPECT(conn(2, 1) == 7); EXPECT(conn(2, 2) == 8); EXPECT(conn.name() == "bla"); } } //----------------------------------------------------------------------------- CASE("test_stream_block_connectivity") { eckit::Buffer b{0}; eckit::ResizableMemoryStream s{b}; // Create stream { BlockConnectivity conn(2, 3, {1, 3, 4, 3, 7, 8}); s << conn; } s.rewind(); // Read from stream { BlockConnectivity conn(s); EXPECT(conn.rows() == 2); EXPECT(conn.cols() == 3); EXPECT(conn(0, 0) == 1); EXPECT(conn(0, 1) == 3); EXPECT(conn(0, 2) == 4); EXPECT(conn(1, 0) == 3); EXPECT(conn(1, 1) == 7); EXPECT(conn(1, 2) == 8); } s.rewind(); // Read from stream { BlockConnectivity conn; s >> conn; EXPECT(conn.rows() == 2); EXPECT(conn.cols() == 3); EXPECT(conn(0, 0) == 1); EXPECT(conn(0, 1) == 3); EXPECT(conn(0, 2) == 4); EXPECT(conn(1, 0) == 3); EXPECT(conn(1, 1) == 7); EXPECT(conn(1, 2) == 8); } } CASE("test_stream_multiblock_connectivity") { eckit::Buffer b{0}; eckit::ResizableMemoryStream s{b}; // Create stream { MultiBlockConnectivity conn("mbc"); conn.add(BlockConnectivity{3, 5, {3, 7, 1, 4, 5, 6, 4, 56, 8, 4, 1, 3, 76, 4, 3}}); conn.add(BlockConnectivity{3, 2, {4, 75, 65, 45, 51, 35}}); s << conn; } s.rewind(); // Read from stream { MultiBlockConnectivity conn{s}; EXPECT(conn.name() == "mbc"); EXPECT(conn.blocks() == 2); EXPECT(conn(3, 1) == 75); EXPECT(conn(4, 1) == 45); EXPECT(conn(5, 0) == 51); EXPECT(conn(1, 0, 1) == 75); EXPECT(conn(1, 1, 1) == 45); EXPECT(conn(1, 2, 0) == 51); } s.rewind(); // Read from stream { MultiBlockConnectivity conn; s >> conn; EXPECT(conn.name() == "mbc"); EXPECT(conn.blocks() == 2); EXPECT(conn(3, 1) == 75); EXPECT(conn(4, 1) == 45); EXPECT(conn(5, 0) == 51); EXPECT(conn(1, 0, 1) == 75); EXPECT(conn(1, 1, 1) == 45); EXPECT(conn(1, 2, 0) == 51); } } //----------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/mesh/test_meshgen3d.cc0000664000175000017500000000514315160212070021176 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "atlas/array.h" #include "atlas/grid.h" #include "atlas/mesh/Mesh.h" #include "atlas/mesh/Nodes.h" #include "atlas/meshgenerator.h" #include "atlas/option/Options.h" #include "atlas/output/Gmsh.h" #include "tests/AtlasTestEnvironment.h" using namespace atlas::output; using namespace atlas::meshgenerator; using namespace atlas::grid; namespace atlas { namespace test { //----------------------------------------------------------------------------- CASE("test_create_mesh_simple") { auto mesh = Mesh{Grid{"O32"}}; Gmsh{"O32.msh"}.write(mesh); } CASE("test_create_mesh") { Mesh m; util::Config opts; opts.set("3d", true); ///< creates links along date-line opts.set("include_pole", true); ///< triangulate the pole point StructuredMeshGenerator generate(opts); // opts.set("nb_parts",1); // default = 1 // opts.set("part", 0); // default = 0 m = generate(Grid("N24")); Grid grid = m.grid(); std::cout << grid.spec() << std::endl; Gmsh gmsh("out.msh", util::Config("coordinates", "xyz")); gmsh.write(m); Field f1("f1", array::make_datatype(), array::make_shape(m.nodes().size())); auto v1 = array::make_view(f1); for (idx_t i = 0; i < v1.size(); ++i) { v1[i] = i + 1; } gmsh.write(f1); std::vector v2(m.nodes().size()); for (size_t i = 0; i < v2.size(); ++i) { v2[i] = i + 1; } Field f2("f2", v2.data(), array::make_shape(v2.size())); gmsh.write(f2); } //----------------------------------------------------------------------------- CASE("METV-3396 / ATLAS-375") { auto opts = util::Config("triangulate", true); StructuredMeshGenerator generate(opts); SECTION("N16 [-180,180]") { auto mesh = generate(Grid("N16", RectangularDomain({-180., 180.}, {-90., 90.}))); Gmsh{"N16.msh"}.write(mesh); } SECTION("O8 [-45,315]") { auto mesh = generate(Grid("O8", RectangularDomain({-45., 315.}, {-90., 90.}))); Gmsh{"O8.msh"}.write(mesh); } } //----------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/mesh/test_parfields.cc0000664000175000017500000001573615160212070021303 0ustar alastairalastair/* * (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 "atlas/parallel/mpi/mpi.h" #include "atlas/array.h" #include "atlas/field/Field.h" #include "atlas/grid.h" #include "atlas/grid/detail/partitioner/EqualRegionsPartitioner.h" #include "atlas/library/config.h" #include "atlas/mesh/Mesh.h" #include "atlas/mesh/Nodes.h" #include "atlas/mesh/actions/BuildParallelFields.h" #include "atlas/mesh/actions/BuildPeriodicBoundaries.h" #include "atlas/meshgenerator.h" #include "atlas/output/Gmsh.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/util/CoordinateEnums.h" #include "atlas/util/Metadata.h" #include "tests/AtlasTestEnvironment.h" using Topology = atlas::mesh::Nodes::Topology; using namespace atlas::array; using namespace atlas::output; using namespace atlas::meshgenerator; namespace atlas { namespace test { //----------------------------------------------------------------------------- class IsGhost { public: IsGhost(const mesh::Nodes& nodes): part_(make_view(nodes.partition())), ridx_(make_indexview(nodes.remote_index())), mypart_(mpi::comm().rank()) {} bool operator()(idx_t idx) const { if (part_(idx) != mypart_) { return true; } if (ridx_(idx) != idx) { return true; } return false; } private: array::ArrayView part_; IndexView ridx_; int mypart_; }; #define DISABLE if (0) #define ENABLE if (1) //----------------------------------------------------------------------------- CASE("test1") { Mesh m; mesh::Nodes& nodes = m.nodes(); nodes.resize(10); auto xy = make_view(nodes.xy()); auto lonlat = make_view(nodes.lonlat()); auto glb_idx = make_view(nodes.global_index()); auto part = make_view(nodes.partition()); auto flags = make_view(nodes.flags()); flags.assign(Topology::NONE); // This is typically available glb_idx(0) = 1; part(0) = 0; glb_idx(1) = 2; part(1) = 0; glb_idx(2) = 3; part(2) = 0; glb_idx(3) = 4; part(3) = 0; glb_idx(4) = 5; part(4) = 0; glb_idx(5) = 6; part(5) = std::min(1, (int)mpi::comm().size() - 1); glb_idx(6) = 7; part(6) = std::min(1, (int)mpi::comm().size() - 1); glb_idx(7) = 8; part(7) = std::min(1, (int)mpi::comm().size() - 1); glb_idx(8) = 9; part(8) = std::min(1, (int)mpi::comm().size() - 1); glb_idx(9) = 10; part(9) = std::min(1, (int)mpi::comm().size() - 1); xy(0, XX) = 0.; xy(0, YY) = 80.; Topology::set(flags(0), Topology::BC | Topology::WEST); xy(1, XX) = 0.; xy(1, YY) = -80.; Topology::set(flags(1), Topology::BC | Topology::WEST); xy(2, XX) = 90.; xy(2, YY) = 80.; xy(3, XX) = 90.; xy(3, YY) = -80.; xy(4, XX) = 180.; xy(4, YY) = 80.; xy(5, XX) = 180.; xy(5, YY) = -80.; xy(6, XX) = 270.; xy(6, YY) = 80.; xy(7, XX) = 270.; xy(7, YY) = -80.; xy(8, XX) = 360.; xy(8, YY) = 80.; Topology::set(flags(8), Topology::BC | Topology::EAST); xy(9, XX) = 360.; xy(9, YY) = -80.; Topology::set(flags(9), Topology::BC | Topology::EAST); for (idx_t n = 0; n < xy.shape(0); ++n) { lonlat(n, LON) = xy(n, XX); lonlat(n, LAT) = xy(n, YY); } mesh::actions::build_parallel_fields(m); EXPECT(nodes.has_field("remote_idx")); auto loc = make_indexview(nodes.remote_index()); EXPECT(loc(0) == 0); EXPECT(loc(1) == 1); EXPECT(loc(2) == 2); EXPECT(loc(3) == 3); EXPECT(loc(4) == 4); EXPECT(loc(5) == 5); EXPECT(loc(6) == 6); EXPECT(loc(7) == 7); EXPECT(loc(8) == 8); EXPECT(loc(9) == 9); test::IsGhost is_ghost(m.nodes()); switch (mpi::comm().rank()) { case 0: EXPECT(is_ghost(0) == false); EXPECT(is_ghost(1) == false); EXPECT(is_ghost(2) == false); EXPECT(is_ghost(3) == false); EXPECT(is_ghost(4) == false); EXPECT(is_ghost(5) == true); EXPECT(is_ghost(6) == true); EXPECT(is_ghost(7) == true); EXPECT(is_ghost(8) == true); EXPECT(is_ghost(9) == true); break; case 1: EXPECT(is_ghost(0) == true); EXPECT(is_ghost(1) == true); EXPECT(is_ghost(2) == true); EXPECT(is_ghost(3) == true); EXPECT(is_ghost(4) == true); EXPECT(is_ghost(5) == false); EXPECT(is_ghost(6) == false); EXPECT(is_ghost(7) == false); EXPECT(is_ghost(8) == false); EXPECT(is_ghost(9) == false); break; } mesh::actions::build_periodic_boundaries(m); // points 8 and 9 are periodic slave points of points 0 and 1 EXPECT(part(8) == 0); EXPECT(part(9) == 0); EXPECT(loc(8) == 0); EXPECT(loc(9) == 1); if (mpi::comm().rank() == 1) { EXPECT(is_ghost(8) == true); EXPECT(is_ghost(9) == true); } } CASE("test2") { util::Config meshgen_options; meshgen_options.set("angle", 27.5); meshgen_options.set("triangulate", false); meshgen_options.set("partitioner", "equal_regions"); StructuredMeshGenerator generate(meshgen_options); Mesh m = generate(Grid("N32")); mesh::actions::build_parallel_fields(m); mesh::Nodes& nodes = m.nodes(); test::IsGhost is_ghost(nodes); idx_t nb_ghost = 0; for (idx_t jnode = 0; jnode < nodes.size(); ++jnode) { if (is_ghost(jnode)) { ++nb_ghost; } } ATLAS_DEBUG_VAR(nb_ghost); if (mpi::comm().rank() == 0) { EXPECT(nb_ghost == 128); // South boundary of Northern hemisphere } if (mpi::comm().rank() == 1) { EXPECT(nb_ghost == 0); // Southern hemisphere has no ghosts } mesh::actions::build_periodic_boundaries(m); int nb_periodic = -nb_ghost; for (idx_t jnode = 0; jnode < nodes.size(); ++jnode) { if (is_ghost(jnode)) { ++nb_periodic; } } ATLAS_DEBUG_VAR(nb_periodic); if (mpi::comm().rank() == 0) { EXPECT(nb_periodic == 33); // Periodic East boundary of Northern hemisphere } // (plus one point south) if (mpi::comm().rank() == 1) { EXPECT(nb_periodic == 32); // Periodic East boundary of Southern hemisphere } Gmsh("periodic.msh", util::Config("info", true)).write(m); } //----------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/acc/0000775000175000017500000000000015160212070015542 5ustar alastairalastairatlas-0.46.0/src/tests/acc/fctest_unified_memory_with_openacc.F900000664000175000017500000000435515160212070025137 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. ! @author Willem Deconinck #include "fckit/fctest.h" ! ----------------------------------------------------------------------------- module allocate_unified_module interface subroutine allocate_unified_impl( a, N ) bind(C,name="allocate_unified_impl") use, intrinsic :: iso_c_binding type(c_ptr) :: a integer(c_int), value :: N end subroutine end interface contains subroutine allocate_unified( a, N ) use, intrinsic :: iso_c_binding real(c_double), pointer :: a(:) integer(c_int) :: N type(c_ptr) :: value_cptr call allocate_unified_impl(value_cptr, N) call c_f_pointer(value_cptr,a,(/N/)) end subroutine end module TESTSUITE(fctest_atlas_openacc_with_unified_memory) ! ----------------------------------------------------------------------------- TESTSUITE_INIT END_TESTSUITE_INIT ! ----------------------------------------------------------------------------- TESTSUITE_FINALIZE END_TESTSUITE_FINALIZE ! ----------------------------------------------------------------------------- TEST( test_openacc_with_unified_memory ) use, intrinsic :: iso_c_binding use allocate_unified_module implicit none real(c_double), pointer :: value(:) integer :: i integer :: N N = 100 call allocate_unified(value, N) ! On host: do i=1,N value(i) = i enddo ! On device (automatic copies on access because unified memory) !$acc kernels do i=1,N value(i) = value(i) * 10 enddo !$acc end kernels ! On host: do i=1,N FCTEST_CHECK_CLOSE( value(i), real( 10*i, c_double), real( 0.0001, c_double ) ) enddo END_TEST ! ----------------------------------------------------------------------------- ! ! NOTE: To be certain that cuda kernel is being generated and launched, run this ! test with "nvprof -s " ! ! ----------------------------------------------------------------------------- END_TESTSUITE atlas-0.46.0/src/tests/acc/fctest_connectivity_openacc.F900000664000175000017500000002201015160212070023573 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. ! This File contains Unit Tests for testing the ! C++ / Fortran Interfaces to the Connectivity ! @author Willem Deconinck #include "fckit/fctest.h" #include "atlas/atlas_f.h" ! ----------------------------------------------------------------------------- TESTSUITE(fctest_atlas_Connectivity_acc) ! ----------------------------------------------------------------------------- TESTSUITE_INIT() use fckit_module call fckit_main%init() END_TESTSUITE_INIT TESTSUITE_FINALIZE() #if ATLAS_FCKIT_VERSION_AT_LEAST(0,13,3) || ATLAS_FCKIT_DEVELOP use fckit_module call fckit_mpi%finalize() #endif write(0,*) "END TESTSUITE" END_TESTSUITE_FINALIZE TEST( test_connectivity ) #if 1 use fckit_module use atlas_connectivity_module use atlas_kinds_module use atlas_field_module use, intrinsic :: iso_c_binding implicit none type(atlas_Connectivity) :: connectivity integer(ATLAS_KIND_IDX), pointer :: padded(:,:), row(:), data(:,:) integer(ATLAS_KIND_IDX), pointer :: cols(:) integer(c_int) :: ncols type(atlas_Field) :: field real(8), pointer :: values(:) integer :: i, j call fckit_log%info("test_connectivity starting") connectivity = atlas_Connectivity("hybrid") FCTEST_CHECK_EQUAL( connectivity%owners(), 1 ) FCTEST_CHECK_EQUAL(connectivity%name(),"hybrid") FCTEST_CHECK_EQUAL(connectivity%rows(),0) FCTEST_CHECK_EQUAL(connectivity%missing_value(),0) call connectivity%add(2,4, & & [ 1, 2, 3, 4, & & 5, 6, 7, 8 ] ) FCTEST_CHECK_EQUAL(connectivity%mincols(),4) FCTEST_CHECK_EQUAL(connectivity%maxcols(),4) FCTEST_CHECK_EQUAL(connectivity%rows(), 2) call connectivity%data(data,ncols) FCTEST_CHECK_EQUAL(ncols,4) FCTEST_CHECK_EQUAL(data(1,1), 1) FCTEST_CHECK_EQUAL(data(2,1), 2) FCTEST_CHECK_EQUAL(data(3,1), 3) FCTEST_CHECK_EQUAL(data(4,1), 4) FCTEST_CHECK_EQUAL(data(1,2), 5) FCTEST_CHECK_EQUAL(data(2,2), 6) FCTEST_CHECK_EQUAL(data(3,2), 7) FCTEST_CHECK_EQUAL(data(4,2), 8) field = atlas_Field(atlas_real(8),shape=[20]) call field%data(values) values(:) = 0. call field%update_device() !$acc data present(values) !$acc kernels do i=1,2 do j=1,4 values(i) = values(i) + data(j,i) end do enddo !$acc end kernels !$acc end data call field%update_host() FCTEST_CHECK_EQUAL( values(1) , 10._8 ) FCTEST_CHECK_EQUAL( values(2) , 26._8 ) values(:) = 0. call field%update_device() call connectivity%add(2,3, & & [ 9, 10, 11, & & 12, 13, 14 ] ) FCTEST_CHECK_EQUAL(connectivity%mincols(),3) FCTEST_CHECK_EQUAL(connectivity%maxcols(),4) FCTEST_CHECK_EQUAL(connectivity%rows(), 4) call connectivity%add(2,4) call connectivity%add(2,3) !============= Functional access =============! FCTEST_CHECK_EQUAL(connectivity%value(1,1), 1) FCTEST_CHECK_EQUAL(connectivity%value(2,1), 2) FCTEST_CHECK_EQUAL(connectivity%value(3,1), 3) FCTEST_CHECK_EQUAL(connectivity%value(4,1), 4) FCTEST_CHECK_EQUAL(connectivity%value(1,2), 5) FCTEST_CHECK_EQUAL(connectivity%value(2,2), 6) FCTEST_CHECK_EQUAL(connectivity%value(3,2), 7) FCTEST_CHECK_EQUAL(connectivity%value(4,2), 8) FCTEST_CHECK_EQUAL(connectivity%value(1,3), 9) FCTEST_CHECK_EQUAL(connectivity%value(2,3), 10) FCTEST_CHECK_EQUAL(connectivity%value(3,3), 11) FCTEST_CHECK_EQUAL(connectivity%value(1,4), 12) FCTEST_CHECK_EQUAL(connectivity%value(2,4), 13) FCTEST_CHECK_EQUAL(connectivity%value(3,4), 14) !============= Padded Data pointer access =============! call connectivity%padded_data(padded,cols) FCTEST_CHECK_EQUAL(padded(1,1), 1) FCTEST_CHECK_EQUAL(padded(2,1), 2) FCTEST_CHECK_EQUAL(padded(3,1), 3) FCTEST_CHECK_EQUAL(padded(4,1), 4) FCTEST_CHECK_EQUAL(padded(1,2), 5) FCTEST_CHECK_EQUAL(padded(2,2), 6) FCTEST_CHECK_EQUAL(padded(3,2), 7) FCTEST_CHECK_EQUAL(padded(4,2), 8) FCTEST_CHECK_EQUAL(padded(1,3), 9) FCTEST_CHECK_EQUAL(padded(2,3), 10) FCTEST_CHECK_EQUAL(padded(3,3), 11) FCTEST_CHECK_EQUAL(padded(1,4), 12) FCTEST_CHECK_EQUAL(padded(2,4), 13) FCTEST_CHECK_EQUAL(padded(3,4), 14) !$acc data present(values) !$acc kernels do i=1,4 do j=1,cols(i) values(i) = values(i) + padded(j,i) end do enddo !$acc end kernels !$acc end data call field%update_host() FCTEST_CHECK_EQUAL( values(1) , 10._8 ) FCTEST_CHECK_EQUAL( values(2) , 26._8 ) FCTEST_CHECK_EQUAL( values(3) , 30._8 ) FCTEST_CHECK_EQUAL( values(4) , 39._8 ) values(:) = 0. call field%update_device() #endif call connectivity%final() END_TEST ! ----------------------------------------------------------------------------- TEST( test_multiblockconnectivity ) #if 1 use fckit_module use atlas_connectivity_module use atlas_kinds_module use atlas_field_module use, intrinsic :: iso_c_binding implicit none type(atlas_MultiBlockConnectivity) :: multiblock type(atlas_BlockConnectivity) :: block integer(ATLAS_KIND_IDX), pointer :: data(:,:), padded(:,:) integer(ATLAS_KIND_IDX), pointer :: cols(:) type(atlas_Field) :: field real(8), pointer :: values(:) integer :: i, j type(atlas_Connectivity) :: base call fckit_log%info("test_multiblockconnectivity starting") field = atlas_Field(atlas_real(8),shape=[20]) call field%data(values) values(:) = 0 call field%update_device() multiblock = atlas_MultiBlockConnectivity() FCTEST_CHECK_EQUAL( multiblock%owners(), 1 ) FCTEST_CHECK_EQUAL(multiblock%name(),"") FCTEST_CHECK_EQUAL(multiblock%rows(),0) FCTEST_CHECK_EQUAL(multiblock%blocks(),0) call multiblock%add(2,4, & & [ 1, 2, 3, 4, & & 5, 6, 7, 8 ] ) FCTEST_CHECK_EQUAL(multiblock%mincols(),4) FCTEST_CHECK_EQUAL(multiblock%maxcols(),4) FCTEST_CHECK_EQUAL(multiblock%rows(), 2) FCTEST_CHECK_EQUAL(multiblock%blocks(), 1) call multiblock%add(2,3, & & [ 9, 10, 11, & & 12, 13, 14 ] ) FCTEST_CHECK_EQUAL(multiblock%mincols(),3) FCTEST_CHECK_EQUAL(multiblock%maxcols(),4) FCTEST_CHECK_EQUAL(multiblock%blocks(), 2) block = multiblock%block(1_ATLAS_KIND_IDX) !FCTEST_CHECK_EQUAL( block%owners(), 2 ) FCTEST_CHECK_EQUAL( block%rows(), 2 ) FCTEST_CHECK_EQUAL( block%cols(), 4 ) call block%data(data) FCTEST_CHECK_EQUAL(data(1,1), 1) FCTEST_CHECK_EQUAL(data(2,1), 2) FCTEST_CHECK_EQUAL(data(3,1), 3) FCTEST_CHECK_EQUAL(data(4,1), 4) FCTEST_CHECK_EQUAL(data(1,2), 5) FCTEST_CHECK_EQUAL(data(2,2), 6) FCTEST_CHECK_EQUAL(data(3,2), 7) FCTEST_CHECK_EQUAL(data(4,2), 8) !$acc data present(values) !$acc kernels do i=1,2 do j=1,4 values(i) = values(i) + data(j,i) end do enddo !$acc end kernels !$acc end data call field%update_host() FCTEST_CHECK_EQUAL( values(1) , 10._8 ) FCTEST_CHECK_EQUAL( values(2) , 26._8 ) values(:) = 0. call field%update_device() block = multiblock%block(2_ATLAS_KIND_IDX) !FCTEST_CHECK_EQUAL( block%owners(), 2 ) FCTEST_CHECK_EQUAL( block%rows(), 2 ) FCTEST_CHECK_EQUAL( block%cols(), 3 ) call block%data(data) FCTEST_CHECK_EQUAL(data(1,1), 9) FCTEST_CHECK_EQUAL(data(2,1), 10) FCTEST_CHECK_EQUAL(data(3,1), 11) FCTEST_CHECK_EQUAL(data(1,2), 12) FCTEST_CHECK_EQUAL(data(2,2), 13) FCTEST_CHECK_EQUAL(data(3,2), 14) !$acc data present(values) !$acc kernels do i=1,2 do j=1,3 values(i) = values(i) + data(j,i) end do enddo !$acc end kernels !$acc end data call field%update_host() FCTEST_CHECK_EQUAL( values(1) , 30._8 ) FCTEST_CHECK_EQUAL( values(2) , 39._8 ) values(:) = 0. call field%update_device() call block%final() FCTEST_CHECK_EQUAL( multiblock%owners(), 1 ) base = multiblock FCTEST_CHECK_EQUAL( base%owners(), 2 ) FCTEST_CHECK_EQUAL( multiblock%owners(), 2 ) call base%padded_data(padded,cols) FCTEST_CHECK_EQUAL(padded(1,1), 1) FCTEST_CHECK_EQUAL(padded(2,1), 2) FCTEST_CHECK_EQUAL(padded(3,1), 3) FCTEST_CHECK_EQUAL(padded(4,1), 4) FCTEST_CHECK_EQUAL(padded(1,2), 5) FCTEST_CHECK_EQUAL(padded(2,2), 6) FCTEST_CHECK_EQUAL(padded(3,2), 7) FCTEST_CHECK_EQUAL(padded(4,2), 8) FCTEST_CHECK_EQUAL(padded(1,3), 9) FCTEST_CHECK_EQUAL(padded(2,3), 10) FCTEST_CHECK_EQUAL(padded(3,3), 11) FCTEST_CHECK_EQUAL(padded(1,4), 12) FCTEST_CHECK_EQUAL(padded(2,4), 13) FCTEST_CHECK_EQUAL(padded(3,4), 14) !$acc data present(values) !$acc kernels do i=1,4 do j=1,cols(i) values(i) = values(i) + padded(j,i) end do enddo !$acc end kernels !$acc end data call field%update_host() FCTEST_CHECK_EQUAL( values(1) , 10._8 ) FCTEST_CHECK_EQUAL( values(2) , 26._8 ) FCTEST_CHECK_EQUAL( values(3) , 30._8 ) FCTEST_CHECK_EQUAL( values(4) , 39._8 ) values(:) = 0. call field%update_device() call multiblock%final() FCTEST_CHECK_EQUAL( base%owners(), 1 ) call base%final() #endif END_TEST ! ----------------------------------------------------------------------------- END_TESTSUITE atlas-0.46.0/src/tests/acc/fctest_unified_memory_with_openacc_cxx.cc0000664000175000017500000000102615160212070026040 0ustar alastairalastair/* * (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 "hic/hic.h" extern "C" { void allocate_unified_impl(double** a, int N) { HIC_CALL(hicMallocManaged(a, N * sizeof(double))); } } atlas-0.46.0/src/tests/acc/CMakeLists.txt0000664000175000017500000000222215160212070020300 0ustar alastairalastair# (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. if( HAVE_GPU AND HAVE_TESTS AND HAVE_FCTEST AND HAVE_ACC ) add_fctest( TARGET atlas_test_unified_memory_with_openacc SOURCES fctest_unified_memory_with_openacc.F90 fctest_unified_memory_with_openacc_cxx.cc LIBS atlas_f OpenACC::OpenACC_Fortran hic LINKER_LANGUAGE Fortran ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ATLAS_RUN_NGPUS=1 ) set_tests_properties( atlas_test_unified_memory_with_openacc PROPERTIES LABELS "gpu;acc") add_fctest( TARGET atlas_test_connectivity_openacc SOURCES fctest_connectivity_openacc.F90 LIBS atlas_f OpenACC::OpenACC_Fortran LINKER_LANGUAGE Fortran ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ATLAS_RUN_NGPUS=1 ) set_tests_properties ( atlas_test_connectivity_openacc PROPERTIES LABELS "gpu;acc") endif() atlas-0.46.0/src/tests/io/0000775000175000017500000000000015160212070015423 5ustar alastairalastairatlas-0.46.0/src/tests/io/test_io_encoding.cc0000664000175000017500000007166315160212070021263 0ustar alastairalastair/* * (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 "atlas/array/Array.h" #include "atlas/array/ArrayView.h" #include "atlas/io/atlas-io.h" #include "tests/AtlasTestEnvironment.h" namespace atlas { namespace test { using io::ArrayReference; // ------------------------------------------------------------------------------------------------------- struct UnencodableType { std::string _; }; // ------------------------------------------------------------------------------------------------------- // Example type that can be encoded / decoded with atlas::io // The "operations" performed on this type are stored for unit-test purposes class EncodableType { public: using Operations = std::vector; EncodableType(std::string s, std::shared_ptr operations = std::make_shared()): str(s), ops(operations) { ATLAS_TRACE("EncodableType[" + str + "] construct"); ops->push_back("constructor"); } EncodableType(): ops(std::make_shared()) {} EncodableType(const EncodableType& other) { // This constructor should not be called. str = other.str; ATLAS_TRACE("EncodableType[" + str + "] copy constructor"); ops = other.ops; ops->push_back("copy constructor"); } EncodableType(EncodableType&& other) { str = std::move(other.str); ATLAS_TRACE("EncodableType[" + str + "] move constructor"); ops = other.ops; ops->push_back("move constructor"); } EncodableType& operator=(const EncodableType& rhs) { // This assignment should not be called. str = rhs.str; ATLAS_TRACE("EncodableType[" + str + "] assignment"); ops = rhs.ops; ops->push_back("assignment"); return *this; } EncodableType& operator=(const EncodableType&& rhs) { // This assignment should not be called. str = std::move(rhs.str); ATLAS_TRACE("EncodableType[" + str + "] move"); ops = rhs.ops; ops->push_back("move"); return *this; } friend void encode_data(const EncodableType& in, atlas::io::Data& out) { in.ops->push_back("encode_data"); out.assign(in.str.data(), in.str.size()); } friend size_t encode_metadata(const EncodableType& in, atlas::io::Metadata& metadata) { in.ops->push_back("encode_metadata"); metadata.set("type", "EncodableType"); metadata.set("bytes", in.str.size()); return in.str.size(); } friend void decode(const atlas::io::Metadata&, const atlas::io::Data& b, EncodableType& out) { out.ops->push_back("decode"); const char* data = static_cast(b.data()); out.str = std::string(data, data + b.size()); } const std::vector& operations() const { return *ops; } private: std::string str; mutable std::shared_ptr> ops; }; // ------------------------------------------------------------------------------------------------------- CASE("test exceptions") { EXPECT(not(io::is_interpretable())); EXPECT(not io::is_encodable()); EXPECT(not io::can_encode_metadata()); EXPECT(not io::can_encode_data()); UnencodableType in; atlas::io::Data data; atlas::io::Metadata metadata; EXPECT_THROWS_AS(io::ref(in, io::tag::disable_static_assert()), io::NotEncodable); EXPECT_THROWS_AS(io::copy(in, io::tag::disable_static_assert()), io::NotEncodable); EXPECT_THROWS_AS(io::encode(in, metadata, data, io::tag::disable_static_assert()), io::NotEncodable); } // ------------------------------------------------------------------------------------------------------- CASE("encoding test::EncodableType") { static_assert(not io::is_interpretable(), ""); static_assert(io::is_encodable(), ""); static_assert(io::is_decodable(), ""); const std::string encoded_string{"encoded string"}; EncodableType in(encoded_string); atlas::io::Data data; atlas::io::Metadata metadata; EXPECT_NO_THROW(encode(in, metadata, data)); EXPECT(metadata.type() == "EncodableType"); EXPECT(data.size() == encoded_string.size()); EXPECT(::memcmp(data, encoded_string.data(), encoded_string.size()) == 0); } // ------------------------------------------------------------------------------------------------------- CASE("encoding atlas::io::types::ArrayView") { static_assert(not io::is_interpretable(), ""); static_assert(io::can_encode_data(), ""); static_assert(io::can_encode_metadata(), ""); static_assert(io::is_encodable(), ""); } // ------------------------------------------------------------------------------------------------------- template void assert_Vector() { static_assert(not io::is_interpretable, ArrayReference>(), ""); static_assert(io::can_encode_data>(), ""); static_assert(io::can_encode_metadata>(), ""); static_assert(io::is_encodable>(), ""); } CASE("encoding atlas::vector") { assert_Vector(); assert_Vector(); assert_Vector(); assert_Vector(); assert_Vector(); { using T = double; atlas::vector in{1, 2, 3, 4, 5}; atlas::io::Data data; atlas::io::Metadata metadata; EXPECT_NO_THROW(encode(in, metadata, data)); EXPECT(data.size() == size_t(in.size()) * sizeof(T)); EXPECT(::memcmp(in.data(), data.data(), data.size()) == 0); EXPECT(metadata.type() == "array"); EXPECT(metadata.getString("datatype") == atlas::array::DataType::str()); } { using T = std::byte; std::bitset<8> bits; atlas::vector in; in.resize(5); size_t n{0}; for (auto& byte : in) { bits.set(n++, true); byte = *reinterpret_cast(&bits); } atlas::io::Data data; atlas::io::Metadata metadata; EXPECT_NO_THROW(encode(in, metadata, data)); EXPECT(data.size() == size_t(in.size()) * sizeof(T)); EXPECT(::memcmp(in.data(), data.data(), data.size()) == 0); EXPECT(metadata.type() == "array"); EXPECT(metadata.getString("datatype") == atlas::array::DataType::str()); } } // ------------------------------------------------------------------------------------------------------- template void assert_StdVector() { static_assert(io::is_interpretable, ArrayReference>(), ""); static_assert(not io::can_encode_data>(), ""); static_assert(not io::can_encode_metadata>(), ""); static_assert(not io::is_encodable>(), ""); } template void encode_StdVector() { std::vector in{1, 2, 3, 4, 5}; ArrayReference interpreted; interprete(in, interpreted); atlas::io::Data data; atlas::io::Metadata metadata; encode(interpreted, metadata, data); EXPECT(data.size() == in.size() * sizeof(T)); EXPECT(::memcmp(in.data(), data.data(), data.size()) == 0); EXPECT(metadata.type() == "array"); EXPECT(metadata.getString("datatype") == atlas::array::DataType::str()); } CASE("encoding std::vector") { assert_StdVector(); assert_StdVector(); assert_StdVector(); assert_StdVector(); assert_StdVector(); encode_StdVector(); encode_StdVector(); encode_StdVector(); encode_StdVector(); { using T = std::byte; std::bitset<8> bits; std::vector in; in.resize(5); size_t n{0}; for (auto& byte : in) { bits.set(n++, true); byte = *reinterpret_cast(&bits); } ArrayReference interpreted; interprete(in, interpreted); atlas::io::Data data; atlas::io::Metadata metadata; encode(interpreted, metadata, data); EXPECT(data.size() == in.size() * sizeof(T)); EXPECT(::memcmp(in.data(), data.data(), data.size()) == 0); EXPECT(metadata.type() == "array"); EXPECT(metadata.getString("datatype") == atlas::array::DataType::str()); } } // ------------------------------------------------------------------------------------------------------- template void assert_StdArray() { static_assert(io::is_interpretable, ArrayReference>(), ""); static_assert(not io::can_encode_data>(), ""); static_assert(not io::can_encode_metadata>(), ""); static_assert(not io::is_encodable>(), ""); } template void encode_StdArray() { std::array in{1, 2, 3, 4, 5}; ArrayReference interpreted; interprete(in, interpreted); atlas::io::Data data; atlas::io::Metadata metadata; encode(interpreted, metadata, data); EXPECT(data.size() == in.size() * sizeof(T)); EXPECT(::memcmp(in.data(), data.data(), data.size()) == 0); EXPECT(metadata.type() == "array"); EXPECT(metadata.getString("datatype") == atlas::array::DataType::str()); } CASE("encoding std::array") { assert_StdArray(); assert_StdArray(); assert_StdArray(); assert_StdArray(); assert_StdArray(); encode_StdVector(); encode_StdVector(); encode_StdVector(); encode_StdVector(); { using T = std::byte; std::bitset<8> bits; std::vector in; in.resize(5); size_t n{0}; for (auto& byte : in) { bits.set(n++, true); byte = *reinterpret_cast(&bits); } ArrayReference interpreted; interprete(in, interpreted); atlas::io::Data data; atlas::io::Metadata metadata; encode(interpreted, metadata, data); EXPECT(data.size() == in.size() * sizeof(T)); EXPECT(::memcmp(in.data(), data.data(), data.size()) == 0); EXPECT(metadata.type() == "array"); EXPECT(metadata.getString("datatype") == atlas::array::DataType::str()); } } // ------------------------------------------------------------------------------------------------------- CASE("encoding atlas::array::Array") { static_assert(io::is_interpretable(), ""); static_assert(not io::can_encode_data(), ""); static_assert(not io::can_encode_metadata(), ""); static_assert(not io::is_encodable(), ""); using T = double; array::ArrayT in(4, 2); array::make_view(in).assign({1, 2, 3, 4, 5, 6, 7, 8}); { auto interpreted = io::interprete(in); EXPECT_EQ(interpreted.datatype().str(), in.datatype().str()); EXPECT_EQ(interpreted.rank(), 2); EXPECT_EQ(interpreted.shape(0), 4); EXPECT_EQ(interpreted.shape(1), 2); EXPECT_EQ(interpreted.size(), in.size()); } atlas::io::Data data; atlas::io::Metadata metadata; EXPECT_NO_THROW(encode(in, metadata, data)); io::ArrayMetadata array_metadata{metadata}; EXPECT_EQ(array_metadata.datatype().str(), in.datatype().str()); EXPECT_EQ(array_metadata.rank(), 2); EXPECT_EQ(array_metadata.shape(0), 4); EXPECT_EQ(array_metadata.shape(1), 2); EXPECT_EQ(array_metadata.size(), in.size()); EXPECT_EQ(data.size(), in.size() * in.datatype().size()); EXPECT(::memcmp(in.data(), data, data.size()) == 0); EXPECT(metadata.type() == "array"); EXPECT(metadata.getString("datatype") == in.datatype().str()); EXPECT(array::ArrayShape{metadata.getIntVector("shape")} == in.shape()); } // ------------------------------------------------------------------------------------------------------- CASE("test Encoder") { SECTION("default constructor") { io::Encoder encoder; EXPECT(encoder == false); io::Metadata metadata; io::Data data; EXPECT_THROWS_AS(encode(encoder, metadata, data), eckit::AssertionFailed); } Log::info() << "here" << std::endl; SECTION("Encoder via reference") { io::Encoder encoder; auto ops = std::make_shared(); EncodableType encodable("string", ops); EXPECT_EQ(ops->size(), 1); EXPECT_EQ(ops->back(), "constructor"); io::ref(encodable); EXPECT_EQ(ops->size(), 1); EXPECT_EQ(ops->back(), "constructor"); encoder = io::Encoder{io::ref(encodable)}; EXPECT_EQ(ops->size(), 2); EXPECT_EQ(ops->back(), "encode_metadata"); io::Metadata metadata; io::Data data; encode(encoder, metadata, data); EXPECT_EQ(ops->size(), 3); EXPECT_EQ(ops->back(), "encode_data"); } SECTION("Encoder via copy") { io::Encoder encoder; auto ops = std::make_shared(); EncodableType encodable("string", ops); EXPECT_EQ(ops->size(), 1); EXPECT_EQ(ops->back(), "constructor"); encoder = io::Encoder{io::copy(encodable)}; EXPECT_EQ(ops->size(), 3); EXPECT_EQ(ops->at(1), "encode_metadata"); EXPECT_EQ(ops->at(2), "encode_data"); io::Metadata metadata; io::Data data; encode(encoder, metadata, data); EXPECT_EQ(ops->size(), 3); EXPECT_EQ(ops->at(2), "encode_data"); } SECTION("Encoder via move") { io::Encoder encoder; auto ops = std::make_shared(); EncodableType encodable("string", ops); EXPECT_EQ(ops->size(), 1); EXPECT_EQ(ops->back(), "constructor"); encoder = io::Encoder{std::move(encodable)}; EXPECT_EQ(ops->size(), 3); EXPECT_EQ(ops->at(1), "move constructor"); EXPECT_EQ(ops->at(2), "encode_metadata"); io::Metadata metadata; io::Data data; encode(encoder, metadata, data); EXPECT_EQ(ops->size(), 4); EXPECT_EQ(ops->at(3), "encode_data"); } } // ------------------------------------------------------------------------------------------------------- CASE("Encoder for std::vector") { SECTION("ref") { using T = double; std::vector v{1, 2, 3, 4, 5, 6, 7, 8}; io::Encoder encoder(io::ref(v)); // We can only encode with reference to original vector (no copies were made) io::Metadata metadata; io::Data data; encode(encoder, metadata, data); EXPECT(data.size() == v.size() * sizeof(T)); EXPECT(::memcmp(data, v.data(), data.size()) == 0); } SECTION("copy") { using T = double; std::vector v{1, 2, 3, 4, 5, 6, 7, 8}; io::Encoder encoder; { std::vector scoped = v; encoder = io::Encoder(io::copy(scoped)); scoped.assign(scoped.size(), 0); // zero out before destruction } // We can now encode with scoped vector destroyed io::Metadata metadata; io::Data data; encode(encoder, metadata, data); EXPECT_EQ(data.size(), v.size() * sizeof(T)); EXPECT(::memcmp(data, v.data(), data.size()) == 0); } } // ------------------------------------------------------------------------------------------------------- CASE("Encoder for std::array") { SECTION("ref") { using T = double; std::array v{1, 2, 3, 4, 5, 6, 7, 8}; io::Encoder encoder(io::ref(v)); // We can only encode with reference to original vector (no copies were made) io::Metadata metadata; io::Data data; encode(encoder, metadata, data); EXPECT(data.size() == v.size() * sizeof(T)); EXPECT(::memcmp(data, v.data(), data.size()) == 0); } SECTION("copy") { using T = double; std::array v{1, 2, 3, 4, 5, 6, 7, 8}; io::Encoder encoder; { std::array scoped = v; encoder = io::Encoder(io::copy(scoped)); std::fill(std::begin(scoped), std::end(scoped), 0); // zero out before destruction } // We can now encode with scoped vector destroyed io::Metadata metadata; io::Data data; encode(encoder, metadata, data); EXPECT_EQ(data.size(), v.size() * sizeof(T)); EXPECT(::memcmp(data, v.data(), data.size()) == 0); } } // ------------------------------------------------------------------------------------------------------- CASE("Encoder for atlas::vector") { SECTION("ref") { using T = double; atlas::vector v{1, 2, 3, 4, 5, 6, 7, 8}; io::Encoder encoder(io::ref(v)); // We can only encode with reference to original vector (no copies were made) io::Metadata metadata; io::Data data; encode(encoder, metadata, data); EXPECT_EQ(data.size(), size_t(v.size()) * sizeof(T)); EXPECT(::memcmp(data, v.data(), data.size()) == 0); } SECTION("copy") { using T = double; atlas::vector v{1, 2, 3, 4, 5, 6, 7, 8}; io::Encoder encoder; { atlas::vector scoped = v; encoder = io::Encoder(io::copy(scoped)); scoped.assign(scoped.size(), 0); // zero out before destruction } // We can now encode with scoped vector destroyed io::Metadata metadata; io::Data data; encode(encoder, metadata, data); EXPECT_EQ(data.size(), size_t(v.size()) * sizeof(T)); EXPECT(::memcmp(data, v.data(), data.size()) == 0); } } // ------------------------------------------------------------------------------------------------------- CASE("Encoder for atlas::array::Array") { SECTION("ref") { using T = double; array::ArrayT v(4, 2); array::make_view(v).assign({1, 2, 3, 4, 5, 6, 7, 8}); io::Encoder encoder(io::ref(v)); // We can only encode with reference to original vector (no copies were made) io::Metadata metadata; io::Data data; encode(encoder, metadata, data); EXPECT_EQ(data.size(), v.size() * sizeof(T)); EXPECT(::memcmp(data, v.data(), data.size()) == 0); } SECTION("copy") { using T = double; array::ArrayT v(4, 2); array::make_view(v).assign({1, 2, 3, 4, 5, 6, 7, 8}); io::Encoder encoder; { array::ArrayT scoped(4, 2); ::memcpy(scoped.data(), v.data(), v.size() * sizeof(T)); encoder = io::Encoder(io::copy(v)); array::make_view(scoped).assign(0); } // We can now encode with scoped vector destroyed io::Metadata metadata; io::Data data; encode(encoder, metadata, data); EXPECT_EQ(data.size(), v.size() * sizeof(T)); EXPECT(::memcmp(data, v.data(), data.size()) == 0); } } CASE("Encoder of encoder") { using T = double; array::ArrayT v(4, 2); array::make_view(v).assign({1, 2, 3, 4, 5, 6, 7, 8}); io::Encoder encoder(io::ref(v)); io::Encoder encoder_of_encoder(io::ref(encoder)); io::Metadata metadata; io::Data data; encode(encoder_of_encoder, metadata, data); EXPECT_EQ(data.size(), v.size() * sizeof(T)); EXPECT(::memcmp(data, v.data(), data.size()) == 0); } // ------------------------------------------------------------------------------------------------------- /// Helper class to be used in testing decoding of arrays. template struct EncodedArray { atlas::io::Data data; atlas::io::Metadata metadata; EncodedArray(): in(4, 2) { array::make_view(in).assign({1, 2, 3, 4, 5, 6, 7, 8}); encode(in, metadata, data); } friend bool operator==(const std::vector& lhs, const EncodedArray& rhs) { if (lhs.size() != rhs.in.size()) { return false; } return ::memcmp(lhs.data(), rhs.in.data(), rhs.in.size() * rhs.in.datatype().size()) == 0; } friend bool operator==(const atlas::vector& lhs, const EncodedArray& rhs) { if (size_t(lhs.size()) != rhs.in.size()) { return false; } return ::memcmp(lhs.data(), rhs.in.data(), rhs.in.size() * rhs.in.datatype().size()) == 0; } friend bool operator==(const std::array& lhs, const EncodedArray& rhs) { if (lhs.size() != rhs.in.size()) { return false; } return ::memcmp(lhs.data(), rhs.in.data(), rhs.in.size() * rhs.in.datatype().size()) == 0; } friend bool operator==(const atlas::array::Array& lhs, const EncodedArray& rhs) { if (lhs.datatype() != rhs.in.datatype()) { return false; } if (lhs.size() != rhs.in.size()) { return false; } return ::memcmp(lhs.data(), rhs.in.data(), rhs.in.size() * rhs.in.datatype().size()) == 0; } private: atlas::array::ArrayT in; }; template <> struct EncodedArray { using T = std::byte; atlas::io::Data data; atlas::io::Metadata metadata; EncodedArray() { std::bitset<8> bits; in.resize(5); size_t n{0}; for (auto& byte : in) { bits.set(n++, true); byte = *reinterpret_cast(&bits); } encode(in, metadata, data); } friend bool operator==(const std::vector& lhs, const EncodedArray& rhs) { if (lhs.size() != rhs.in.size()) { return false; } return ::memcmp(lhs.data(), rhs.in.data(), rhs.in.size() * sizeof(T)) == 0; } friend bool operator==(const atlas::vector& lhs, const EncodedArray& rhs) { if (size_t(lhs.size()) != rhs.in.size()) { return false; } return ::memcmp(lhs.data(), rhs.in.data(), rhs.in.size() * sizeof(T)) == 0; } private: std::vector in; }; // ------------------------------------------------------------------------------------------------------- CASE("Decoding to std::vector") { using T = double; EncodedArray encoded; std::vector out; SECTION("decode std::vector directly") { EXPECT_NO_THROW(decode(encoded.metadata, encoded.data, out)); EXPECT(out == encoded); } SECTION("decode using rvalue io::Decoder (type erasure)") { EXPECT_NO_THROW(decode(encoded.metadata, encoded.data, io::Decoder(out))); EXPECT(out == encoded); } SECTION("decode using lvalue io::Decoder (type erasure)") { io::Decoder decoder(out); EXPECT_NO_THROW(decode(encoded.metadata, encoded.data, io::Decoder(out))); EXPECT(out == encoded); } SECTION("decode using decoder of decoder") { io::Decoder decoder(out); EXPECT_NO_THROW(decode(encoded.metadata, encoded.data, io::Decoder(decoder))); EXPECT(out == encoded); } } // ------------------------------------------------------------------------------------------------------- CASE("Decoding to std::array") { using T = double; EncodedArray encoded; std::array out; SECTION("decode std::vector directly") { EXPECT_NO_THROW(decode(encoded.metadata, encoded.data, out)); EXPECT(out == encoded); } SECTION("decode using rvalue io::Decoder (type erasure)") { EXPECT_NO_THROW(decode(encoded.metadata, encoded.data, io::Decoder(out))); EXPECT(out == encoded); } SECTION("decode using lvalue io::Decoder (type erasure)") { io::Decoder decoder(out); EXPECT_NO_THROW(decode(encoded.metadata, encoded.data, io::Decoder(out))); EXPECT(out == encoded); } SECTION("decode using decoder of decoder") { io::Decoder decoder(out); EXPECT_NO_THROW(decode(encoded.metadata, encoded.data, io::Decoder(decoder))); EXPECT(out == encoded); } } // ------------------------------------------------------------------------------------------------------- CASE("Decoding to atlas::vector") { using T = double; EncodedArray encoded; atlas::vector out; SECTION("decode directly") { EXPECT_NO_THROW(decode(encoded.metadata, encoded.data, out)); EXPECT(out == encoded); } SECTION("decode using rvalue io::Decoder (type erasure)") { EXPECT_NO_THROW(decode(encoded.metadata, encoded.data, io::Decoder(out))); EXPECT(out == encoded); } SECTION("decode using lvalue io::Decoder (type erasure)") { io::Decoder decoder(out); EXPECT_NO_THROW(decode(encoded.metadata, encoded.data, io::Decoder(out))); EXPECT(out == encoded); } SECTION("decode using decoder of decoder") { io::Decoder decoder(out); EXPECT_NO_THROW(decode(encoded.metadata, encoded.data, io::Decoder(decoder))); EXPECT(out == encoded); } } // ------------------------------------------------------------------------------------------------------- CASE("Decoding to atlas::array::Array") { using T = double; EncodedArray encoded; atlas::array::ArrayT out(0, 0); SECTION("decode directly") { EXPECT_NO_THROW(decode(encoded.metadata, encoded.data, out)); EXPECT(out == encoded); } SECTION("decode using rvalue io::Decoder (type erasure)") { EXPECT_NO_THROW(decode(encoded.metadata, encoded.data, io::Decoder(out))); EXPECT(out == encoded); } SECTION("decode using lvalue io::Decoder (type erasure)") { io::Decoder decoder(out); EXPECT_NO_THROW(decode(encoded.metadata, encoded.data, io::Decoder(out))); EXPECT(out == encoded); } SECTION("decode using decoder of decoder") { io::Decoder decoder(out); EXPECT_NO_THROW(decode(encoded.metadata, encoded.data, io::Decoder(decoder))); EXPECT(out == encoded); } } // ------------------------------------------------------------------------------------------------------- CASE("Encode/Decode byte array") { using T = std::byte; EncodedArray encoded; atlas::vector out; auto validate = [&]() { EXPECT(out == encoded); auto to_byte = []( const char* str) { return std::byte(std::bitset<8>(str).to_ulong()); }; EXPECT(out[0] == to_byte("00000001")); EXPECT(out[1] == to_byte("00000011")); EXPECT(out[2] == to_byte("00000111")); EXPECT(out[3] == to_byte("00001111")); EXPECT(out[4] == to_byte("00011111")); }; SECTION("decode directly") { EXPECT_NO_THROW(decode(encoded.metadata, encoded.data, out)); validate(); } SECTION("decode using rvalue io::Decoder (type erasure)") { EXPECT_NO_THROW(decode(encoded.metadata, encoded.data, io::Decoder(out))); validate(); } SECTION("decode using lvalue io::Decoder (type erasure)") { io::Decoder decoder(out); EXPECT_NO_THROW(decode(encoded.metadata, encoded.data, io::Decoder(out))); validate(); } SECTION("decode using decoder of decoder") { io::Decoder decoder(out); EXPECT_NO_THROW(decode(encoded.metadata, encoded.data, io::Decoder(decoder))); validate(); } } // ------------------------------------------------------------------------------------------------------- CASE("Encode/Decode string") { std::string in{"short string"}; io::Metadata metadata; io::Data data; encode(in, metadata, data); EXPECT_EQ(data.size(), 0); std::string out; decode(metadata, data, out); EXPECT_EQ(out, in); } // ------------------------------------------------------------------------------------------------------- template void test_encode_decode_scalar() { T in{std::numeric_limits::max()}, out; io::Metadata metadata; io::Data data; encode(in, metadata, data); EXPECT_EQ(data.size(), 0); decode(metadata, data, out); EXPECT_EQ(out, in); } CASE("Encode/Decode scalar") { // bit identical encoding via Base64 string within the metadata! SECTION("int32") { test_encode_decode_scalar(); } SECTION("int64") { test_encode_decode_scalar(); } SECTION("real32") { test_encode_decode_scalar(); } SECTION("real64") { test_encode_decode_scalar(); } SECTION("uint64") { test_encode_decode_scalar(); } } // ------------------------------------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/io/test_io_record.cc0000664000175000017500000005071615160212070020747 0ustar alastairalastair/* * (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/io/MemoryHandle.h" #include "atlas/array.h" #include "atlas/util/vector.h" #include "atlas/io/atlas-io.h" #include "tests/AtlasTestEnvironment.h" namespace atlas { namespace test { struct Arrays { std::vector v1; atlas::vector v2; atlas::array::ArrayT v3{0, 0}; bool operator==(const Arrays& other) const { return v1 == other.v1 && ::memcmp(v2.data(), other.v2.data(), v2.size() * sizeof(float)) == 0 && ::memcmp(v3.data(), other.v3.data(), v3.size() * v3.datatype().size()) == 0; } bool operator!=(const Arrays& other) const { return not operator==(other); } }; //----------------------------------------------------------------------------- static util::Config no_compression("compression", "none"); //----------------------------------------------------------------------------- std::string suffix() { static std::string suffix = eckit::Resource("--suffix", ""); return suffix; } //----------------------------------------------------------------------------- namespace globals { struct TestRecord { Arrays data; TestRecord() = default; TestRecord(const std::function& initializer) { initializer(data); } }; static TestRecord record1{[](Arrays& data) { data.v1 = {0, 1, 2, 3, 4}; data.v2 = {3, 2, 1}; data.v3.resize(3, 2); array::make_view(data.v3).assign({11, 12, 21, 22, 31, 32}); }}; static TestRecord record2{[](Arrays& data) { data.v1 = {0, 10, 20, 30, 40, 50}; data.v2 = {30, 20, 10, 40}; data.v3.resize(2, 3); array::make_view(data.v3).assign({11, 12, 13, 21, 22, 23}); }}; static TestRecord record3{[](Arrays& data) { data.v1.assign(1024 / 8 - 1, 2.); data.v2.assign(1023 * 1024 / 4 + 512 / 4, 1.); data.v3.resize(1024, 1024); array::make_view(data.v3).assign(3); }}; std::vector records; } // namespace globals //----------------------------------------------------------------------------- template void write_length(Length length, const std::string& path) { std::ofstream file(path); file << length; file.close(); } //-----------------------------------------------------------------------------// // // // Writing records // // // //-----------------------------------------------------------------------------// CASE("Write records, each in separate file (offset=0)") { auto write_record = [&](const Arrays& data, const eckit::PathName& path) { io::RecordWriter record; record.set("v1", io::ref(data.v1), no_compression); record.set("v2", io::ref(data.v2), no_compression); record.set("v3", io::ref(data.v3)); auto length = record.write(path); write_length(length, path + ".length"); }; SECTION("record1.atlas" + suffix()) { write_record(globals::record1.data, "record1.atlas" + suffix()); } SECTION("record2.atlas" + suffix()) { write_record(globals::record2.data, "record2.atlas" + suffix()); } SECTION("record3.atlas" + suffix()) { write_record(globals::record3.data, "record3.atlas" + suffix()); } } //----------------------------------------------------------------------------- CASE("Write records to same file using record.write(path,io::Mode)") { // This will reopen files upon every append, bad for performance static std::vector lengths; static std::vector offsets{0}; auto write_record = [&](Arrays& data, const eckit::PathName& path, io::Mode mode) { io::RecordWriter record; record.set("v1", io::ref(data.v1), no_compression); record.set("v2", io::ref(data.v2), no_compression); record.set("v3", io::ref(data.v3)); globals::records.emplace_back(path, offsets.back()); lengths.emplace_back(record.write(path, mode)); offsets.emplace_back(offsets.back() + lengths.back()); }; SECTION("record1 -> records.atlas" + suffix()) { write_record(globals::record1.data, "records.atlas" + suffix(), io::Mode::write); } SECTION("record2 -> records.atlas" + suffix()) { write_record(globals::record2.data, "records.atlas" + suffix(), io::Mode::append); } SECTION("record3 -> records.atlas" + suffix()) { write_record(globals::record3.data, "records.atlas" + suffix(), io::Mode::append); } } //----------------------------------------------------------------------------- CASE("Write records to same file using record.write(Stream) keeping Stream open") { // This should give exactly the same output file as previous, except no auto write_record = [&](const Arrays& data, io::Stream stream) { io::RecordWriter record; record.set("v1", io::ref(data.v1), no_compression); record.set("v2", io::ref(data.v2), no_compression); record.set("v3", io::ref(data.v3)); record.write(stream); }; static io::OutputFileStream stream("records.atlas" + suffix() + ".duplicate"); SECTION("record1 -> records.atlas" + suffix() + ".duplicate") { EXPECT_EQ(stream.position(), globals::records[0].offset); write_record(globals::record1.data, stream); } SECTION("record2 -> records.atlas" + suffix() + ".duplicate") { EXPECT_EQ(stream.position(), globals::records[1].offset); write_record(globals::record2.data, stream); } SECTION("record3 -> records.atlas" + suffix() + ".duplicate") { EXPECT_EQ(stream.position(), globals::records[2].offset); write_record(globals::record3.data, stream); } SECTION("close stream") { stream.close(); // required because stream is a static variable } } //----------------------------------------------------------------------------- CASE("Write master record referencing record1 and record2 and record3") { io::RecordWriter record; record.set("v1", io::link("file:record1.atlas" + suffix() + "?key=v1")); record.set("v2", io::link("file:record1.atlas" + suffix() + "?key=v2")); record.set("v3", io::link("file:record1.atlas" + suffix() + "?key=v3")); record.set("v4", io::link("file:record2.atlas" + suffix() + "?key=v1")); record.set("v5", io::link("file:record2.atlas" + suffix() + "?key=v2")); record.set("v6", io::link("file:record2.atlas" + suffix() + "?key=v3")); record.set("v7", io::link("file:record3.atlas" + suffix() + "?key=v1")); record.set("v8", io::link("file:record3.atlas" + suffix() + "?key=v2")); record.set("v9", io::link("file:record3.atlas" + suffix() + "?key=v3")); record.write("record.atlas" + suffix()); } //----------------------------------------------------------------------------- CASE("Write records in nested subdirectories") { auto reference_path = eckit::PathName{"atlas_test_io_refpath"}; { eckit::PathName{reference_path / "links" / "1"}.mkdir(); io::RecordWriter record; record.set("v1", io::ref(globals::record1.data.v1)); record.set("v2", io::ref(globals::record1.data.v2)); record.set("v3", io::ref(globals::record1.data.v3)); record.set("s1", std::string("short string")); record.set("s2", double(1. / 3.)); record.write(reference_path / "links" / "1" / "record.atlas" + suffix()); } { eckit::PathName{reference_path / "links" / "2"}.mkdir(); io::RecordWriter record; record.set("v1", io::ref(globals::record2.data.v1)); record.set("v2", io::ref(globals::record2.data.v2)); record.set("v3", io::ref(globals::record2.data.v3)); record.set("s1", size_t(10000000000)); record.write(reference_path / "links" / "2" / "record.atlas" + suffix()); } { io::RecordWriter record; record.set("l1", io::link("file:1/record.atlas" + suffix() + "?key=v1")); record.set("l2", io::link("file:1/record.atlas" + suffix() + "?key=v2")); record.set("l3", io::link("file:1/record.atlas" + suffix() + "?key=v3")); record.set("l4", io::link("file:2/record.atlas" + suffix() + "?key=v1")); record.set("l5", io::link("file:2/record.atlas" + suffix() + "?key=v2")); record.set("l6", io::link("file:2/record.atlas" + suffix() + "?key=v3")); record.set("l7", io::link("file:1/record.atlas" + suffix() + "?key=s1")); record.set("l8", io::link("file:1/record.atlas" + suffix() + "?key=s2")); record.set("l9", io::link("file:2/record.atlas" + suffix() + "?key=s1")); record.write(reference_path / "links" / "record.atlas" + suffix()); } { io::RecordWriter record; record.set("l1", io::link("file:links/record.atlas" + suffix() + "?key=l1")); record.set("l2", io::link("file:links/record.atlas" + suffix() + "?key=l2")); record.set("l3", io::link("file:links/record.atlas" + suffix() + "?key=l3")); record.set("l4", io::link("file:links/record.atlas" + suffix() + "?key=l4")); record.set("l5", io::link("file:links/record.atlas" + suffix() + "?key=l5")); record.set("l6", io::link("file:links/record.atlas" + suffix() + "?key=l6")); record.set("l7", io::link("file:links/record.atlas" + suffix() + "?key=l7")); record.set("l8", io::link("file:links/record.atlas" + suffix() + "?key=l8")); record.set("l9", io::link("file:links/record.atlas" + suffix() + "?key=l9")); record.write(reference_path / "record.atlas" + suffix()); } } //-----------------------------------------------------------------------------// // // // Reading tests // // // //-----------------------------------------------------------------------------// CASE("Test RecordItemReader") { SECTION("file:record1.atlas" + suffix() + "?key=v2") { io::RecordItemReader reader{"file:record1.atlas" + suffix() + "?key=v2"}; { // When we only want to read metadata io::Metadata metadata; reader.read(metadata); EXPECT(metadata.link() == false); EXPECT_EQ(metadata.type(), "array"); EXPECT(metadata.data.compressed() == false); EXPECT_EQ(metadata.data.compression(), "none"); EXPECT_EQ(metadata.data.size(), globals::record1.data.v2.size() * sizeof(float)); } { // When we want to read both metadata and data io::Metadata metadata; io::Data data; reader.read(metadata, data); EXPECT(metadata.data.compressed() == false); EXPECT_EQ(metadata.data.compression(), "none"); EXPECT_EQ(metadata.data.size(), globals::record1.data.v2.size() * sizeof(float)); EXPECT_EQ(data.size(), metadata.data.size()); EXPECT_EQ(data.size(), metadata.data.compressed_size()); EXPECT(::memcmp(data, globals::record1.data.v2.data(), data.size()) == 0); } } SECTION("file:record.atlas" + suffix() + "?key=v9") { io::RecordItemReader reader{"file:record.atlas" + suffix() + "?key=v9"}; { // When we only want to read metadata io::Metadata metadata; reader.read(metadata); EXPECT(metadata.link() == true); EXPECT_EQ(metadata.link().str(), "file:record3.atlas" + suffix() + "?key=v3"); EXPECT_EQ(metadata.type(), "array"); } { // When we want to read both metadata and data io::Metadata metadata; io::Data data; reader.read(metadata, data); EXPECT(metadata.data.compressed() == (io::defaults::compression_algorithm() != "none")); EXPECT_EQ(metadata.data.compression(), io::defaults::compression_algorithm()); EXPECT_EQ(metadata.data.size(), globals::record3.data.v3.size() * sizeof(int)); EXPECT_EQ(data.size(), metadata.data.compressed_size()); } } } //----------------------------------------------------------------------------- CASE("Read records from different files") { Arrays data1, data2, data3; auto read_record = [&](const eckit::PathName& path, Arrays& data) { io::RecordReader record(path); record.read("v1", data.v1).wait(); record.read("v2", data.v2).wait(); record.read("v3", data.v3).wait(); }; read_record("record1.atlas" + suffix(), data1); read_record("record2.atlas" + suffix(), data2); read_record("record3.atlas" + suffix(), data3); EXPECT(data1 == globals::record1.data); EXPECT(data2 == globals::record2.data); EXPECT(data3 == globals::record3.data); } //----------------------------------------------------------------------------- CASE("Read multiple records from same file") { Arrays data1, data2; io::RecordReader record1(globals::records[0]); io::RecordReader record2(globals::records[1]); record1.read("v1", data1.v1).wait(); record1.read("v2", data1.v2).wait(); record1.read("v3", data1.v3).wait(); record2.read("v1", data2.v1).wait(); record2.read("v2", data2.v2).wait(); record2.read("v3", data2.v3).wait(); EXPECT(data1 == globals::record1.data); EXPECT(data2 == globals::record2.data); } //----------------------------------------------------------------------------- CASE("Write master record referencing record1 and record2") { io::RecordWriter record; record.set("v1", io::link("file:record1.atlas" + suffix() + "?key=v1")); record.set("v2", io::link("file:record1.atlas" + suffix() + "?key=v2")); record.set("v3", io::link("file:record1.atlas" + suffix() + "?key=v3")); record.set("v4", io::link("file:record2.atlas" + suffix() + "?key=v1")); record.set("v5", io::link("file:record2.atlas" + suffix() + "?key=v2")); record.set("v6", io::link("file:record2.atlas" + suffix() + "?key=v3")); record.write("record.atlas" + suffix()); } //----------------------------------------------------------------------------- CASE("Read master record") { Arrays data1, data2; io::RecordReader record("record.atlas" + suffix()); ATLAS_DEBUG_VAR(record.metadata("v1")); record.read("v1", data1.v1).wait(); record.read("v2", data1.v2).wait(); record.read("v3", data1.v3).wait(); record.read("v4", data2.v1).wait(); record.read("v5", data2.v2).wait(); record.read("v6", data2.v3).wait(); EXPECT(data1 == globals::record1.data); EXPECT(data2 == globals::record2.data); } //----------------------------------------------------------------------------- CASE("Async read") { Arrays data1, data2; io::RecordReader record("record.atlas" + suffix()); // Request reads record.read("v1", data1.v1); record.read("v2", data1.v2); record.read("v3", data1.v3); record.read("v4", data2.v1); record.read("v5", data2.v2); record.read("v6", data2.v3); // Wait for specific requests record.wait("v4"); record.wait("v5"); record.wait("v6"); // Should have completed EXPECT(data2 == globals::record2.data); // Should not be complete yet EXPECT(data1 != globals::record1.data); // Wait for all requests; record.wait(); // Should have completed EXPECT(data1 == globals::record1.data); } //----------------------------------------------------------------------------- CASE("Recursive Write/read records in nested subdirectories") { auto reference_path = eckit::PathName{"atlas_test_io_refpath"}; // Read Arrays data1, data2; io::RecordReader record(reference_path / "record.atlas" + suffix()); record.read("l1", data1.v1).wait(); record.read("l2", data1.v2).wait(); record.read("l3", data1.v3).wait(); record.read("l4", data2.v1).wait(); record.read("l5", data2.v2).wait(); record.read("l6", data2.v3).wait(); std::string l7; double l8; size_t l9; record.read("l7", l7).wait(); record.read("l8", l8).wait(); record.read("l9", l9).wait(); EXPECT(data1 == globals::record1.data); EXPECT(data2 == globals::record2.data); EXPECT_EQ(l7, "short string"); EXPECT_EQ(l8, 1. / 3.); EXPECT_EQ(l9, 10000000000ul); } //----------------------------------------------------------------------------- CASE("Write record to memory") { const auto& data_write = globals::record3.data; const auto& v1 = globals::record3.data.v1; const auto& v2 = globals::record3.data.v2; const auto& v3 = globals::record3.data.v3; eckit::Buffer memory; // write { ATLAS_TRACE("write"); io::RecordWriter record; record.compression(false); record.checksum(false); record.set("v1", io::ref(v1)); record.set("v2", io::ref(v2)); record.set("v3", io::ref(v3)); memory.resize(record.estimateMaximumSize()); ATLAS_DEBUG_VAR(memory.size()); eckit::MemoryHandle datahandle_out{memory}; datahandle_out.openForWrite(0); auto record_length = record.write(datahandle_out); datahandle_out.close(); // Without compression, this should be exact EXPECT_EQ(memory.size(), record_length); } // read with individual RecordItemReader { ATLAS_TRACE("read with RecordItemReader"); io::Session session; eckit::MemoryHandle datahandle_in{memory}; datahandle_in.openForRead(); { io::RecordItemReader reader(datahandle_in, "v1"); io::Metadata metadata; io::Data data; reader.read(metadata, data); EXPECT(::memcmp(data, data_write.v1.data(), data.size()) == 0); } { io::RecordItemReader reader(datahandle_in, "v2"); io::Metadata metadata; io::Data data; reader.read(metadata, data); EXPECT(::memcmp(data, data_write.v2.data(), data.size()) == 0); } { io::RecordItemReader reader(datahandle_in, "v3"); io::Metadata metadata; io::Data data; reader.read(metadata, data); EXPECT(::memcmp(data, data_write.v3.data(), data.size()) == 0); } datahandle_in.close(); } // read with RecordReader { ATLAS_TRACE("read with RecordReader"); Arrays data_read; eckit::MemoryHandle datahandle_in{memory}; datahandle_in.openForRead(); io::RecordReader reader(datahandle_in); reader.read("v1", data_read.v1); reader.read("v2", data_read.v2); reader.read("v3", data_read.v3); reader.wait(); datahandle_in.close(); EXPECT(data_read == data_write); } } //-----------------------------------------------------------------------------// // // // Reading tests // // // //-----------------------------------------------------------------------------// CASE("RecordPrinter") { SECTION("table") { util::Config table_with_details; table_with_details.set("format", "table"); table_with_details.set("details", true); io::RecordPrinter record{eckit::PathName("record1.atlas" + suffix()), table_with_details}; std::stringstream out; EXPECT_NO_THROW(out << record); Log::debug() << out.str(); } SECTION("yaml") { util::Config yaml_with_details; yaml_with_details.set("format", "yaml"); yaml_with_details.set("details", true); io::RecordPrinter record{eckit::PathName("record1.atlas" + suffix()), yaml_with_details}; std::stringstream out; EXPECT_NO_THROW(out << record); Log::debug() << out.str(); } } //----------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/io/CMakeLists.txt0000664000175000017500000000205115160212070020161 0ustar alastairalastair# (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. ecbuild_add_test( TARGET atlas_test_io_encoding SOURCES test_io_encoding.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) ecbuild_add_executable( TARGET atlas_test_io_record SOURCES test_io_record.cc LIBS atlas NOINSTALL ) foreach( algorithm none bzip2 aec lz4 snappy ) string( TOUPPER ${algorithm} feature ) if( eckit_HAVE_${feature} OR algorithm MATCHES "none" ) ecbuild_add_test( TARGET atlas_test_io_record_COMPRESSION_${algorithm} COMMAND atlas_test_io_record ARGS --suffix ".${algorithm}" ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ATLAS_IO_COMPRESSION=${algorithm} ) endif() endforeach() atlas-0.46.0/src/tests/TestMeshes.h0000664000175000017500000000173115160212070017253 0ustar alastairalastair/* * (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/grid.h" #include "atlas/library/config.h" #include "atlas/mesh/Mesh.h" #include "atlas/meshgenerator.h" #include "atlas/parallel/mpi/mpi.h" using namespace atlas; using namespace atlas::grid; namespace atlas { namespace test { Mesh generate_mesh(const StructuredGrid& grid) { auto config = util::Config("partitioner", "equal_regions"); StructuredMeshGenerator generate(config); return generate(grid); } Mesh generate_mesh(std::initializer_list nx) { return generate_mesh(ReducedGaussianGrid(nx)); } } // end namespace test } // end namespace atlas atlas-0.46.0/src/tests/linalg/0000775000175000017500000000000015160212070016262 5ustar alastairalastairatlas-0.46.0/src/tests/linalg/test_linalg_sparse_matrix.cc0000664000175000017500000002356115160212070024046 0ustar alastairalastair/* * (C) Copyright 2024- 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 "tests/AtlasTestEnvironment.h" #include "atlas/linalg/sparse/SparseMatrixStorage.h" #include "atlas/linalg/sparse/SparseMatrixView.h" #include "atlas/linalg/sparse/MakeSparseMatrixStorageEigen.h" #include "atlas/linalg/sparse/MakeSparseMatrixStorageEckit.h" using atlas::linalg::SparseMatrixStorage; using atlas::linalg::make_sparse_matrix_storage; namespace atlas { namespace test { template void do_matrix_multiply(const atlas::linalg::SparseMatrixView& W, const XValue* x, YValue* y) { const auto outer = W.outer(); const auto inner = W.inner(); const auto weight = W.value(); int rows = W.rows(); for (int r = 0; r < rows; ++r) { y[r] = 0.; for (IndexType c = outer[r]; c < outer[r + 1]; ++c) { IndexType n = inner[c]; y[r] += weight[c] * x[n]; } } } #if ATLAS_HAVE_EIGEN template > Eigen::SparseMatrix create_eigen_sparsematrix() { Eigen::SparseMatrix matrix(3,3); std::vector> triplets; triplets.reserve(4); triplets.emplace_back(0, 0, 2.); triplets.emplace_back(0, 2, -3.); triplets.emplace_back(1, 1, 2.); triplets.emplace_back(2, 2, 2.); matrix.setFromTriplets(triplets.begin(), triplets.end()); return matrix; } #endif eckit::linalg::SparseMatrix create_eckit_sparsematrix() { return eckit::linalg::SparseMatrix{3, 3, {{0, 0, 2.}, {0, 2, -3.}, {1, 1, 2.}, {2, 2, 2.}}}; } template SparseMatrixStorage create_sparsematrix_storage() { return make_sparse_matrix_storage(create_eckit_sparsematrix()); } template void check_array(const array::Array& array, const std::vector& expected) { EXPECT_EQ(array.size(), expected.size()); auto view = array::make_view(array); for( std::size_t i=0; i void check_matrix(const SparseMatrixStorage& m) { std::vector expected_value{2., -3., 2., 2.}; std::vector expected_outer{0, 2, 3, 4}; std::vector expected_inner{0, 2, 1, 2}; EXPECT_EQ(m.rows(), 3); EXPECT_EQ(m.cols(), 3); EXPECT_EQ(m.nnz(), 4); check_array(m.value(),expected_value); check_array(m.outer(),expected_outer); check_array(m.inner(),expected_inner); auto host_matrix_view = linalg::make_host_view(m); std::vector host_outer(host_matrix_view.outer_size()); std::vector host_inner(host_matrix_view.inner_size()); std::vector host_value(host_matrix_view.value_size()); array::ArrayT x(m.cols()); array::ArrayT y(m.rows()); array::make_host_view(x).assign({1.0, 2.0, 3.0}); do_matrix_multiply(host_matrix_view, x.host_data(), y.host_data()); // Check result std::vector expected_y{-7.0, 4.0, 6.0}; check_array(y, expected_y); } void check_matrix(const SparseMatrixStorage& m) { if (m.value().datatype() == atlas::make_datatype() && m.outer().datatype() == atlas::make_datatype()) { check_matrix(m); } else if (m.value().datatype() == atlas::make_datatype() && m.outer().datatype() == atlas::make_datatype()) { check_matrix(m); } else if (m.value().datatype() == atlas::make_datatype() && m.outer().datatype() == atlas::make_datatype()) { check_matrix(m); } else if (m.value().datatype() == atlas::make_datatype() && m.outer().datatype() == atlas::make_datatype()) { check_matrix(m); } else if (m.value().datatype() == atlas::make_datatype() && m.outer().datatype() == atlas::make_datatype()) { check_matrix(m); } else if (m.value().datatype() == atlas::make_datatype() && m.outer().datatype() == atlas::make_datatype()) { check_matrix(m); } else if (m.value().datatype() == atlas::make_datatype() && m.outer().datatype() == atlas::make_datatype()) { check_matrix(m); } else if (m.value().datatype() == atlas::make_datatype() && m.outer().datatype() == atlas::make_datatype()) { check_matrix(m); } else { ATLAS_NOTIMPLEMENTED; } } CASE("Create SparseMatrix moving eckit::linalg::SparseMatrix") { SparseMatrixStorage S = make_sparse_matrix_storage(create_eckit_sparsematrix()); EXPECT_NO_THROW(check_matrix(S)); } CASE("Create SparseMatrix moving eckit::linalg::SparseMatrix using std::move") { auto A = create_eckit_sparsematrix(); SparseMatrixStorage S = make_sparse_matrix_storage(std::move(A)); EXPECT_NO_THROW(check_matrix(S)); } CASE("Create SparseMatrix copying eckit::linalg::SparseMatrix") { auto A = create_eckit_sparsematrix(); SparseMatrixStorage S = make_sparse_matrix_storage(A); EXPECT_NO_THROW(check_matrix(S)); } CASE("Create single precision SparseMatrix copying from double precision SparseMatrix ") { auto Sdouble = create_sparsematrix_storage(); SparseMatrixStorage S = make_sparse_matrix_storage(Sdouble); EXPECT_NO_THROW(check_matrix(S)); } CASE("Create single precision SparseMatrix moving from double precision SparseMatrix which causes copy") { SparseMatrixStorage S = make_sparse_matrix_storage(create_sparsematrix_storage()); EXPECT_NO_THROW(check_matrix(S)); } CASE("Create double precision SparseMatrix copying from single precision SparseMatrix ") { auto Sfloat = create_sparsematrix_storage(); SparseMatrixStorage S = make_sparse_matrix_storage(Sfloat); EXPECT_NO_THROW(check_matrix(S)); } CASE("Create double precision SparseMatrix moving from single precision SparseMatrix which causes copy") { SparseMatrixStorage S = make_sparse_matrix_storage(create_sparsematrix_storage()); EXPECT_NO_THROW(check_matrix(S)); } CASE("Create base with copy constructor from double precision") { auto Sdouble = create_sparsematrix_storage(); SparseMatrixStorage S(Sdouble); EXPECT_NO_THROW(check_matrix(S)); } CASE("Create base with move constructor from double precision") { SparseMatrixStorage S(create_sparsematrix_storage()); EXPECT_NO_THROW(check_matrix(S)); } CASE("Create base with copy constructor from single precision") { auto Sfloat = create_sparsematrix_storage(); SparseMatrixStorage S(Sfloat); EXPECT_NO_THROW(check_matrix(S)); } CASE("Create base with move constructor from single precision") { SparseMatrixStorage S(create_sparsematrix_storage()); EXPECT_NO_THROW(check_matrix(S)); } #if ATLAS_HAVE_EIGEN CASE("Copy from Eigen double") { auto eigen_matrix = create_eigen_sparsematrix(); SparseMatrixStorage S = make_sparse_matrix_storage(eigen_matrix); EXPECT_NO_THROW(check_matrix(S)); } CASE("Move from Eigen double, avoiding copy") { SparseMatrixStorage S = make_sparse_matrix_storage(create_eigen_sparsematrix()); EXPECT_NO_THROW(check_matrix(S)); } CASE("Copy and convert double from Eigen to single precision") { auto eigen_matrix = create_eigen_sparsematrix(); SparseMatrixStorage S = make_sparse_matrix_storage(eigen_matrix); EXPECT_NO_THROW(check_matrix(S)); } CASE("Move and convert double from Eigen to single precision, should cause copy") { SparseMatrixStorage S = make_sparse_matrix_storage(create_eigen_sparsematrix()); EXPECT_NO_THROW(check_matrix(S)); } CASE("Copy from Eigen single") { auto eigen_matrix = create_eigen_sparsematrix(); SparseMatrixStorage S = make_sparse_matrix_storage(eigen_matrix); EXPECT_NO_THROW(check_matrix(S)); } CASE("Move from Eigen single, avoiding copy") { SparseMatrixStorage S = make_sparse_matrix_storage(create_eigen_sparsematrix()); EXPECT_NO_THROW(check_matrix(S)); } CASE("Copy and convert single from Eigen to double precision") { auto eigen_matrix = create_eigen_sparsematrix(); SparseMatrixStorage S = make_sparse_matrix_storage(eigen_matrix); EXPECT_NO_THROW(check_matrix(S)); } CASE("Move and convert single from Eigen to double precision, should cause copy") { SparseMatrixStorage S = make_sparse_matrix_storage(create_eigen_sparsematrix()); EXPECT_NO_THROW(check_matrix(S)); } CASE("Create chain of moves from eigen double") { SparseMatrixStorage S = make_sparse_matrix_storage(create_eigen_sparsematrix()); EXPECT_NO_THROW(check_matrix(S)); } CASE("Create chain of moves from eigen single") { SparseMatrixStorage S = make_sparse_matrix_storage(create_eigen_sparsematrix()); EXPECT_NO_THROW(check_matrix(S)); } #endif CASE("Test exceptions in make_host_view") { SparseMatrixStorage S( create_sparsematrix_storage() ); EXPECT_THROWS(linalg::make_host_view(S)); // value data type mismatch EXPECT_THROWS((linalg::make_host_view(S))); // index data type mismatch } } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/linalg/test_linalg_sparse.cc0000664000175000017500000003652615160212070022467 0ustar alastairalastair/* * (C) Copyright 2024- 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 "tests/AtlasTestEnvironment.h" #include "tests/linalg/helper_linalg_sparse.h" using namespace atlas::linalg; namespace atlas { namespace test { using SparseMatrix = eckit::linalg::SparseMatrix; //---------------------------------------------------------------------------------------------------------------------- // strings to be used in the tests static std::string eckit_linalg = sparse::backend::eckit_linalg::type(); static std::string openmp = sparse::backend::openmp::type(); static std::string hicsparse = sparse::backend::hicsparse::type(); //---------------------------------------------------------------------------------------------------------------------- CASE("test introspection") { SECTION("ArrayView") { array::ArrayT array(4, 3); auto view = array::make_view(array); using View = decltype(view); static_assert(introspection::has_contiguous::value, "ArrayView expected to have contiguous"); static_assert(introspection::has_rank::value, "ArrayView expected to have rank"); static_assert(introspection::has_shape::value, "ArrayView expected to have shape"); EXPECT(introspection::contiguous(view)); EXPECT_EQ(introspection::shape<0>(view), 4); EXPECT_EQ(introspection::shape<1>(view), 3); } SECTION("std::vector") { using View = std::vector; static_assert(not introspection::has_contiguous::value, "std::vector does not have contiguous"); static_assert(not introspection::has_rank::value, "std::vector does not have rank"); static_assert(not introspection::has_shape::value, "std::vector does not have shape"); static_assert(introspection::rank() == 1, "std::vector is of rank 1"); auto v = View{1, 2, 3, 4}; EXPECT(introspection::contiguous(v)); EXPECT_EQ(introspection::shape<0>(v), 4); EXPECT_EQ(introspection::shape<1>(v), 4); } SECTION("eckit::linlag::Vector") { using View = Vector; static_assert(not introspection::has_contiguous::value, "eckit::linalg::Vector does not have contiguous"); static_assert(not introspection::has_rank::value, "eckit::linalg::Vector does not have rank"); static_assert(not introspection::has_shape::value, "seckit::linalg::Vector does not have shape"); static_assert(introspection::rank() == 1, "eckit::linalg::Vector is of rank 1"); auto v = Vector{1, 2, 3, 4}; EXPECT(introspection::contiguous(v)); EXPECT_EQ(introspection::shape<0>(v), 4); } SECTION("eckit::linlag::Matrix") { using View = Matrix; static_assert(not introspection::has_contiguous::value, "eckit::linalg::Matrix does not have contiguous"); static_assert(not introspection::has_rank::value, "eckit::linalg::Matrix does not have rank"); static_assert(not introspection::has_shape::value, "seckit::linalg::Matrix does not have shape"); static_assert(introspection::rank() == 2, "eckit::linalg::Matrix is of rank 1"); auto m = Matrix{{1., 2.}, {3., 4.}, {5., 6.}}; EXPECT(introspection::contiguous(m)); // Following is reversed because M has column-major ordering EXPECT_EQ(introspection::shape<0>(m), 3); EXPECT_EQ(introspection::shape<1>(m), 2); } } //---------------------------------------------------------------------------------------------------------------------- CASE("test backend functionalities") { sparse::current_backend(openmp); EXPECT_EQ(sparse::current_backend().type(), openmp); EXPECT_EQ(sparse::current_backend().getString("backend", "undefined"), "undefined"); sparse::current_backend(eckit_linalg); EXPECT_EQ(sparse::current_backend().type(), "eckit_linalg"); EXPECT_EQ(sparse::current_backend().getString("backend", "undefined"), "undefined"); sparse::current_backend().set("backend", "default"); EXPECT_EQ(sparse::current_backend().getString("backend"), "default"); sparse::current_backend(openmp); EXPECT_EQ(sparse::current_backend().getString("backend", "undefined"), "undefined"); EXPECT_EQ(sparse::default_backend(eckit_linalg).getString("backend"), "default"); sparse::current_backend(hicsparse); EXPECT_EQ(sparse::current_backend().type(), "hicsparse"); EXPECT_EQ(sparse::current_backend().getString("backend", "undefined"), "undefined"); sparse::default_backend(eckit_linalg).set("backend", "generic"); EXPECT_EQ(sparse::default_backend(eckit_linalg).getString("backend"), "generic"); const sparse::Backend backend_default = sparse::Backend(); const sparse::Backend backend_openmp = sparse::backend::openmp(); const sparse::Backend backend_eckit_linalg = sparse::backend::eckit_linalg(); const sparse::Backend backend_hicsparse = sparse::backend::hicsparse(); EXPECT_EQ(backend_default.type(), hicsparse); EXPECT_EQ(backend_openmp.type(), openmp); EXPECT_EQ(backend_eckit_linalg.type(), eckit_linalg); EXPECT_EQ(backend_hicsparse.type(), hicsparse); EXPECT_EQ(std::string(backend_openmp), openmp); EXPECT_EQ(std::string(backend_eckit_linalg), eckit_linalg); } //---------------------------------------------------------------------------------------------------------------------- CASE("sparse_matrix vector multiply (spmv)") { // "square" matrix // A = 2 . -3 // . 2 . // . . 2 // x = 1 2 3 // y = 1 2 3 SparseMatrix A{3, 3, {{0, 0, 2.}, {0, 2, -3.}, {1, 1, 2.}, {2, 2, 2.}}}; for (std::string backend : {openmp, eckit_linalg}) { sparse::current_backend(backend); SECTION("test_identity [backend=" + sparse::current_backend().type() + "]") { { Vector y1(3); SparseMatrix B; B.setIdentity(3, 3); sparse_matrix_multiply(B, Vector{1., 2., 3.}, y1); expect_equal(y1, Vector{1., 2., 3.}); } { SparseMatrix C; C.setIdentity(6, 3); Vector y2(6); sparse_matrix_multiply(C, Vector{1., 2., 3.}, y2); expect_equal(y2, Vector{1., 2., 3.}); expect_equal(y2.data() + 3, Vector{0., 0., 0.}.data(), 3); } { SparseMatrix D; D.setIdentity(2, 3); Vector y3(2); sparse_matrix_multiply(D, Vector{1., 2., 3.}, y3); expect_equal(y3, Vector{1., 2., 3.}); } } SECTION("eckit::Vector [backend=" + sparse::current_backend().type() + "]") { Vector y(3); sparse_matrix_multiply(A, Vector{1., 2., 3.}, y); expect_equal(y, Vector{-7., 4., 6.}); // spmv of sparse matrix and vector of non-matching sizes should fail EXPECT_THROWS_AS(sparse_matrix_multiply(A, Vector(2), y), eckit::AssertionFailed); } SECTION("View of atlas::Array [backend=" + backend + "]") { ArrayVector x(Vector{1., 2., 3.}); ArrayVector y(3); auto x_view = x.view(); auto y_view = y.view(); sparse_matrix_multiply(A, x_view, y_view); expect_equal(y.view(), Vector{-7., 4., 6.}); // sparse_matrix_multiply of sparse matrix and vector of non-matching sizes should fail { ArrayVector x2(2); auto x2_view = x2.view(); EXPECT_THROWS_AS(sparse_matrix_multiply(A, x2_view, y_view), eckit::AssertionFailed); } } SECTION("View of atlas::Array [backend=" + backend + "]") { ArrayVector x(Vector{1., 2., 3.}); ArrayVector y(3); auto x_view = x.view(); auto y_view = y.view(); auto spmm = SparseMatrixMultiply{backend}; spmm(A, x_view, y_view); expect_equal(y_view, Vector{-7., 4., 6.}); } SECTION("View of atlas::Array [backend=" + backend + "]") { ArrayVector x(Vector{1., 2., 3.}); ArrayVector y(3); auto x_view = x.view(); auto y_view = y.view(); auto spmm = SparseMatrixMultiply{backend}; spmm.multiply(A, x_view, y_view); expect_equal(y_view, Vector{-7., 4., 6.}); } } } CASE("sparse_matrix vector multiply-add (spmv)") { // "square" matrix // A = 2 . -3 // . 2 . // . . 2 // x = 1 2 3 // y = 1 2 3 SparseMatrix A{3, 3, {{0, 0, 2.}, {0, 2, -3.}, {1, 1, 2.}, {2, 2, 2.}}}; for (std::string backend : {openmp, eckit_linalg}) { sparse::current_backend(backend); SECTION("View of atlas::Array [backend=" + backend + "]") { ArrayVector x(Vector{1., 2., 3.}); ArrayVector y(Vector{4., 5., 6.}); auto x_view = x.view(); auto y_view = y.view(); sparse_matrix_multiply_add(A, x_view, y_view); expect_equal(y.view(), Vector{-3., 9., 12.}); // sparse_matrix_multiply_add of sparse matrix and vector of non-matching sizes should fail { ArrayVector x2(2); auto x2_view = x2.view(); EXPECT_THROWS_AS(sparse_matrix_multiply_add(A, x2_view, y_view), eckit::AssertionFailed); } } SECTION("sparse_matrix_multiply_add [backend=" + backend + "]") { ArrayVector x(Vector{1., 2., 3.}); ArrayVector y(Vector{1., 2., 3.}); auto x_view = x.view(); auto y_view = y.view(); auto spmm = SparseMatrixMultiply{sparse::backend::openmp()}; spmm.multiply_add(A, x_view, y_view); expect_equal(y_view, Vector{-6., 6., 9.}); } } } CASE("sparse_matrix matrix multiply (spmm)") { // "square" // A = 2 . -3 // . 2 . // . . 2 // x = 1 2 3 // y = 1 2 3 SparseMatrix A{3, 3, {{0, 0, 2.}, {0, 2, -3.}, {1, 1, 2.}, {2, 2, 2.}}}; Matrix m{{1., 2.}, {3., 4.}, {5., 6.}}; Matrix c_exp{{-13., -14.}, {6., 8.}, {10., 12.}}; for (std::string backend : {openmp, eckit_linalg}) { sparse::current_backend(backend); SECTION("eckit::Matrix [backend=" + sparse::current_backend().type() + "]") { auto c = Matrix(3, 2); sparse_matrix_multiply(A, m, c); expect_equal(c, c_exp); // spmm of sparse matrix and matrix of non-matching sizes should fail EXPECT_THROWS_AS(sparse_matrix_multiply(A, Matrix(2, 2), c), eckit::AssertionFailed); } SECTION("View of eckit::Matrix [backend=" + backend + "]") { auto c = Matrix(3, 2); auto mv = atlas::linalg::make_view(m); // convert Matrix to array::LocalView auto cv = atlas::linalg::make_view(c); sparse_matrix_multiply(A, mv, cv, Indexing::layout_right); expect_equal(c, c_exp); } SECTION("View of atlas::Array PointsRight [backend=" + sparse::current_backend().type() + "]") { ArrayMatrix ma(m); ArrayMatrix c(3, 2); auto ma_view = ma.view(); auto c_view = c.view(); sparse_matrix_multiply(A, ma_view, c_view, Indexing::layout_right); expect_equal(c_view, c_exp); } } SECTION("sparse_matrix_multiply [backend=openmp]") { sparse::current_backend(eckit_linalg); // expected to be ignored further auto backend = sparse::backend::openmp(); ArrayMatrix ma(m); ArrayMatrix c(3, 2); auto ma_view = ma.view(); auto c_view = c.view(); sparse_matrix_multiply(A, ma_view, c_view, backend); expect_equal(c_view, ArrayMatrix(c_exp).view()); } SECTION("SparseMatrixMultiply [backend=openmp] 1") { sparse::current_backend(eckit_linalg); // expected to be ignored auto spmm = SparseMatrixMultiply{sparse::backend::openmp()}; ArrayMatrix ma(m); ArrayMatrix c(3, 2); auto ma_view = ma.view(); auto c_view = c.view(); spmm(A, ma_view, c_view); expect_equal(c_view, ArrayMatrix(c_exp).view()); } SECTION("SparseMatrixMultiply [backend=openmp] 2") { sparse::current_backend(eckit_linalg); // expected to be ignored auto spmm = SparseMatrixMultiply{openmp}; ArrayMatrix ma(m); ArrayMatrix c(3, 2); auto ma_view = ma.view(); auto c_view = c.view(); spmm(A, ma_view, c_view); expect_equal(c_view, ArrayMatrix(c_exp).view()); } SECTION("SparseMatrixMultiply::multiply [backend=openmp]") { sparse::current_backend(eckit_linalg); // expected to be ignored auto spmm = SparseMatrixMultiply{openmp}; ArrayMatrix ma(m); ArrayMatrix c(3, 2); auto ma_view = ma.view(); auto c_view = c.view(); spmm.multiply(A, ma_view, c_view); expect_equal(c_view, ArrayMatrix(c_exp).view()); } } CASE("sparse_matrix matrix multiply-add (spmm)") { // "square" // A = 2 . -3 // . 2 . // . . 2 SparseMatrix A{3, 3, {{0, 0, 2.}, {0, 2, -3.}, {1, 1, 2.}, {2, 2, 2.}}}; Matrix m{{1., 2.}, {3., 4.}, {5., 6.}}; Matrix y_exp{{-12., -12.}, {9., 12.}, {15., 18.}}; for (std::string backend : {openmp, eckit_linalg}) { sparse::current_backend(backend); SECTION("View of atlas::Array PointsRight [backend=" + sparse::current_backend().type() + "]") { ArrayMatrix x(m); ArrayMatrix y(m); auto x_view = x.view(); auto y_view = y.view(); sparse_matrix_multiply_add(A, x_view, y_view, Indexing::layout_right); expect_equal(y_view, y_exp); } } SECTION("sparse_matrix_multiply_add [backend=openmp]") { ArrayMatrix x(m); ArrayMatrix y(m); auto x_view = x.view(); auto y_view = y.view(); sparse_matrix_multiply_add(A, x_view, y_view, sparse::backend::openmp()); expect_equal(y_view, ArrayMatrix(y_exp).view()); } SECTION("SparseMatrixMultiply::multiply_add [backend=openmp]") { auto spmm = SparseMatrixMultiply{sparse::backend::openmp()}; ArrayMatrix x(m); ArrayMatrix y(m); auto x_view = x.view(); auto y_view = y.view(); spmm.multiply_add(A, x_view, y_view); expect_equal(y_view, ArrayMatrix(y_exp).view()); } } //---------------------------------------------------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/linalg/test_linalg_dense.cc0000664000175000017500000001162515160212070022261 0ustar alastairalastair/* * (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 #include #include "atlas/library/config.h" #if ATLAS_ECKIT_HAVE_ECKIT_585 #include "eckit/linalg/LinearAlgebraDense.h" #else #include "eckit/linalg/LinearAlgebra.h" #endif #include "eckit/linalg/Matrix.h" #include "eckit/linalg/Vector.h" #include "atlas/linalg/dense.h" #include "tests/AtlasTestEnvironment.h" using namespace atlas::linalg; namespace atlas { namespace test { //---------------------------------------------------------------------------------------------------------------------- // strings to be used in the tests static std::string eckit_linalg = dense::backend::eckit_linalg::type(); //---------------------------------------------------------------------------------------------------------------------- // Only reason to define these derived classes is for nicer constructors and convenience in the tests class Matrix : public eckit::linalg::Matrix { public: using Scalar = eckit::linalg::Scalar; using eckit::linalg::Matrix::Matrix; Matrix(const std::initializer_list>& m): eckit::linalg::Matrix::Matrix(m.size(), m.size() ? m.begin()->size() : 0) { size_t r = 0; for (auto& row : m) { for (size_t c = 0; c < cols(); ++c) { operator()(r, c) = row[c]; } ++r; } } }; //---------------------------------------------------------------------------------------------------------------------- template void expect_equal(T* v, T* r, size_t s) { EXPECT(is_approximately_equal(eckit::testing::make_view(v, s), eckit::testing::make_view(r, s), T(1.e-5))); } template void expect_equal(const T1& v, const T2& r) { expect_equal(v.data(), r.data(), std::min(v.size(), r.size())); } //---------------------------------------------------------------------------------------------------------------------- CASE("test configuration via resource") { if (atlas::Library::instance().linalgDenseBackend().empty()) { #if ATLAS_ECKIT_HAVE_ECKIT_585 if (eckit::linalg::LinearAlgebraDense::hasBackend("mkl")) { #else if (eckit::linalg::LinearAlgebra::hasBackend("mkl")) { #endif EXPECT_EQ(dense::current_backend().type(), "mkl"); } else { EXPECT_EQ(dense::current_backend().type(), eckit_linalg); } } else { EXPECT_EQ(dense::current_backend().type(), atlas::Library::instance().linalgDenseBackend()); } } CASE("test backend functionalities") { EXPECT_EQ(dense::current_backend().getString("backend", "undefined"), "undefined"); dense::current_backend(eckit_linalg); EXPECT_EQ(dense::current_backend().type(), "eckit_linalg"); EXPECT_EQ(dense::current_backend().getString("backend", "undefined"), "undefined"); dense::current_backend().set("backend", "default"); EXPECT_EQ(dense::current_backend().getString("backend"), "default"); EXPECT_EQ(dense::default_backend(eckit_linalg).getString("backend"), "default"); dense::default_backend(eckit_linalg).set("backend", "generic"); EXPECT_EQ(dense::default_backend(eckit_linalg).getString("backend"), "generic"); const dense::Backend backend_default = dense::Backend(); const dense::Backend backend_eckit_linalg = dense::backend::eckit_linalg(); EXPECT_EQ(backend_default.type(), eckit_linalg); EXPECT_EQ(backend_eckit_linalg.type(), eckit_linalg); EXPECT_EQ(std::string(backend_eckit_linalg), eckit_linalg); } //---------------------------------------------------------------------------------------------------------------------- CASE("matrix matrix multiply (gemm)") { Matrix A = Matrix{{1., -2.}, {-4., 2.}}; Matrix Z = Matrix{{9., -6.}, {-12., 12.}}; SECTION("bad sizes") { Matrix Y = Matrix{{-42., -42.}, {-42., -42.}}; EXPECT_THROWS_AS(linalg::matrix_multiply(A, Matrix{{0, 0}}, Y), eckit::AssertionFailed); } std::vector backends{"eckit_linalg", "generic", "openmp", "lapack", "eigen"}; for (auto& backend : backends) { if (dense::Backend{backend}.available()) { SECTION(backend) { Matrix Y = Matrix{{-42., -42.}, {-42., -42.}}; linalg::matrix_multiply(A, A, Y, dense::Backend{backend}); expect_equal(Y, Z); } } } } //---------------------------------------------------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/linalg/test_linalg_triplet.cc0000664000175000017500000001542215160212070022645 0ustar alastairalastair/* * (C) Crown Copyright 2025 Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ #include #include #include #include #include #include "atlas/linalg/sparse/SparseMatrixStorage.h" #include "atlas/linalg/sparse/SparseMatrixTriplet.h" #include "atlas/runtime/Exception.h" #include "eckit/testing/Test.h" #include "tests/AtlasTestEnvironment.h" namespace atlas::test { using namespace atlas::linalg; using TripletType = Triplet; CASE("Test triplet.") { SECTION("Test triplet construction.") { const auto t = Triplet{1, 2, 3.}; static_assert(std::is_same_v); ATLAS_ASSERT(t.row() == 1); ATLAS_ASSERT(t.col() == 2); ATLAS_ASSERT(t.value() == 3.); } SECTION("Test triplet less than operator.") { const auto t1 = Triplet{1, 2, 0.}; const auto t2 = Triplet{1, 3, 0.}; const auto t3 = Triplet{2, 1, 0.}; ATLAS_ASSERT(t1 < t2); ATLAS_ASSERT(t1 < t3); ATLAS_ASSERT(!(t2 < t1)); ATLAS_ASSERT(!(t3 < t1)); ATLAS_ASSERT(!(t1 < t1)); } } std::tuple, int, int> make_test_triplets() { // Return triplet vector, n_rows and n_cols. return {{ {0, 1, 2.}, {1, 0, 1.}, {1, 1, 3.}, {3, 0, 4.}, {3, 2, 5.}, }, 4, 3}; } CASE("Test sparse matrix construction from triplets.") { const auto test_triplets = [](const SparseMatrixStorage& matrix) { ATLAS_ASSERT(matrix.rows() == 4); ATLAS_ASSERT(matrix.cols() == 3); ATLAS_ASSERT(matrix.nnz() == 5); const auto outer_view = array::make_view(matrix.outer()); const auto inner_view = array::make_view(matrix.inner()); const auto values_view = array::make_view(matrix.value()); ATLAS_ASSERT(outer_view(0) == 0); ATLAS_ASSERT(outer_view(1) == 1); ATLAS_ASSERT(outer_view(2) == 3); ATLAS_ASSERT(outer_view(3) == 3); ATLAS_ASSERT(outer_view(4) == 5); ATLAS_ASSERT(inner_view(0) == 1); ATLAS_ASSERT(inner_view(1) == 0); ATLAS_ASSERT(inner_view(2) == 1); ATLAS_ASSERT(inner_view(3) == 0); ATLAS_ASSERT(inner_view(4) == 2); ATLAS_ASSERT(values_view(0) == 2.); ATLAS_ASSERT(values_view(1) == 1.); ATLAS_ASSERT(values_view(2) == 3.); ATLAS_ASSERT(values_view(3) == 4.); ATLAS_ASSERT(values_view(4) == 5.); }; const auto shuffle = [](const auto& begin, const auto& end) { std::shuffle(begin, end, std::mt19937{0}); }; SECTION("Test sparse matrix construction from std::vector.") { auto [triplets, n_rows, n_cols] = make_test_triplets(); // Triplets do not need to be sorted. test_triplets(make_sparse_matrix_storage_from_triplets(n_rows, n_cols, triplets, true)); shuffle(triplets.begin(), triplets.end()); // Triplets implicitly sorted. test_triplets(make_sparse_matrix_storage_from_triplets(n_rows, n_cols, triplets)); // Should fail as triplets not sorted. shuffle(triplets.begin(), triplets.end()); EXPECT_THROWS(make_sparse_matrix_storage_from_triplets(n_rows, n_cols, triplets, true)); } SECTION("Test sparse matrix construction from TripletType[].") { auto [triplets, n_rows, n_cols] = make_test_triplets(); test_triplets(make_sparse_matrix_storage_from_triplets(n_rows, n_cols, triplets.size(), triplets.data(), true)); shuffle(triplets.begin(), triplets.end()); test_triplets(make_sparse_matrix_storage_from_triplets(n_rows, n_cols, triplets.size(), triplets.data())); shuffle(triplets.begin(), triplets.end()); EXPECT_THROWS(make_sparse_matrix_storage_from_triplets(n_rows, n_cols, triplets.size(), triplets.data(), true)); } SECTION("Test sparse matrix construction from non-random-access-containers.") { auto [triplets, n_rows, n_cols] = make_test_triplets(); const auto list_triplets = std::list{triplets.begin(), triplets.end()}; shuffle(triplets.begin(), triplets.end()); const auto shuffled_list_triplets = std::list{triplets.begin(), triplets.end()}; // Note: std::set is sorted by construction. const auto set_triplets = std::set{triplets.begin(), triplets.end()}; test_triplets( make_sparse_matrix_storage_from_triplets(n_rows, n_cols, list_triplets.begin(), list_triplets.end())); test_triplets( make_sparse_matrix_storage_from_triplets(n_rows, n_cols, set_triplets.begin(), set_triplets.end())); EXPECT_THROWS(make_sparse_matrix_storage_from_triplets(n_rows, n_cols, shuffled_list_triplets.begin(), shuffled_list_triplets.end())); } SECTION("Test incorrect rows/cols throw") { auto [triplets, n_rows, n_cols] = make_test_triplets(); shuffle(triplets.begin(), triplets.end()); EXPECT_THROWS(make_sparse_matrix_storage_from_triplets(n_rows - 1, n_cols, triplets)); EXPECT_THROWS(make_sparse_matrix_storage_from_triplets(n_rows, n_cols - 1, triplets)); } } CASE("Test 'for each triplet' methods") { auto [triplets, n_rows, n_cols] = make_test_triplets(); const auto ref_triplets = triplets; const auto matrix_storage = make_sparse_matrix_storage_from_triplets(n_rows, n_cols, triplets); const auto matrix_view = make_host_view(matrix_storage); SECTION("Test 'for each triplet' on matrix row.") { { auto ref_iter = ref_triplets.begin(); for (std::size_t r = 0; r < matrix_view.rows(); ++r) { sparse_matrix_for_each_row(r, matrix_view, [&](int row, int col, double value) { ATLAS_ASSERT(row == ref_iter->row()); ATLAS_ASSERT(col == ref_iter->col()); ATLAS_ASSERT(value == ref_iter->value()); ++ref_iter; }); } } } SECTION("Test 'for each triplet' method on full matrix.") { { auto ref_iter = ref_triplets.begin(); sparse_matrix_for_each(matrix_view, [&](int row, int col, double value) { ATLAS_ASSERT(row == ref_iter->row()); ATLAS_ASSERT(col == ref_iter->col()); ATLAS_ASSERT(value == ref_iter->value()); ++ref_iter; }); } } } } // namespace atlas::test int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/linalg/helper_linalg_sparse.h0000664000175000017500000000773115160212070022625 0ustar alastairalastair/* * (C) Copyright 2024- 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 "eckit/linalg/Matrix.h" #include "eckit/linalg/Vector.h" #include "atlas/array.h" #include "atlas/linalg/sparse.h" using namespace atlas::linalg; namespace atlas { namespace test { class Vector : public eckit::linalg::Vector { public: using Scalar = eckit::linalg::Scalar; using eckit::linalg::Vector::Vector; Vector(const std::initializer_list& v): eckit::linalg::Vector::Vector(v.size()) { size_t i = 0; for (auto& s : v) { operator[](i++) = s; } } }; class Matrix : public eckit::linalg::Matrix { public: using Scalar = eckit::linalg::Scalar; using eckit::linalg::Matrix::Matrix; Matrix(const std::initializer_list>& m): eckit::linalg::Matrix::Matrix(m.size(), m.size() ? m.begin()->size() : 0) { size_t r = 0; for (auto& row : m) { for (size_t c = 0; c < cols(); ++c) { operator()(r, c) = row[c]; } ++r; } } }; // 2D array constructable from eckit::linalg::Matrix // Indexing/memorylayout and data type can be customized for testing template struct ArrayMatrix { array::ArrayView view() { return host_view(); } array::ArrayView host_view() { array.updateHost(); return array::make_host_view(array); } array::ArrayView device_view() { array.updateDevice(); return array::make_device_view(array); } ArrayMatrix(const eckit::linalg::Matrix& m): ArrayMatrix(m.rows(), m.cols()) { auto view_ = array::make_view(array); for (int r = 0; r < m.rows(); ++r) { for (int c = 0; c < m.cols(); ++c) { auto& v = layout_left ? view_(r, c) : view_(c, r); v = m(r, c); } } } ArrayMatrix(int r, int c): array(make_shape(r, c)) {} private: static constexpr bool layout_left = (indexing == Indexing::layout_left); static array::ArrayShape make_shape(int rows, int cols) { return layout_left ? array::make_shape(rows, cols) : array::make_shape(cols, rows); } array::ArrayT array; }; // 1D array constructable from eckit::linalg::Vector template struct ArrayVector { array::ArrayView view() { return host_view(); } array::ArrayView host_view() { array.updateHost(); return array::make_host_view(array); } array::ArrayView device_view() { array.updateDevice(); return array::make_device_view(array); } ArrayVector(const eckit::linalg::Vector& v): ArrayVector(v.size()) { auto view_ = array::make_view(array); for (int n = 0; n < v.size(); ++n) { view_[n] = v[n]; } } ArrayVector(int size): array(size) {} private: array::ArrayT array; }; //---------------------------------------------------------------------------------------------------------------------- template void expect_equal(T* v, T* r, size_t s) { EXPECT(is_approximately_equal(eckit::testing::make_view(v, s), eckit::testing::make_view(r, s), T(1.e-5))); } template void expect_equal(const T1& v, const T2& r) { expect_equal(v.data(), r.data(), std::min(v.size(), r.size())); } //---------------------------------------------------------------------------------------------------------------------- } // namespace test } // namespace atlas atlas-0.46.0/src/tests/linalg/test_linalg_sparse_matrix_gpu.cc0000664000175000017500000003277715160212070024732 0ustar alastairalastair/* * (C) Copyright 2024- 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/linalg/SparseMatrix.h" #include "hic/hic.h" #include "hic/hicsparse.h" #include "tests/AtlasTestEnvironment.h" #include "atlas/linalg/sparse/SparseMatrixStorage.h" #include "atlas/linalg/sparse/SparseMatrixView.h" #include "atlas/linalg/sparse/MakeSparseMatrixStorageEigen.h" #include "atlas/linalg/sparse/MakeSparseMatrixStorageEckit.h" using atlas::linalg::SparseMatrixStorage; using atlas::linalg::make_sparse_matrix_storage; using atlas::linalg::make_host_view; using atlas::linalg::make_device_view; namespace atlas { namespace test { template void do_hicsparse_matrix_multiply(const atlas::linalg::SparseMatrixView& device_A, const XValue* device_x, YValue* device_y) { // Create sparse library handle hicsparseHandle_t handle; HICSPARSE_CALL( hicsparseCreate(&handle) ); auto get_hic_value_type = [](const auto& dummy) -> hicDataType { if(std::is_same_v,double> ) { return HIC_R_64F; } else if (std::is_same_v,float> ) { return HIC_R_32F; } ATLAS_NOTIMPLEMENTED; }; auto get_hic_index_type = [](const auto& dummy) -> hicsparseIndexType_t { if (std::is_same_v>,int> ) { return HICSPARSE_INDEX_32I; } else if (std::is_same_v>,long> ) { return HICSPARSE_INDEX_64I; } ATLAS_NOTIMPLEMENTED; }; auto compute_type = get_hic_value_type(MatrixValue()); auto index_type = get_hic_index_type(IndexType()); hicsparseConstSpMatDescr_t matA; HICSPARSE_CALL( hicsparseCreateConstCsr( &matA, device_A.rows(), device_A.cols(), device_A.nnz(), device_A.outer(), // row_offsets device_A.inner(), // column_indices device_A.value(), // values index_type, index_type, HICSPARSE_INDEX_BASE_ZERO, get_hic_value_type(MatrixValue())) ); // Create dense matrix descriptors hicsparseConstDnVecDescr_t vecX; HICSPARSE_CALL( hicsparseCreateConstDnVec( &vecX, device_A.cols(), device_x, get_hic_value_type(XValue())) ); hicsparseDnVecDescr_t vecY; HICSPARSE_CALL( hicsparseCreateDnVec( &vecY, device_A.rows(), device_y, get_hic_value_type(YValue())) ); // Set parameters in compute_type const MatrixValue alpha = 1.0; const MatrixValue beta = 0.0; // Determine buffer size size_t bufferSize = 0; HICSPARSE_CALL( hicsparseSpMV_bufferSize( handle, HICSPARSE_OPERATION_NON_TRANSPOSE, &alpha, matA, vecX, &beta, vecY, compute_type, HICSPARSE_SPMV_ALG_DEFAULT, &bufferSize) ); // Allocate buffer char* buffer; HIC_CALL( hicMalloc(&buffer, bufferSize) ); // Perform SpMV HICSPARSE_CALL( hicsparseSpMV( handle, HICSPARSE_OPERATION_NON_TRANSPOSE, &alpha, matA, vecX, &beta, vecY, compute_type, HICSPARSE_SPMV_ALG_DEFAULT, buffer) ); HIC_CALL( hicFree(buffer) ); HICSPARSE_CALL( hicsparseDestroyDnVec(vecY) ); HICSPARSE_CALL( hicsparseDestroyDnVec(vecX) ); HICSPARSE_CALL( hicsparseDestroySpMat(matA) ); HICSPARSE_CALL( hicsparseDestroy(handle) ); } #if ATLAS_HAVE_EIGEN template > Eigen::SparseMatrix create_eigen_sparsematrix() { Eigen::SparseMatrix matrix(3,3); std::vector> triplets; triplets.reserve(4); triplets.emplace_back(0, 0, 2.); triplets.emplace_back(0, 2, -3.); triplets.emplace_back(1, 1, 2.); triplets.emplace_back(2, 2, 2.); matrix.setFromTriplets(triplets.begin(), triplets.end()); return matrix; } #endif eckit::linalg::SparseMatrix create_eckit_sparsematrix() { return eckit::linalg::SparseMatrix{3, 3, {{0, 0, 2.}, {0, 2, -3.}, {1, 1, 2.}, {2, 2, 2.}}}; } template SparseMatrixStorage create_sparsematrix_storage() { return make_sparse_matrix_storage(create_eckit_sparsematrix()); } template void check_array(const array::Array& array, const std::vector& expected) { EXPECT_EQ(array.size(), expected.size()); auto view = array::make_view(array); for( std::size_t i=0; i void check_matrix(const SparseMatrixStorage& m) { std::vector expected_value{2., -3., 2., 2.}; std::vector expected_outer{0, 2, 3, 4}; std::vector expected_inner{0, 2, 1, 2}; EXPECT_EQ(m.rows(), 3); EXPECT_EQ(m.cols(), 3); EXPECT_EQ(m.nnz(), 4); check_array(m.value(),expected_value); check_array(m.outer(),expected_outer); check_array(m.inner(),expected_inner); m.updateDevice(); auto host_matrix_view = make_host_view(m); auto device_matrix_view = make_device_view(m); std::vector host_outer(host_matrix_view.outer_size()); std::vector host_inner(host_matrix_view.inner_size()); std::vector host_value(host_matrix_view.value_size()); HIC_CALL(hicMemcpy(host_outer.data(), device_matrix_view.outer(), device_matrix_view.outer_size() * sizeof(Index), hicMemcpyDeviceToHost)); HIC_CALL(hicMemcpy(host_inner.data(), device_matrix_view.inner(), device_matrix_view.inner_size() * sizeof(Index), hicMemcpyDeviceToHost)); HIC_CALL(hicMemcpy(host_value.data(), device_matrix_view.value(), device_matrix_view.value_size() * sizeof(Value), hicMemcpyDeviceToHost)); EXPECT(host_outer == expected_outer); EXPECT(host_inner == expected_inner); EXPECT(host_value == expected_value); array::ArrayT x(m.cols()); array::ArrayT y(m.rows()); array::make_host_view(x).assign({1.0, 2.0, 3.0}); m.updateDevice(); x.updateDevice(); y.allocateDevice(); do_hicsparse_matrix_multiply(device_matrix_view, x.device_data(), y.device_data()); // Check result y.updateHost(); std::vector expected_y{-7.0, 4.0, 6.0}; check_array(y, expected_y); } void check_matrix(const SparseMatrixStorage& m) { if (m.value().datatype() == atlas::make_datatype() && m.outer().datatype() == atlas::make_datatype()) { check_matrix(m); } else if (m.value().datatype() == atlas::make_datatype() && m.outer().datatype() == atlas::make_datatype()) { check_matrix(m); } else if (m.value().datatype() == atlas::make_datatype() && m.outer().datatype() == atlas::make_datatype()) { check_matrix(m); } else if (m.value().datatype() == atlas::make_datatype() && m.outer().datatype() == atlas::make_datatype()) { check_matrix(m); } else if (m.value().datatype() == atlas::make_datatype() && m.outer().datatype() == atlas::make_datatype()) { check_matrix(m); } else if (m.value().datatype() == atlas::make_datatype() && m.outer().datatype() == atlas::make_datatype()) { check_matrix(m); } else if (m.value().datatype() == atlas::make_datatype() && m.outer().datatype() == atlas::make_datatype()) { check_matrix(m); } else if (m.value().datatype() == atlas::make_datatype() && m.outer().datatype() == atlas::make_datatype()) { check_matrix(m); } else { ATLAS_NOTIMPLEMENTED; } } CASE("Create SparseMatrix moving eckit::linalg::SparseMatrix") { SparseMatrixStorage S = make_sparse_matrix_storage(create_eckit_sparsematrix()); EXPECT_NO_THROW(check_matrix(S)); } CASE("Create SparseMatrix moving eckit::linalg::SparseMatrix using std::move") { auto A = create_eckit_sparsematrix(); SparseMatrixStorage S = make_sparse_matrix_storage(std::move(A)); EXPECT_NO_THROW(check_matrix(S)); } CASE("Create SparseMatrix copying eckit::linalg::SparseMatrix") { auto A = create_eckit_sparsematrix(); SparseMatrixStorage S = make_sparse_matrix_storage(A); EXPECT_NO_THROW(check_matrix(S)); } CASE("Create single precision SparseMatrix copying from double precision SparseMatrix ") { auto Sdouble = create_sparsematrix_storage(); SparseMatrixStorage S = make_sparse_matrix_storage(Sdouble); EXPECT_NO_THROW(check_matrix(S)); } CASE("Create single precision SparseMatrix moving from double precision SparseMatrix which causes copy") { SparseMatrixStorage S = make_sparse_matrix_storage(create_sparsematrix_storage()); EXPECT_NO_THROW(check_matrix(S)); } CASE("Create double precision SparseMatrix copying from single precision SparseMatrix ") { auto Sfloat = create_sparsematrix_storage(); SparseMatrixStorage S = make_sparse_matrix_storage(Sfloat); EXPECT_NO_THROW(check_matrix(S)); } CASE("Create double precision SparseMatrix moving from single precision SparseMatrix which causes copy") { SparseMatrixStorage S = make_sparse_matrix_storage(create_sparsematrix_storage()); EXPECT_NO_THROW(check_matrix(S)); } CASE("Create base with copy constructor from double precision") { auto Sdouble = create_sparsematrix_storage(); SparseMatrixStorage S(Sdouble); EXPECT_NO_THROW(check_matrix(S)); } CASE("Create base with move constructor from double precision") { SparseMatrixStorage S(create_sparsematrix_storage()); EXPECT_NO_THROW(check_matrix(S)); } CASE("Create base with copy constructor from single precision") { auto Sfloat = create_sparsematrix_storage(); SparseMatrixStorage S(Sfloat); EXPECT_NO_THROW(check_matrix(S)); } CASE("Create base with move constructor from single precision") { SparseMatrixStorage S(create_sparsematrix_storage()); EXPECT_NO_THROW(check_matrix(S)); } #if ATLAS_HAVE_EIGEN CASE("Copy from Eigen double") { auto eigen_matrix = create_eigen_sparsematrix(); SparseMatrixStorage S = make_sparse_matrix_storage(eigen_matrix); EXPECT_NO_THROW(check_matrix(S)); } CASE("Move from Eigen double, avoiding copy") { SparseMatrixStorage S = make_sparse_matrix_storage(create_eigen_sparsematrix()); EXPECT_NO_THROW(check_matrix(S)); } CASE("Copy and convert double from Eigen to single precision") { auto eigen_matrix = create_eigen_sparsematrix(); SparseMatrixStorage S = make_sparse_matrix_storage(eigen_matrix); EXPECT_NO_THROW(check_matrix(S)); } CASE("Move and convert double from Eigen to single precision, should cause copy") { SparseMatrixStorage S = make_sparse_matrix_storage(create_eigen_sparsematrix()); EXPECT_NO_THROW(check_matrix(S)); } CASE("Copy from Eigen single") { auto eigen_matrix = create_eigen_sparsematrix(); SparseMatrixStorage S = make_sparse_matrix_storage(eigen_matrix); EXPECT_NO_THROW(check_matrix(S)); } CASE("Move from Eigen single, avoiding copy") { SparseMatrixStorage S = make_sparse_matrix_storage(create_eigen_sparsematrix()); EXPECT_NO_THROW(check_matrix(S)); } CASE("Copy and convert single from Eigen to double precision") { auto eigen_matrix = create_eigen_sparsematrix(); SparseMatrixStorage S = make_sparse_matrix_storage(eigen_matrix); EXPECT_NO_THROW(check_matrix(S)); } CASE("Move and convert single from Eigen to double precision, should cause copy") { SparseMatrixStorage S = make_sparse_matrix_storage(create_eigen_sparsematrix()); EXPECT_NO_THROW(check_matrix(S)); } CASE("Create chain of moves from eigen double") { SparseMatrixStorage S = make_sparse_matrix_storage(create_eigen_sparsematrix()); EXPECT_NO_THROW(check_matrix(S)); } CASE("Create chain of moves from eigen single") { SparseMatrixStorage S = make_sparse_matrix_storage(create_eigen_sparsematrix()); EXPECT_NO_THROW(check_matrix(S)); } #endif CASE("Test exceptions in make_host_view") { SparseMatrixStorage S( create_sparsematrix_storage() ); EXPECT_THROWS(make_host_view(S)); // value data type mismatch EXPECT_THROWS((make_host_view(S))); // index data type mismatch } CASE("Test exceptions in make_device_view") { SparseMatrixStorage S( create_sparsematrix_storage() ); EXPECT_THROWS(make_device_view(S)); // device is not allocated S.updateDevice(); EXPECT_THROWS(make_device_view(S)); // value data type mismatch EXPECT_THROWS((make_device_view(S))); // index data type mismatch } } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/linalg/test_linalg_sparse_gpu.cc0000664000175000017500000002003615160212070023327 0ustar alastairalastair/* * (C) Copyright 2024- 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 "tests/AtlasTestEnvironment.h" #include "tests/linalg/helper_linalg_sparse.h" using namespace atlas::linalg; namespace atlas { namespace test { using SparseMatrix = eckit::linalg::SparseMatrix; //---------------------------------------------------------------------------------------------------------------------- // strings to be used in the tests static std::string hicsparse = sparse::backend::hicsparse::type(); //---------------------------------------------------------------------------------------------------------------------- template atlas::linalg::SparseMatrixStorage createSparseMatrixStorage() { auto S = make_sparse_matrix_storage( eckit::linalg::SparseMatrix{3, 3, {{0, 0, 2.}, {0, 2, -3.}, {1, 1, 2.}, {2, 2, 2.}}} ); S.updateDevice(); return S; } //---------------------------------------------------------------------------------------------------------------------- CASE("sparse-matrix vector multiply (spmv) in double [backend=hicsparse]") { // "square" matrix // A = 2 . -3 // . 2 . // . . 2 // x = 1 2 3 // y = 1 2 3 sparse::current_backend(hicsparse); auto A = createSparseMatrixStorage(); auto A_device_view = make_device_view(A); SECTION("View of atlas::Array [backend=hicsparse]") { ArrayVector x(Vector{1., 2., 3.}); ArrayVector y(3); const auto x_device_view = x.device_view(); auto y_device_view = y.device_view(); sparse_matrix_multiply(A_device_view, x_device_view, y_device_view); expect_equal(y.host_view(), ArrayVector(Vector{-7., 4., 6.}).host_view()); // sparse_matrix_multiply of sparse matrix and vector of non-matching sizes should fail { ArrayVector x2(2); auto x2_device_view = x2.device_view(); EXPECT_THROWS_AS(sparse_matrix_multiply(A_device_view, x2_device_view, y_device_view), eckit::AssertionFailed); } } SECTION("sparse_matrix_multiply_add [backend=hicsparse]") { ArrayVector x(Vector{1., 2., 3.}); ArrayVector y(Vector{4., 5., 6.}); auto x_device_view = x.device_view(); auto y_device_view = y.device_view(); sparse_matrix_multiply_add(A_device_view, x_device_view, y_device_view); expect_equal(y.host_view(), ArrayVector(Vector{-3., 9., 12.}).host_view()); // sparse_matrix_multiply of sparse matrix and vector of non-matching sizes should fail { ArrayVector x2(2); auto x2_device_view = x2.device_view(); EXPECT_THROWS_AS(sparse_matrix_multiply_add(A_device_view, x2_device_view, y_device_view), eckit::AssertionFailed); } } } CASE("sparse-matrix vector multiply (spmv) in float [backend=hicsparse]") { // "square" matrix // A = 2 . -3 // . 2 . // . . 2 // x = 1 2 3 // y = 1 2 3 sparse::current_backend(hicsparse); auto A = createSparseMatrixStorage(); auto A_device_view = make_device_view(A); SECTION("View of atlas::Array [backend=hicsparse]") { ArrayVector x(Vector{1., 2., 3.}); ArrayVector y(3); const auto x_device_view = x.device_view(); auto y_device_view = y.device_view(); sparse_matrix_multiply(A_device_view, x_device_view, y_device_view); expect_equal(y.host_view(), ArrayVector(Vector{-7., 4., 6.}).host_view()); // sparse_matrix_multiply of sparse matrix and vector of non-matching sizes should fail { ArrayVector x2(2); auto x2_device_view = x2.device_view(); EXPECT_THROWS_AS(sparse_matrix_multiply(A_device_view, x2_device_view, y_device_view), eckit::AssertionFailed); } } } CASE("sparse-matrix matrix multiply (spmm) in double [backend=hicsparse]") { // "square" // A = 2 . -3 // . 2 . // . . 2 // x = 1 2 3 // y = 1 2 3 sparse::current_backend(hicsparse); auto A = createSparseMatrixStorage(); auto A_device_view = make_device_view(A); Matrix m{{1., 2.}, {3., 4.}, {5., 6.}}; Matrix c_exp{{-13., -14.}, {6., 8.}, {10., 12.}}; SECTION("View of atlas::Array PointsRight [backend=hicsparse]") { ArrayMatrix ma(m); ArrayMatrix c(3, 2); auto ma_device_view = ma.device_view(); auto c_device_view = c.device_view(); sparse_matrix_multiply(A_device_view, ma_device_view, c_device_view, Indexing::layout_right); expect_equal(c.host_view(), ArrayMatrix(c_exp).host_view()); } SECTION("sparse_matrix_multiply [backend=hicsparse]") { auto backend = sparse::backend::hicsparse(); ArrayMatrix ma(m); ArrayMatrix c(3, 2); auto ma_device_view = ma.device_view(); auto c_device_view = c.device_view(); sparse_matrix_multiply(A_device_view, ma_device_view, c_device_view, backend); expect_equal(c.host_view(), ArrayMatrix(c_exp).host_view()); } SECTION("SparseMatrixMultiply [backend=hicsparse] 1") { auto spmm = SparseMatrixMultiply{sparse::backend::hicsparse()}; ArrayMatrix ma(m); ArrayMatrix c(3, 2); auto ma_device_view = ma.device_view(); auto c_device_view = c.device_view(); spmm(A_device_view, ma_device_view, c_device_view); expect_equal(c.host_view(), ArrayMatrix(c_exp).host_view()); } SECTION("SparseMatrixMultiply [backend=hicsparse] 2") { auto spmm = SparseMatrixMultiply{hicsparse}; ArrayMatrix ma(m); ArrayMatrix c(3, 2); auto ma_device_view = ma.device_view(); auto c_device_view = c.device_view(); spmm(A_device_view, ma_device_view, c_device_view); expect_equal(c.host_view(), ArrayMatrix(c_exp).host_view()); } SECTION("sparse_matrix_multiply_add [backend=hicsparse]") { ArrayMatrix x(m); ArrayMatrix y(m); Matrix y_exp{{-12., -12.}, {9., 12.}, {15., 18.}}; auto x_device_view = x.device_view(); auto y_device_view = y.device_view(); sparse_matrix_multiply_add(A_device_view, x_device_view, y_device_view, sparse::backend::hicsparse()); expect_equal(y.host_view(), ArrayMatrix(y_exp).host_view()); } } CASE("sparse-matrix matrix multiply (spmm) in float [backend=hicsparse]") { // "square" // A = 2 . -3 // . 2 . // . . 2 // x = 1 2 3 // y = 1 2 3 sparse::current_backend(hicsparse); auto A = createSparseMatrixStorage(); auto A_device_view = make_device_view(A); Matrix m{{1., 2.}, {3., 4.}, {5., 6.}}; Matrix c_exp{{-13., -14.}, {6., 8.}, {10., 12.}}; SECTION("View of atlas::Array PointsRight [backend=hicsparse]") { ArrayMatrix ma(m); ArrayMatrix c(3, 2); auto ma_device_view = ma.device_view(); auto c_device_view = c.device_view(); sparse_matrix_multiply(A_device_view, ma_device_view, c_device_view, Indexing::layout_right); expect_equal(c.host_view(), ArrayMatrix(c_exp).host_view()); } } //---------------------------------------------------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); }atlas-0.46.0/src/tests/linalg/CMakeLists.txt0000664000175000017500000000330215160212070021020 0ustar alastairalastair# (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. if( atlas_HAVE_ATLAS_FUNCTIONSPACE ) ecbuild_add_test( TARGET atlas_test_linalg_sparse_gpu SOURCES test_linalg_sparse_gpu.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ATLAS_RUN_NGPUS=1 CONDITION HAVE_CUDA OR HAVE_HIP ) if( TARGET atlas_test_linalg_sparse_gpu ) set_tests_properties( atlas_test_linalg_sparse_gpu PROPERTIES LABELS "gpu") endif() ecbuild_add_test( TARGET atlas_test_linalg_sparse SOURCES test_linalg_sparse.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) ecbuild_add_test( TARGET atlas_test_linalg_sparse_matrix SOURCES test_linalg_sparse_matrix.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) ecbuild_add_test( TARGET atlas_test_linalg_sparse_matrix_gpu SOURCES test_linalg_sparse_matrix_gpu.cc LIBS atlas hicsparse CONDITION HAVE_CUDA OR HAVE_HIP ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ATLAS_RUN_NGPUS=1 ) if( TARGET atlas_test_linalg_sparse_matrix_gpu ) set_tests_properties( atlas_test_linalg_sparse_matrix_gpu PROPERTIES LABELS "gpu") endif() ecbuild_add_test( TARGET atlas_test_linalg_dense SOURCES test_linalg_dense.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) ecbuild_add_test( TARGET atlas_test_linalg_triplet SOURCES test_linalg_triplet.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) endif()atlas-0.46.0/src/tests/AtlasTestEnvironment.h0000664000175000017500000004650115160212070021324 0ustar alastairalastair/* * (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 #include #include #include #include #include "eckit/config/LibEcKit.h" #include "eckit/config/Resource.h" #include "eckit/eckit.h" #include "eckit/log/FileTarget.h" #include "eckit/log/PrefixTarget.h" #include "eckit/mpi/Comm.h" #include "eckit/runtime/Main.h" #include "eckit/system/LibraryManager.h" #include "eckit/testing/Test.h" #include "eckit/types/Types.h" #include "atlas/library/Library.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Log.h" #include "atlas/runtime/Trace.h" #include "atlas/runtime/trace/StopWatch.h" #include "atlas/util/Config.h" #include "atlas/util/Point.h" namespace atlas { namespace test { using eckit::types::is_approximately_equal; class Test; static Test* current_test_{nullptr}; static size_t ATLAS_MAX_FAILED_EXPECTS() { static size_t v = size_t(eckit::Resource("$ATLAS_MAX_FAILED_EXPECTS", 100)); return v; } class Test { struct Failure { std::string message; eckit::CodeLocation location; }; public: Test(const std::string& description, const eckit::CodeLocation& location): description_(description), location_(location) { current_test_ = this; } ~Test() { current_test_ = nullptr; } void expect_failed(const std::string& message, const eckit::CodeLocation& location) { failures_.emplace_back(Failure{message, location}); eckit::Log::error() << message << std::endl; if (failures_.size() == ATLAS_MAX_FAILED_EXPECTS()) { std::stringstream msg; msg << "Maximum number of allowed EXPECTS have failed (${ATLAS_MAX_FAILED_EXPECTS}=" << ATLAS_MAX_FAILED_EXPECTS() << ")."; throw eckit::testing::TestException(msg.str(), location_); } } bool failed() const { return failures_.size() > 0; } void throw_on_failed_expects() { if (failed()) { std::stringstream msg; msg << failures_.size() << " EXPECTS have failed"; throw eckit::testing::TestException(msg.str(), location_); } } const std::string& description() const { return description_; } private: std::vector failures_; std::string description_; eckit::CodeLocation location_; }; Test& current_test() { ATLAS_ASSERT(current_test_); return *current_test_; } //---------------------------------------------------------------------------------------------------------------------- #ifdef MAYBE_UNUSED #elif defined(__GNUC__) #define MAYBE_UNUSED __attribute__((unused)) #else #define MAYBE_UNUSED #endif // Redefine macro's defined in "eckit/testing/Test.h" to include trace // information #undef CASE #define CASE(description) \ void UNIQUE_NAME2(test_, __LINE__)(std::string&, int&, int); \ static const eckit::testing::TestRegister UNIQUE_NAME2(test_registration_, __LINE__)( \ description, &UNIQUE_NAME2(test_, __LINE__)); \ void UNIQUE_NAME2(traced_test_, __LINE__)(std::string & _test_subsection, int& _num_subsections, int _subsection); \ void UNIQUE_NAME2(test_, __LINE__)(std::string & _test_subsection, int& _num_subsections, int _subsection) { \ Test UNIQUE_NAME2(testobj_, __LINE__)(description, Here()); \ ATLAS_TRACE(description); \ UNIQUE_NAME2(traced_test_, __LINE__) \ (_test_subsection, _num_subsections, _subsection); \ current_test().throw_on_failed_expects(); \ if (atlas::test::barrier_timeout(atlas::test::ATLAS_MPI_BARRIER_TIMEOUT())) { \ atlas::Log::warning() << "\nWARNING: Test \"" << description \ << "\" failed with MPI deadlock. (${ATLAS_MPI_BARRIER_TIMEOUT}=" \ << atlas::test::ATLAS_MPI_BARRIER_TIMEOUT() << ").\nCalling MPI_Abort..." \ << std::endl; \ eckit::mpi::comm().abort(); \ } \ } \ void UNIQUE_NAME2(traced_test_, __LINE__)(MAYBE_UNUSED std::string & _test_subsection, \ MAYBE_UNUSED int& _num_subsections, MAYBE_UNUSED int _subsection) #undef SECTION #define SECTION(name) \ _num_subsections += 1; \ _test_subsection = (name); \ if ((_num_subsections - 1) == _subsection) { \ atlas::Log::info() << "Running section \"" << _test_subsection << "\" ..." << std::endl; \ } \ if ((_num_subsections - 1) == _subsection) \ ATLAS_TRACE_SCOPE(_test_subsection) #ifdef EXPECT_EQ #undef EXPECT_EQ #endif #ifdef EXPECT_APPROX_EQ #undef EXPECT_APPROX_EQ #endif #ifdef EXPECT #undef EXPECT #endif #define REQUIRE(expr) \ do { \ if (!(expr)) { \ throw eckit::testing::TestException("EXPECT condition failed: " #expr, Here()); \ } \ } while (false) #define EXPECT(expr) \ do { \ if (!(expr)) { \ current_test().expect_failed("EXPECT condition failed: " #expr, Here()); \ } \ } while (false) template struct Printer { static void print(std::ostream& out, const Value& v) { out << v; } }; template <> struct Printer { static void print(std::ostream& out, const double& v) { out << std::fixed << std::setprecision(12) << v; } }; template <> struct Printer { static void print(std::ostream& out, const PointLonLat& v) { out << std::fixed << std::setprecision(12) << v; } }; template <> struct Printer { static void print(std::ostream& out, const eckit::CodeLocation& location) { out << eckit::PathName{location.file()}.baseName() << " +" << location.line(); } }; template struct PrintValue { const Value& value; PrintValue(const Value& v): value(v) {} void print(std::ostream& out) const { Printer::print(out, value); } friend std::ostream& operator<<(std::ostream& out, const PrintValue& v) { v.print(out); return out; } }; template PrintValue print(const Value& v) { return PrintValue(v); } bool approx_eq(const float& v1, const float& v2) { return is_approximately_equal(v1, v2); } bool approx_eq(const float& v1, const float& v2, const float& t) { return is_approximately_equal(v1, v2, t); } bool approx_eq(const double& v1, const double& v2) { return is_approximately_equal(v1, v2); } bool approx_eq(const double& v1, const double& v2, const double& t) { return is_approximately_equal(v1, v2, t); } bool approx_eq(const Point2& v1, const Point2& v2) { return approx_eq(v1[0], v2[0]) && approx_eq(v1[1], v2[1]); } bool approx_eq(const Point2& v1, const Point2& v2, const double& t) { return approx_eq(v1[0], v2[0], t) && approx_eq(v1[1], v2[1], t); } bool approx_eq(const Point3& v1, const Point3& v2) { return approx_eq(v1[0], v2[0]) && approx_eq(v1[1], v2[1]) && approx_eq(v1[2], v2[2]); } bool approx_eq(const Point3& v1, const Point3& v2, const double& t) { return approx_eq(v1[0], v2[0], t) && approx_eq(v1[1], v2[1], t) && approx_eq(v1[2], v2[2], t); } template bool approx_eq(const std::vector& v1, const std::vector& v2) { if (v1.size() != v2.size()) { return false; } bool _approx_eq = true; for (size_t j=0; j bool approx_eq(const std::vector& v1, const std::vector& v2, const double& t) { if (v1.size() != v2.size()) { return false; } bool _approx_eq = true; for (size_t j=0; j std::string expect_message(const std::string& condition, const T1& lhs, const T2& rhs, const eckit::CodeLocation& loc) { std::stringstream msg; msg << eckit::Colour::red << condition << " FAILED @ " << print(loc) << eckit::Colour::reset << "\n" << eckit::Colour::red << " --> lhs = " << print(lhs) << eckit::Colour::reset << "\n" << eckit::Colour::red << " --> rhs = " << print(rhs) << eckit::Colour::reset; return msg.str(); } template std::string expect_message(const std::string& condition, const T1& lhs, const T2& rhs, const T3& tol, const eckit::CodeLocation& loc) { std::stringstream msg; msg << eckit::Colour::red << condition << " FAILED @ " << print(loc) << eckit::Colour::reset << "\n" << eckit::Colour::red << " --> lhs = " << print(lhs) << eckit::Colour::reset << "\n" << eckit::Colour::red << " --> rhs = " << print(rhs) << eckit::Colour::reset << "\n" << eckit::Colour::red << " --> tol = " << print(tol) << eckit::Colour::reset; return msg.str(); } #define EXPECT_EQ(lhs, rhs) \ do { \ if (!(lhs == rhs)) { \ current_test().expect_failed(expect_message("EXPECT_EQ( " #lhs ", " #rhs " )", lhs, rhs, Here()), Here()); \ } \ } while (false) #define __EXPECT_APPROX_EQ(lhs, rhs) \ do { \ if (!(approx_eq(lhs, rhs))) { \ current_test().expect_failed(expect_message("EXPECT_APPROX_EQ( " #lhs ", " #rhs " )", lhs, rhs, Here()), \ Here()); \ } \ } while (false) #define __EXPECT_APPROX_EQ_TOL(lhs, rhs, tol) \ do { \ if (!(approx_eq(lhs, rhs, tol))) { \ current_test().expect_failed( \ expect_message("EXPECT_APPROX_EQ( " #lhs ", " #rhs ", " #tol " )", lhs, rhs, tol, Here()), Here()); \ } \ } while (false) #define EXPECT_APPROX_EQ(...) __ATLAS_SPLICE(__EXPECT_APPROX_EQ__, __ATLAS_NARG(__VA_ARGS__))(__VA_ARGS__) #define __EXPECT_APPROX_EQ__2 __EXPECT_APPROX_EQ #define __EXPECT_APPROX_EQ__3 __EXPECT_APPROX_EQ_TOL //---------------------------------------------------------------------------------------------------------------------- static double ATLAS_MPI_BARRIER_TIMEOUT() { static double v = eckit::Resource("$ATLAS_MPI_BARRIER_TIMEOUT", 3.); return v; } static int barrier_timeout(double seconds) { auto req = eckit::mpi::comm().iBarrier(); runtime::trace::StopWatch watch; while (not req.test()) { watch.start(); std::this_thread::sleep_for(std::chrono::milliseconds(100)); watch.stop(); if (watch.elapsed() > seconds) { return 1; } } return 0; } namespace { int digits(int number) { int d = 0; while (number) { number /= 10; d++; } return d; } static std::string debug_prefix(const std::string& libname) { std::string s = libname; std::transform(s.begin(), s.end(), s.begin(), ::toupper); s += "_DEBUG"; return s; } void debug_addTarget(eckit::LogTarget* target) { for (std::string libname : eckit::system::LibraryManager::list()) { const eckit::system::Library& lib = eckit::system::LibraryManager::lookup(libname); if (lib.debug()) { lib.debugChannel().addTarget(new eckit::PrefixTarget(debug_prefix(libname), target)); } } if (eckit::Log::debug()) eckit::Log::debug().addTarget(target); } void debug_setTarget(eckit::LogTarget* target) { for (std::string libname : eckit::system::LibraryManager::list()) { const eckit::system::Library& lib = eckit::system::LibraryManager::lookup(libname); if (lib.debug()) { lib.debugChannel().setTarget(new eckit::PrefixTarget(debug_prefix(libname), target)); } } if (eckit::Log::debug()) eckit::Log::debug().setTarget(target); } void debug_reset() { for (std::string libname : eckit::system::LibraryManager::list()) { const eckit::system::Library& lib = eckit::system::LibraryManager::lookup(libname); if (lib.debug()) { lib.debugChannel().reset(); } } if (eckit::Log::debug()) eckit::Log::debug().reset(); } bool getEnv(const std::string& env, bool default_value) { const char* cenv = ::getenv(env.c_str()); if (cenv != nullptr) { return eckit::Translator()(cenv); } return default_value; } int getEnv(const std::string& env, int default_value) { const char* cenv = ::getenv(env.c_str()); if (cenv != nullptr) { return eckit::Translator()(cenv); } return default_value; } void setEnv(const std::string& env, bool value) { constexpr int DO_NOT_REPLACE_IF_EXISTS = 0; ::setenv(env.c_str(), eckit::Translator()(value).c_str(), DO_NOT_REPLACE_IF_EXISTS); } } // namespace struct AtlasTestEnvironment { AtlasTestEnvironment(int argc, char* argv[]) { eckit::Main::initialise(argc, argv); eckit::Main::instance().taskID(eckit::mpi::comm("world").rank()); long log_rank = getEnv("ATLAS_LOG_RANK", 0); bool use_logfile = getEnv("ATLAS_LOG_FILE", false); auto rank_str = []() { int d = digits(eckit::mpi::comm().size()); std::string str = std::to_string(eckit::Main::instance().taskID()); for (int i = str.size(); i < d; ++i) str = "0" + str; return str; }; if (use_logfile) { eckit::LogTarget* logfile = new eckit::FileTarget(eckit::Main::instance().displayName() + ".log.p" + rank_str()); if (eckit::Main::instance().taskID() == log_rank) { eckit::Log::info().addTarget(logfile); eckit::Log::warning().addTarget(logfile); eckit::Log::error().setTarget(logfile); debug_addTarget(logfile); } else { eckit::Log::info().setTarget(logfile); eckit::Log::warning().setTarget(logfile); eckit::Log::error().setTarget(logfile); debug_setTarget(logfile); } } else { if (eckit::Main::instance().taskID() != log_rank) { eckit::Log::info().reset(); eckit::Log::warning().reset(); debug_reset(); } eckit::Log::error().reset(); } Log::error().addTarget(new eckit::PrefixTarget("[" + std::to_string(eckit::mpi::comm().rank()) + "]")); eckit::LibEcKit::instance().setAbortHandler([] { Log::error() << "Calling MPI_Abort" << std::endl; eckit::mpi::comm().abort(); }); setEnv("ATLAS_FPE", true); setEnv("ATLAS_SIGNAL_HANDLER", true); atlas::initialize(); eckit::mpi::comm().barrier(); } ~AtlasTestEnvironment() { atlas::finalize(); atlas::mpi::finalize(); } }; //---------------------------------------------------------------------------------------------------------------------- template int run(int argc, char* argv[]) { Environment env(argc, argv); int errors = eckit::testing::run_tests(argc, argv, false); if (eckit::mpi::comm().size() > 1) { if (barrier_timeout(ATLAS_MPI_BARRIER_TIMEOUT())) { eckit::Log::warning() << "\nWARNING: Tests failed with MPI deadlock " "(${ATLAS_MPI_BARRIER_TIMEOUT}=" << ATLAS_MPI_BARRIER_TIMEOUT() << ").\nCalling MPI_Abort..." << std::endl; eckit::mpi::comm().abort(); } eckit::mpi::comm().allReduceInPlace(errors, eckit::mpi::max()); } return errors; } int run(int argc, char* argv[]) { return run(argc, argv); } //---------------------------------------------------------------------------------------------------------------------- } // namespace test } // namespace atlas atlas-0.46.0/src/tests/grid/0000775000175000017500000000000015160212070015741 5ustar alastairalastairatlas-0.46.0/src/tests/grid/test_stretchedrotatedgaussian.cc0000664000175000017500000024273115160212070024423 0ustar alastairalastair#include #include #include "atlas/grid.h" #include "atlas/option.h" #include "atlas/util/Config.h" #include "tests/AtlasTestEnvironment.h" using namespace atlas::util; using namespace atlas::grid; namespace { const double lonlat_arp_t32c24[] = { 1.99999999999999600, 44.93291171299576092, 2.77237262934735273, 45.01674523281888440, 3.47555862489311007, 45.26073815098433784, 4.04494770792090641, 45.64287023699112211, 4.42528132740345281, 46.12813070368466839, 4.57569351104372046, 46.67109320702403608, 4.47479391972813190, 47.21956618120344018, 4.12512266986032206, 47.71926346185548340, 3.55581027759415491, 48.11926090196744354, 2.82200931024728874, 48.37772379915030996, 1.99999999999999556, 48.46708828700425187, 1.17799068975270305, 48.37772379915030996, 0.44418972240583693, 48.11926090196744354, -0.12512266986032983, 47.71926346185548340, -0.47479391972814028, 47.21956618120344018, -0.57569351104372879, 46.67109320702403608, -0.42528132740346147, 46.12813070368466839, -0.04494770792091433, 45.64287023699112211, 0.52444137510688182, 45.26073815098433784, 1.22762737065263883, 45.01674523281888440, 1.99999999999999556, 42.63725270983827897, 3.27473332072834422, 42.73934376403467184, 4.49349321557291415, 43.04109625964867547, 5.60127364516099924, 43.52904513460186564, 6.54514794659825672, 44.18107605134951399, 7.27568782798168190, 44.96693492678289061, 7.74887351496657040, 45.84898874281991965, 7.92867515230084408, 46.78330526703850012, 7.79040894364469949, 47.72115423047301164, 7.32476598915540844, 48.61105462603097038, 6.54204071489340944, 49.40147266886311428, 5.47559260124316793, 50.04417716102324221, 4.18315343237871584, 50.49806364409691639, 2.74458547194322389, 50.73299668020899134, 1.25541452805676457, 50.73299668020899134, -0.18315343237872406, 50.49806364409691639, -1.47559260124317815, 50.04417716102324221, -2.54204071489341921, 49.40147266886311428, -3.32476598915541599, 48.61105462603097038, -3.79040894364470748, 47.72115423047301164, -3.92867515230085296, 46.78330526703850012, -3.74887351496657928, 45.84898874281991965, -3.27568782798168812, 44.96693492678289061, -2.54514794659826427, 44.18107605134951399, -1.60127364516100812, 43.52904513460186564, -0.49349321557292075, 43.04109625964867547, 0.72526667927164967, 42.73934376403467184, 1.99999999999999556, 40.31236211945285675, 3.63376292032076975, 40.42261301497919845, 5.22173788476928014, 40.75019560382368411, 6.71826031811739099, 41.28563184510834105, 8.07794770476515467, 42.01322983391511201, 9.25594876339864392, 42.91123363953519743, 10.20837670733705771, 43.95202523375940729, 10.89307303713296271, 45.10238649522262477, 11.27090590027328432, 46.32385288536406875, 11.30784824781773779, 47.57322688923063225, 10.97806564551987840, 48.80336562297891589, 10.26810804950859612, 49.96440199105025926, 9.18197003975669723, 51.00557820511744467, 7.74622775989939338, 51.87782553848720823, 6.01378910799337074, 52.53707313311167582, 4.06435502534209725, 52.94800110445672203, 1.99999999999999600, 53.08763788054714894, -0.06435502534210560, 52.94800110445672203, -2.01378910799337962, 52.53707313311167582, -3.74622775989940182, 51.87782553848720823, -5.18197003975670256, 51.00557820511744467, -6.26810804950860057, 49.96440199105025926, -6.97806564551988551, 48.80336562297891589, -7.30784824781774667, 47.57322688923063225, -7.27090590027329498, 46.32385288536406875, -6.89307303713297248, 45.10238649522262477, -6.20837670733706481, 43.95202523375940729, -5.25594876339865458, 42.91123363953519743, -4.07794770476516355, 42.01322983391511201, -2.71826031811739988, 41.28563184510834105, -1.22173788476928813, 40.75019560382368411, 0.36623707967922192, 40.42261301497919845, 1.99999999999999600, 37.95945956416753120, 3.72994195085756930, 38.05275970261258323, 5.43190111561111433, 38.33106975566338548, 7.07783882564464850, 38.78962916423042628, 8.63959771096107509, 39.42053684981195261, 10.08882962579568954, 40.21279467492543347, 11.39692139773956114, 41.15235690381517486, 12.53494002107632888, 42.22217718966415845, 13.47363915243209043, 43.40224634648851065, 14.18359521918176469, 44.66961909752426152, 14.63557388477568644, 45.99843704779454612, 14.80126342211737089, 47.35996933207513848, 14.65454278958190670, 48.72271255422421632, 14.17346129570704250, 50.05261748260089405, 13.34306219743110233, 51.31353838046217675, 12.15903975007321236, 52.46802323745790630, 10.63193584333677499, 53.47856301011842106, 8.79116029542177024, 54.30937174533021761, 6.68766853851381526, 54.92865613654313961, 4.39391037845720334, 55.31115535850048559, 1.99999999999999600, 55.44054043583248159, -0.39391037845721066, 55.31115535850048559, -2.68766853851382415, 54.92865613654313961, -4.79116029542177646, 54.30937174533021761, -6.63193584333678299, 53.47856301011842106, -8.15903975007321947, 52.46802323745790630, -9.34306219743110766, 51.31353838046217675, -10.17346129570704960, 50.05261748260089405, -10.65454278958191559, 48.72271255422421632, -10.80126342211738155, 47.35996933207513848, -10.63557388477569354, 45.99843704779454612, -10.18359521918177357, 44.66961909752426152, -9.47363915243210286, 43.40224634648851065, -8.53494002107633776, 42.22217718966415845, -7.39692139773957003, 41.15235690381517486, -6.08882962579569842, 40.21279467492543347, -4.63959771096108220, 39.42053684981195261, -3.07783882564465516, 38.78962916423042628, -1.43190111561112299, 38.33106975566338548, 0.27005804914242265, 38.05275970261258323, 1.99999999999999600, 35.57084905987729684, 3.89475329148972449, 35.66164776728690811, 5.76748883913266752, 35.93289207465146262, 7.59612881396671380, 36.38113516165864070, 9.35846198524136241, 37.00065953311393940, 11.03204592267791284, 37.78351258180431671, 12.59407748901272228, 38.71954642363609622, 14.02123063562953753, 39.79645326718445375, 15.28946940417034384, 40.99978674800681233, 16.37385639149118433, 42.31296002482778107, 17.24839405994832120, 43.71721334442865725, 17.88595991605807711, 45.19154778206701195, 18.25842850603561374, 46.71262882888417067, 18.33711398818940452, 48.25467471095996785, 18.09371358173445188, 49.78936139305309894, 17.50197280996727400, 51.28580055994029152, 16.54030106040206149, 52.71067834344860614, 15.19549149366584473, 54.02867680201524081, 13.46747450557555048, 55.20332460427978560, 11.37460140494845717, 56.19841400024800038, 8.95834334902436602, 56.98004544675784899, 6.28571777452580616, 57.51919556269155720, 3.44765611568870733, 57.79446760315521914, 0.55234388431128512, 57.79446760315521914, -2.28571777452581371, 57.51919556269155720, -4.95834334902437313, 56.98004544675784899, -7.37460140494846428, 56.19841400024800038, -9.46747450557555759, 55.20332460427978560, -11.19549149366585006, 54.02867680201524081, -12.54030106040206860, 52.71067834344860614, -13.50197280996728288, 51.28580055994029152, -14.09371358173445721, 49.78936139305309894, -14.33711398818941340, 48.25467471095996785, -14.25842850603561907, 46.71262882888417067, -13.88595991605808244, 45.19154778206701195, -13.24839405994833008, 43.71721334442865725, -12.37385639149119498, 42.31296002482778107, -11.28946940417035272, 40.99978674800681233, -10.02123063562954464, 39.79645326718445375, -8.59407748901273116, 38.71954642363609622, -7.03204592267792172, 37.78351258180431671, -5.35846198524136952, 37.00065953311393940, -3.59612881396672091, 36.38113516165864070, -1.76748883913267574, 35.93289207465146262, 0.10524670851026705, 35.66164776728690811, 1.99999999999999600, 33.13704588354342917, 4.09718755711548610, 33.23124463343540214, 6.17473678976558027, 33.51283479810019372, 8.21297051898842767, 33.97880825794451454, 10.19211732580419394, 34.62418375193038855, 12.09222434325374529, 35.44205010622848562, 13.89302536639832120, 36.42361680044041350, 15.57375501535205942, 37.55826279697889447, 17.11290455740948246, 38.83357308646102268, 18.48792157318536056, 40.23535159216240942, 19.67486490585671888, 41.74759899443786537, 20.64804001511417653, 43.35244484209054860, 21.37966075266355404, 45.03002540480565585, 21.83961562141898582, 46.75830290430587155, 21.99546453290828296, 48.51282952087984768, 21.81285996059914467, 50.26647329190694080, 21.25667322722224029, 51.98914605534769606, 20.29319722451073105, 53.64760968250063655, 18.89384545876525578, 55.20548791890712437, 17.04067793585433321, 56.62367212326139310, 14.73370655373201643, 57.86135900031617751, 11.99912115224313958, 58.87795079119066344, 8.89637618755807047, 59.63591573610307250, 5.52099396835736211, 60.10440004572244277, 1.99999999999999645, 60.26295411645657651, -1.52099396835736900, 60.10440004572244277, -4.89637618755807846, 59.63591573610307250, -7.99912115224314757, 58.87795079119066344, -10.73370655373202531, 57.86135900031617751, -13.04067793585434032, 56.62367212326139310, -14.89384545876526467, 55.20548791890712437, -16.29319722451073460, 53.64760968250063655, -17.25667322722224739, 51.98914605534769606, -17.81285996059915178, 50.26647329190694080, -17.99546453290829362, 48.51282952087984768, -17.83961562141899648, 46.75830290430587155, -17.37966075266356114, 45.03002540480565585, -16.64804001511417653, 43.35244484209054860, -15.67486490585672598, 41.74759899443786537, -14.48792157318536944, 40.23535159216240942, -13.11290455740949135, 38.83357308646102268, -11.57375501535207007, 37.55826279697889447, -9.89302536639833185, 36.42361680044041350, -8.09222434325375239, 35.44205010622848562, -6.19211732580420193, 34.62418375193038855, -4.21297051898843566, 33.97880825794451454, -2.17473678976558960, 33.51283479810019372, -0.09718755711549353, 33.23124463343540214, 1.99999999999999600, 30.64757019281734074, 3.92669240244598505, 30.71678191817960268, 5.84270801296843700, 30.92395598105918353, 7.73736939611796171, 31.26771342119434749, 9.59999239238874047, 31.74577019145994683, 11.41986936545086984, 32.35495718584155611, 13.18623692372253586, 33.09124574576952682, 14.88822378743806851, 33.94977626735131793, 16.51477509868480453, 34.92488704934376642, 18.05455017155122732, 36.01014014726423795, 19.49579145727240714, 37.19834072287546434, 20.82616341777591984, 38.48154618161253637, 22.03256121225549435, 39.85106125278488065, 23.10089088783405131, 41.29741507659190347, 24.01582559662802652, 42.81031632956308641, 24.76054697060166987, 44.37858250245571412, 25.31648826424518717, 45.99003977573201496, 25.66310777053267245, 47.63139077670033572, 25.77773936992416637, 49.28804930270956675, 25.63559425749198795, 50.94394459368972150, 25.21002592824931199, 52.58130404832083116, 24.47321926655621738, 54.18043392376822709, 23.39751805719552991, 55.71953428872550518, 21.95764487214750105, 57.17460858105235388, 20.13405311103923623, 58.51955862209560166, 17.91751559979281083, 59.72658657388572578, 15.31471471060360479, 60.76704034614789407, 12.35401191716921154, 61.61281120273996237, 9.08984714396560278, 62.23829052070698253, 5.60371952785031446, 62.62270582496653049, 1.99999999999999556, 62.75242980718265784, -1.60371952785032224, 62.62270582496653049, -5.08984714396560900, 62.23829052070698253, -8.35401191716922042, 61.61281120273996237, -11.31471471060361367, 60.76704034614789407, -13.91751559979281616, 59.72658657388572578, -16.13405311103924689, 58.51955862209560166, -17.95764487214750815, 57.17460858105235388, -19.39751805719554056, 55.71953428872550518, -20.47321926655623159, 54.18043392376822709, -21.21002592824931554, 52.58130404832083116, -21.63559425749199860, 50.94394459368972150, -21.77773936992418058, 49.28804930270956675, -21.66310777053267955, 47.63139077670033572, -21.31648826424519783, 45.99003977573201496, -20.76054697060167697, 44.37858250245571412, -20.01582559662803362, 42.81031632956308641, -19.10089088783406197, 41.29741507659190347, -18.03256121225550146, 39.85106125278488065, -16.82616341777592694, 38.48154618161253637, -15.49579145727241603, 37.19834072287546434, -14.05455017155123620, 36.01014014726423795, -12.51477509868481341, 34.92488704934376642, -10.88822378743808095, 33.94977626735131793, -9.18623692372254474, 33.09124574576952682, -7.41986936545088049, 32.35495718584155611, -5.59999239238874935, 31.74577019145994683, -3.73736939611796970, 31.26771342119434749, -1.84270801296844478, 30.92395598105918353, 0.07330759755400772, 30.71678191817960268, 1.99999999999999600, 28.09100109536852230, 4.16843782661930362, 28.16889262543881145, 6.32564933647368566, 28.40205318377053345, 8.46042949045145143, 28.78894734383437992, 10.56160821625925195, 29.32703776203888069, 12.61804920168178690, 30.01281590885307438, 14.61862699669389087, 30.84184157026976436, 16.55217628249286577, 31.80878785141789322, 18.40740784814350306, 32.90748779465315010, 20.17278641740253420, 34.13097826525053335, 21.83636590786353437, 35.47153642700349963, 23.38557795842459797, 36.92070387379718710, 24.80696969977402233, 38.46929323359992026, 26.08588698105331716, 40.10737174655498194, 27.20610004359020806, 41.82421587722975431, 28.14937074714974585, 43.60823042135866956, 28.89496528133906494, 45.44682484300304282, 29.41912613625676443, 47.32623888788154431, 29.69453569648833735, 49.23130925676766623, 29.68983696232584180, 51.14517010976478417, 29.36933297612858240, 53.04888396821036167, 28.69307609487012201, 54.92100894435518654, 27.61769060836896728, 56.73712764036463341, 26.09844461997003151, 58.46939890910893922, 24.09325961331439458, 60.08625295311978931, 21.56939664790981936, 61.55243525845713748, 18.51323316925390117, 62.82970044453450953, 14.94248311853021427, 63.87850997409253040, 10.91821393310038246, 64.66099291349783584, 6.55165837938126572, 65.14506774788591770, 1.99999999999999645, 65.30899890463147983, -2.55165837938127327, 65.14506774788591770, -6.91821393310038868, 64.66099291349783584, -10.94248311853022138, 63.87850997409253040, -14.51323316925390650, 62.82970044453450953, -17.56939664790982647, 61.55243525845713748, -20.09325961331440169, 60.08625295311978931, -22.09844461997003862, 58.46939890910893922, -23.61769060836897793, 56.73712764036463341, -24.69307609487013266, 54.92100894435518654, -25.36933297612859306, 53.04888396821036167, -25.68983696232585245, 51.14517010976478417, -25.69453569648835156, 49.23130925676766623, -25.41912613625677508, 47.32623888788154431, -24.89496528133907205, 45.44682484300304282, -24.14937074714975651, 43.60823042135866956, -23.20610004359021872, 41.82421587722975431, -22.08588698105333137, 40.10737174655498194, -20.80696969977403299, 38.46929323359992026, -19.38557795842460862, 36.92070387379718710, -17.83636590786354148, 35.47153642700349963, -16.17278641740254486, 34.13097826525053335, -14.40740784814351372, 32.90748779465315010, -12.55217628249287110, 31.80878785141789322, -10.61862699669389798, 30.84184157026976436, -8.61804920168179400, 30.01281590885307438, -6.56160821625925994, 29.32703776203888069, -4.46042949045146031, 28.78894734383437992, -2.32564933647369187, 28.40205318377053345, -0.16843782661931131, 28.16889262543881145, 1.99999999999999600, 25.45485763546421154, 4.25579267831604824, 25.53081635683327377, 6.50189378344491864, 25.75824904385734015, 8.72865139696445169, 26.13583181560235502, 10.92648650789847409, 26.66137960320710931, 13.08591371200912867, 27.33187675649334025, 15.19754366142444368, 28.14351701208744316, 17.25206214192900589, 29.09175016726920759, 19.24018123567453742, 30.17133232687284305, 21.15255849246134545, 31.37637626233231813, 22.97968026705780886, 32.70039821822412307, 24.71170528699354918, 34.13635737784334623, 26.33826403031282837, 35.67668409468497259, 27.84820859564654683, 37.31329283883029291, 29.22930648122764197, 39.03757551632935474, 30.46787020033522708, 40.84037031204417190, 31.54831327014242248, 42.71190039889389567, 32.45262246278107199, 44.64167567492752653, 33.15973753507462618, 46.61834908938177335, 33.64483525740958214, 48.62951713112133945, 33.87852862440772839, 50.66145188613276673, 33.82602201650912122, 52.69875031809839783, 33.44632120220701665, 54.72388646563504011, 32.69170289014788011, 56.71665696607986717, 31.50782877801381332, 58.65352526302899605, 29.83517004248913196, 60.50690475312735828, 27.61278608277912383, 62.24449065808524040, 24.78586461017365750, 63.82887077001763032, 21.31841683118042496, 65.21781824976642383, 17.21138564172178320, 66.36584339331807314, 12.52326664933801226, 67.22759281764989225, 7.38533547682125846, 67.76325974975715383, 1.99999999999999600, 67.94514236453578349, -3.38533547682126690, 67.76325974975715383, -8.52326664933801936, 67.22759281764989225, -13.21138564172179031, 66.36584339331807314, -17.31841683118043562, 65.21781824976642383, -20.78586461017366460, 63.82887077001763032, -23.61278608277912738, 62.24449065808524040, -25.83517004248913906, 60.50690475312735828, -27.50782877801382398, 58.65352526302899605, -28.69170289014789077, 56.71665696607986717, -29.44632120220701665, 54.72388646563504011, -29.82602201650912477, 52.69875031809839783, -29.87852862440772839, 50.66145188613276673, -29.64483525740958925, 48.62951713112133945, -29.15973753507462618, 46.61834908938177335, -28.45262246278107554, 44.64167567492752653, -27.54831327014242959, 42.71190039889389567, -26.46787020033523774, 40.84037031204417190, -25.22930648122764907, 39.03757551632935474, -23.84820859564655038, 37.31329283883029291, -22.33826403031283903, 35.67668409468497259, -20.71170528699355629, 34.13635737784334623, -18.97968026705781242, 32.70039821822412307, -17.15255849246134900, 31.37637626233231813, -15.24018123567454097, 30.17133232687284305, -13.25206214192901299, 29.09175016726920759, -11.19754366142445434, 28.14351701208744316, -9.08591371200913933, 27.33187675649334025, -6.92648650789848030, 26.66137960320710931, -4.72865139696445969, 26.13583181560235502, -2.50189378344492708, 25.75824904385734015, -0.25579267831605540, 25.53081635683327377, 1.99999999999999556, 22.72540973598210456, 4.47629214130109165, 22.80878975038074330, 6.94242093509331060, 23.05843251151436846, 9.38829543151647350, 23.47285525677742868, 11.80396120930039139, 24.04961640006232315, 14.17964838758363122, 24.78535803035031293, 16.50579641562137567, 25.67586152120415477, 18.77304947679039060, 26.71611272875198395, 20.97221728412494102, 27.90037270541385439, 23.09419678103115103, 29.22224954465700009, 25.12985059268006793, 30.67476685924402702, 27.06983782140639860, 32.25042441190934994, 28.90439178940929921, 33.94124647006630369, 30.62303747247054275, 35.73881343521246379, 32.21423852322426029, 37.63427208010365632, 33.66495985223924237, 39.61831917500946787, 34.96012666362174315, 41.68115224019865650, 36.08195470333167520, 43.81237943576640248, 37.00911969333093765, 46.00087796034757304, 37.71572773969587189, 48.23458650173874673, 38.17004600591188535, 50.50021197832979425, 38.33296113360039925, 52.78282385843581892, 38.15616674923441565, 55.06530104282313687, 37.58017141013022666, 57.32758829402004608, 36.53242307522426557, 59.54571651024583190, 34.92626834358740950, 61.69055635034530383, 32.66225471925319113, 63.72633555655123416, 29.63456841105837825, 65.60910965714572285, 25.74696599568290623, 67.28571148577094618, 20.94303945184942251, 68.69426490502790728, 15.25145387811900122, 69.76794994271870110, 8.83336207267744022, 70.44359392189112157, 1.99999999999999600, 70.67459026401789401, -4.83336207267745088, 70.44359392189112157, -11.25145387811901188, 69.76794994271870110, -16.94303945184943316, 68.69426490502790728, -21.74696599568291333, 67.28571148577094618, -25.63456841105838890, 65.60910965714572285, -28.66225471925320534, 63.72633555655123416, -30.92626834358741306, 61.69055635034530383, -32.53242307522427978, 59.54571651024583190, -33.58017141013024087, 57.32758829402004608, -34.15616674923442986, 55.06530104282313687, -34.33296113360040636, 52.78282385843581892, -34.17004600591189956, 50.50021197832979425, -33.71572773969587900, 48.23458650173874673, -33.00911969333094476, 46.00087796034757304, -32.08195470333168231, 43.81237943576640248, -30.96012666362175025, 41.68115224019865650, -29.66495985223924237, 39.61831917500946787, -28.21423852322426740, 37.63427208010365632, -26.62303747247054631, 35.73881343521246379, -24.90439178940930276, 33.94124647006630369, -23.06983782140640216, 32.25042441190934994, -21.12985059268007149, 30.67476685924402702, -19.09419678103115459, 29.22224954465700009, -16.97221728412494102, 27.90037270541385439, -14.77304947679039948, 26.71611272875198395, -12.50579641562138455, 25.67586152120415477, -10.17964838758364365, 24.78535803035031293, -7.80396120930039938, 24.04961640006232315, -5.38829543151647972, 23.47285525677742868, -2.94242093509332037, 23.05843251151436846, -0.47629214130110020, 22.80878975038074330, 1.99999999999999600, 19.88744176867359670, 4.69641128035122435, 19.97822987242788884, 7.38211423303385939, 20.25003331964319031, 10.04651538477556549, 20.70118176192879389, 12.67924035399200378, 21.32893128539882710, 15.27021753707410312, 22.12952164603582617, 17.80973254683686946, 23.09825113914619976, 20.28844626261447459, 24.22956442487017270, 22.69737092792362532, 25.51714801636046204, 25.02780000980339636, 26.95402789495272344, 27.27118821325056430, 28.53266378578365448, 29.41897786066868292, 30.24503490367624536, 31.46236656888654792, 32.08271233019573287, 33.39200856069422940, 34.03691345955112979, 35.19763777135730720, 36.09853399317180589, 36.86759479425492714, 38.25815259440047811, 38.38823113470312620, 40.50600232791481403, 39.74315247327933776, 42.83190113242096686, 40.91224672040999621, 45.22513042719018728, 41.87042151784342536, 47.67424596736638165, 42.58594887503602422, 50.16679739569313057, 43.01828312042781022, 52.68892136253921876, 43.11518954384546731, 55.22475594928482678, 42.80901819915838757, 57.75559962131574565, 42.01204327449660525, 60.25870541573158334, 40.61111913565843423, 62.70556553816149403, 38.46283411458643542, 65.05952649321666570, 35.39257932570031784, 67.27265449907143591, 31.20553396293055215, 69.28213523998356038, 25.72462936947633239, 71.00751554077180572, 18.87413258170036201, 72.35214877025040892, 10.80643587234801295, 73.21444539538327945, 1.99999999999999600, 73.51255823132640899, -6.80643587234802006, 73.21444539538327945, -14.87413258170037622, 72.35214877025040892, -21.72462936947634304, 71.00751554077180572, -27.20553396293055570, 69.28213523998356038, -31.39257932570032139, 67.27265449907143591, -34.46283411458643542, 65.05952649321666570, -36.61111913565844134, 62.70556553816149403, -38.01204327449661946, 60.25870541573158334, -38.80901819915838047, 57.75559962131574565, -39.11518954384547442, 55.22475594928482678, -39.01828312042781022, 52.68892136253921876, -38.58594887503603843, 50.16679739569313057, -37.87042151784342536, 47.67424596736638165, -36.91224672041000332, 45.22513042719018728, -35.74315247327935197, 42.83190113242096686, -34.38823113470313331, 40.50600232791481403, -32.86759479425493424, 38.25815259440047811, -31.19763777135731431, 36.09853399317180589, -29.39200856069424717, 34.03691345955112979, -27.46236656888655858, 32.08271233019573287, -25.41897786066869003, 30.24503490367624536, -23.27118821325056786, 28.53266378578365448, -21.02780000980340702, 26.95402789495272344, -18.69737092792362887, 25.51714801636046204, -16.28844626261448170, 24.22956442487017270, -13.80973254683687301, 23.09825113914619976, -11.27021753707411023, 22.12952164603582617, -8.67924035399200910, 21.32893128539882710, -6.04651538477557171, 20.70118176192879389, -3.38211423303386916, 20.25003331964319031, -0.69641128035123179, 19.97822987242788884, 1.99999999999999556, 16.92397162343523931, 4.91799021933642688, 17.02221659443387480, 7.82460955068693664, 17.31631470128239059, 10.70865569654628935, 17.80437171431731258, 13.55924980839402139, 18.48328342761256238, 16.36596498789072385, 19.34881113120014362, 19.11891780975726363, 20.39568010883170501, 21.80881477377849720, 21.61769490666709714, 24.42694818201795570, 23.00786444863284430, 26.96513811238917668, 24.55852997196867804, 29.41561850057672345, 26.26148912292403637, 31.77086550998261671, 28.10811023218007065, 34.02336509370287132, 30.08943159705432180, 36.16531367916817175, 32.19624133969532664, 38.18824090805679106, 34.41913389699745807, 40.08253581911941410, 36.74853923728758076, 41.83684688715484867, 39.17472027603687934, 43.43731045663169255, 41.68773239540036002, 44.86653894639825779, 44.27733603455973110, 46.10226597817306526, 46.93284831506371546, 47.11549450115907689, 49.64291142290011294, 47.86791757533286074, 52.39514197266055362, 48.30826862884845241, 55.17560336970880286, 48.36709840828364548, 57.96800639328736793, 47.94927569193657035, 60.75248226909388904, 46.92334661203305046, 63.50367340927639503, 45.10709162054252630, 66.18773615332521842, 42.25028545833759352, 68.75766348420629015, 38.02194152863378918, 71.14627956201685777, 32.02735732207737840, 73.25702648451857613, 23.91726594386999949, 74.95625177955801632, 13.67376613190776347, 76.07990221077976400, 1.99999999999999600, 76.47602837656475572, -9.67376613190777235, 76.07990221077976400, -19.91726594387001015, 74.95625177955801632, -28.02735732207739261, 73.25702648451857613, -34.02194152863380339, 71.14627956201685777, -38.25028545833759352, 68.75766348420629015, -41.10709162054251919, 66.18773615332521842, -42.92334661203306467, 63.50367340927639503, -43.94927569193657746, 60.75248226909388904, -44.36709840828364548, 57.96800639328736793, -44.30826862884845241, 55.17560336970880286, -43.86791757533286784, 52.39514197266055362, -43.11549450115907689, 49.64291142290011294, -42.10226597817306526, 46.93284831506371546, -40.86653894639826490, 44.27733603455973110, -39.43731045663169965, 41.68773239540036002, -37.83684688715485578, 39.17472027603687934, -36.08253581911941410, 36.74853923728758076, -34.18824090805679816, 34.41913389699745807, -32.16531367916818596, 32.19624133969532664, -30.02336509370288908, 30.08943159705432180, -27.77086550998262737, 28.10811023218007065, -25.41561850057673766, 26.26148912292403637, -22.96513811238918734, 24.55852997196867804, -20.42694818201796636, 23.00786444863284430, -17.80881477377850075, 21.61769490666709714, -15.11891780975727251, 20.39568010883170501, -12.36596498789073095, 19.34881113120014362, -9.55924980839402849, 18.48328342761256238, -6.70865569654630001, 17.80437171431731258, -3.82460955068694464, 17.31631470128239059, -0.91799021933643477, 17.02221659443387480, 1.99999999999999556, 13.81592192037676625, 5.14299451095195792, 13.92173872937387102, 8.27378779825514954, 14.23846093632396581, 11.38041466390652978, 14.76392503362292530, 14.45136393475438119, 15.49459412897926924, 17.47576217013061139, 16.42565612762181004, 20.44350999615491205, 17.55115134744557182, 23.34536199373425092, 18.86412113035556004, 26.17294518233925160, 20.35676830949395466, 28.91871466466540141, 22.02062053097340311, 31.57584735879237314, 23.84668824688734645, 34.13807554901531205, 25.82561045671564415, 36.59946098845733786, 27.94778271439298933, 38.95410730493897233, 30.20346328869342756, 41.19580324741197330, 32.58285444844916867, 43.31758136998609388, 35.07615645611910082, 45.31116510791174079, 37.67359181604234664, 47.16626004327477517, 40.36539642075791789, 48.86961926381131605, 43.14177213432515856, 50.40377244059970963, 45.99279145534946167, 51.74524369263552614, 48.90823812634897649, 52.86197695521289575, 51.87735585154322138, 53.70950811496559396, 54.88845672164920586, 54.22511448054331140, 57.92830381592441569, 54.31863539857850753, 60.98111330635318694, 53.85773173662808233, 64.02688869849654907, 52.64383991815974895, 67.03853932314146391, 50.37308749001456931, 69.97672254379963874, 46.57629367046713753, 72.78039612779683409, 40.54693285975561423, 75.34972495190746145, 31.34759503822197502, 77.51867325981696411, 18.24038894313819625, 79.03039199474196153, 1.99999999999999489, 79.58407807962322522, -14.24038894313820691, 79.03039199474196153, -27.34759503822198567, 77.51867325981696411, -36.54693285975562134, 75.34972495190746145, -42.57629367046714464, 72.78039612779683409, -46.37308749001456931, 69.97672254379963874, -48.64383991815974895, 67.03853932314146391, -49.85773173662808233, 64.02688869849654907, -50.31863539857852174, 60.98111330635318694, -50.22511448054332561, 57.92830381592441569, -49.70950811496560817, 54.88845672164920586, -48.86197695521290996, 51.87735585154322138, -47.74524369263553325, 48.90823812634897649, -46.40377244059971673, 45.99279145534946167, -44.86961926381133026, 43.14177213432515856, -43.16626004327478938, 40.36539642075791789, -41.31116510791174079, 37.67359181604234664, -39.31758136998610098, 35.07615645611910082, -37.19580324741197330, 32.58285444844916867, -34.95410730493897233, 30.20346328869342756, -32.59946098845735207, 27.94778271439298933, -30.13807554901532626, 25.82561045671564415, -27.57584735879238025, 23.84668824688734645, -24.91871466466541207, 22.02062053097340311, -22.17294518233926226, 20.35676830949395466, -19.34536199373426157, 18.86412113035556004, -16.44350999615492626, 17.55115134744557182, -13.47576217013062028, 16.42565612762181004, -10.45136393475438830, 15.49459412897926924, -7.38041466390653600, 14.76392503362292530, -4.27378779825515931, 14.23846093632396581, -1.14299451095196680, 13.92173872937387102, 1.99999999999999600, 10.54173785529561158, 5.37360609917943943, 10.65531494200142149, 8.73395228112667965, 10.99520717814031556, 12.06809948196381832, 11.55892497220584403, 15.36372621835939078, 12.34240842556757656, 18.60937983520193129, 13.34015413091371016, 21.79466590782825364, 14.54537904344351951, 24.91036566952359266, 15.95020993686824440, 27.94847769357361855, 17.54588626406437513, 30.90218550361061745, 19.32296477964253967, 33.76575655354334060, 21.27151577227775903, 36.53437968696508875, 23.38130282889363798, 39.20394762734519389, 25.64194033091114022, 41.77078829775712165, 28.04302503644353095, 44.23134383513491485, 30.57423988556289629, 46.58178882927892772, 33.22542941501406233, 48.81756887288366187, 35.98664677769215103, 50.93282539675248444, 38.84817223653458029, 52.91964996438991164, 41.80050199367043007, 54.76707508825830928, 44.83430401955754974, 56.45964870701569538, 47.94033352757975308, 57.97533567047437231, 51.10929358481432416, 59.28230228211859298, 54.33161333463023368, 60.33378767939932885, 57.59709162031844443, 61.05957379451161415, 60.89430451319173443, 61.35114187047394552, 64.20957103321013903, 61.03453256694833584, 67.52503657439316953, 59.81801935702030448, 70.81486693953938527, 57.18593275925195485, 74.03707888623914357, 52.17735288037920327, 77.11452529323830163, 42.96575654499160635, 79.88802174923021937, 26.58690897303194944, 82.01033616765489853, 1.99999999999999756, 82.85826214470436923, -22.58690897303195300, 82.01033616765489853, -38.96575654499161345, 79.88802174923021937, -48.17735288037921748, 77.11452529323830163, -53.18593275925197617, 74.03707888623914357, -55.81801935702031159, 70.81486693953938527, -57.03453256694834295, 67.52503657439316953, -57.35114187047395262, 64.20957103321013903, -57.05957379451162836, 60.89430451319173443, -56.33378767939933596, 57.59709162031844443, -55.28230228211860720, 54.33161333463023368, -53.97533567047437231, 51.10929358481432416, -52.45964870701570248, 47.94033352757975308, -50.76707508825830928, 44.83430401955754974, -48.91964996438991875, 41.80050199367043007, -46.93282539675249154, 38.84817223653458029, -44.81756887288366897, 35.98664677769215103, -42.58178882927894193, 33.22542941501406233, -40.23134383513492196, 30.57423988556289629, -37.77078829775713587, 28.04302503644353095, -35.20394762734520100, 25.64194033091114022, -32.53437968696510296, 23.38130282889363798, -29.76575655354335481, 21.27151577227775903, -26.90218550361062810, 19.32296477964253967, -23.94847769357362921, 17.54588626406437513, -20.91036566952360332, 15.95020993686824440, -17.79466590782826074, 14.54537904344351951, -14.60937983520194017, 13.34015413091371016, -11.36372621835939611, 12.34240842556757656, -8.06809948196382898, 11.55892497220584403, -4.73395228112668676, 10.99520717814031556, -1.37360609917944720, 10.65531494200142149, 1.99999999999999600, 7.07694595379052860, 5.61234064761578821, 7.19855645524002874, 9.21005712455210990, 7.56241362477411005, 12.77895396829166508, 8.16563014493928208, 16.30566013367806733, 9.00350968307661859, 19.77796305903054730, 10.06971032975657643, 23.18506021909562875, 11.35645428598743756, 26.51771689758311723, 12.85476800444172873, 29.76832854644279536, 14.55473640862136264, 32.93089420696504277, 16.44575600838861362, 36.00091302933286386, 18.51677422677156315, 38.97521854780546846, 20.75650546647104022, 41.85176517580521960, 23.15361781597290403, 44.62937881000767248, 25.69688738457191945, 47.30747895739604303, 28.37531978094453677, 49.88577372237665486, 31.17824007995576707, 52.36392121341754091, 34.09535373993877982, 54.74114071516557090, 37.11678138456306897, 57.01574257648425004, 40.23307020728445593, 59.18452370168172649, 43.43518400478754415, 61.24193904387428233, 46.71447239476329827, 63.17889520468774833, 50.06261728049957327, 64.98089250042356468, 53.47155025248752480, 66.62500642980749888, 56.93332642202356197, 68.07470870335012592, 60.43992354246547904, 69.27043361461275595, 63.98289851135272244, 70.11115128919476547, 67.55274433031233627, 70.41516373728855172, 71.13755089586132385, 69.82714145548160900, 74.71983727667713993, 67.56415050352947560, 78.26774990878784877, 61.58131858106001033, 81.70467639663139892, 45.28583165458982052, 84.76914638163376026, 1.99999999999999689, 86.32305404620944955, -41.28583165458982762, 84.76914638163376026, -57.58131858106002454, 81.70467639663139892, -63.56415050352948981, 78.26774990878784877, -65.82714145548162321, 74.71983727667713993, -66.41516373728856593, 71.13755089586132385, -66.11115128919477968, 67.55274433031233627, -65.27043361461275595, 63.98289851135272244, -64.07470870335011170, 60.43992354246547904, -62.62500642980752019, 56.93332642202356197, -60.98089250042357889, 53.47155025248752480, -59.17889520468775544, 50.06261728049957327, -57.24193904387428233, 46.71447239476329827, -55.18452370168172649, 43.43518400478754415, -53.01574257648425004, 40.23307020728445593, -50.74114071516557800, 37.11678138456306897, -48.36392121341754802, 34.09535373993877982, -45.88577372237665486, 31.17824007995576707, -43.30747895739605013, 28.37531978094453677, -40.62937881000767959, 25.69688738457191945, -37.85176517580522670, 23.15361781597290403, -34.97521854780547557, 20.75650546647104022, -32.00091302933286386, 18.51677422677156315, -28.93089420696504632, 16.44575600838861362, -25.76832854644280957, 14.55473640862136264, -22.51771689758312434, 12.85476800444172873, -19.18506021909564296, 11.35645428598743756, -15.77796305903055796, 10.06971032975657643, -12.30566013367807621, 9.00350968307661859, -8.77895396829167574, 8.16563014493928208, -5.21005712455211967, 7.56241362477411005, -1.61234064761579732, 7.19855645524002874, 1.99999999999999556, 3.39364961385021102, 5.86220744039440511, 3.52366797872772963, 9.70801791517021506, 3.91258202080964379, 13.52160273595813145, 4.55701513369246314, 17.28822357553576694, 5.45149036402020482, 20.99466904928960531, 6.58864168091601599, 24.62957864939021135, 7.95948261839810378, 28.18364165385172626, 9.55371034930797514, 31.64967308093981302, 11.36002298851478365, 35.02258051120933402, 13.36643021383323671, 38.29924330014637235, 15.56054131855884215, 41.47832910130669859, 17.92981963683195801, 44.56007232189227807, 20.46179708442632261, 47.54603622089215520, 23.14424674184217068, 50.43887601207384819, 25.96531465470105005, 53.24211556220075892, 28.91361425616476666, 55.95994579953058690, 31.97828811153562256, 58.59704918478396252, 35.14904222145087687, 61.15845171105517863, 38.41615810166681655, 63.64940188352277062, 41.77048747717444854, 66.07527488350018530, 45.20343384586799118, 68.44149951448399349, 48.70692450151241104, 70.75350545455997064, 52.27337593848611164, 73.01668877482984499, 55.89565494125256606, 75.23639478559491067, 59.56703711552955127, 77.41791965045068480, 63.28116415502339720, 79.56653776770211550, 67.03200075627316323, 81.68757690134610527, 70.81379178736983704, 83.78661098119418682, 74.62102007313750107, 85.87003129087820241, 78.44836496333871878, 87.94730489861872513, 82.29066165213365025, 90.04670840724658376, 86.14286053253557895, -177.99999999999985789, 89.99364961382421768, -86.04670840724655534, 86.14286053253557895, -83.94730489861876777, 82.29066165213365025, -81.87003129087820241, 78.44836496333871878, -79.78661098119418682, 74.62102007313750107, -77.68757690134613370, 70.81379178736983704, -75.56653776770212971, 67.03200075627316323, -73.41791965045068480, 63.28116415502339720, -71.23639478559491067, 59.56703711552955127, -69.01668877482984499, 55.89565494125256606, -66.75350545455997064, 52.27337593848611164, -64.44149951448400770, 48.70692450151241104, -62.07527488350018530, 45.20343384586799118, -59.64940188352277772, 41.77048747717444854, -57.15845171105518574, 38.41615810166681655, -54.59704918478396252, 35.14904222145087687, -51.95994579953059400, 31.97828811153562256, -49.24211556220076602, 28.91361425616476666, -46.43887601207384819, 25.96531465470105005, -43.54603622089217652, 23.14424674184217068, -40.56007232189229228, 20.46179708442632261, -37.47832910130670570, 17.92981963683195801, -34.29924330014637945, 15.56054131855884215, -31.02258051120933402, 13.36643021383323671, -27.64967308093982012, 11.36002298851478365, -24.18364165385173337, 9.55371034930797514, -20.62957864939022556, 7.95948261839810378, -16.99466904928961597, 6.58864168091601599, -13.28822357553577582, 5.45149036402020482, -9.52160273595813855, 4.55701513369246314, -5.70801791517022572, 3.91258202080964379, -1.86220744039441377, 3.52366797872772963, 1.99999999999999556, -0.54003843869415158, 6.12693783758673050, -0.40111214042971866, 10.23515434659413970, 0.01431798130226895, 14.30668142622436534, 0.70226688878527466, 18.32499114491300674, 1.65629062554436168, 22.27556116130715580, 2.86776143955401341, 26.14628266758476016, 4.32621388981252775, 29.92769777693464306, 6.01973109672258033, 33.61307490596014702, 7.93534078498540918, 37.19834740797973183, 10.05939491571368904, 40.68195074563112712, 12.37791309905906267, 44.06459700293881809, 14.87687712846902421, 47.34902402209520034, 17.54247066454981052, 50.53975201683548590, 20.36126354510683711, 53.64287529119709319, 23.32034407821754485, 56.66591251909911620, 26.40740499964346455, 59.61773745881250619, 29.61078975921034129, 62.50861449014276872, 32.91950572923986584, 65.35037195723165837, 36.32321005821969351, 68.15676435641833564, 39.81217237539897269, 70.94410839385281520, 43.37721635788606989, 73.73234087330637010, 47.00963897352001908, 76.54676554128549526, 50.70110112272598712, 79.42099130058548440, 54.44347433172315220, 82.40205491898922219, 58.22861013398449614, 85.55981529780903827, 62.04795929474799721, 89.00534896567364740, 65.89187266757559769, 92.93011750021230455, 69.74815881341463353, 97.69886509454546797, 73.59868574092438109, 104.10346048738931302, 77.40995108179684792, 114.19717929614462548, 81.10053146183234674, 134.58473801020662108, 84.39088722277180921, -178.00000000000002842, 86.05996156130575514, -130.58473801020659266, 84.39088722277180921, -110.19717929614465390, 81.10053146183234674, -100.10346048738929881, 77.40995108179684792, -93.69886509454548218, 73.59868574092438109, -88.93011750021230455, 69.74815881341463353, -85.00534896567366161, 65.89187266757559769, -81.55981529780905248, 62.04795929474799721, -78.40205491898922219, 58.22861013398449614, -75.42099130058548440, 54.44347433172315220, -72.54676554128549526, 50.70110112272598712, -69.73234087330638431, 47.00963897352001908, -66.94410839385282941, 43.37721635788606989, -64.15676435641836406, 39.81217237539897269, -61.35037195723166548, 36.32321005821969351, -58.50861449014277582, 32.91950572923986584, -55.61773745881251330, 29.61078975921034129, -52.66591251909913041, 26.40740499964346455, -49.64287529119710030, 23.32034407821754485, -46.53975201683548590, 20.36126354510683711, -43.34902402209520034, 17.54247066454981052, -40.06459700293881809, 14.87687712846902421, -36.68195074563112712, 12.37791309905906267, -33.19834740797973893, 10.05939491571368904, -29.61307490596015057, 7.93534078498540918, -25.92769777693465727, 6.01973109672258033, -22.14628266758476371, 4.32621388981252775, -18.27556116130716291, 2.86776143955401341, -14.32499114491301384, 1.65629062554436168, -10.30668142622437244, 0.70226688878527466, -6.23515434659414680, 0.01431798130226895, -2.12693783758673804, -0.40111214042971866, 1.99999999999999556, -4.76061810234754645, 6.41132592404072454, -4.61212239418478553, 10.80084816537008230, -4.16824654595516098, 15.14776873556008674, -3.43374257581263365, 19.43320327160517635, -2.41626750988024641, 23.64091162595336115, -1.12602081331554671, 27.75780275269853448, 0.42470591907018324, 31.77420154083880988, 2.22202305439570047, 35.68389780238808129, 4.25093893994811900, 39.48402100288601702, 6.49582334126627270, 43.17479644658765636, 8.94081392759362181, 46.75924066894447861, 11.57015196980179894, 50.24284889074777283, 14.36844259642658628, 53.63331929525543273, 17.32084182287968943, 56.94035098918124049, 20.41317688045037571, 60.17554747078253286, 23.63200834498020697, 63.35245736607463840, 26.96464262473803686, 66.48679120890342631, 30.39910192691253599, 69.59687011372152199, 33.92405612452549946, 72.70439458886511375, 37.52871692222674938, 75.83567948061782715, 41.20268880474466755, 79.02360369831293951, 44.93576201889581512, 82.31071017117776023, 48.71761719224470966, 85.75424365182692554, 52.53738247574246856, 89.43460651588344490, 56.38292751246400769, 93.47013935832343634, 60.23965935256792648, 98.04421718085441739, 64.08831694761893516, 103.45761459629794388, 67.90061168070626252, 110.23509874248559015, 71.62987479878029262, 119.34882263320223217, 75.18923730101467129, 132.64763601333982024, 78.39753950634394641, 153.15668933029419918, 80.85560324742944260, -178.00000000000002842, 81.83938189765245852, -149.15668933029422760, 80.85560324742944260, -128.64763601333984866, 78.39753950634394641, -115.34882263320223217, 75.18923730101467129, -106.23509874248556173, 71.62987479878029262, -99.45761459629794388, 67.90061168070626252, -94.04421718085441739, 64.08831694761893516, -89.47013935832346476, 60.23965935256792648, -85.43460651588344490, 56.38292751246400769, -81.75424365182695396, 52.53738247574246856, -78.31071017117777444, 48.71761719224470966, -75.02360369831293951, 44.93576201889581512, -71.83567948061784136, 41.20268880474466755, -68.70439458886511375, 37.52871692222674938, -65.59687011372152199, 33.92405612452549946, -62.48679120890344052, 30.39910192691253599, -59.35245736607464551, 26.96464262473803686, -56.17554747078254707, 23.63200834498020697, -52.94035098918124760, 20.41317688045037571, -49.63331929525544695, 17.32084182287968943, -46.24284889074779414, 14.36844259642658628, -42.75924066894448572, 11.57015196980179894, -39.17479644658766347, 8.94081392759362181, -35.48402100288603123, 6.49582334126627270, -31.68389780238809195, 4.25093893994811900, -27.77420154083882409, 2.22202305439570047, -23.75780275269854513, 0.42470591907018324, -19.64091162595336826, -1.12602081331554671, -15.43320327160518879, -2.41626750988024641, -11.14776873556009562, -3.43374257581263365, -6.80084816537009029, -4.16824654595516098, -2.41132592404073298, -4.61212239418478553, 1.99999999999999556, -9.30985393851741705, 6.72175805422021355, -9.15091230991673932, 11.41756212661333514, -8.67603648837036268, 16.06282113870961226, -7.89096231408012549, 20.63552097548324227, -6.80489321395614688, 25.11717183125960418, -5.43001433583735604, 29.49342310484702523, -3.78089833576525836, 33.75433817509684786, -1.87386656696368870, 37.89437094593881028, 0.27363605581641121, 41.91211819209927114, 2.64360721178193936, 45.80993456941938291, 5.21796518077428217, 49.59349430020984784, 7.97892504367260536, 53.27137129744098587, 10.90927185095380025, 56.85469423510678411, 13.99253787292509266, 60.35691981311008192, 17.21309536720795919, 63.79375952890369206, 20.55617726580611659, 67.18329478816110623, 24.00783659504739731, 70.54632400302143935, 27.55485191826680236, 73.90700610430916129, 31.18458087158649406, 77.29390254081907585, 34.88475663106463287, 80.74158359961856490, 38.64321178012773572, 84.29307189810650414, 42.44749809608728697, 88.00357763838491110, 46.28434439562811065, 91.94629406419836926, 50.13884820321026581, 96.22157019495104180, 53.99321139219064491, 100.97173315822050199, 57.82466562901674934, 106.40541928769302160, 61.60190913800559542, 112.83745587761723073, 65.27873175231235336, 120.75095597801100666, 68.78228497629521598, 130.87442892417493567, 71.99165187875782124, 144.18467631053604805, 74.70285264202438213, 161.47781378002883912, 76.59572545663588983, -178.00000000000002842, 77.29014606148258792, -157.47781378002883912, 76.59572545663588983, -140.18467631053607647, 74.70285264202438213, -126.87442892417496410, 71.99165187875782124, -116.75095597801099245, 68.78228497629521598, -108.83745587761723073, 65.27873175231235336, -102.40541928769302160, 61.60190913800559542, -96.97173315822051620, 57.82466562901674934, -92.22157019495104180, 53.99321139219064491, -87.94629406419838347, 50.13884820321026581, -84.00357763838492531, 46.28434439562811065, -80.29307189810650414, 42.44749809608728697, -76.74158359961857911, 38.64321178012773572, -73.29390254081907585, 34.88475663106463287, -69.90700610430916129, 31.18458087158649406, -66.54632400302145356, 27.55485191826680236, -63.18329478816111333, 24.00783659504739731, -59.79375952890369916, 20.55617726580611659, -56.35691981311008192, 17.21309536720795919, -52.85469423510679832, 13.99253787292509266, -49.27137129744098587, 10.90927185095380025, -45.59349430020986915, 7.97892504367260536, -41.80993456941939712, 5.21796518077428217, -37.91211819209929246, 2.64360721178193936, -33.89437094593881739, 0.27363605581641121, -29.75433817509685497, -1.87386656696368870, -25.49342310484703944, -3.78089833576525836, -21.11717183125961128, -5.43001433583735604, -16.63552097548325293, -6.80489321395614688, -12.06282113870962114, -7.89096231408012549, -7.41756212661334136, -8.67603648837036268, -2.72175805422022110, -9.15091230991673932, 1.99999999999999556, -14.23540593490034212, 7.06707279862612125, -14.06484423228424063, 12.10248720394761790, -13.55555303790300137, 17.07647532339787944, -12.71455858250937965, 21.96281473836326015, -11.55307649365679623, 26.74006765897273041, -10.08584610023314454, 31.39231472226267528, -8.33033151194296018, 35.90939060170597230, -6.30588392466837799, 40.28670514160292271, -4.03294710811188395, 44.52477649322944586, -1.53236497867592791, 48.62861191461712451, 1.17517574056815022, 52.60705694133754662, 4.06955754229703981, 56.47220691225791711, 7.13150506907926562, 60.23894695811429756, 10.34273467530950796, 63.92466443809040300, 13.68599977255221845, 67.54916484650964037, 17.14505004500527363, 71.13481958525987636, 20.70451849248449960, 74.70698212428176532, 24.34974403770184992, 78.29472888122153051, 28.06652943785655552, 81.93201512996088809, 31.84082402428231973, 85.65938939021744147, 35.65830698143167155, 89.52649044264860834, 39.50382677214758331, 93.59567144235911940, 43.36062114047545890, 97.94726867835277062, 47.20919167069192923, 102.68725830824645584, 51.02562354013957702, 107.95825647893494192, 54.77900423463843538, 113.95470750710921948, 58.42738157410047961, 120.94158660124729465, 61.91142197433249095, 129.26976638159490562, 65.14478524125112813, 139.36255278451201889, 68.00113945880528377, 151.60742176334809983, 70.30250402254893061, 166.05644118521092878, 71.82662636485281382, -178.00000000000002842, 72.36459406509962378, -162.05644118521095720, 71.82662636485281382, -147.60742176334812825, 70.30250402254893061, -135.36255278451201889, 68.00113945880528377, -125.26976638159489141, 65.14478524125112813, -116.94158660124729465, 61.91142197433249095, -109.95470750710923369, 58.42738157410047961, -103.95825647893495614, 54.77900423463843538, -98.68725830824647005, 51.02562354013957702, -93.94726867835278483, 47.20919167069192923, -89.59567144235911940, 43.36062114047545890, -85.52649044264862255, 39.50382677214758331, -81.65938939021744147, 35.65830698143167155, -77.93201512996090230, 31.84082402428231973, -74.29472888122153051, 28.06652943785655552, -70.70698212428177953, 24.34974403770184992, -67.13481958525987636, 20.70451849248449960, -63.54916484650964748, 17.14505004500527363, -59.92466443809041010, 13.68599977255221845, -56.23894695811430466, 10.34273467530950796, -52.47220691225792422, 7.13150506907926562, -48.60705694133756083, 4.06955754229703981, -44.62861191461713162, 1.17517574056815022, -40.52477649322946007, -1.53236497867592791, -36.28670514160293692, -4.03294710811188395, -31.90939060170597941, -6.30588392466837799, -27.39231472226267883, -8.33033151194296018, -22.74006765897273752, -10.08584610023314454, -17.96281473836326725, -11.55307649365679623, -13.07647532339788832, -12.71455858250937965, -8.10248720394762678, -13.55555303790300137, -3.06707279862612925, -14.06484423228424063, 1.99999999999999600, -19.59134287356426896, 7.46002704385165138, -19.40755726556070115, 12.88033504279662367, -18.85919797266866738, 18.22391622121717347, -17.95503295463869264, 23.45879403720244127, -16.70896594558295334, 28.55966518490710015, -15.13909773195327269, 33.50874372060166451, -13.26662598462660014, 38.29585521437642370, -11.11472732454917889, 42.91794868987490474, -8.70753794849322738, 47.37824652323212149, -6.06930752874207258, 51.68524592294901510, -3.22375917751487107, 55.85174332801063457, -0.19365520038940884, 59.89399913408759346, 2.99945241539763385, 63.83111160507218784, 6.33531713186757095, 67.68463403089357655, 9.79496963802120746, 71.47845003929684538, 13.36059248585088000, 75.23891693218338617, 17.01531594444593409, 78.99529352424693229, 20.74294398926493699, 82.78048521884274180, 24.52760876444716587, 86.63216392910494790, 28.35333972343855180, 90.59435351833845118, 32.20351875194761959, 94.71961138434764393, 36.06017287638244539, 99.07197748871266185, 39.90302877816822757, 103.73088255981890882, 43.70821480745370025, 108.79614728017998004, 47.44644408577498496, 114.39390839554110357, 51.08045112569507751, 120.68240638414135901, 54.56141374911145192, 127.85428534979440940, 57.82417470821775396, 136.12712761110682891, 60.78156430751474204, 145.70578215046154469, 63.31961305621511116, 156.69437932769909594, 65.29863962109097031, 168.95645291666264143, 66.56908368553207822, -178.00000000000002842, 67.00865712643570760, -164.95645291666266985, 66.56908368553207822, -152.69437932769912436, 65.29863962109097031, -141.70578215046154469, 63.31961305621511116, -132.12712761110685733, 60.78156430751474204, -123.85428534979440940, 57.82417470821775396, -116.68240638414138743, 54.56141374911145192, -110.39390839554111778, 51.08045112569507751, -104.79614728017998004, 47.44644408577498496, -99.73088255981890882, 43.70821480745370025, -95.07197748871266185, 39.90302877816822757, -90.71961138434764393, 36.06017287638244539, -86.59435351833845118, 32.20351875194761959, -82.63216392910496211, 28.35333972343855180, -78.78048521884275601, 24.52760876444716587, -74.99529352424694650, 20.74294398926493699, -71.23891693218340038, 17.01531594444593409, -67.47845003929684538, 13.36059248585088000, -63.68463403089357655, 9.79496963802120746, -59.83111160507219495, 6.33531713186757095, -55.89399913408760057, 2.99945241539763385, -51.85174332801064168, -0.19365520038940884, -47.68524592294902931, -3.22375917751487107, -43.37824652323212860, -6.06930752874207258, -38.91794868987490474, -8.70753794849322738, -34.29585521437643081, -11.11472732454917889, -29.50874372060167872, -13.26662598462660014, -24.55966518490711081, -15.13909773195327269, -19.45879403720245193, -16.70896594558295334, -14.22391622121718058, -17.95503295463869264, -8.88033504279663255, -18.85919797266866738, -3.46002704385166071, -19.40755726556070115, 1.99999999999999556, -25.43837119820565817, 7.91994134356474966, -25.23910692117755517, 13.78834011215970534, -24.64516330804185529, 19.55772615655293478, -23.66774871950556403, 25.18808138888132220, -22.32448212182583092, 30.64904852681420522, -20.63801909702449677, 35.92081946357622968, -18.63449057116782726, 40.99386004100856695, -16.34197985921977292, 45.86781613095838850, -13.78920372951385431, 50.54999441522539882, -11.00448645615600007, 55.05375641269628773, -8.01504773475606846, 59.39706222812605318, -4.84657887125735343, 63.60129716846514469, -1.52305829704237761, 67.69043463465565935, 1.93324784086672352, 71.69053915995505122, 5.50164833296652045, 75.62959031828353318, 9.16278970353470612, 79.53760403336076479, 12.89834287286321945, 83.44703540885105042, 16.69062430422997068, 87.39346098727328638, 20.52214967325533124, 91.41655400532015108, 24.37510563102819106, 95.56137924921046078, 28.23071203179445021, 99.88003723653145016, 32.06843215731343832, 104.43366587620558050, 35.86497163386139420, 109.29473060869932510, 39.59298941537671368, 114.54933935400606515, 43.21943287866469774, 120.29889149413013172, 46.70342202473147353, 126.65952710171551132, 49.99368843662634987, 133.75638606304184464, 53.02581189527145256, 141.70772458687929429, 55.72003625021812923, 150.59283359977422379, 57.98141283097308474, 160.40162010671977555, 59.70518607312001080, 170.97884079028881388, 60.79043532425958318, -178.00000000000002842, 61.16162880179432193, -166.97884079028878546, 60.79043532425958318, -156.40162010671977555, 59.70518607312001080, -146.59283359977425221, 57.98141283097308474, -137.70772458687926587, 55.72003625021812923, -129.75638606304184464, 53.02581189527145256, -122.65952710171549711, 49.99368843662634987, -116.29889149413011751, 46.70342202473147353, -110.54933935400606515, 43.21943287866469774, -105.29473060869931089, 39.59298941537671368, -100.43366587620559471, 35.86497163386139420, -95.88003723653146437, 32.06843215731343832, -91.56137924921046078, 28.23071203179445021, -87.41655400532016529, 24.37510563102819106, -83.39346098727328638, 20.52214967325533124, -79.44703540885105042, 16.69062430422997068, -75.53760403336077900, 12.89834287286321945, -71.62959031828354739, 9.16278970353470612, -67.69053915995506543, 5.50164833296652045, -63.69043463465567356, 1.93324784086672352, -59.60129716846515180, -1.52305829704237761, -55.39706222812606740, -4.84657887125735343, -51.05375641269629483, -8.01504773475606846, -46.54999441522540593, -11.00448645615600007, -41.86781613095839560, -13.78920372951385431, -36.99386004100857406, -16.34197985921977292, -31.92081946357624389, -18.63449057116782726, -26.64904852681421943, -20.63801909702449677, -21.18808138888132575, -22.32448212182583092, -15.55772615655294011, -23.66774871950556403, -9.78834011215971245, -24.64516330804185529, -3.91994134356475854, -25.23910692117755517, 1.99999999999999600, -31.84350531560286868, 8.47781225763252877, -31.62546289221583962, 14.88583393507996711, -30.97645080660140593, 21.16079514785619509, -29.91127029421520689, 27.25113130544622919, -28.45290874783366775, 33.11998552133064067, -26.63042818683534207, 38.74589265732009835, -24.47666503668304117, 44.12160159745183563, -22.02610835172851722, 49.25177543408178593, -19.31318879069118211, 54.15029168417838434, -16.37106576006627279, 58.83767345094592827, -13.23089104518995640, 63.33895182482493169, -9.92146778444056032, 67.68207179147748320, -6.46920580966549874, 71.89683474738467339, -2.89828246890513030, 76.01431282527251199, 0.76906207426874740, 80.06665407661380129, 4.51214364124032485, 84.08720437104130951, 8.31139512800513636, 88.11088800598805904, 12.14788119395578647, 92.17480572169650088, 16.00276430383186366, 96.31902021078285259, 19.85670886149115333, 100.58749981932022877, 23.68920100972959730, 105.02917319698201482, 27.47775433026597725, 109.69899804029304846, 31.19696754875727152, 114.65884380836618561, 34.81740352705595143, 119.97779813807069615, 38.30427835916992052, 125.73118977168117283, 41.61600262291977259, 131.99715883033749719, 44.70273256173389598, 138.84908760812987794, 47.50530446205828383, 146.34201331309026273, 49.95526128262041254, 154.49218436749947614, 51.97706503648645082, 163.25249258416440057, 53.49372597518600259, 172.49285933765111167, 54.43639475342744305, -178.00000000000002842, 54.75649468439711143, -168.49285933765114009, 54.43639475342744305, -159.25249258416440057, 53.49372597518600259, -150.49218436749950456, 51.97706503648645082, -142.34201331309029115, 49.95526128262041254, -134.84908760812987794, 47.50530446205828383, -127.99715883033752561, 44.70273256173389598, -121.73118977168118704, 41.61600262291977259, -115.97779813807071037, 38.30427835916992052, -110.65884380836619982, 34.81740352705595143, -105.69899804029306267, 31.19696754875727152, -101.02917319698202903, 27.47775433026597725, -96.58749981932022877, 23.68920100972959730, -92.31902021078286680, 19.85670886149115333, -88.17480572169651509, 16.00276430383186366, -84.11088800598805904, 12.14788119395578647, -80.08720437104132372, 8.31139512800513636, -76.06665407661381550, 4.51214364124032485, -72.01431282527251199, 0.76906207426874740, -67.89683474738467339, -2.89828246890513030, -63.68207179147749031, -6.46920580966549874, -59.33895182482493880, -9.92146778444056032, -54.83767345094592827, -13.23089104518995640, -50.15029168417838434, -16.37106576006627279, -45.25177543408179304, -19.31318879069118211, -40.12160159745184274, -22.02610835172851722, -34.74589265732009835, -24.47666503668304117, -29.11998552133064422, -26.63042818683534207, -23.25113130544624340, -28.45290874783366775, -17.16079514785620930, -29.91127029421520689, -10.88583393507997776, -30.97645080660140593, -4.47781225763253765, -31.62546289221583962, 1.99999999999999556, -38.87875527701606160, 9.18708125479774296, -38.63683300066042392, 16.27414365511956262, -37.91820250638208734, 23.17259140539264095, -36.74332279213027164, 29.81377244810832394, -35.14341202945125531, 36.15298964123235947, -33.15698491696964822, 42.16912769952225659, -30.82630445836842625, 47.86123932751760179, -28.19436585459229860, 53.24376548995357439, -25.30271165018388402, 58.34173321358173325, -22.19009961204914561, 63.18670666190918439, -18.89188184637555779, 67.81377209114522486, -15.43989911427395079, 72.25952713288313589, -11.86270778471484277, 76.56090370117536281, -8.18599910867232694, 80.75462189677602964, -4.43311662588922584, 84.87709425893895343, -0.62561625366165385, 88.96463936229967828, 3.21615784383777248, 93.05390189983984328, 7.07248905242842518, 97.18240447461120368, 10.92369066480776318, 101.38917071735671982, 14.74948212031801020, 105.71535755141637480, 18.52832281285148497, 110.20481280215611264, 22.23668760419472790, 114.90442707083992957, 25.84827445710250160, 119.86406835852177721, 29.33315055785289260, 125.13576891124590418, 32.65687547770472321, 130.77168357131759535, 35.77969749489390949, 136.82020109922683559, 38.65601141858346068, 143.31958252044233859, 41.23439386179803279, 150.28885177460347222, 43.45866798082070659, 157.71667350566602295, 45.27051130755705799, 165.55074086228117380, 46.61395503653132977, 173.69217969866488716, 47.44157216781382402, -178.00000000000002842, 47.72124472298391851, -169.69217969866491558, 47.44157216781382402, -161.55074086228117380, 46.61395503653132977, -153.71667350566602295, 45.27051130755705799, -146.28885177460344380, 43.45866798082070659, -139.31958252044236701, 41.23439386179803279, -132.82020109922686402, 38.65601141858346068, -126.77168357131760956, 35.77969749489390949, -121.13576891124588997, 32.65687547770472321, -115.86406835852179142, 29.33315055785289260, -110.90442707083994378, 25.84827445710250160, -106.20481280215612685, 22.23668760419472790, -101.71535755141638901, 18.52832281285148497, -97.38917071735673403, 14.74948212031801020, -93.18240447461121789, 10.92369066480776318, -89.05390189983984328, 7.07248905242842518, -84.96463936229969249, 3.21615784383777248, -80.87709425893895343, -0.62561625366165385, -76.75462189677602964, -4.43311662588922584, -72.56090370117536281, -8.18599910867232694, -68.25952713288315010, -11.86270778471484277, -63.81377209114523907, -15.43989911427395079, -59.18670666190919150, -18.89188184637555779, -54.34173321358174746, -22.19009961204914561, -49.24376548995358149, -25.30271165018388402, -43.86123932751760890, -28.19436585459229860, -38.16912769952225659, -30.82630445836842625, -32.15298964123236658, -33.15698491696964822, -25.81377244810833815, -35.14341202945125531, -19.17259140539265516, -36.74332279213027164, -12.27414365511956973, -37.91820250638208734, -5.18708125479775184, -38.63683300066042392, 1.99999999999999600, -46.61822072475269607, 10.68839576675080139, -46.30624543028367412, 19.18911080317679918, -45.38412606933155757, 27.34546416576215222, -43.89055765093645078, 35.05142021090807702, -41.88213387768103502, 42.25559890256131013, -39.42480136235260346, 48.95316063861176303, -36.58631299796493153, 55.17233704527057370, -33.43100825355479344, 60.96117255247643385, -30.01702215459737744, 66.37723925505210332, -26.39536140379773599, 71.48085419487965453, -22.61014977269736548, 76.33122188826865795, -18.69947873486282575, 80.98463901984250413, -14.69649678201448850, 85.49400025392819202, -10.63053585607601548, 89.90905356028720519, -6.52818370181667884, 94.27704477012913742, -2.41427530523583922, 98.64352777118510573, 1.68719008387413583, 103.05319914949195947, 5.75218534812640314, 107.55065416712271542, 9.75582109859903390, 112.18096363521173942, 13.67152082594358298, 116.98994385952536845, 17.47019805768939449, 122.02393786261990272, 21.11944988981388605, 127.32885205682140395, 24.58281725765701253, 132.94811747553873715, 27.81921722445741807, 138.91921485966395267, 30.78272820361294038, 145.26850797118672176, 33.42299645292871446, 152.00450116657361832, 35.68659928106228563, 159.11038900941949237, 37.51968052984662449, 166.53782868216146085, 38.87197455633482690, 174.20476659278793363, 39.70189618399167131, -178.00000000000002842, 39.98177927524729114, -170.20476659278793363, 39.70189618399167131, -162.53782868216148927, 38.87197455633482690, -155.11038900941949237, 37.51968052984662449, -148.00450116657361832, 35.68659928106228563, -141.26850797118672176, 33.42299645292871446, -134.91921485966395267, 30.78272820361294038, -128.94811747553873715, 27.81921722445741807, -123.32885205682140395, 24.58281725765701253, -118.02393786261990272, 21.11944988981388605, -112.98994385952538266, 17.47019805768939449, -108.18096363521175363, 13.67152082594358298, -103.55065416712271542, 9.75582109859903390, -99.05319914949195947, 5.75218534812640314, -94.64352777118511995, 1.68719008387413583, -90.27704477012913742, -2.41427530523583922, -85.90905356028720519, -6.52818370181667884, -81.49400025392820623, -10.63053585607601548, -76.98463901984250413, -14.69649678201448850, -72.33122188826867216, -18.69947873486282575, -67.48085419487966874, -22.61014977269736548, -62.37723925505212463, -26.39536140379773599, -56.96117255247644096, -30.01702215459737744, -51.17233704527057370, -33.43100825355479344, -44.95316063861176303, -36.58631299796493153, -38.25559890256131723, -39.42480136235260346, -31.05142021090808413, -41.88213387768103502, -23.34546416576216643, -43.89055765093645078, -15.18911080317680451, -45.38412606933155757, -6.68839576675080849, -46.30624543028367412, 1.99999999999999556, -55.13279625041899834, 12.21451927262477000, -54.76594013073760436, 22.09872704828332957, -53.68864395349499574, 31.39769711559288012, -51.96422430511800883, 39.96960367385942448, -49.68040408706254141, 47.77902429867568657, -46.93247902115530223, 54.86545369174167064, -43.81109166541020983, 61.30916699148811233, -40.39598469592146301, 67.20576633421555357, -36.75430974219705860, 72.65107920217620574, -32.94148403257256064, 77.73388667252179118, -29.00309226896920123, 82.53343020155088539, -24.97699141478170048, 87.11942634360052296, -20.89524495725207132, 91.55320457280680557, -16.78577161643180204, 95.88921878897831164, -12.67371220268469578, 100.17656220612792595, -8.58256192749418645, 104.46031614728221371, -4.53512373056416251, 108.78265670493264849, -0.55433148808729282, 113.18367588372132104, 3.33602075627005812, 117.70187269530691765, 7.11062322287716331, 122.37425008640165913, 10.74206713847307348, 127.23592626204656142, 14.20035582641498806, 132.31914580979304219, 17.45256126555711873, 137.65157520973264127, 20.46271167815037728, 143.25381666270027381, 23.19203115841211726, 149.13620894054645305, 25.59967735271198208, 155.29523174265207786, 27.64411687523928052, 161.71017626763577368, 29.28521267182223298, 168.34108626834759548, 30.48695101230148552, 175.12910064316457692, 31.22051723140710777, -178.00000000000002842, 31.46720374958099598, -171.12910064316457692, 31.22051723140710777, -164.34108626834762390, 30.48695101230148552, -157.71017626763574526, 29.28521267182223298, -151.29523174265207786, 27.64411687523928052, -145.13620894054648147, 25.59967735271198208, -139.25381666270027381, 23.19203115841211726, -133.65157520973266969, 20.46271167815037728, -128.31914580979304219, 17.45256126555711873, -123.23592626204654721, 14.20035582641498806, -118.37425008640165913, 10.74206713847307348, -113.70187269530693186, 7.11062322287716331, -109.18367588372132104, 3.33602075627005812, -104.78265670493266271, -0.55433148808729282, -100.46031614728222792, -4.53512373056416251, -96.17656220612794016, -8.58256192749418645, -91.88921878897831164, -12.67371220268469578, -87.55320457280681978, -16.78577161643180204, -83.11942634360052296, -20.89524495725207132, -78.53343020155088539, -24.97699141478170048, -73.73388667252179118, -29.00309226896920123, -68.65107920217620574, -32.94148403257256064, -63.20576633421556068, -36.75430974219705860, -57.30916699148811233, -40.39598469592146301, -50.86545369174167774, -43.81109166541020983, -43.77902429867569367, -46.93247902115530223, -35.96960367385943158, -49.68040408706254141, -27.39769711559288723, -51.96422430511800883, -18.09872704828334022, -53.68864395349499574, -8.21451927262477888, -54.76594013073760436, 1.99999999999999556, -64.48162218246979194, 17.98037118830460201, -63.76341508305665684, 32.62428016143745424, -61.72174914658255318, 45.26325159838053480, -58.62403790832021144, 55.90710322947827393, -54.76300160634040992, 64.89227178252910733, -50.38082342875218700, 72.61131289029847835, -45.65520975111467550, 79.40370496549049051, -40.71192873614232610, 85.53547443418635510, -35.64102404029981841, 91.20945067766287195, -30.50961194071436822, 96.58116190590757810, -25.37072818781791383, 101.77265218881962028, -20.26925377022463337, 106.88281945262124850, -15.24594064653214254, 111.99465974304708027, -10.34025377357217934, 117.18006743498143862, -5.59247248797413388, 122.50268451884780063, -1.04529436283411115, 128.01906809075350679, 3.25496397729521814, 133.77826339847268855, 7.25759760165333567, 139.81978239976487544, 10.90731557455581502, 146.17005258660478262, 14.14485028302005709, 152.83768595785250000, 16.90865838596959492, 159.80845268931921055, 19.13794220572577487, 167.04151372648448159, 20.77699151155475477, 174.46891148411387462, 21.78042227826199806, -178.00000000000002842, 22.11837781753019527, -170.46891148411387462, 21.78042227826199806, -163.04151372648448159, 20.77699151155475477, -155.80845268931921055, 19.13794220572577487, -148.83768595785252842, 16.90865838596959492, -142.17005258660481104, 14.14485028302005709, -135.81978239976487544, 10.90731557455581502, -129.77826339847271697, 7.25759760165333567, -124.01906809075352101, 3.25496397729521814, -118.50268451884778642, -1.04529436283411115, -113.18006743498142441, -5.59247248797413388, -107.99465974304709448, -10.34025377357217934, -102.88281945262124850, -15.24594064653214254, -97.77265218881962028, -20.26925377022463337, -92.58116190590757810, -25.37072818781791383, -87.20945067766288616, -30.50961194071436822, -81.53547443418635510, -35.64102404029981841, -75.40370496549049051, -40.71192873614232610, -68.61131289029849256, -45.65520975111467550, -60.89227178252910733, -50.38082342875218700, -51.90710322947828104, -54.76300160634040992, -41.26325159838054901, -58.62403790832021144, -28.62428016143746490, -61.72174914658255318, -13.98037118830460557, -63.76341508305665684, 1.99999999999999489, -74.69966643845165777, 26.73698176730493614, -73.50791820369254026, 46.45208209886830275, -70.36956689494368788, 60.80977159628141493, -66.05660901455554779, 71.45848324441054444, -61.11675773175377913, 79.80759217874309286, -55.85321872580696834, 86.74466514239207982, -50.43213273346431436, 92.80540124701705906, -44.95206516381727369, 98.32251211109890221, -39.47845092120540045, 103.51336526710929320, -34.06038011063251503, 108.52834753231333309, -28.73921107936183361, 113.47768355265095863, -23.55333544230752452, 118.44656525084906207, -18.54103902309049090, 123.50369215326308847, -13.74234202233444968, 128.70582948829564884, -9.20020460514958671, 134.09971622507504208, -4.96122571165357140, 139.72202213508867885, -1.07580930674149267, 145.59779383610683112, 2.40232548413760538, 151.73783261271663036, 5.41746050329454931, 158.13565186637288207, 7.91416414131925894, 164.76497871932286898, 9.84055665005913660, 171.57900980622665088, 11.15231427016150256, 178.51254101195692670, 11.81685015330946520, -174.51254101195692670, 11.81685015330946520, -167.57900980622667930, 11.15231427016150256, -160.76497871932289740, 9.84055665005913660, -154.13565186637291049, 7.91416414131925894, -147.73783261271665879, 5.41746050329454931, -141.59779383610683112, 2.40232548413760538, -135.72202213508870727, -1.07580930674149267, -130.09971622507501365, -4.96122571165357140, -124.70582948829567727, -9.20020460514958671, -119.50369215326308847, -13.74234202233444968, -114.44656525084907628, -18.54103902309049090, -109.47768355265095863, -23.55333544230752452, -104.52834753231334730, -28.73921107936183361, -99.51336526710929320, -34.06038011063251503, -94.32251211109890221, -39.47845092120540045, -88.80540124701705906, -44.95206516381727369, -82.74466514239209403, -50.43213273346431436, -75.80759217874310707, -55.85321872580696834, -67.45848324441054444, -61.11675773175377913, -56.80977159628143625, -66.05660901455554779, -42.45208209886832407, -70.36956689494368788, -22.73698176730495391, -73.50791820369254026, 1.99999999999999600, -85.78166687108885924, 61.87324357773108119, -82.33459525449173100, 80.27768438369075454, -76.54083121758115738, 89.42499621356618889, -70.41813086842684299, 95.82057104813959825, -64.24948773689966686, 101.11263541171206271, -58.11928872485961506, 105.90317607822892398, -52.07335345715245722, 110.47134737780878311, -46.14758359783259323, 114.97138028465980142, -40.37601690822956613, 119.50120511177952665, -34.79395188944583595, 124.13018318901127657, -29.43951176908901957, 128.91129483912590104, -24.35455549606792047, 133.88641650799124250, -19.58517424494497305, 139.08801440266989857, -15.18177122287804615, 144.53869867413172301, -11.19861017149900739, 150.24942513902280439, -7.69266569608215622, 156.21700460768090579, -4.72161659803606781, 162.42170502724644621, -2.34091526057735644, 168.82591385569978115, -0.60006016949341945, 175.37485237931247184, 0.46152536384328491, -178.00000000000002842, 0.81833312891115950, -171.37485237931247184, 0.46152536384328491, -164.82591385569978115, -0.60006016949341945, -158.42170502724641779, -2.34091526057735644, -152.21700460768090579, -4.72161659803606781, -146.24942513902283281, -7.69266569608215622, -140.53869867413175143, -11.19861017149900739, -135.08801440266989857, -15.18177122287804615, -129.88641650799127092, -19.58517424494497305, -124.91129483912591525, -24.35455549606792047, -120.13018318901130499, -29.43951176908901957, -115.50120511177955507, -34.79395188944583595, -110.97138028465980142, -40.37601690822956613, -106.47134737780881153, -46.14758359783259323, -101.90317607822893820, -52.07335345715245722, -97.11263541171207692, -58.11928872485961506, -91.82057104813961246, -64.24948773689966686, -85.42499621356618889, -70.41813086842684299, -76.27768438369074033, -76.54083121758115738, -57.87324357773110250, -82.33459525449173100, -178.00000000000002842, -82.33591847955618448, 143.22778135121401988, -79.54230031515010069, 128.60898756414843547, -73.87349687490876704, 124.32470998353319658, -67.47696540683871547, 124.01228809564837263, -60.93070105520797597, 125.59604777627561134, -54.43751136022503090, 128.28024682035200499, -48.10755262562803125, 131.71480673751509016, -42.02394287044850074, 135.73150447900647464, -36.26259289105208694, 140.24424010430539056, -30.89942669541638764, 145.20455296082769792, -26.01294410398721979, 150.57821873782722832, -21.68429473238734317, 156.33085313434582986, -17.99542592709287092, 162.41822389716838870, -15.02545489467462048, 168.78020742076520833, -12.84550987892848184, 175.33870321353637678, -11.51266748991093536, -178.00000000000002842, -11.06408152044378745, -171.33870321353637678, -11.51266748991093536, -164.78020742076523675, -12.84550987892848184, -158.41822389716841712, -15.02545489467462048, -152.33085313434585828, -17.99542592709287092, -146.57821873782722832, -21.68429473238734317, -141.20455296082769792, -26.01294410398721979, -136.24424010430539056, -30.89942669541638764, -131.73150447900647464, -36.26259289105208694, -127.71480673751507595, -42.02394287044850074, -124.28024682035203341, -48.10755262562803125, -121.59604777627559713, -54.43751136022503090, -120.01228809564838684, -60.93070105520797597, -120.32470998353322500, -67.47696540683871547, -124.60898756414846389, -73.87349687490876704, -139.22778135121404830, -79.54230031515010069, -178.00000000000002842, -69.79343463141964321, 167.63134400963940607, -68.62304968612301082, 156.89871695437406629, -65.48334135458465255, 150.54736374030309776, -61.10651485448561715, 147.65287319536125210, -56.10790502157546911, 147.17953110490091717, -50.89570090116846757, 148.39568884343808008, -45.73876083396746139, 150.83543074335267420, -40.82969132012996027, 154.20166627754437627, -36.32066069380964279, 158.29374271187359113, -32.34078423455542861, 162.96127389198957758, -29.00296899656860816, 168.07572003197756771, -26.40464312032804983, 173.51374580520655400, -24.62495078480371546, 179.14909640850049755, -23.72034636702055366, -175.14909640850046912, -23.72034636702055366, -169.51374580520655400, -24.62495078480371546, -164.07572003197756771, -26.40464312032806049, -158.96127389198957758, -29.00296899656861527, -154.29374271187361956, -32.34078423455542861, -150.20166627754440469, -36.32066069380964279, -146.83543074335270262, -40.82969132012996027, -144.39568884343808008, -45.73876083396746139, -143.17953110490091717, -50.89570090116849599, -143.65287319536128052, -56.10790502157547621, -146.54736374030309776, -61.10651485448561715, -152.89871695437406629, -65.48334135458465255, -163.63134400963940607, -68.62304968612301082, -178.00000000000002842, -56.85258712078380228, 176.37525674045562596, -56.23764348052387874, 171.72066876086481102, -54.50623527875517738, 168.62326642581021474, -51.94637057143930292, 167.22080918295719698, -48.91487419033276751, 167.36621335583259906, -45.75617589753742465, 168.79950790329058918, -42.76713193279059766, 171.24101930549505823, -40.18968456371464271, 174.42266827604018431, -38.21219158951615213, 178.09065200901943626, -36.97055608025468132, -178.00000000000002842, -36.54741287921620341, -174.09065200901946469, -36.97055608025468132, -170.42266827604018431, -38.21219158951615213, -167.24101930549508666, -40.18968456371464271, -164.79950790329061761, -42.76713193279059766, -163.36621335583262749, -45.75617589753742465, -163.22080918295719698, -48.91487419033276751, -164.62326642581024316, -51.94637057143930292, -167.72066876086483944, -54.50623527875517738, -172.37525674045562596, -56.23764348052387874 }; }; namespace atlas { namespace test { CASE("t31c2.4") { auto nx = std::vector{20, 27, 32, 40, 45, 48, 60, 60, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 60, 60, 48, 45, 40, 32, 27, 20}; auto proj = Projection("rotated_schmidt", Config("stretching_factor", 2.4) | Config("rotation_angle", 180.0) | Config("north_pole", {2.0, 46.7})); auto grid = ReducedGaussianGrid(nx, proj); for (int j = 0, jglo = 0; j < grid.ny(); j++) { for (int i = 0; i < grid.nx(j); i++, jglo++) { auto ll1 = PointLonLat(lonlat_arp_t32c24[2 * jglo + 0], lonlat_arp_t32c24[2 * jglo + 1]); auto ll2 = grid.lonlat(i, j); EXPECT_APPROX_EQ(ll1.lon(), ll2.lon(), 1.e-9); EXPECT_APPROX_EQ(ll1.lat(), ll2.lat(), 1.e-9); } } } } // namespace test } // namespace atlas int main(int argc, char* argv[]) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/grid/test_cubedsphere.cc0000664000175000017500000005556215160212070021615 0ustar alastairalastair/* * (C) Crown Copyright 2021 Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ #include "atlas/array/MakeView.h" #include "atlas/field/FieldSet.h" #include "atlas/functionspace/NodeColumns.h" #include "atlas/grid.h" #include "atlas/grid/Partitioner.h" #include "atlas/grid/Tiles.h" #include "atlas/grid/detail/partitioner/CubedSpherePartitioner.h" #include "atlas/grid/detail/tiles/Tiles.h" #include "atlas/mesh.h" #include "atlas/meshgenerator.h" #include "atlas/option.h" #include "atlas/output/Gmsh.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/util/CoordinateEnums.h" #include "tests/AtlasTestEnvironment.h" namespace atlas { namespace test { namespace { using grid::detail::partitioner::CubedSpherePartitioner; void partition(const CubedSpherePartitioner& partitioner, const Grid& grid, CubedSpherePartitioner::CubedSphere& cb, std::vector& part) { std::vector nodes(static_cast(grid.size())); std::size_t n(0); for (std::size_t it = 0; it < 6; ++it) { for (idx_t iy = 0; iy < cb.ny[it]; ++iy) { for (idx_t ix = 0; ix < cb.nx[it]; ++ix) { nodes[n].t = static_cast(it); nodes[n].x = static_cast(ix); nodes[n].y = static_cast(iy); nodes[n].n = static_cast(n); ++n; } } } partitioner.partition(cb, grid.size(), nodes.data(), part.data()); } } // namespace CASE("cubedsphere_tile_test") { auto tileConfig1 = atlas::util::Config("type", "cubedsphere_lfric"); auto lfricTiles = atlas::grid::CubedSphereTiles(tileConfig1); EXPECT(lfricTiles.type() == "cubedsphere_lfric"); auto tileConfig2 = atlas::util::Config("type", "cubedsphere_fv3"); auto fv3Tiles = atlas::grid::CubedSphereTiles(tileConfig2); EXPECT(fv3Tiles.type() == "cubedsphere_fv3"); auto lfricTiles2 = atlas::grid::CubedSphereTiles("cubedsphere_lfric"); EXPECT(lfricTiles2.type() == "cubedsphere_lfric"); auto fv3Tiles2 = atlas::grid::CubedSphereTiles("cubedsphere_fv3"); EXPECT(fv3Tiles.type() == "cubedsphere_fv3"); } //----------------------------------------------------------------------------- CASE("test_iterator") { std::vector resolutions{1, 2, 4, 8}; std::vector grid_prefixes{"CS-EA-L-", "CS-ED-L-", "CS-EA-C-", "CS-ED-C-", "CS-LFR-L-", "CS-LFR-C-"}; for (auto resolution : resolutions) { for (auto& grid_prefix : grid_prefixes) { std::string grid_name = grid_prefix + std::to_string(resolution); SECTION(grid_name) { if (grid_name == "CS-LFR-L-1") { Log::error() << eckit::Colour::red << "TODO: Fix me!!!. Skipping..." << eckit::Colour::reset << std::endl; continue; } Grid g(grid_name); // Test xy { std::vector coordinates_1; std::vector coordinates_2; std::vector coordinates_3; { for (auto crd : g.xy()) { coordinates_1.push_back(crd); } } { auto iterator = g.xy().begin(); for (int n = 0; n < g.size(); ++n) { coordinates_2.push_back(*iterator); iterator += 1; } } { auto iterator = g.xy().begin(); PointXY crd; while (iterator.next(crd)) { coordinates_3.push_back(crd); } } EXPECT_EQ(coordinates_1.size(), g.size()); EXPECT_EQ(coordinates_2.size(), g.size()); EXPECT_EQ(coordinates_3.size(), g.size()); EXPECT_EQ(coordinates_2, coordinates_1); EXPECT_EQ(coordinates_3, coordinates_1); } // Test lonlat { std::vector coordinates_1; std::vector coordinates_2; std::vector coordinates_3; { for (auto crd : g.lonlat()) { coordinates_1.push_back(crd); } } { auto iterator = g.lonlat().begin(); for (int n = 0; n < g.size(); ++n) { coordinates_2.push_back(*iterator); iterator += 1; } } { auto iterator = g.lonlat().begin(); PointLonLat crd; while (iterator.next(crd)) { coordinates_3.push_back(crd); } } EXPECT_EQ(coordinates_1.size(), g.size()); EXPECT_EQ(coordinates_2.size(), g.size()); EXPECT_EQ(coordinates_3.size(), g.size()); EXPECT_EQ(coordinates_2, coordinates_1); EXPECT_EQ(coordinates_3, coordinates_1); } } } } } CASE("cubedsphere_FV3_mesh_test") { // THIS IS TEMPORARY! // I expect this will be replaced by some more aggressive tests. // Set grid. const auto grid = atlas::Grid("CS-EA-L-2"); atlas::Log::info() << grid.name() << std::endl; atlas::Log::info() << grid.size() << std::endl; // Set mesh. auto meshGen = atlas::MeshGenerator("nodal-cubedsphere"); auto mesh = meshGen.generate(grid); // Set functionspace auto functionSpace = atlas::functionspace::NodeColumns(mesh); // Set field auto field = functionSpace.ghost(); // Set gmsh config. auto gmshConfigXy = atlas::util::Config("coordinates", "xy") | atlas::util::Config("ghost", false); auto gmshConfigXyz = atlas::util::Config("coordinates", "xyz") | atlas::util::Config("ghost", false); auto gmshConfigLonLat = atlas::util::Config("coordinates", "lonlat") | atlas::util::Config("ghost", false); // Set source gmsh object. const auto gmshXy = atlas::output::Gmsh("FV3_xy_mesh.msh", gmshConfigXy); const auto gmshXyz = atlas::output::Gmsh("FV3_xyz_mesh.msh", gmshConfigXyz); const auto gmshLonLat = atlas::output::Gmsh("FV3_lonlat_mesh.msh", gmshConfigLonLat); // Write gmsh. gmshXy.write(mesh); gmshXy.write(field); gmshXyz.write(mesh); gmshXyz.write(field); gmshLonLat.write(mesh); gmshLonLat.write(field); } CASE("cubedsphere_generic_mesh_test") { // THIS IS TEMPORARY! // I expect this will be replaced by some more aggressive tests. // Set grid. const auto grid = atlas::Grid("CS-LFR-C-32"); atlas::Log::info() << grid.name() << std::endl; atlas::Log::info() << grid.size() << std::endl; // Set mesh. auto meshGen = atlas::MeshGenerator("cubedsphere"); auto mesh = meshGen.generate(grid); // Visually inspect the fields. // remote indices should be equal at tile boundaries. // Set gmsh config. auto gmshConfigXy = atlas::util::Config("coordinates", "xy"); auto gmshConfigXyz = atlas::util::Config("coordinates", "xyz"); auto gmshConfigLonLat = atlas::util::Config("coordinates", "lonlat"); gmshConfigXy.set("ghost", true); gmshConfigXy.set("info", true); gmshConfigXyz.set("ghost", true); gmshConfigXyz.set("info", true); gmshConfigLonLat.set("ghost", true); gmshConfigLonLat.set("info", true); // Set source gmsh object. const auto gmshXy = atlas::output::Gmsh("cs_xy_mesh.msh", gmshConfigXy); const auto gmshXyz = atlas::output::Gmsh("cs_xyz_mesh.msh", gmshConfigXyz); const auto gmshLonLat = atlas::output::Gmsh("cs_lonlat_mesh.msh", gmshConfigLonLat); // Write gmsh. gmshXy.write(mesh); gmshXyz.write(mesh); gmshLonLat.write(mesh); } CASE("cubedsphere_tileCubePeriodicity_test") { auto tileConfig1 = atlas::util::Config("type", "cubedsphere_lfric"); auto lfricTiles = atlas::grid::CubedSphereTiles(tileConfig1); // create a nodal cubed-sphere grid and check that no point are changed by // iterating through points. int resolution(2); std::vector grid_names{ "CS-LFR-L-" + std::to_string(resolution), }; Grid grid{grid_names[0]}; for (auto crd : grid.xy()) { atlas::PointXY initialXY{crd[XX], crd[YY]}; double xy[2] = {initialXY.x(), initialXY.y()}; idx_t t = lfricTiles.indexFromXY(xy); atlas::PointXY finalXY = lfricTiles.tileCubePeriodicity(initialXY, t); EXPECT_APPROX_EQ(initialXY, finalXY); } std::vector startingXYTile0{{0., 315.}, {90., 315.}, {0., 225.}, {90., 225.}, {0., 135.}, {90., 135.}, {45., 0.}, {45., 90.}, {0., -45.}, {90., -45.}, {0, -135.}, {90, -135.}, {-90., 45.}, {180., 45.}, {270., 45.}, {360., 45.}, {-90., -45.}, {180., -45.}, {270., -45.}, {360., -45.}}; std::vector expectedXYTile0{{0., -45.}, {90., -45.}, {270., -45.}, {180., -45.}, {0., 135.}, {90., 135.}, {45., 0.}, {45., 90.}, {0., -45.}, {90., -45.}, {270., -45.}, {180., -45.}, {0., 135.}, {90., 135.}, {0., 135.}, {0., 45.}, {270., -45.}, {180., -45.}, {270., -45.}, {0., -45.}}; std::vector startingXYTile1{{90., 315.}, {180., 315.}, {90., 225.}, {180., 225.}, {90., 135.}, {180., 135.}, {135., 0.}, {135., 90.}, {90., -45.}, {180., -45.}, {90., -135.}, {180, -135.}, {0., 45.}, {270., 45.}, {360., 45.}, {450., 45.}, {0., -45.}, {270., -45.}, {360., -45.}, {450., -45.}}; std::vector expectedXYTile1{{90., -45.}, {180., -45.}, {0., -45.}, {270., -45.}, {0., 45.}, {0., 135.}, {135., 0.}, {45., 90.}, {90., -45.}, {180., -45.}, {0., -45.}, {270., -45.}, {0., 45.}, {0., 135.}, {0., 45.}, {90., 45.}, {0., -45.}, {270., -45.}, {0., -45.}, {90., -45.}}; std::vector startingXYTile2{{180., 315.}, {270., 315.}, {180., 225.}, {270., 225.}, {180., 135.}, {270., 135.}, {225., 0.}, {225., 90.}, {180., -45.}, {270., -45.}, {180., -135.}, {270, -135.}, {90., 45.}, {0., 45.}, {-90., 45.}, {180., 45.}, {90., -45.}, {0., -45.}, {90., -45.}, {180., -45.}}; std::vector expectedXYTile2{{180., -45.}, {270., -45.}, {90., -45.}, {0., -45.}, {90., 45.}, {0., 45.}, {225., 0.}, {45., 90.}, {180., -45.}, {270., -45.}, {90., -45.}, {0., -45.}, {90., 45.}, {0., 45.}, {0., 135.}, {90.0, 135.0}, {90., -45.}, {0., -45.}, {90., -45.}, {180., -45.}}; std::vector startingXYTile3{{270., 315.}, {360., 315.}, {270., 225.}, {360., 225.}, {270., 135.}, {360., 135.}, {315., 90.}, {315., -90.}, {270., -45.}, {360., -45.}, {270., -135.}, {360, -135.}, {180., 45.}, {90., 45.}, {360., 45.}, {270., 45.}, {180., -45.}, {90., -45.}, {360., -45.}, {270., -45.}}; std::vector expectedXYTile3{{270., -45.}, {0., -45.}, {180., -45.}, {90., -45.}, {90., 135.}, {90., 45.}, {45., 90.}, {45., -90.}, {270., -45.}, {0., -45.}, {180., -45.}, {90., -45.}, {90., 135.}, {90., 45.}, {0., 45.}, {0.0, 135.0}, {180., -45.}, {90., -45.}, {0., -45.}, {270., -45.}}; std::vector startingXYTile4{{0., 405.}, {90., 405.}, {0., 315.}, {90., 315.}, {0., 225.}, {90., 225.}, {0, -135.}, {45., -90.}, {0., 45.}, {90., 45.}, {0, -45.}, {90, -45.}, {-90., 135.}, {180., 135.}, {270., 135.}, {360., 135.}, {-90., 45.}, {180., 45.}, {270., 45.}, {360., 45.}}; std::vector expectedXYTile4{{0., 45.}, {90., 45.}, {0., -45.}, {90., -45.}, {270., -45.}, {180., -45.}, {270., -45.}, {45., -90.}, {0., 45.}, {90., 45.}, {0., -45.}, {90., -45.}, {270., -45.}, {180., -45.}, {270., -45.}, {0., 135.}, {0., -45.}, {90., -45.}, {0., -45.}, {0., 45.}}; std::vector startingXYTile5{ {0., 225.}, {90., 225.}, {0., 135.}, {90., 135.}, {0., 45.}, {90., 45.}, {45., -90.}, {45., 0.}, {0., -135.}, {90., -135.}, {0, -225.}, {90, -225.}, {-90., -45.}, {180., -45.}, {270., -45.}, {360., -45.}, {-90., -135.}, {180., -135.}, {270., -135.}, {360., -135.}}; std::vector expectedXYTile5{{270., -45.}, {180., -45.}, {0., 135.}, {90., 135.}, {0., 45.}, {90., 45.}, {45., -90.}, {45., 0.}, {270., -45.}, {180., -45.}, {0., 135.}, {90., 135.}, {0., 45.}, {90., 45.}, {0., 45.}, {0., -45.}, {0., 135.}, {90., 135.}, {0., 135.}, {270., -45.}}; // testing tile 0 for (atlas::idx_t t = 0; t < 6; t++) { std::vector startingXY; std::vector expectedXY; if (t == 0) { startingXY = startingXYTile0; expectedXY = expectedXYTile0; } if (t == 1) { startingXY = startingXYTile1; expectedXY = expectedXYTile1; } if (t == 2) { startingXY = startingXYTile2; expectedXY = expectedXYTile2; } if (t == 3) { startingXY = startingXYTile3; expectedXY = expectedXYTile3; } if (t == 4) { startingXY = startingXYTile4; expectedXY = expectedXYTile4; } if (t == 5) { startingXY = startingXYTile5; expectedXY = expectedXYTile5; } std::size_t jn{0}; for (atlas::PointXY p : startingXY) { atlas::PointXY middleXY = lfricTiles.tileCubePeriodicity(p, t); atlas::PointXY finalXY = lfricTiles.tileCubePeriodicity(middleXY, 0); EXPECT_APPROX_EQ(middleXY, finalXY); EXPECT_APPROX_EQ(middleXY, expectedXY[jn]); ++jn; } } } CASE("cubedsphere_partitioner_test") { int resolution(4); std::vector grid_names{ "CS-LFR-C-" + std::to_string(resolution), }; Grid grid{grid_names[0]}; using grid::detail::partitioner::CubedSpherePartitioner; if (mpi::size() == 1) { // factory based constructor SECTION("factory based constructor") { std::vector globalProcStartPE{0, 0, 0, 1, 1, 1}; std::vector globalProcEndPE{0, 0, 0, 1, 1, 1}; std::vector nprocx{1, 1, 1, 1, 1, 1}; std::vector nprocy{1, 1, 1, 1, 1, 1}; atlas::util::Config conf; conf.set("starting rank on tile", globalProcStartPE); conf.set("final rank on tile", globalProcEndPE); conf.set("nprocx", nprocx); conf.set("nprocy", nprocy); grid::Partitioner partitioner("cubedsphere", conf); grid::Distribution d_cs = partitioner.partition(grid); grid::Partitioner partitioner2("cubedsphere", 2); grid::Distribution d_cs2 = partitioner2.partition(grid); } // 2 partitions via configuration and distribution object SECTION("2 partitions via configuration and distribution object") { std::vector globalProcStartPE{0, 0, 0, 1, 1, 1}; std::vector globalProcEndPE{0, 0, 0, 1, 1, 1}; std::vector nprocx{1, 1, 1, 1, 1, 1}; std::vector nprocy{1, 1, 1, 1, 1, 1}; atlas::util::Config conf; conf.set("starting rank on tile", globalProcStartPE); conf.set("final rank on tile", globalProcEndPE); conf.set("nprocx", nprocx); conf.set("nprocy", nprocy); grid::Distribution d_cs = grid::Partitioner(new CubedSpherePartitioner(2, conf)).partition(grid); for (idx_t t = 0; t < 96; ++t) { EXPECT(d_cs.partition(t) == t / 48); } } // 3 partitions via vector constructor and distribution object SECTION("3 partitions") { std::vector globalProcStartPE{0, 0, 1, 1, 2, 2}; std::vector globalProcEndPE{0, 0, 1, 1, 2, 2}; std::vector nprocx{1, 1, 1, 1, 1, 1}; std::vector nprocy{1, 1, 1, 1, 1, 1}; grid::Distribution d_cs = grid::Partitioner(new CubedSpherePartitioner(3, globalProcStartPE, globalProcEndPE, nprocx, nprocy)) .partition(grid); for (idx_t t = 0; t < 96; ++t) { EXPECT(d_cs.partition(t) == t / 32); } } // 4 partitions SECTION("4 partitions") { CubedSpherePartitioner partitioner(4); CubedSpherePartitioner::CubedSphere cb = partitioner.cubedsphere(grid); std::vector part(static_cast(grid.size()), 0); partition(partitioner, grid, cb, part); for (std::size_t t = 0; t < 4; ++t) { EXPECT(cb.nproc[t] == 1); EXPECT(cb.nprocx[t] == 1); EXPECT(cb.nprocy[t] == 1); EXPECT(cb.nx[t] == 4); EXPECT(cb.ny[t] == 4); EXPECT(cb.globalProcStartPE[t] == static_cast(t)); EXPECT(cb.globalProcEndPE[t] == static_cast(t)); } for (std::size_t t = 4; t < 6; ++t) { EXPECT(cb.nproc[t] == 0); EXPECT(cb.nprocx[t] == 1); EXPECT(cb.nprocy[t] == 1); EXPECT(cb.nx[t] == 4); EXPECT(cb.ny[t] == 4); EXPECT(cb.globalProcStartPE[t] == static_cast(3)); EXPECT(cb.globalProcEndPE[t] == static_cast(3)); } for (size_t i = 0; i < static_cast(grid.size()); ++i) { if (i < 64) { EXPECT(part[i] == static_cast(i / 16)); } else { EXPECT(part[i] == 3); } } } // 12 partitions SECTION("12 partitions") { CubedSpherePartitioner partitioner(12); CubedSpherePartitioner::CubedSphere cb = partitioner.cubedsphere(grid); std::vector part(static_cast(grid.size()), 0); partition(partitioner, grid, cb, part); for (std::size_t t = 0; t < 6; ++t) { EXPECT(cb.nproc[t] == 2); EXPECT(cb.nprocx[t] == 1); EXPECT(cb.nprocy[t] == 2); EXPECT(cb.nx[t] == 4); EXPECT(cb.ny[t] == 4); EXPECT(cb.globalProcStartPE[t] == static_cast(2 * t)); EXPECT(cb.globalProcEndPE[t] == static_cast(2 * t + 1)); for (size_t i = 0; i < static_cast(grid.size()); ++i) { EXPECT(part[i] == static_cast(i / 8)); } } } // 24 partitions SECTION("24 partitions") { CubedSpherePartitioner partitioner(24); CubedSpherePartitioner::CubedSphere cb = partitioner.cubedsphere(grid); std::vector part(static_cast(grid.size()), 0); partition(partitioner, grid, cb, part); for (std::size_t t = 0; t < 6; ++t) { EXPECT(cb.nproc[t] == 4); EXPECT(cb.nprocx[t] == 2); EXPECT(cb.nprocy[t] == 2); EXPECT(cb.nx[t] == 4); EXPECT(cb.ny[t] == 4); EXPECT(cb.globalProcStartPE[t] == static_cast(4 * t)); EXPECT(cb.globalProcEndPE[t] == static_cast(4 * t + 3)); std::size_t l(0); for (idx_t t = 0; t < 6; ++t) { for (idx_t j = 0; j < 4; ++j) { for (idx_t i = 0; i < 4; ++i, ++l) { gidx_t temp = l / 2; gidx_t temp2 = temp % 2; gidx_t temp8 = l / 8; EXPECT(part[l] == temp2 + 2 * temp8); } } } } } // 24 partitions, creating distribution object SECTION("24 partitions, creating distribution object") { grid::Distribution d_cs = grid::Partitioner(new CubedSpherePartitioner(24)).partition(grid); gidx_t l(0); for (idx_t t = 0; t < 6; ++t) { for (idx_t j = 0; j < 4; ++j) { for (idx_t i = 0; i < 4; ++i, ++l) { gidx_t temp = l / 2; gidx_t temp2 = temp % 2; gidx_t temp8 = l / 8; EXPECT(d_cs.partition(l) == temp2 + 2 * temp8); } } } } } else { // Factory test using mpi to determine number of partitions grid::Partitioner partitioner2("cubedsphere", atlas::mpi::size()); grid::Distribution d_cs2 = partitioner2.partition(grid); } } } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/grid/test_domain.cc0000664000175000017500000001167215160212070020565 0ustar alastairalastair/* * (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/domain.h" #include "atlas/grid/StructuredGrid.h" #include "atlas/runtime/Log.h" #include "atlas/util/Config.h" #include "atlas/mesh.h" #include "atlas/functionspace/NodeColumns.h" #include "tests/AtlasTestEnvironment.h" namespace atlas { namespace test { //---------------------------------------------------------------------------------------------------------------------- CASE("test_domain_rectangular") { Domain domain = RectangularDomain({0, 180}, {-25, 25}); EXPECT(not domain.global()); EXPECT(domain.type() == std::string("rectangular")); util::Config domain_cfg = domain.spec(); Domain from_cfg(domain_cfg); Log::info() << from_cfg.spec() << std::endl; EXPECT(from_cfg.type() == std::string("rectangular")); } CASE("test_domain_rectangular_tolerance") { using grid::LinearSpacing; Domain domain = RectangularDomain({-27, 45}, {33, 73}); RectangularDomain rd = domain; StructuredGrid::XSpace xspace(LinearSpacing(rd.xmin(), rd.xmax(), 721, true)); StructuredGrid::YSpace yspace(LinearSpacing(rd.ymin(), rd.ymax(), 401)); StructuredGrid grid(xspace, yspace, StructuredGrid::Projection(), rd); EXPECT(grid.nx(0) == 721); EXPECT(grid.ny() == 401); } CASE("test_domain_zonal_from_rectangular") { Domain domain = RectangularDomain({0, 360}, {-25, 25}); EXPECT(not domain.global()); EXPECT(domain.type() == std::string("zonal_band")); util::Config domain_cfg = domain.spec(); Domain from_cfg(domain_cfg); Log::info() << from_cfg.spec() << std::endl; EXPECT(from_cfg.type() == std::string("zonal_band")); } CASE("test_domain_global_from_rectangular") { Domain domain = RectangularDomain({-180, 180}, {-90, 90}); EXPECT(domain.global()); EXPECT(domain.type() == std::string("global")); util::Config domain_cfg = domain.spec(); Domain from_cfg(domain_cfg); Log::info() << from_cfg.spec() << std::endl; EXPECT(from_cfg.type() == std::string("global")); RectangularDomain rd = domain; EXPECT(rd == true); EXPECT(rd.xmin() == -180.); EXPECT(rd.xmax() == 180.); EXPECT(rd.ymin() == -90.); EXPECT(rd.ymax() == 90.); ZonalBandDomain zd = domain; EXPECT(zd == true); EXPECT(zd.xmin() == -180.); EXPECT(zd.xmax() == 180.); EXPECT(zd.ymin() == -90.); EXPECT(zd.ymax() == 90.); } CASE("test_domain_global_from_zonalband") { Domain domain = ZonalBandDomain({-45, 45}); EXPECT(not domain.global()); EXPECT(domain.type() == std::string("zonal_band")); util::Config domain_cfg = domain.spec(); Domain from_cfg(domain_cfg); Log::info() << from_cfg.spec() << std::endl; EXPECT(from_cfg.type() == std::string("zonal_band")); RectangularDomain rd = domain; EXPECT(rd == true); EXPECT(rd.xmin() == 0); EXPECT(rd.xmax() == 360.); EXPECT(rd.ymin() == -45.); EXPECT(rd.ymax() == 45.); ZonalBandDomain zd = domain; EXPECT(zd == true); EXPECT(zd.ymin() == -45.); EXPECT(zd.ymax() == 45.); } CASE("test github #282") { size_t expected_points = 209019; auto computed_points = [](Domain domain) -> size_t { auto grid = Grid("L576x361", domain); auto fs = functionspace::NodeColumns(Mesh{grid}, option::halo(1)); return fs.size(); }; EXPECT_EQ(expected_points, computed_points(Domain{})); EXPECT_EQ(expected_points, computed_points(GlobalDomain{})); EXPECT_EQ(expected_points, computed_points(GlobalDomain{-180})); EXPECT_EQ(expected_points, computed_points(ZonalBandDomain{{-90,90}})); EXPECT_EQ(expected_points, computed_points(ZonalBandDomain{{-90,90},-180})); EXPECT_EQ(expected_points, computed_points(Domain{util::Config ("type","zonal_band") ("ymin",-90)("ymax",90)})); EXPECT_EQ(expected_points, computed_points(Domain{util::Config ("type","zonal_band") ("west",-180)("ymin",-90)("ymax",90)})); EXPECT_EQ(expected_points, computed_points(RectangularDomain{{-180,180},{-90,90}})); EXPECT_EQ(expected_points, computed_points(Domain{util::Config ("type","rectangular")("units","degrees") ("xmin",0) ("xmax",360)("ymin",-90)("ymax",90)})); EXPECT_EQ(expected_points, computed_points(Domain{util::Config ("type","rectangular")("units","degrees") ("xmin",-180)("xmax",180)("ymin",-90)("ymax",90)})); } //----------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/grid/fctest_unstructuredgrid.F900000664000175000017500000001541415160212070023213 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. ! This File contains Unit Tests for testing the ! C++ / Fortran Interfaces to the Mesh Datastructure ! @author Willem Deconinck #include "fckit/fctest.h" ! ----------------------------------------------------------------------------- TESTSUITE(fctest_unstructuredgrid) ! ----------------------------------------------------------------------------- TESTSUITE_INIT use atlas_module call atlas_library%initialise() END_TESTSUITE_INIT ! ----------------------------------------------------------------------------- TESTSUITE_FINALIZE use atlas_module call atlas_library%finalise() END_TESTSUITE_FINALIZE ! ---------------------------------------------------------------------------- TEST( test_unstructuredgrid ) use, intrinsic :: iso_c_binding use atlas_module implicit none type(atlas_UnstructuredGrid) :: grid type(atlas_Mesh) :: mesh type(atlas_Output) :: gmsh type(atlas_MeshGenerator) :: meshgenerator real(c_double) :: xy(2,173) xy(:,1) = [180,0] xy(:,2) = [90,0] xy(:,3) = [-90,0] xy(:,4) = [0,90] xy(:,5) = [0,-90] xy(:,6) = [0,0] xy(:,7) = [18,0] xy(:,8) = [36,0] xy(:,9) = [54,0] xy(:,10) = [72,0] xy(:,11) = [108,0] xy(:,12) = [126,0] xy(:,13) = [144,0] xy(:,14) = [162,0] xy(:,15) = [-162,0] xy(:,16) = [-144,0] xy(:,17) = [-126,0] xy(:,18) = [-108,0] xy(:,19) = [-72,0] xy(:,20) = [-54,0] xy(:,21) = [-36,0] xy(:,22) = [-18,0] xy(:,23) = [0,18] xy(:,24) = [0,36] xy(:,25) = [0,54] xy(:,26) = [0,72] xy(:,27) = [180,72] xy(:,28) = [180,54] xy(:,29) = [180,36] xy(:,30) = [180,18] xy(:,31) = [180,-18] xy(:,32) = [180,-36] xy(:,33) = [180,-54] xy(:,34) = [180,-72] xy(:,35) = [0,-72] xy(:,36) = [0,-54] xy(:,37) = [0,-36] xy(:,38) = [0,-18] xy(:,39) = [90,18] xy(:,40) = [90,36] xy(:,41) = [90,54] xy(:,42) = [90,72] xy(:,43) = [-90,72] xy(:,44) = [-90,54] xy(:,45) = [-90,36] xy(:,46) = [-90,18] xy(:,47) = [-90,-18] xy(:,48) = [-90,-36] xy(:,49) = [-90,-54] xy(:,50) = [-90,-72] xy(:,51) = [90,-72] xy(:,52) = [90,-54] xy(:,53) = [90,-36] xy(:,54) = [90,-18] xy(:,55) = [123.974,-58.6741] xy(:,56) = [154.087,-16.9547] xy(:,57) = [154.212,-58.8675] xy(:,58) = [114.377,-41.9617] xy(:,59) = [125.567,-23.5133] xy(:,60) = [137.627,-40.8524] xy(:,61) = [106.162,-24.5874] xy(:,62) = [158.508,-38.55] xy(:,63) = [137.826,-72.8109] xy(:,64) = [142.103,-26.799] xy(:,65) = [138.256,-13.8871] xy(:,66) = [168.39,-24.3266] xy(:,67) = [168.954,-12.0094] xy(:,68) = [117.333,-12.35] xy(:,69) = [102.254,-11.1537] xy(:,70) = [120.307,59.7167] xy(:,71) = [107.196,26.0167] xy(:,72) = [144.768,28.3721] xy(:,73) = [150.891,60.0343] xy(:,74) = [164.566,25.5053] xy(:,75) = [116.851,14.0295] xy(:,76) = [124.84,28.3978] xy(:,77) = [157.901,42.042] xy(:,78) = [111.41,43.1056] xy(:,79) = [134.333,44.6677] xy(:,80) = [103.277,11.707] xy(:,81) = [135.358,73.2119] xy(:,82) = [135.349,14.2311] xy(:,83) = [153.48,13.386] xy(:,84) = [168.071,11.5344] xy(:,85) = [-162.99,26.3775] xy(:,86) = [-147.519,56.1313] xy(:,87) = [-122.579,27.4824] xy(:,88) = [-117.909,59.2376] xy(:,89) = [-104.052,27.3616] xy(:,90) = [-153.107,14.9717] xy(:,91) = [-110.833,41.7436] xy(:,92) = [-144.847,32.8534] xy(:,93) = [-161.546,42.1031] xy(:,94) = [-129.866,44.5201] xy(:,95) = [-133.883,72.4163] xy(:,96) = [-166.729,11.8907] xy(:,97) = [-135.755,15.2529] xy(:,98) = [-106.063,14.4869] xy(:,99) = [-119.452,11.7037] xy(:,100) = [-146.026,-58.6741] xy(:,101) = [-115.913,-16.9547] xy(:,102) = [-115.788,-58.8675] xy(:,103) = [-155.623,-41.9617] xy(:,104) = [-144.433,-23.5133] xy(:,105) = [-132.373,-40.8524] xy(:,106) = [-163.838,-24.5874] xy(:,107) = [-111.492,-38.55] xy(:,108) = [-132.174,-72.8109] xy(:,109) = [-127.897,-26.799] xy(:,110) = [-131.744,-13.8871] xy(:,111) = [-101.61,-24.3266] xy(:,112) = [-101.046,-12.0094] xy(:,113) = [-152.667,-12.35] xy(:,114) = [-167.746,-11.1537] xy(:,115) = [-14.0127,-27.2963] xy(:,116) = [-59.193,-57.0815] xy(:,117) = [-56.465,-19.5751] xy(:,118) = [-27.056,-59.3077] xy(:,119) = [-57.124,-35.9752] xy(:,120) = [-33.4636,-28.3914] xy(:,121) = [-74.8037,-46.8602] xy(:,122) = [-40.089,-45.1376] xy(:,123) = [-74.8149,-28.3136] xy(:,124) = [-21.3072,-42.2177] xy(:,125) = [-44.0778,-72.6353] xy(:,126) = [-19.6969,-12.8527] xy(:,127) = [-40.1318,-12.1601] xy(:,128) = [-72.691,-11.4129] xy(:,129) = [-56.0261,58.6741] xy(:,130) = [-25.9127,16.9547] xy(:,131) = [-25.7876,58.8675] xy(:,132) = [-65.6229,41.9617] xy(:,133) = [-54.4335,23.5133] xy(:,134) = [-42.373,40.8524] xy(:,135) = [-73.838,24.5874] xy(:,136) = [-21.4917,38.55] xy(:,137) = [-42.1744,72.8109] xy(:,138) = [-37.8974,26.799] xy(:,139) = [-41.7437,13.8871] xy(:,140) = [-11.6095,24.3266] xy(:,141) = [-11.0459,12.0094] xy(:,142) = [-62.667,12.35] xy(:,143) = [-77.7456,11.1537] xy(:,144) = [30.3071,59.7167] xy(:,145) = [17.1956,26.0167] xy(:,146) = [54.7676,28.3721] xy(:,147) = [60.8915,60.0343] xy(:,148) = [74.5657,25.5053] xy(:,149) = [26.8506,14.0295] xy(:,150) = [34.8398,28.3978] xy(:,151) = [67.9014,42.042] xy(:,152) = [21.41,43.1056] xy(:,153) = [44.3335,44.6677] xy(:,154) = [13.2772,11.707] xy(:,155) = [45.3579,73.2119] xy(:,156) = [45.3492,14.2311] xy(:,157) = [63.4799,13.386] xy(:,158) = [78.0714,11.5344] xy(:,159) = [17.01,-26.3775] xy(:,160) = [32.4806,-56.1313] xy(:,161) = [57.4213,-27.4824] xy(:,162) = [62.0912,-59.2376] xy(:,163) = [75.9483,-27.3616] xy(:,164) = [26.893,-14.9717] xy(:,165) = [69.1672,-41.7436] xy(:,166) = [35.1527,-32.8534] xy(:,167) = [18.4543,-42.1031] xy(:,168) = [50.1344,-44.5201] xy(:,169) = [46.1172,-72.4163] xy(:,170) = [13.2711,-11.8907] xy(:,171) = [44.2448,-15.2529] xy(:,172) = [73.9368,-14.4869] xy(:,173) = [60.5478,-11.7037] grid = atlas_UnstructuredGrid(xy) FCTEST_CHECK_EQUAL( grid%owners(), 1 ) meshgenerator = atlas_MeshGenerator("delaunay") mesh = meshgenerator%generate(grid) FCTEST_CHECK_EQUAL( mesh%owners(), 1 ) FCTEST_CHECK_EQUAL( grid%owners(), 2 ) gmsh = atlas_output_Gmsh("atlas_fctest_unstructuredgrid.msh",coordinates="xyz") call gmsh%write(mesh) call mesh%final() FCTEST_CHECK_EQUAL( grid%owners(), 1 ) call gmsh%final() call grid%final() call meshgenerator%final() END_TEST ! ----------------------------------------------------------------------------- END_TESTSUITE atlas-0.46.0/src/tests/grid/test_vertical.cc0000664000175000017500000000332115160212070021117 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include #include "atlas/grid/Vertical.h" #include "atlas/option.h" #include "tests/AtlasTestEnvironment.h" namespace atlas { namespace test { //----------------------------------------------------------------------------- std::vector zrange(idx_t nlev, double min, double max) { std::vector zcoord(nlev); double dzcoord = (max - min) / double(nlev - 1); for (idx_t jlev = 0; jlev < nlev; ++jlev) { zcoord[jlev] = min + jlev * dzcoord; } return zcoord; } //----------------------------------------------------------------------------- CASE("test vertical; default constructor") { Vertical vertical; EXPECT(vertical.size() == 0); EXPECT(vertical.k_begin() == 0); EXPECT(vertical.k_end() == 0); } CASE("test vertical; config levels") { Vertical vertical(option::levels(10)); EXPECT(vertical.size() == 10); EXPECT(vertical.k_begin() == 0); EXPECT(vertical.k_end() == 10); } CASE("test vertical; array") { Vertical vertical(5, zrange(5, 0., 1.)); EXPECT(vertical.size() == 5); EXPECT(vertical.k_begin() == 0); EXPECT(vertical.k_end() == 5); } //----------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/grid/test_cubedsphere_2.cc0000664000175000017500000000713315160212070022025 0ustar alastairalastair#include "atlas/grid/CubedSphereGrid2.h" #include "atlas/grid/Iterator.h" #include "eckit/geometry/Point2.h" #include "tests/AtlasTestEnvironment.h" namespace atlas { namespace test { namespace { using Point2 = eckit::geometry::Point2; // XY/lonlat for a Cubed Sphere, with N = 2 std::vector kgo_lonlat { {-22.5,20.941}, {22.5,20.941}, {-22.5,-20.941}, {22.5,-20.941}, {67.5,20.941}, {112.5,20.941}, {67.5,-20.941}, {112.5,-20.941}, {157.5,20.941}, {-157.5,20.941}, {157.5,-20.941}, {-157.5,-20.941}, {-112.5,20.941}, {-67.5,20.941}, {-112.5,-20.941}, {-67.5,-20.941}, {-45,59.6388}, {-135,59.6388}, {45,59.6388}, {135,59.6388}, {45,-59.6388}, {135,-59.6388}, {-45,-59.6388}, {-135,-59.6388} }; template bool compare_2D_points(std::vector a, std::vector b, bool print_diff = true, double tolerance = 1e-4) { // Uses a tolerance as the values are stored more precisely than they are printed ATLAS_ASSERT(a.size() == b.size()); bool equal = true; for (int i = 0; i < b.size(); ++i) { for (int j = 0; j < 2; ++j) { if (std::abs(b[i][j] - a[i][j]) > tolerance) { if (print_diff) { std::cout << "[" << i << ", " << j << "]\n\t" << a[i][j] << " != " << b[i][j] << "\n\tdiff = " << b[i][j] - a[i][j] << std::endl; } equal = false; } } } return equal; } CASE("cubed_sphere_instantiation") { const int n = 2; const std::string name = "CS-LFR-" + std::to_string(n) + "-2"; const Grid grid = Grid(name); EXPECT(grid.name() == name); EXPECT(grid.type() == "cubedsphere2"); EXPECT(grid.size() == n * n * 6); } CASE("constructor_with_grid") { auto grid_og = Grid("O32"); auto grid_cs = Grid("CS-LFR-4-2"); EXPECT( CubedSphereGrid2( grid_og ).valid() == false ); EXPECT( bool(CubedSphereGrid2( grid_og )) == false ); EXPECT( CubedSphereGrid2( grid_cs ).valid() == true ); EXPECT( bool(CubedSphereGrid2( grid_cs )) == true ); } CASE("cubed_sphere_grid_kgo") { const Grid grid = CubedSphereGrid2(2); // LonLat std::cout << "Checking lonlat" << std::endl; std::vector points_lonlat; for (const auto &ll : grid.lonlat()) { points_lonlat.push_back(ll); } EXPECT(points_lonlat.size() == static_cast(grid.size())); EXPECT(compare_2D_points(points_lonlat, kgo_lonlat)); // XY std::cout << "Checking xy" << std::endl; std::vector points_xy; for (const auto &xy : grid.xy()) { points_xy.push_back(xy); } EXPECT(points_xy.size() == static_cast(grid.size())); EXPECT(compare_2D_points(points_xy, kgo_lonlat)); } CASE("cubed_sphere_rotated_lonlat") { const auto grid_rotated = CubedSphereGrid2(2, Projection(util::Config("type", "rotated_lonlat")("north_pole", std::vector{4., 54.}))); // Expect XY points to still match std::vector points_xy; for (const auto &xy : grid_rotated.xy()) { points_xy.push_back(xy); } EXPECT(compare_2D_points(points_xy, kgo_lonlat) == true); // Expect lonlats to be different std::vector points_lonlat; for (const auto &lonlat : grid_rotated.lonlat()) { points_lonlat.push_back(lonlat); } EXPECT(compare_2D_points(points_lonlat, kgo_lonlat, false) == false); } } // namespace } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/grid/fctest_grids.F900000664000175000017500000000432415160212070020704 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. ! @author Willem Deconinck #include "fckit/fctest.h" ! ----------------------------------------------------------------------------- TESTSUITE(fctest_Grid) ! ----------------------------------------------------------------------------- TESTSUITE_INIT use atlas_module call atlas_library%initialise() END_TESTSUITE_INIT ! ----------------------------------------------------------------------------- TESTSUITE_FINALIZE use atlas_module call atlas_library%finalise() END_TESTSUITE_FINALIZE TEST( test_ij2gidx ) use atlas_module implicit none type(atlas_StructuredGrid) :: grid integer(ATLAS_KIND_IDX) :: i, j, i1, j1 integer(ATLAS_KIND_IDX) :: jglo, jglo1 grid = atlas_StructuredGrid ("N16") jglo = 1 do j = 1, grid%ny () do i = 1, grid%nx (j) call grid%index2ij (jglo, i1, j1) jglo1 = grid%index (i, j) FCTEST_CHECK_EQUAL( jglo, jglo1 ) FCTEST_CHECK_EQUAL( i, i1 ) FCTEST_CHECK_EQUAL( j, j1 ) jglo = jglo + 1 enddo enddo call grid%final() END_TEST TEST( test_spec ) use atlas_module implicit none type(atlas_Grid) :: grid type(atlas_Config) :: spec character(:), allocatable :: json_sorted, json_ordered, json grid = atlas_Grid ("O32") spec = grid%spec() FCTEST_CHECK_EQUAL( spec%owners(), 1 ) json_sorted = '{"domain":{"type":"global"},"name":"O32","projection":{"type":"lonlat"}}' json_ordered = '{"name":"O32","domain":{"type":"global"},"projection":{"type":"lonlat"}}' json = spec%json() FCTEST_CHECK( json == json_sorted .or. json == json_ordered ) grid = atlas_RegularGaussianGrid(8) grid = atlas_RegularLonLatGrid(8,8) grid = atlas_ShiftedLonLatGrid(8,8) grid = atlas_ShiftedLonGrid(8,8) grid = atlas_ShiftedLatGrid(8,8) call spec%final() call grid%final() END_TEST ! ----------------------------------------------------------------------------- END_TESTSUITE atlas-0.46.0/src/tests/grid/test_largegrid.cc0000664000175000017500000000425315160212070021253 0ustar alastairalastair#include #include #include "eckit/log/Bytes.h" #include "eckit/log/ResourceUsage.h" #include "eckit/system/ResourceUsage.h" #include "atlas/domain.h" #include "atlas/grid.h" #include "atlas/runtime/Trace.h" #include "atlas/runtime/trace/StopWatch.h" #include "tests/AtlasTestEnvironment.h" namespace atlas { namespace test { // Computation of gaussian latitudes for large grids can be quite costly in terms of // time and memory. // Atlas has a number of commonly used resolutions precomputed which can speed up Gaussian grids // and prevents large peaks in memory usage. See gaussian Spacing. // // If more high resolution Gaussian latitudes need to be precomputed they can be added there, or in // a dynamic library with self-registration. struct Trace { struct Indentor { Indentor() { Log::info().indent(); } ~Indentor() { Log::info().unindent(); } } indentor; eckit::ResourceUsage resource_usage; atlas::Trace trace; Trace(const eckit::CodeLocation& where, const std::string& what): indentor{}, resource_usage{what, Log::debug()}, trace{where, what} {} double elapsed() const { return trace.elapsed(); } static size_t peakMemory() { return eckit::system::ResourceUsage().maxResidentSetSize(); } void stopAndReport() { trace.stop(); Log::info() << "Time taken: " << elapsed() << " sec" << std::endl; Log::info() << "Peak memory usage: " << eckit::Bytes(peakMemory()) << std::endl; } }; constexpr size_t MB = 1024 * 1024; CASE("test resources for cropping large grids") { std::vector gridnames{"L40000x20000", "N8000", "O8000"}; for (auto& gridname : gridnames) { EXPECT(Trace::peakMemory() < 256 * MB); SECTION(std::string("section") + gridname) { Trace trace(Here(), "Grid{" + gridname + ", GlobalDomain{-180}}"); auto grid = Grid{gridname, GlobalDomain{-180}}; trace.stopAndReport(); EXPECT(trace.elapsed() < 10.); EXPECT(trace.peakMemory() < 256 * MB); } } } } // namespace test } // namespace atlas int main(int argc, char* argv[]) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/grid/test_state.cc0000664000175000017500000001532115160212070020431 0ustar alastairalastair/* * (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/log/JSON.h" #include "eckit/parser/JSONParser.h" #include "atlas/array/ArrayView.h" #include "atlas/array/DataType.h" #include "atlas/array/MakeView.h" #include "atlas/field/Field.h" #include "atlas/field/State.h" #include "atlas/grid/Grid.h" #include "atlas/mesh/Mesh.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Log.h" #include "tests/AtlasTestEnvironment.h" using namespace atlas::field; using namespace atlas::field; namespace atlas { namespace test { // ------------------------------------------------------------------- // Example State generator // --- Declaration (in .h file) class MyStateGenerator : public StateGenerator { public: MyStateGenerator(const eckit::Parametrisation& p = util::Config()): StateGenerator(p) {} ~MyStateGenerator() override = default; void generate(State& state, const eckit::Parametrisation& p = util::Config()) const override; }; // --- Implementation (in .cc file) void MyStateGenerator::generate(State& state, const eckit::Parametrisation& p) const { const util::Config* params = dynamic_cast(&p); if (!params) { throw_Exception("Parametrisation has to be of atlas::util::Config type"); } util::Config geometry; if (!params->get("geometry", geometry)) { throw_Exception("Could not find 'geometry' in Parametrisation", Here()); } std::string grid_uid; if (geometry.get("grid", grid_uid)) { Grid grid(grid_uid); if (!geometry.has("ngptot")) { geometry.set("ngptot", grid.size()); } } if (!geometry.has("ngptot")) { throw_Exception("Could not find 'ngptot' in Parametrisation"); } std::vector fields; if (params->get("fields", fields)) { for (size_t i = 0; i < fields.size(); ++i) { util::Config fieldparams; // Subsequent "set" calls can overwrite eachother, so that // finetuning is possible in e.g. the fields Parametrisation (such as // creator, nlev, ngptot) fieldparams.set("creator", "IFS"); fieldparams.set(geometry); fieldparams.set(fields[i]); state.add(Field(fieldparams)); // debug info std::stringstream s; eckit::JSON json(s); json << fieldparams; Log::debug() << "fieldparams = " << s.str() << std::endl; } } } // Register in factory StateGeneratorBuilder __MyStateGenerator("MyStateGenerator"); // =================================================================== // BEGIN TESTS // =================================================================== CASE("state") { State state; EXPECT(state.size() == 0); state.add(Field("myfield", array::make_datatype(), array::make_shape(10, 1))); state.add(Field("", array::make_datatype(), array::make_shape(10, 2))); state.add(Field("", array::make_datatype(), array::make_shape(10, 3))); EXPECT(state.size() == 3); EXPECT(state.has("myfield")); EXPECT(state.has("field_00001")); EXPECT(state.has("field_00002")); EXPECT(state.field(0).name() == std::string("field_00001")); EXPECT(state.field(1).name() == std::string("field_00002")); EXPECT(state.field(2).name() == std::string("myfield")); state.remove("myfield"); EXPECT(state.size() == 2); EXPECT(!state.has("myfield")); state.remove("field_00002"); EXPECT(state.size() == 1); EXPECT(!state.has("field_00002")); } CASE("state_generator") { EXPECT(StateGeneratorFactory::has("MyStateGenerator")); std::unique_ptr stategenerator(StateGeneratorFactory::build("MyStateGenerator")); } CASE("state_create") { util::Config p; util::Config geometry; geometry.set("grid", "O80"); geometry.set("ngptot", 350); geometry.set("nproma", 3); geometry.set("nlev", 5); p.set("geometry", geometry); std::vector fields(5); fields[0].set("name", "temperature"); fields[0].set("datatype", array::DataType::real32().str()); fields[1].set("name", "wind"); fields[1].set("nvar", 2); // vector field u,v fields[1].set("datatype", array::DataType::real64().str()); fields[2].set("name", "soiltype"); fields[2].set("datatype", array::DataType::int32().str()); fields[2].set("nlev", 1); // We can overwrite nlev from geometry here fields[3].set("name", "GFL"); fields[3].set("nvar", 12); // assume 12 variables in GFL array fields[3].set("datatype", array::DataType::real64().str()); fields[4].set("name", "array"); fields[4].set("datatype", array::DataType::int64().str()); fields[4].set("creator", "ArraySpec"); fields[4].set("shape", array::make_shape(10, 2)); p.set("fields", fields); // We can also translate parameters to a json: std::stringstream json; eckit::JSON js(json); js << p; Log::info() << "json = " << json.str() << std::endl; // And we can create back parameters from json: util::Config from_json_stream(json); // And if we have a json file, we could create Parameters from the file: // StateGenerater::Parameters from_json_file( eckit::PathName("file.json") ); State state("MyStateGenerator", p); EXPECT(state.has("temperature")); EXPECT(state.has("wind")); EXPECT(state.has("soiltype")); Log::info() << state.field("temperature") << std::endl; Log::info() << state.field("wind") << std::endl; Log::info() << state.field("soiltype") << std::endl; Log::info() << state.field("GFL") << std::endl; array::ArrayView temperature = array::make_view(state.field("temperature")); temperature(0, 0, 0, 0) = 0; array::ArrayView wind = array::make_view(state.field("wind")); wind(0, 0, 0, 0) = 0; array::ArrayView soiltype = array::make_view(state.field("soiltype")); soiltype(0, 0, 0, 0) = 0; array::ArrayView array = array::make_view(state["array"]); array(0, 0) = 0; } //----------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/grid/test_grid_iterator.cc0000664000175000017500000000614615160212070022154 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include #include #include #include "atlas/grid/Iterator.h" #include "atlas/grid/StructuredGrid.h" #include "atlas/grid/UnstructuredGrid.h" #include "atlas/runtime/Log.h" #include "atlas/util/Config.h" #include "tests/AtlasTestEnvironment.h" using Grid = atlas::Grid; using Config = atlas::util::Config; namespace atlas { namespace test { bool operator==(const std::vector& ll, const std::vector& xy) { if (ll.size() != xy.size()) { return false; } for (size_t i = 0; i < xy.size(); ++i) { if (ll[i] != xy[i]) { return false; } } return true; } //----------------------------------------------------------------------------- CASE("test_iterator") { std::vector grids; std::vector points{{0, 90}, {90, 90}, {180, 90}, {270, 90}, {0, 0}, {90, 0}, {180, 0}, {270, 0}, {0, -90}, {90, -90}, {180, -90}, {270, -90}}; grids.emplace_back("L4x3"); grids.emplace_back(UnstructuredGrid{points}); for (auto grid : grids) { Log::debug() << "grid : " << grid.name() << std::endl; std::vector points_xy; std::vector points_lonlat; // Iteration of xy in range-based for for (const PointXY& xy : grid.xy()) { points_xy.push_back(xy); } EXPECT(points_xy.size() == static_cast(grid.size())); EXPECT(points_xy == points); // Iteration of lonlat in range-based for for (const PointLonLat& ll : grid.lonlat()) { points_lonlat.push_back(ll); } EXPECT(points_lonlat.size() == static_cast(grid.size())); EXPECT(points_lonlat == points); // Use of xy iterator in STL algorithms std::copy(grid.xy().begin(), grid.xy().end(), points_xy.begin()); EXPECT(points_xy == points); // Use of lonlat iterator in STL algorithms std::copy(grid.lonlat().begin(), grid.lonlat().end(), points_lonlat.begin()); EXPECT(points_lonlat == points); // Random access for xy iterator, required for parallelisation EXPECT(*(grid.xy().begin() + grid.size() / 2) == PointXY(180., 0.)); EXPECT(grid.xy().begin() + grid.size() == grid.xy().end()); // Random access for lonlat iterator, required for parallelisation EXPECT(*(grid.lonlat().begin() + grid.size() / 2) == PointLonLat(180., 0.)); EXPECT(grid.lonlat().begin() + grid.size() == grid.lonlat().end()); } } //----------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/grid/test_grids.cc0000664000175000017500000006250315160212070020425 0ustar alastairalastair/* * (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/grid.h" #include "atlas/grid/Grid.h" #include "atlas/mesh/Mesh.h" #include "atlas/meshgenerator.h" #include "atlas/output/Gmsh.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/util/Config.h" #include "tests/AtlasTestEnvironment.h" using Grid = atlas::Grid; using Config = atlas::util::Config; namespace atlas { namespace test { //----------------------------------------------------------------------------- CASE("test_ij2gidx") { StructuredGrid n16 = Grid("N16"); for (int j = 0, jglo = 0; j < n16.ny(); j++) { for (int i = 0; i < n16.nx(j); i++, jglo++) { idx_t i1, j1; n16.index2ij(jglo, i1, j1); EXPECT(n16.index(i, j) == jglo); EXPECT(i1 == i); EXPECT(j1 == j); } } } CASE("test_factory") { StructuredGrid structured = Grid("N80"); Grid grid = Grid("N24"); std::cout << "structured.ny() = " << structured.ny() << std::endl; std::cout << "grid.npts() = " << grid.size() << std::endl; } CASE("test_regular_gg") { RegularGrid grid("F32"); EXPECT(grid.ny() == 64); EXPECT(grid.size() == 8192); // EXPECT(grid.type() == "regular_gaussian"); // Full Gaussian Grid Grid::Config config; config.set("type", "regular_gaussian"); config.set("N", 32); grid = Grid(config); EXPECT(grid.size() == 8192); // EXPECT(grid.type() == "regular_gaussian"); } CASE("test_reduced_gg") { StructuredGrid grid; grid = Grid("N32"); EXPECT(grid.ny() == 64); EXPECT(grid.size() == 6114); grid = ReducedGaussianGrid({4, 6, 8, 8, 6, 4}); EXPECT(grid.ny() == 6); EXPECT(grid.size() == 8 + 12 + 16); } CASE("test_reduced_gg_ifs") { StructuredGrid grid("N32"); // EXPECT(grid.N() == 32); EXPECT(grid.ny() == 64); EXPECT(grid.size() == 6114); // EXPECT(grid.type() == "classic_gaussian"); } CASE("test_regular_ll") { // Constructor for N=8 idx_t nlon = 32; idx_t nlat = 16; std::stringstream name; name << "Slat" << nlon << "x" << nlat; RegularGrid grid(name.str()); EXPECT(grid.nx() == nlon); EXPECT(grid.ny() == nlat); EXPECT(grid.size() == 512); // EXPECT(grid.type() == "shifted_lat"); EXPECT(is_approximately_equal(grid.y(0), 90. - 0.5 * (180. / 16.))); EXPECT(is_approximately_equal(grid.y(grid.ny() - 1), -90. + 0.5 * (180. / 16.))); EXPECT(is_approximately_equal(grid.x(0), 0.)); EXPECT(is_approximately_equal(grid.x(grid.nx() - 1), 360. - 360. / 32.)); // Construct using builders/factories // Global Grid Grid::Config config1; config1.set("type", "shifted_lat"); config1.set("nx", 32); config1.set("ny", 16); grid = Grid(config1); EXPECT(grid.size() == 512); // EXPECT(gridptr->type() == "shifted_lat"); Grid::Config config2; config2.set("type", "shifted_lat"); config2.set("N", 8); grid = Grid(config2); EXPECT(grid.size() == 512); // EXPECT(gridptr->type() == "shifted_lat"); RegularGrid ll_poles("L4x3"); EXPECT(ll_poles.nx() == 4); EXPECT(ll_poles.ny() == 3); RegularGrid ll_nopoles("Slat4x2"); EXPECT(ll_nopoles.nx() == 4); EXPECT(ll_nopoles.ny() == 2); EXPECT(is_approximately_equal(ll_nopoles.y(0), 45.)); // tolerance was previously 1.e-5 EXPECT(is_approximately_equal(ll_nopoles.y(1), -45.)); // tolerance was previously 1.e-5 EXPECT(is_approximately_equal(ll_nopoles.x(0), 0.)); // tolerance was previously 1.e-5 EXPECT(is_approximately_equal(ll_nopoles.x(1), 90.)); // tolerance was previously 1.e-5 } CASE("test_reducedgaussian") { StructuredGrid N640("N640"); EXPECT(N640.size() == 2140702); ReducedGaussianGrid custom(N640.nx()); EXPECT(N640.size() == custom.size()); } CASE("test_cropping previous case") { StructuredGrid grid("N32"); EXPECT(grid.ny() == 64); EXPECT(grid.size() == 6114); StructuredGrid cropped(grid, RectangularDomain({-27, 45}, {33, 73})); EXPECT(cropped.ny() == 14); EXPECT(cropped.size() == 267); } CASE("cropping with line at north pole") { StructuredGrid grid("L16", RectangularDomain({0, 360}, {90, 90})); EXPECT(grid.ny() == 1); EXPECT(grid.nx(0) == 64); EXPECT(grid.size() == 64); } CASE("cropping with line at south pole") { StructuredGrid grid("L16", RectangularDomain({0, 360}, {-90, -90})); EXPECT(grid.ny() == 1); EXPECT(grid.nx(0) == 64); EXPECT(grid.size() == 64); } CASE("cropping with line at equator") { StructuredGrid grid("L16", RectangularDomain({0, 360}, {0, 0})); EXPECT(grid.ny() == 1); EXPECT(grid.nx(0) == 64); EXPECT(grid.size() == 64); } CASE("cropping single point at equator") { StructuredGrid grid("L16", RectangularDomain({0, 0}, {0, 0})); EXPECT(grid.ny() == 1); EXPECT(grid.nx(0) == 1); EXPECT(grid.size() == 1); } CASE("Create cropped unstructured grid using rectangular domain") { StructuredGrid agrid("L8"); auto domain = RectangularDomain({-27, 45}, {33, 73}); StructuredGrid sgrid(agrid, domain); UnstructuredGrid ugrid(agrid, domain); EXPECT(ugrid.size() == sgrid.size()); } CASE("Create cropped unstructured grid using zonal domain") { StructuredGrid agrid("L8"); auto domain = ZonalBandDomain({33, 73}); StructuredGrid sgrid(agrid, domain); UnstructuredGrid ugrid(agrid, domain); EXPECT(ugrid.size() == sgrid.size()); } CASE("Create unstructured from unstructured") { StructuredGrid agrid("L8"); UnstructuredGrid global_unstructured(agrid, Domain()); EXPECT(UnstructuredGrid(global_unstructured)); EXPECT(global_unstructured.size() == agrid.size()); auto domain = ZonalBandDomain({33, 73}); UnstructuredGrid ugrid(global_unstructured, domain); EXPECT(ugrid.size() == StructuredGrid(agrid, domain).size()); } CASE("ATLAS-255: regular Gaussian grid with global domain") { GlobalDomain globe; Grid grid("F80", globe); EXPECT(GaussianGrid(grid)); } CASE("ATLAS-255: reduced Gaussian grid with global domain") { GlobalDomain globe; Grid grid("O80", globe); EXPECT(GaussianGrid(grid)); } CASE("test_from_string_L32") { Grid grid; EXPECT(not grid); grid = Grid("L32"); EXPECT(grid); EXPECT(StructuredGrid(grid) == true); EXPECT(RegularGrid(grid) == true); auto structured = StructuredGrid(grid); EXPECT(structured.ny() == 65); EXPECT(structured.periodic() == true); EXPECT(structured.nx(0) == 128); EXPECT(structured.y().front() == 90.); EXPECT(structured.y().back() == -90.); auto regular = RegularGrid(grid); EXPECT(regular.ny() == 65); EXPECT(regular.periodic() == true); EXPECT(regular.nx() == 128); EXPECT(regular.y().front() == 90.); EXPECT(regular.y().back() == -90.); } CASE("test_from_string_O32") { Grid grid; EXPECT(not grid); grid = Grid("O32"); EXPECT(grid); EXPECT(StructuredGrid(grid)); EXPECT(!RegularGrid(grid)); auto structured = StructuredGrid(grid); EXPECT(structured.ny() == 64); EXPECT(structured.periodic() == true); EXPECT(structured.nx().front() == 20); } CASE("test_from_string_O32_with_domain") { Grid grid; EXPECT(not grid); grid = Grid("O32", RectangularDomain({0, 90}, {0, 90})); EXPECT(grid); EXPECT(StructuredGrid(grid)); EXPECT(!RegularGrid(grid)); auto structured = StructuredGrid(grid); EXPECT(structured.ny() == 32); EXPECT(structured.periodic() == false); EXPECT(structured.nx().front() == 6); output::Gmsh gmsh("test_grid_ptr_O32_subdomain.msh"); Mesh mesh = StructuredMeshGenerator().generate(grid); gmsh.write(mesh); } CASE("test_structured_1") { std::stringstream json; json << "{" "\"type\" : \"structured\"," "\"yspace\" : { \"type\":\"linear\", \"N\":9, \"start\":90, " "\"end\":-90 }," "\"xspace\" : { \"type\":\"linear\", \"N\":16, \"start\":0, " "\"end\":360, \"endpoint\":false }" "}"; json.seekp(0); Grid grid; EXPECT(not grid); Config json_config(json); grid = StructuredGrid(json_config); EXPECT(grid); EXPECT(StructuredGrid(grid)); EXPECT(RegularGrid(grid)); auto structured = StructuredGrid(grid); EXPECT(structured.ny() == 9); EXPECT(structured.periodic() == true); EXPECT(structured.nx(0) == 16); EXPECT(structured.y().front() == 90.); EXPECT(structured.y().back() == -90.); auto regular = RegularGrid(grid); EXPECT(regular.ny() == 9); EXPECT(regular.periodic() == true); EXPECT(regular.nx() == 16); EXPECT(regular.y().front() == 90.); EXPECT(regular.y().back() == -90.); output::Gmsh gmsh("test_grid_ptr.msh"); Mesh mesh = StructuredMeshGenerator().generate(grid); gmsh.write(mesh); } CASE("test_structured_2") { using XSpace = StructuredGrid::XSpace; using YSpace = StructuredGrid::YSpace; using Domain = StructuredGrid::Domain; using Projection = StructuredGrid::Projection; StructuredGrid grid(XSpace({0., 360.}, {2, 4, 6, 6, 4, 2}, false), YSpace(grid::LinearSpacing({90., -90.}, 6)), Projection(), Domain()); EXPECT(grid); output::Gmsh gmsh("test_grid_ptr_structured_2.msh"); Mesh mesh = StructuredMeshGenerator().generate(grid); gmsh.write(mesh); Log::info() << grid.spec() << std::endl; Grid newgrid(grid.spec()); Log::info() << newgrid.spec() << std::endl; Log::info() << "original: " << grid.uid() << std::endl; Log::info() << "fromspec: " << newgrid.uid() << std::endl; EXPECT(grid == newgrid); } CASE("test_structured_3") { StructuredGrid grid("O32"); EXPECT(grid); Log::info() << grid.spec() << std::endl; Grid newgrid(grid.spec()); Log::info() << newgrid.spec() << std::endl; Log::info() << "original: " << grid.uid() << std::endl; Log::info() << "fromspec: " << newgrid.uid() << std::endl; EXPECT(grid == newgrid); EXPECT(grid.name() == "O32"); EXPECT(newgrid.name() == "O32"); } CASE("test_structured_triangulated") { Grid grid; // Create grid { using XSpace = StructuredGrid::XSpace; using YSpace = StructuredGrid::YSpace; auto xspace = util::Config{}; xspace.set("type", "linear"); xspace.set("N", 16); xspace.set("length", 360); xspace.set("endpoint", false); xspace.set("start[]", []() { auto startpts = std::vector(8); for (int i = 0; i < 8; ++i) { startpts[i] = i * 12.; } return startpts; }()); grid = StructuredGrid{XSpace{xspace}, YSpace{grid::LinearSpacing{{90., -90.}, 8}}}; } EXPECT(grid); Log::info() << grid.spec() << std::endl; // Create and output mesh { auto meshgen = StructuredMeshGenerator{util::Config("angle", -1.)}; auto mesh = meshgen.generate(grid); auto gmsh = output::Gmsh{"structured_triangulated.msh"}; gmsh.write(mesh); } } CASE("test_structured_from_config") { Config config; config.set("type", "structured"); config.set("xspace", []() { Config config; config.set("type", "linear"); config.set("N", 40); config.set("start", 5); config.set("end", 365); config.set("endpoint", false); return config; }()); config.set("yspace", []() { Config config; config.set("type", "custom"); config.set("N", 9); config.set("values", std::vector{5., 15., 25., 35., 45., 55., 65., 75., 85.}); return config; }()); StructuredGrid g{config}; for (idx_t j = 0; j < g.ny(); ++j) { EXPECT_EQ(g.nx(j), 40); EXPECT_EQ(g.x(0, j), 5.); EXPECT_EQ(g.x(g.nx(j), j), 365.); EXPECT_EQ(g.dx(j), 9.); EXPECT_EQ(g.xmin(j), 5.); } EXPECT(not g.domain().global()); } struct PointLatLon : public Point2 { using Point2::Point2; PointLatLon(): Point2() {} // Allow initialization through PointXY lonlat = {0,0}; PointLatLon(std::initializer_list list): PointLatLon(list.begin()) {} double lon() const { return x_[1]; } double lat() const { return x_[0]; } }; CASE("test_cubedsphere") { constexpr double rad2deg = 180. / M_PI; const double cornerLat = rad2deg * std::atan(std::sin(M_PI / 4.0)); const std::vector resolutions{1, 2, 4, 8}; for (auto resolution : resolutions) { const std::vector grid_names{ "CS-EA-L-" + std::to_string(resolution), "CS-ED-L-" + std::to_string(resolution), "CS-LFR-L-" + std::to_string(resolution), "CS-EA-C-" + std::to_string(resolution), "CS-ED-C-" + std::to_string(resolution), "CS-LFR-C-" + std::to_string(resolution)}; for (const std::string& grid_name : grid_names) { Log::info() << "Testing " << grid_name << std::endl; if (grid_name == "CS-LFR-L-1") { Log::error() << eckit::Colour::red << "TODO: Fix me!!!. Skipping..." << eckit::Colour::reset << std::endl; continue; } Grid grid{grid_name}; EXPECT(grid); std::vector pointLonLats_from_XY; std::vector pointXYs; std::vector pointLonLats; std::vector pointXYs_from_LonLat; std::vector expectedLatLon; std::vector expectedXY; const double tolerance = 1e-13; for (auto crd : grid.xy()) { pointXYs.push_back(crd); grid->projection().xy2lonlat(crd); pointLonLats_from_XY.emplace_back(crd); } for (auto crd : grid.lonlat()) { pointLonLats.emplace_back(crd); grid->projection().lonlat2xy(crd); pointXYs_from_LonLat.emplace_back(crd); } int numAdditionalPoints = 0; if (grid_name.substr(grid_name.rfind("-") - 1, 1) == "L") { numAdditionalPoints = 2; } EXPECT(pointLonLats.size() == 6 * resolution * resolution + numAdditionalPoints); EXPECT(pointXYs.size() == 6 * resolution * resolution + numAdditionalPoints); EXPECT(grid.size() == 6 * resolution * resolution + numAdditionalPoints); // Note that with nodal points on the cubed-sphere // for a equiangular and equidistant projections and a resolution of 2 are the same. // Expected latitudes/longitude per tile if ((grid_name == "CS-EA-L-2") || (grid_name == "CS-ED-L-2")) { expectedLatLon = std::vector{ {-cornerLat, 315.0}, {-45.0, 0.0}, {0.0, 315.0}, {0.0, 0.0}, {cornerLat, 315.0}, {-cornerLat, 45.0}, {-45.0, 90.0}, {-cornerLat, 135.0}, {0.0, 45.0}, {0.0, 90.0}, {cornerLat, 45.0}, {45.0, 90.0}, {45.0, 0.0}, {90, 0.0}, {cornerLat, 135.0}, {0.0, 135.0}, {45.0, 180.0}, {0.0, 180.0}, {cornerLat, 225.0}, {0.0, 225.0}, {45.0, 270.0}, {0.0, 270.0}, {-cornerLat, 225.0}, {-45.0, 180.0}, {-45.0, 270.0}, {-90.0, 0.0}}; expectedXY = std::vector{ {0.0, -45.0}, {45.0, -45.0}, {0.0, 0.0}, {45.0, 0.0}, {0.0, 45.0}, {90.0, -45.0}, {135.0, -45.0}, {180.0, -45.0}, {90.0, 0.0}, {135.0, 0.0}, {90.0, 45.0}, {135.0, 45.0}, {90.0, 90.0}, {135.0, 90.0}, {180.0, 45.0}, {180.0, 0.0}, {225.0, 45.0}, {225.0, 0.0}, {270.0, 45.0}, {270.0, 0.0}, {315.0, 45.0}, {315.0, 0.0}, {270.0, -45.0}, {270.0, -90.0}, {315.0, -45.0}, {315.0, -90.0}}; } else if (grid_name == "CS-LFR-L-2") { expectedLatLon = std::vector{ {-cornerLat, 315.0}, {-45.0, 0.0}, {0.0, 315.0}, {0.0, 0.0}, // tile 0 {-cornerLat, 45.0}, {-45.0, 90.0}, {0.0, 45.0}, {0.0, 90.0}, // tile 1 {-45.0, 180.0}, {0.0, 180.0}, {-cornerLat, 135.0}, {0.0, 135.0}, // tile 2 {-45.0, 270.0}, {0.0, 270.0}, {-cornerLat, 225.0}, {0.0, 225.0}, // tile 3 {cornerLat, 315.0}, {45.0, 0.0}, {cornerLat, 45.0}, // tile 4 {45.0, 270.0}, {90.0, 0.0}, {45.0, 90.0}, {cornerLat, 225.0}, {45.0, 180.0}, {cornerLat, 135.0}, {-90, 0.0}}; // tile 5 expectedXY = std::vector{ {0.0, -45.0}, {45.0, -45.0}, {0.0, 0.0}, {45.0, 0.0}, {90.0, -45.0}, {135.0, -45.0}, {90.0, 0.0}, {135.0, 0.0}, {225.0, -45.0}, {225.0, 0.0}, {180.0, -45.0}, {180.0, 0.0}, {315.0, -45.0}, {315.0, 0.0}, {270.0, -45.0}, {270.0, 0.0}, {0.0, 45.0}, {45.0, 45.0}, {90.0, 45.0}, {0.0, 90.0}, {45.0, 90.0}, {90.0, 90.0}, {0.0, 135.0}, {45.0, 135.0}, {90.0, 135.0}, {45.0, -90.0}}; } else if (grid_name == "CS-LFR-C-2") { expectedLatLon = std::vector{ {-20.941020472243846, 337.5}, {-20.941020472243846, 22.5}, {20.941020472243846, 337.5}, {20.941020472243846, 22.5}, {-20.941020472243846, 67.5}, {-20.941020472243846, 112.5}, {20.941020472243846, 67.5}, {20.941020472243846, 112.5}, {-20.941020472243846, 202.5}, {20.941020472243846, 202.5}, {-20.941020472243846, 157.5}, {20.941020472243846, 157.5}, {-20.941020472243846, 292.5}, {20.941020472243846, 292.5}, {-20.941020472243846, 247.5}, {20.941020472243846, 247.5}, {59.638806595178281, 315.}, {59.638806595178281, 45.}, {59.638806595178281, 225.}, {59.638806595178281, 135.}, {-59.638806595178281, 315.}, {-59.638806595178281, 225.}, {-59.638806595178281, 45.}, {-59.638806595178281, 135.}}; expectedXY = std::vector{ {22.5, -22.5}, {67.5, -22.5}, {22.5, 22.5}, {67.5, 22.5}, {112.5, -22.5}, {157.5, -22.5}, {112.5, 22.5}, {157.5, 22.5}, {247.5, -22.5}, {247.5, 22.5}, {202.5, -22.5}, {202.5, 22.5}, {337.5, -22.5}, {337.5, 22.5}, {292.5, -22.5}, {292.5, 22.5}, {22.5, 67.5}, {67.5, 67.5}, {22.5, 112.5}, {67.5, 112.5}, {22.5, -67.5}, {22.5, -112.5}, {67.5, -67.5}, {67.5, -112.5}}; } else if (grid_name == "CS-ED-C-2") { expectedLatLon = std::vector{{-24.094842552110702, 333.43494882292202}, {-24.094842552110702, 26.565051177078004}, {24.094842552110702, 333.43494882292202}, {24.094842552110702, 26.565051177078004}, {-24.094842552110702, 63.434948822921996}, {-24.094842552110702, 116.56505117707802}, {24.094842552110702, 63.434948822921996}, {24.094842552110702, 116.56505117707802}, {54.735610317245339, 45.}, {54.735610317245339, 135.}, {54.735610317245339, 315.}, {54.735610317245339, 225.}, {24.094842552110702, 153.434948822922}, {-24.094842552110702, 153.434948822922}, {24.094842552110702, 206.565051177078}, {-24.094842552110702, 206.565051177078}, {24.094842552110702, 243.43494882292197}, {-24.094842552110702, 243.43494882292197}, {24.094842552110702, 296.56505117707798}, {-24.094842552110702, 296.56505117707798}, {-54.735610317245339, 225.}, {-54.735610317245339, 135.}, {-54.735610317245339, 315.}, {-54.735610317245339, 45.}}; expectedXY = std::vector{ {22.5, -22.5}, {67.5, -22.5}, {22.5, 22.5}, {67.5, 22.5}, {112.5, -22.5}, {157.5, -22.5}, {112.5, 22.5}, {157.5, 22.5}, {112.5, 67.5}, {157.5, 67.5}, {112.5, 112.5}, {157.5, 112.5}, {202.5, 22.5}, {202.5, -22.5}, {247.5, 22.5}, {247.5, -22.5}, {292.5, 22.5}, {292.5, -22.5}, {337.5, 22.5}, {337.5, -22.5}, {292.5, -67.5}, {292.5, -112.5}, {337.5, -67.5}, {337.5, -112.5}}; } else if (grid_name == "CS-EA-C-2") { expectedLatLon = std::vector{ {-20.941020472243846, 337.5}, {-20.941020472243846, 22.5}, {20.941020472243846, 337.5}, {20.941020472243846, 22.5}, {-20.941020472243846, 67.5}, {-20.941020472243846, 112.5}, {20.941020472243846, 67.5}, {20.941020472243846, 112.5}, {59.638806595178281, 45.}, {59.638806595178281, 135.}, {59.638806595178281, 315.}, {59.638806595178281, 225.}, {20.941020472243846, 157.5}, {-20.941020472243846, 157.5}, {20.941020472243846, 202.5}, {-20.941020472243846, 202.5}, {20.941020472243846, 247.5}, {-20.941020472243846, 247.5}, {20.941020472243846, 292.5}, {-20.941020472243846, 292.5}, {-59.638806595178281, 225.}, {-59.638806595178281, 135.}, {-59.638806595178281, 315.}, {-59.638806595178281, 45.}}; expectedXY = std::vector{ {22.5, -22.5}, {67.5, -22.5}, {22.5, 22.5}, {67.5, 22.5}, {112.5, -22.5}, {157.5, -22.5}, {112.5, 22.5}, {157.5, 22.5}, {112.5, 67.5}, {157.5, 67.5}, {112.5, 112.5}, {157.5, 112.5}, {202.5, 22.5}, {202.5, -22.5}, {247.5, 22.5}, {247.5, -22.5}, {292.5, 22.5}, {292.5, -22.5}, {337.5, 22.5}, {337.5, -22.5}, {292.5, -67.5}, {292.5, -112.5}, {337.5, -67.5}, {337.5, -112.5}}; } // Perform the test comparison now that expected values are set if (expectedXY.size() && expectedLatLon.size()) { for (std::size_t jn = 0; jn < grid.size(); ++jn) { Log::debug() << grid_name << " " << jn << " lon2x " << pointXYs_from_LonLat[jn].x() << " " << expectedXY[jn].x() << std::endl; Log::debug() << grid_name << " " << jn << " lat2y " << pointXYs_from_LonLat[jn].y() << " " << expectedXY[jn].y() << std::endl; EXPECT(std::abs(pointLonLats[jn].lat() - expectedLatLon[jn].lat()) < tolerance); EXPECT(std::abs(pointLonLats[jn].lon() - expectedLatLon[jn].lon()) < tolerance); EXPECT(std::abs(pointLonLats_from_XY[jn].lat() - expectedLatLon[jn].lat()) < tolerance); EXPECT(std::abs(pointLonLats_from_XY[jn].lon() - expectedLatLon[jn].lon()) < tolerance); EXPECT(std::abs(pointXYs[jn].x() - expectedXY[jn].x()) < tolerance); EXPECT(std::abs(pointXYs[jn].y() - expectedXY[jn].y()) < tolerance); EXPECT(std::abs(pointXYs_from_LonLat[jn].x() - expectedXY[jn].x()) < tolerance); EXPECT(std::abs(pointXYs_from_LonLat[jn].y() - expectedXY[jn].y()) < tolerance); } } else { Log::warning() << " No tests implemented for grid " << grid_name << std::endl; } for (std::size_t jn = 0; jn < grid.size(); ++jn) { Log::debug() << grid_name << " jn = " << jn << " x = " << pointXYs[jn].x() << " y = " << pointXYs[jn].y() << " lat = " << pointLonLats[jn].lat() << " lon = " << pointLonLats[jn].lon() << std::endl; } } } } //----------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/grid/fctest_state.F900000664000175000017500000001002115160212070020703 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. ! This File contains Unit Tests for testing the ! C++ / Fortran Interfaces to the State Datastructure ! @author Willem Deconinck #include "fckit/fctest.h" ! ----------------------------------------------------------------------------- module fctest_atlas_State_Fixture use atlas_module use, intrinsic :: iso_c_binding implicit none character(len=1024) :: msg contains end module fctest_atlas_State_Fixture ! ----------------------------------------------------------------------------- TESTSUITE_WITH_FIXTURE(fctest_atlas_State,fctest_atlas_State_Fixture) ! ----------------------------------------------------------------------------- TESTSUITE_INIT call atlas_library%initialise() END_TESTSUITE_INIT ! ----------------------------------------------------------------------------- TESTSUITE_FINALIZE call atlas_library%finalise() END_TESTSUITE_FINALIZE ! ----------------------------------------------------------------------------- TEST( test_state_fields ) #if 1 type(atlas_State) :: state type(atlas_Field) :: temperature_field type(atlas_Field) :: pressure_field type(atlas_Field) :: field type(atlas_Metadata) :: metadata integer :: jfield real(c_double), pointer :: temperature(:,:) real(c_float), pointer :: pressure(:,:,:) ! Create a new state state = atlas_State() ! Create a new fields inside the state call state%add( atlas_Field(shape=[20,10], kind=atlas_real(c_double), name="temperature") ) call state%add( atlas_Field(shape=[20,10,3], kind=atlas_real(c_float), name="pressure" ) ) ! Check how many fields we have write(msg,'(A,I0,A)') "The state contains ",state%size()," fields."; call atlas_log%info(msg) FCTEST_CHECK_EQUAL( state%size(), 2 ) ! Check if wind field exists if( .not. state%has("wind") ) then write(msg,'(A)') "The state does not contain the wind field"; call atlas_log%info(msg) endif FCTEST_CHECK( .not. state%has("wind") ) ! Print existing fields info write(msg,'(A)') "The state contains the fields:"; call atlas_log%info(msg) do jfield=1,state%size() field = state%field(jfield) write(msg,'(2A)') " - ",field%name(); call atlas_log%info(msg) write(msg,'(2A)') " kind = ",field%datatype(); call atlas_log%info(msg) write(msg,'(A,I0)') " size = ",field%size(); call atlas_log%info(msg) write(msg,*) " shape =",field%shape(); call atlas_log%info(msg) enddo ! Get fields out of the state temperature_field = state%field("temperature") pressure_field = state%field("pressure") ! If you want to edit the field, access the data call temperature_field%data(temperature) call pressure_field%data(pressure) temperature(:,:) = 273.15_c_double pressure(:,:,:) = 10000._c_double ! Set some field metadata metadata = temperature_field%metadata() call metadata%set("unit","Kelvin") call metadata%set("iteration",1) call metadata%set("grib_param_id","T") call state%remove("pressure") FCTEST_CHECK(state%has("pressure") .eqv. .False.) ! Delete the state call state%final() #endif END_TEST ! ----------------------------------------------------------------------------- TEST( test_state_factory ) #if 1 type(atlas_State) :: state ! Create a new state state = atlas_State() ! Delete the state call state%final() #endif END_TEST ! ----------------------------------------------------------------------------- TEST( test_state_metadata ) #if 1 type(atlas_State) :: state type(atlas_Metadata) :: state_metadata ! Create a new state state = atlas_State() ! Access metadata state_metadata = state%metadata() call state_metadata%set("integer",1) ! Delete the state call state%final() #endif END_TEST ! ----------------------------------------------------------------------------- END_TESTSUITE atlas-0.46.0/src/tests/grid/test_spacing.cc0000664000175000017500000001075415160212070020742 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include #include #include #include "eckit/types/Fraction.h" #include "atlas/grid/Spacing.h" #include "atlas/grid/StructuredGrid.h" #include "atlas/runtime/Log.h" #include "tests/AtlasTestEnvironment.h" using LinearSpacing = atlas::grid::LinearSpacing; using Spacing = atlas::grid::Spacing; using Config = atlas::util::Config; namespace atlas { namespace test { using std::to_string; std::string to_string(const eckit::Fraction& f) { std::stringstream s; s << f; return s.str(); } //----------------------------------------------------------------------------- CASE("LinearSpacing ") { auto check = [](const Spacing& s, const std::vector& expected) { size_t j = 0; for (double x : s) { if (not is_approximately_equal(x, expected[j++])) { return false; } } return true; }; /// Using the constructor LinearSpacing( start, end, N, endpoint ) we can create EXPECT(check(LinearSpacing(2, 3, 5, true), {2.0, 2.25, 2.5, 2.75, 3.0})); EXPECT(check(LinearSpacing(2, 3, 5, false), {2.0, 2.2, 2.4, 2.6, 2.8})); /// Using the constructor LinearSpacing( {start, end}, N, endpoint ) we can create EXPECT(check(LinearSpacing({2, 3}, 5, true), {2.0, 2.25, 2.5, 2.75, 3.0})); EXPECT(check(LinearSpacing({2, 3}, 5, false), {2.0, 2.2, 2.4, 2.6, 2.8})); /// Configuration parameters can be passed as well with following keys: EXPECT(check(Spacing(Config("type", "linear")("start", 2)("end", 3)("N", 5)("endpoint", true)), {2.0, 2.25, 2.5, 2.75, 3.0})); EXPECT(check(Spacing(Config("type", "linear")("start", 2)("end", 3)("N", 5)("endpoint", false)), {2.0, 2.2, 2.4, 2.6, 2.8})); /// Instead of the "end" key, you can provide the "length" key, to achieve the same results: EXPECT(check(Spacing(Config("type", "linear")("start", 2)("length", 1)("N", 5)("endpoint", true)), {2.0, 2.25, 2.5, 2.75, 3.0})); EXPECT(check(Spacing(Config("type", "linear")("start", 2)("length", 1)("N", 5)("endpoint", false)), {2.0, 2.2, 2.4, 2.6, 2.8})); } CASE("LinearSpacing exactness ATLAS-276") { using eckit::Fraction; double north = 90.; double south = -90.; long N = 217; SECTION("LinearSpacing") { auto y = grid::LinearSpacing(north, south, N); // Ensure y does not go outside interval [90.,-90] EXPECT(!(y.front() > 90.)); EXPECT(!(y.back() < -90.)); // Exact front and end EXPECT(y.front() == 90.); EXPECT(y.back() == -90.); } SECTION("LinearSpacing constructed with Fractions") { Fraction n(north); Fraction s(south); Fraction sn(5, 6); Fraction N_as_fraction = ((n - s) / Fraction{5, 6}) + 1; EXPECT(N_as_fraction.integer()); EXPECT_EQ(N_as_fraction.integralPart(), N); auto y = grid::LinearSpacing(n, s, N); EXPECT_EQ(Fraction{y.step()}, (-Fraction{5, 6})); // Ensure y does not go outside interval [90.,-90] EXPECT(!(y.front() > 90.)); EXPECT(!(y.back() < -90.)); // Exact front and end EXPECT(y.front() == 90.); EXPECT(y.back() == -90.); } SECTION("YSpace ") { StructuredGrid::YSpace y(grid::LinearSpacing(north, south, N)); // Ensure yspace does not go outside interval [90.,-90] EXPECT(!(y.front() > 90.)); EXPECT(!(y.back() < -90.)); // Exact front and end EXPECT(y.front() == 90.); EXPECT(y.back() == -90.); } SECTION("LinearSpacing without endpoint") { auto y = grid::LinearSpacing(north, south, N - 1, /*endpoint*/ false); EXPECT_EQ(y.front(), 90.); EXPECT_EQ(Fraction{y.front() + (N - 1) * y.step()}, -90.); EXPECT_EQ(Fraction{y.back() + y.step()}, -90.); EXPECT_EQ(Fraction{y.step()}, (-Fraction{5, 6})); } } //----------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/grid/fctest_stretchedrotatedgaussiangrid.F900000664000175000017500000033604215160212070025552 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. #include "fckit/fctest.h" ! ----------------------------------------------------------------------------- TESTSUITE(fctest_stretchedrotatedgaussiangrid) ! ----------------------------------------------------------------------------- TESTSUITE_INIT use atlas_module call atlas_library%initialise() END_TESTSUITE_INIT ! ----------------------------------------------------------------------------- TESTSUITE_FINALIZE use atlas_module call atlas_library%finalise() END_TESTSUITE_FINALIZE ! ---------------------------------------------------------------------------- TEST( arpege_t31c24 ) ! Grid provided by Philippe Marguinaud, Meteo France use atlas_module implicit none type(atlas_ReducedGaussianGrid) :: grid type(atlas_LonLatRectangularDomain) :: bounding_box real(dp) :: lonlat(3376) integer, parameter :: nx(32) = & [ 20, 27, 32, 40, 45, 48, 60, 60, 64, 64, 64, 64, 64, 64, 64, 64, & 64, 64, 64, 64, 64, 64, 64, 64, 60, 60, 48, 45, 40, 32, 27, 20 ] real(dp), parameter :: stretch = 2.4_dp real(dp), parameter :: centre(2) = [2.0_dp, 46.7_dp] real(dp), parameter :: angle = 180.0_dp integer :: i, j, jglo lonlat = [ 1.99999999999999600_dp, 44.93291171299576092_dp, & & 2.77237262934735273_dp, 45.01674523281888440_dp, & & 3.47555862489311007_dp, 45.26073815098433784_dp, & & 4.04494770792090641_dp, 45.64287023699112211_dp, & & 4.42528132740345281_dp, 46.12813070368466839_dp, & & 4.57569351104372046_dp, 46.67109320702403608_dp, & & 4.47479391972813190_dp, 47.21956618120344018_dp, & & 4.12512266986032206_dp, 47.71926346185548340_dp, & & 3.55581027759415491_dp, 48.11926090196744354_dp, & & 2.82200931024728874_dp, 48.37772379915030996_dp, & & 1.99999999999999556_dp, 48.46708828700425187_dp, & & 1.17799068975270305_dp, 48.37772379915030996_dp, & & 0.44418972240583693_dp, 48.11926090196744354_dp, & & -0.12512266986032983_dp, 47.71926346185548340_dp, & & -0.47479391972814028_dp, 47.21956618120344018_dp, & & -0.57569351104372879_dp, 46.67109320702403608_dp, & & -0.42528132740346147_dp, 46.12813070368466839_dp, & & -0.04494770792091433_dp, 45.64287023699112211_dp, & & 0.52444137510688182_dp, 45.26073815098433784_dp, & & 1.22762737065263883_dp, 45.01674523281888440_dp, & & 1.99999999999999556_dp, 42.63725270983827897_dp, & & 3.27473332072834422_dp, 42.73934376403467184_dp, & & 4.49349321557291415_dp, 43.04109625964867547_dp, & & 5.60127364516099924_dp, 43.52904513460186564_dp, & & 6.54514794659825672_dp, 44.18107605134951399_dp, & & 7.27568782798168190_dp, 44.96693492678289061_dp, & & 7.74887351496657040_dp, 45.84898874281991965_dp, & & 7.92867515230084408_dp, 46.78330526703850012_dp, & & 7.79040894364469949_dp, 47.72115423047301164_dp, & & 7.32476598915540844_dp, 48.61105462603097038_dp, & & 6.54204071489340944_dp, 49.40147266886311428_dp, & & 5.47559260124316793_dp, 50.04417716102324221_dp, & & 4.18315343237871584_dp, 50.49806364409691639_dp, & & 2.74458547194322389_dp, 50.73299668020899134_dp, & & 1.25541452805676457_dp, 50.73299668020899134_dp, & & -0.18315343237872406_dp, 50.49806364409691639_dp, & & -1.47559260124317815_dp, 50.04417716102324221_dp, & & -2.54204071489341921_dp, 49.40147266886311428_dp, & & -3.32476598915541599_dp, 48.61105462603097038_dp, & & -3.79040894364470748_dp, 47.72115423047301164_dp, & & -3.92867515230085296_dp, 46.78330526703850012_dp, & & -3.74887351496657928_dp, 45.84898874281991965_dp, & & -3.27568782798168812_dp, 44.96693492678289061_dp, & & -2.54514794659826427_dp, 44.18107605134951399_dp, & & -1.60127364516100812_dp, 43.52904513460186564_dp, & & -0.49349321557292075_dp, 43.04109625964867547_dp, & & 0.72526667927164967_dp, 42.73934376403467184_dp, & & 1.99999999999999556_dp, 40.31236211945285675_dp, & & 3.63376292032076975_dp, 40.42261301497919845_dp, & & 5.22173788476928014_dp, 40.75019560382368411_dp, & & 6.71826031811739099_dp, 41.28563184510834105_dp, & & 8.07794770476515467_dp, 42.01322983391511201_dp, & & 9.25594876339864392_dp, 42.91123363953519743_dp, & & 10.20837670733705771_dp, 43.95202523375940729_dp, & & 10.89307303713296271_dp, 45.10238649522262477_dp, & & 11.27090590027328432_dp, 46.32385288536406875_dp, & & 11.30784824781773779_dp, 47.57322688923063225_dp, & & 10.97806564551987840_dp, 48.80336562297891589_dp, & & 10.26810804950859612_dp, 49.96440199105025926_dp, & & 9.18197003975669723_dp, 51.00557820511744467_dp, & & 7.74622775989939338_dp, 51.87782553848720823_dp, & & 6.01378910799337074_dp, 52.53707313311167582_dp, & & 4.06435502534209725_dp, 52.94800110445672203_dp, & & 1.99999999999999600_dp, 53.08763788054714894_dp, & & -0.06435502534210560_dp, 52.94800110445672203_dp, & & -2.01378910799337962_dp, 52.53707313311167582_dp, & & -3.74622775989940182_dp, 51.87782553848720823_dp, & & -5.18197003975670256_dp, 51.00557820511744467_dp, & & -6.26810804950860057_dp, 49.96440199105025926_dp, & & -6.97806564551988551_dp, 48.80336562297891589_dp, & & -7.30784824781774667_dp, 47.57322688923063225_dp, & & -7.27090590027329498_dp, 46.32385288536406875_dp, & & -6.89307303713297248_dp, 45.10238649522262477_dp, & & -6.20837670733706481_dp, 43.95202523375940729_dp, & & -5.25594876339865458_dp, 42.91123363953519743_dp, & & -4.07794770476516355_dp, 42.01322983391511201_dp, & & -2.71826031811739988_dp, 41.28563184510834105_dp, & & -1.22173788476928813_dp, 40.75019560382368411_dp, & & 0.36623707967922192_dp, 40.42261301497919845_dp, & & 1.99999999999999600_dp, 37.95945956416753120_dp, & & 3.72994195085756930_dp, 38.05275970261258323_dp, & & 5.43190111561111433_dp, 38.33106975566338548_dp, & & 7.07783882564464850_dp, 38.78962916423042628_dp, & & 8.63959771096107509_dp, 39.42053684981195261_dp, & & 10.08882962579568954_dp, 40.21279467492543347_dp, & & 11.39692139773956114_dp, 41.15235690381517486_dp, & & 12.53494002107632888_dp, 42.22217718966415845_dp, & & 13.47363915243209043_dp, 43.40224634648851065_dp, & & 14.18359521918176469_dp, 44.66961909752426152_dp, & & 14.63557388477568644_dp, 45.99843704779454612_dp, & & 14.80126342211737089_dp, 47.35996933207513848_dp, & & 14.65454278958190670_dp, 48.72271255422421632_dp, & & 14.17346129570704250_dp, 50.05261748260089405_dp, & & 13.34306219743110233_dp, 51.31353838046217675_dp, & & 12.15903975007321236_dp, 52.46802323745790630_dp, & & 10.63193584333677499_dp, 53.47856301011842106_dp, & & 8.79116029542177024_dp, 54.30937174533021761_dp, & & 6.68766853851381526_dp, 54.92865613654313961_dp, & & 4.39391037845720334_dp, 55.31115535850048559_dp, & & 1.99999999999999600_dp, 55.44054043583248159_dp, & & -0.39391037845721066_dp, 55.31115535850048559_dp, & & -2.68766853851382415_dp, 54.92865613654313961_dp, & & -4.79116029542177646_dp, 54.30937174533021761_dp, & & -6.63193584333678299_dp, 53.47856301011842106_dp, & & -8.15903975007321947_dp, 52.46802323745790630_dp, & & -9.34306219743110766_dp, 51.31353838046217675_dp, & & -10.17346129570704960_dp, 50.05261748260089405_dp, & & -10.65454278958191559_dp, 48.72271255422421632_dp, & & -10.80126342211738155_dp, 47.35996933207513848_dp, & & -10.63557388477569354_dp, 45.99843704779454612_dp, & & -10.18359521918177357_dp, 44.66961909752426152_dp, & & -9.47363915243210286_dp, 43.40224634648851065_dp, & & -8.53494002107633776_dp, 42.22217718966415845_dp, & & -7.39692139773957003_dp, 41.15235690381517486_dp, & & -6.08882962579569842_dp, 40.21279467492543347_dp, & & -4.63959771096108220_dp, 39.42053684981195261_dp, & & -3.07783882564465516_dp, 38.78962916423042628_dp, & & -1.43190111561112299_dp, 38.33106975566338548_dp, & & 0.27005804914242265_dp, 38.05275970261258323_dp, & & 1.99999999999999600_dp, 35.57084905987729684_dp, & & 3.89475329148972449_dp, 35.66164776728690811_dp, & & 5.76748883913266752_dp, 35.93289207465146262_dp, & & 7.59612881396671380_dp, 36.38113516165864070_dp, & & 9.35846198524136241_dp, 37.00065953311393940_dp, & & 11.03204592267791284_dp, 37.78351258180431671_dp, & & 12.59407748901272228_dp, 38.71954642363609622_dp, & & 14.02123063562953753_dp, 39.79645326718445375_dp, & & 15.28946940417034384_dp, 40.99978674800681233_dp, & & 16.37385639149118433_dp, 42.31296002482778107_dp, & & 17.24839405994832120_dp, 43.71721334442865725_dp, & & 17.88595991605807711_dp, 45.19154778206701195_dp, & & 18.25842850603561374_dp, 46.71262882888417067_dp, & & 18.33711398818940452_dp, 48.25467471095996785_dp, & & 18.09371358173445188_dp, 49.78936139305309894_dp, & & 17.50197280996727400_dp, 51.28580055994029152_dp, & & 16.54030106040206149_dp, 52.71067834344860614_dp, & & 15.19549149366584473_dp, 54.02867680201524081_dp, & & 13.46747450557555048_dp, 55.20332460427978560_dp, & & 11.37460140494845717_dp, 56.19841400024800038_dp, & & 8.95834334902436602_dp, 56.98004544675784899_dp, & & 6.28571777452580616_dp, 57.51919556269155720_dp, & & 3.44765611568870733_dp, 57.79446760315521914_dp, & & 0.55234388431128512_dp, 57.79446760315521914_dp, & & -2.28571777452581371_dp, 57.51919556269155720_dp, & & -4.95834334902437313_dp, 56.98004544675784899_dp, & & -7.37460140494846428_dp, 56.19841400024800038_dp, & & -9.46747450557555759_dp, 55.20332460427978560_dp, & & -11.19549149366585006_dp, 54.02867680201524081_dp, & & -12.54030106040206860_dp, 52.71067834344860614_dp, & & -13.50197280996728288_dp, 51.28580055994029152_dp, & & -14.09371358173445721_dp, 49.78936139305309894_dp, & & -14.33711398818941340_dp, 48.25467471095996785_dp, & & -14.25842850603561907_dp, 46.71262882888417067_dp, & & -13.88595991605808244_dp, 45.19154778206701195_dp, & & -13.24839405994833008_dp, 43.71721334442865725_dp, & & -12.37385639149119498_dp, 42.31296002482778107_dp, & & -11.28946940417035272_dp, 40.99978674800681233_dp, & & -10.02123063562954464_dp, 39.79645326718445375_dp, & & -8.59407748901273116_dp, 38.71954642363609622_dp, & & -7.03204592267792172_dp, 37.78351258180431671_dp, & & -5.35846198524136952_dp, 37.00065953311393940_dp, & & -3.59612881396672091_dp, 36.38113516165864070_dp, & & -1.76748883913267574_dp, 35.93289207465146262_dp, & & 0.10524670851026705_dp, 35.66164776728690811_dp, & & 1.99999999999999600_dp, 33.13704588354342917_dp, & & 4.09718755711548610_dp, 33.23124463343540214_dp, & & 6.17473678976558027_dp, 33.51283479810019372_dp, & & 8.21297051898842767_dp, 33.97880825794451454_dp, & & 10.19211732580419394_dp, 34.62418375193038855_dp, & & 12.09222434325374529_dp, 35.44205010622848562_dp, & & 13.89302536639832120_dp, 36.42361680044041350_dp, & & 15.57375501535205942_dp, 37.55826279697889447_dp, & & 17.11290455740948246_dp, 38.83357308646102268_dp, & & 18.48792157318536056_dp, 40.23535159216240942_dp, & & 19.67486490585671888_dp, 41.74759899443786537_dp, & & 20.64804001511417653_dp, 43.35244484209054860_dp, & & 21.37966075266355404_dp, 45.03002540480565585_dp, & & 21.83961562141898582_dp, 46.75830290430587155_dp, & & 21.99546453290828296_dp, 48.51282952087984768_dp, & & 21.81285996059914467_dp, 50.26647329190694080_dp, & & 21.25667322722224029_dp, 51.98914605534769606_dp, & & 20.29319722451073105_dp, 53.64760968250063655_dp, & & 18.89384545876525578_dp, 55.20548791890712437_dp, & & 17.04067793585433321_dp, 56.62367212326139310_dp, & & 14.73370655373201643_dp, 57.86135900031617751_dp, & & 11.99912115224313958_dp, 58.87795079119066344_dp, & & 8.89637618755807047_dp, 59.63591573610307250_dp, & & 5.52099396835736211_dp, 60.10440004572244277_dp, & & 1.99999999999999645_dp, 60.26295411645657651_dp, & & -1.52099396835736900_dp, 60.10440004572244277_dp, & & -4.89637618755807846_dp, 59.63591573610307250_dp, & & -7.99912115224314757_dp, 58.87795079119066344_dp, & & -10.73370655373202531_dp, 57.86135900031617751_dp, & & -13.04067793585434032_dp, 56.62367212326139310_dp, & & -14.89384545876526467_dp, 55.20548791890712437_dp, & & -16.29319722451073460_dp, 53.64760968250063655_dp, & & -17.25667322722224739_dp, 51.98914605534769606_dp, & & -17.81285996059915178_dp, 50.26647329190694080_dp, & & -17.99546453290829362_dp, 48.51282952087984768_dp, & & -17.83961562141899648_dp, 46.75830290430587155_dp, & & -17.37966075266356114_dp, 45.03002540480565585_dp, & & -16.64804001511417653_dp, 43.35244484209054860_dp, & & -15.67486490585672598_dp, 41.74759899443786537_dp, & & -14.48792157318536944_dp, 40.23535159216240942_dp, & & -13.11290455740949135_dp, 38.83357308646102268_dp, & & -11.57375501535207007_dp, 37.55826279697889447_dp, & & -9.89302536639833185_dp, 36.42361680044041350_dp, & & -8.09222434325375239_dp, 35.44205010622848562_dp, & & -6.19211732580420193_dp, 34.62418375193038855_dp, & & -4.21297051898843566_dp, 33.97880825794451454_dp, & & -2.17473678976558960_dp, 33.51283479810019372_dp, & & -0.09718755711549353_dp, 33.23124463343540214_dp, & & 1.99999999999999600_dp, 30.64757019281734074_dp, & & 3.92669240244598505_dp, 30.71678191817960268_dp, & & 5.84270801296843700_dp, 30.92395598105918353_dp, & & 7.73736939611796171_dp, 31.26771342119434749_dp, & & 9.59999239238874047_dp, 31.74577019145994683_dp, & & 11.41986936545086984_dp, 32.35495718584155611_dp, & & 13.18623692372253586_dp, 33.09124574576952682_dp, & & 14.88822378743806851_dp, 33.94977626735131793_dp, & & 16.51477509868480453_dp, 34.92488704934376642_dp, & & 18.05455017155122732_dp, 36.01014014726423795_dp, & & 19.49579145727240714_dp, 37.19834072287546434_dp, & & 20.82616341777591984_dp, 38.48154618161253637_dp, & & 22.03256121225549435_dp, 39.85106125278488065_dp, & & 23.10089088783405131_dp, 41.29741507659190347_dp, & & 24.01582559662802652_dp, 42.81031632956308641_dp, & & 24.76054697060166987_dp, 44.37858250245571412_dp, & & 25.31648826424518717_dp, 45.99003977573201496_dp, & & 25.66310777053267245_dp, 47.63139077670033572_dp, & & 25.77773936992416637_dp, 49.28804930270956675_dp, & & 25.63559425749198795_dp, 50.94394459368972150_dp, & & 25.21002592824931199_dp, 52.58130404832083116_dp, & & 24.47321926655621738_dp, 54.18043392376822709_dp, & & 23.39751805719552991_dp, 55.71953428872550518_dp, & & 21.95764487214750105_dp, 57.17460858105235388_dp, & & 20.13405311103923623_dp, 58.51955862209560166_dp, & & 17.91751559979281083_dp, 59.72658657388572578_dp, & & 15.31471471060360479_dp, 60.76704034614789407_dp, & & 12.35401191716921154_dp, 61.61281120273996237_dp, & & 9.08984714396560278_dp, 62.23829052070698253_dp, & & 5.60371952785031446_dp, 62.62270582496653049_dp, & & 1.99999999999999556_dp, 62.75242980718265784_dp, & & -1.60371952785032224_dp, 62.62270582496653049_dp, & & -5.08984714396560900_dp, 62.23829052070698253_dp, & & -8.35401191716922042_dp, 61.61281120273996237_dp, & & -11.31471471060361367_dp, 60.76704034614789407_dp, & & -13.91751559979281616_dp, 59.72658657388572578_dp, & & -16.13405311103924689_dp, 58.51955862209560166_dp, & & -17.95764487214750815_dp, 57.17460858105235388_dp, & & -19.39751805719554056_dp, 55.71953428872550518_dp, & & -20.47321926655623159_dp, 54.18043392376822709_dp, & & -21.21002592824931554_dp, 52.58130404832083116_dp, & & -21.63559425749199860_dp, 50.94394459368972150_dp, & & -21.77773936992418058_dp, 49.28804930270956675_dp, & & -21.66310777053267955_dp, 47.63139077670033572_dp, & & -21.31648826424519783_dp, 45.99003977573201496_dp, & & -20.76054697060167697_dp, 44.37858250245571412_dp, & & -20.01582559662803362_dp, 42.81031632956308641_dp, & & -19.10089088783406197_dp, 41.29741507659190347_dp, & & -18.03256121225550146_dp, 39.85106125278488065_dp, & & -16.82616341777592694_dp, 38.48154618161253637_dp, & & -15.49579145727241603_dp, 37.19834072287546434_dp, & & -14.05455017155123620_dp, 36.01014014726423795_dp, & & -12.51477509868481341_dp, 34.92488704934376642_dp, & & -10.88822378743808095_dp, 33.94977626735131793_dp, & & -9.18623692372254474_dp, 33.09124574576952682_dp, & & -7.41986936545088049_dp, 32.35495718584155611_dp, & & -5.59999239238874935_dp, 31.74577019145994683_dp, & & -3.73736939611796970_dp, 31.26771342119434749_dp, & & -1.84270801296844478_dp, 30.92395598105918353_dp, & & 0.07330759755400772_dp, 30.71678191817960268_dp, & & 1.99999999999999600_dp, 28.09100109536852230_dp, & & 4.16843782661930362_dp, 28.16889262543881145_dp, & & 6.32564933647368566_dp, 28.40205318377053345_dp, & & 8.46042949045145143_dp, 28.78894734383437992_dp, & & 10.56160821625925195_dp, 29.32703776203888069_dp, & & 12.61804920168178690_dp, 30.01281590885307438_dp, & & 14.61862699669389087_dp, 30.84184157026976436_dp, & & 16.55217628249286577_dp, 31.80878785141789322_dp, & & 18.40740784814350306_dp, 32.90748779465315010_dp, & & 20.17278641740253420_dp, 34.13097826525053335_dp, & & 21.83636590786353437_dp, 35.47153642700349963_dp, & & 23.38557795842459797_dp, 36.92070387379718710_dp, & & 24.80696969977402233_dp, 38.46929323359992026_dp, & & 26.08588698105331716_dp, 40.10737174655498194_dp, & & 27.20610004359020806_dp, 41.82421587722975431_dp, & & 28.14937074714974585_dp, 43.60823042135866956_dp, & & 28.89496528133906494_dp, 45.44682484300304282_dp, & & 29.41912613625676443_dp, 47.32623888788154431_dp, & & 29.69453569648833735_dp, 49.23130925676766623_dp, & & 29.68983696232584180_dp, 51.14517010976478417_dp, & & 29.36933297612858240_dp, 53.04888396821036167_dp, & & 28.69307609487012201_dp, 54.92100894435518654_dp, & & 27.61769060836896728_dp, 56.73712764036463341_dp, & & 26.09844461997003151_dp, 58.46939890910893922_dp, & & 24.09325961331439458_dp, 60.08625295311978931_dp, & & 21.56939664790981936_dp, 61.55243525845713748_dp, & & 18.51323316925390117_dp, 62.82970044453450953_dp, & & 14.94248311853021427_dp, 63.87850997409253040_dp, & & 10.91821393310038246_dp, 64.66099291349783584_dp, & & 6.55165837938126572_dp, 65.14506774788591770_dp, & & 1.99999999999999645_dp, 65.30899890463147983_dp, & & -2.55165837938127327_dp, 65.14506774788591770_dp, & & -6.91821393310038868_dp, 64.66099291349783584_dp, & & -10.94248311853022138_dp, 63.87850997409253040_dp, & & -14.51323316925390650_dp, 62.82970044453450953_dp, & & -17.56939664790982647_dp, 61.55243525845713748_dp, & & -20.09325961331440169_dp, 60.08625295311978931_dp, & & -22.09844461997003862_dp, 58.46939890910893922_dp, & & -23.61769060836897793_dp, 56.73712764036463341_dp, & & -24.69307609487013266_dp, 54.92100894435518654_dp, & & -25.36933297612859306_dp, 53.04888396821036167_dp, & & -25.68983696232585245_dp, 51.14517010976478417_dp, & & -25.69453569648835156_dp, 49.23130925676766623_dp, & & -25.41912613625677508_dp, 47.32623888788154431_dp, & & -24.89496528133907205_dp, 45.44682484300304282_dp, & & -24.14937074714975651_dp, 43.60823042135866956_dp, & & -23.20610004359021872_dp, 41.82421587722975431_dp, & & -22.08588698105333137_dp, 40.10737174655498194_dp, & & -20.80696969977403299_dp, 38.46929323359992026_dp, & & -19.38557795842460862_dp, 36.92070387379718710_dp, & & -17.83636590786354148_dp, 35.47153642700349963_dp, & & -16.17278641740254486_dp, 34.13097826525053335_dp, & & -14.40740784814351372_dp, 32.90748779465315010_dp, & & -12.55217628249287110_dp, 31.80878785141789322_dp, & & -10.61862699669389798_dp, 30.84184157026976436_dp, & & -8.61804920168179400_dp, 30.01281590885307438_dp, & & -6.56160821625925994_dp, 29.32703776203888069_dp, & & -4.46042949045146031_dp, 28.78894734383437992_dp, & & -2.32564933647369187_dp, 28.40205318377053345_dp, & & -0.16843782661931131_dp, 28.16889262543881145_dp, & & 1.99999999999999600_dp, 25.45485763546421154_dp, & & 4.25579267831604824_dp, 25.53081635683327377_dp, & & 6.50189378344491864_dp, 25.75824904385734015_dp, & & 8.72865139696445169_dp, 26.13583181560235502_dp, & & 10.92648650789847409_dp, 26.66137960320710931_dp, & & 13.08591371200912867_dp, 27.33187675649334025_dp, & & 15.19754366142444368_dp, 28.14351701208744316_dp, & & 17.25206214192900589_dp, 29.09175016726920759_dp, & & 19.24018123567453742_dp, 30.17133232687284305_dp, & & 21.15255849246134545_dp, 31.37637626233231813_dp, & & 22.97968026705780886_dp, 32.70039821822412307_dp, & & 24.71170528699354918_dp, 34.13635737784334623_dp, & & 26.33826403031282837_dp, 35.67668409468497259_dp, & & 27.84820859564654683_dp, 37.31329283883029291_dp, & & 29.22930648122764197_dp, 39.03757551632935474_dp, & & 30.46787020033522708_dp, 40.84037031204417190_dp, & & 31.54831327014242248_dp, 42.71190039889389567_dp, & & 32.45262246278107199_dp, 44.64167567492752653_dp, & & 33.15973753507462618_dp, 46.61834908938177335_dp, & & 33.64483525740958214_dp, 48.62951713112133945_dp, & & 33.87852862440772839_dp, 50.66145188613276673_dp, & & 33.82602201650912122_dp, 52.69875031809839783_dp, & & 33.44632120220701665_dp, 54.72388646563504011_dp, & & 32.69170289014788011_dp, 56.71665696607986717_dp, & & 31.50782877801381332_dp, 58.65352526302899605_dp, & & 29.83517004248913196_dp, 60.50690475312735828_dp, & & 27.61278608277912383_dp, 62.24449065808524040_dp, & & 24.78586461017365750_dp, 63.82887077001763032_dp, & & 21.31841683118042496_dp, 65.21781824976642383_dp, & & 17.21138564172178320_dp, 66.36584339331807314_dp, & & 12.52326664933801226_dp, 67.22759281764989225_dp, & & 7.38533547682125846_dp, 67.76325974975715383_dp, & & 1.99999999999999600_dp, 67.94514236453578349_dp, & & -3.38533547682126690_dp, 67.76325974975715383_dp, & & -8.52326664933801936_dp, 67.22759281764989225_dp, & & -13.21138564172179031_dp, 66.36584339331807314_dp, & & -17.31841683118043562_dp, 65.21781824976642383_dp, & & -20.78586461017366460_dp, 63.82887077001763032_dp, & & -23.61278608277912738_dp, 62.24449065808524040_dp, & & -25.83517004248913906_dp, 60.50690475312735828_dp, & & -27.50782877801382398_dp, 58.65352526302899605_dp, & & -28.69170289014789077_dp, 56.71665696607986717_dp, & & -29.44632120220701665_dp, 54.72388646563504011_dp, & & -29.82602201650912477_dp, 52.69875031809839783_dp, & & -29.87852862440772839_dp, 50.66145188613276673_dp, & & -29.64483525740958925_dp, 48.62951713112133945_dp, & & -29.15973753507462618_dp, 46.61834908938177335_dp, & & -28.45262246278107554_dp, 44.64167567492752653_dp, & & -27.54831327014242959_dp, 42.71190039889389567_dp, & & -26.46787020033523774_dp, 40.84037031204417190_dp, & & -25.22930648122764907_dp, 39.03757551632935474_dp, & & -23.84820859564655038_dp, 37.31329283883029291_dp, & & -22.33826403031283903_dp, 35.67668409468497259_dp, & & -20.71170528699355629_dp, 34.13635737784334623_dp, & & -18.97968026705781242_dp, 32.70039821822412307_dp, & & -17.15255849246134900_dp, 31.37637626233231813_dp, & & -15.24018123567454097_dp, 30.17133232687284305_dp, & & -13.25206214192901299_dp, 29.09175016726920759_dp, & & -11.19754366142445434_dp, 28.14351701208744316_dp, & & -9.08591371200913933_dp, 27.33187675649334025_dp, & & -6.92648650789848030_dp, 26.66137960320710931_dp, & & -4.72865139696445969_dp, 26.13583181560235502_dp, & & -2.50189378344492708_dp, 25.75824904385734015_dp, & & -0.25579267831605540_dp, 25.53081635683327377_dp, & & 1.99999999999999556_dp, 22.72540973598210456_dp, & & 4.47629214130109165_dp, 22.80878975038074330_dp, & & 6.94242093509331060_dp, 23.05843251151436846_dp, & & 9.38829543151647350_dp, 23.47285525677742868_dp, & & 11.80396120930039139_dp, 24.04961640006232315_dp, & & 14.17964838758363122_dp, 24.78535803035031293_dp, & & 16.50579641562137567_dp, 25.67586152120415477_dp, & & 18.77304947679039060_dp, 26.71611272875198395_dp, & & 20.97221728412494102_dp, 27.90037270541385439_dp, & & 23.09419678103115103_dp, 29.22224954465700009_dp, & & 25.12985059268006793_dp, 30.67476685924402702_dp, & & 27.06983782140639860_dp, 32.25042441190934994_dp, & & 28.90439178940929921_dp, 33.94124647006630369_dp, & & 30.62303747247054275_dp, 35.73881343521246379_dp, & & 32.21423852322426029_dp, 37.63427208010365632_dp, & & 33.66495985223924237_dp, 39.61831917500946787_dp, & & 34.96012666362174315_dp, 41.68115224019865650_dp, & & 36.08195470333167520_dp, 43.81237943576640248_dp, & & 37.00911969333093765_dp, 46.00087796034757304_dp, & & 37.71572773969587189_dp, 48.23458650173874673_dp, & & 38.17004600591188535_dp, 50.50021197832979425_dp, & & 38.33296113360039925_dp, 52.78282385843581892_dp, & & 38.15616674923441565_dp, 55.06530104282313687_dp, & & 37.58017141013022666_dp, 57.32758829402004608_dp, & & 36.53242307522426557_dp, 59.54571651024583190_dp, & & 34.92626834358740950_dp, 61.69055635034530383_dp, & & 32.66225471925319113_dp, 63.72633555655123416_dp, & & 29.63456841105837825_dp, 65.60910965714572285_dp, & & 25.74696599568290623_dp, 67.28571148577094618_dp, & & 20.94303945184942251_dp, 68.69426490502790728_dp, & & 15.25145387811900122_dp, 69.76794994271870110_dp, & & 8.83336207267744022_dp, 70.44359392189112157_dp, & & 1.99999999999999600_dp, 70.67459026401789401_dp, & & -4.83336207267745088_dp, 70.44359392189112157_dp, & & -11.25145387811901188_dp, 69.76794994271870110_dp, & & -16.94303945184943316_dp, 68.69426490502790728_dp, & & -21.74696599568291333_dp, 67.28571148577094618_dp, & & -25.63456841105838890_dp, 65.60910965714572285_dp, & & -28.66225471925320534_dp, 63.72633555655123416_dp, & & -30.92626834358741306_dp, 61.69055635034530383_dp, & & -32.53242307522427978_dp, 59.54571651024583190_dp, & & -33.58017141013024087_dp, 57.32758829402004608_dp, & & -34.15616674923442986_dp, 55.06530104282313687_dp, & & -34.33296113360040636_dp, 52.78282385843581892_dp, & & -34.17004600591189956_dp, 50.50021197832979425_dp, & & -33.71572773969587900_dp, 48.23458650173874673_dp, & & -33.00911969333094476_dp, 46.00087796034757304_dp, & & -32.08195470333168231_dp, 43.81237943576640248_dp, & & -30.96012666362175025_dp, 41.68115224019865650_dp, & & -29.66495985223924237_dp, 39.61831917500946787_dp, & & -28.21423852322426740_dp, 37.63427208010365632_dp, & & -26.62303747247054631_dp, 35.73881343521246379_dp, & & -24.90439178940930276_dp, 33.94124647006630369_dp, & & -23.06983782140640216_dp, 32.25042441190934994_dp, & & -21.12985059268007149_dp, 30.67476685924402702_dp, & & -19.09419678103115459_dp, 29.22224954465700009_dp, & & -16.97221728412494102_dp, 27.90037270541385439_dp, & & -14.77304947679039948_dp, 26.71611272875198395_dp, & & -12.50579641562138455_dp, 25.67586152120415477_dp, & & -10.17964838758364365_dp, 24.78535803035031293_dp, & & -7.80396120930039938_dp, 24.04961640006232315_dp, & & -5.38829543151647972_dp, 23.47285525677742868_dp, & & -2.94242093509332037_dp, 23.05843251151436846_dp, & & -0.47629214130110020_dp, 22.80878975038074330_dp, & & 1.99999999999999600_dp, 19.88744176867359670_dp, & & 4.69641128035122435_dp, 19.97822987242788884_dp, & & 7.38211423303385939_dp, 20.25003331964319031_dp, & & 10.04651538477556549_dp, 20.70118176192879389_dp, & & 12.67924035399200378_dp, 21.32893128539882710_dp, & & 15.27021753707410312_dp, 22.12952164603582617_dp, & & 17.80973254683686946_dp, 23.09825113914619976_dp, & & 20.28844626261447459_dp, 24.22956442487017270_dp, & & 22.69737092792362532_dp, 25.51714801636046204_dp, & & 25.02780000980339636_dp, 26.95402789495272344_dp, & & 27.27118821325056430_dp, 28.53266378578365448_dp, & & 29.41897786066868292_dp, 30.24503490367624536_dp, & & 31.46236656888654792_dp, 32.08271233019573287_dp, & & 33.39200856069422940_dp, 34.03691345955112979_dp, & & 35.19763777135730720_dp, 36.09853399317180589_dp, & & 36.86759479425492714_dp, 38.25815259440047811_dp, & & 38.38823113470312620_dp, 40.50600232791481403_dp, & & 39.74315247327933776_dp, 42.83190113242096686_dp, & & 40.91224672040999621_dp, 45.22513042719018728_dp, & & 41.87042151784342536_dp, 47.67424596736638165_dp, & & 42.58594887503602422_dp, 50.16679739569313057_dp, & & 43.01828312042781022_dp, 52.68892136253921876_dp, & & 43.11518954384546731_dp, 55.22475594928482678_dp, & & 42.80901819915838757_dp, 57.75559962131574565_dp, & & 42.01204327449660525_dp, 60.25870541573158334_dp, & & 40.61111913565843423_dp, 62.70556553816149403_dp, & & 38.46283411458643542_dp, 65.05952649321666570_dp, & & 35.39257932570031784_dp, 67.27265449907143591_dp, & & 31.20553396293055215_dp, 69.28213523998356038_dp, & & 25.72462936947633239_dp, 71.00751554077180572_dp, & & 18.87413258170036201_dp, 72.35214877025040892_dp, & & 10.80643587234801295_dp, 73.21444539538327945_dp, & & 1.99999999999999600_dp, 73.51255823132640899_dp, & & -6.80643587234802006_dp, 73.21444539538327945_dp, & & -14.87413258170037622_dp, 72.35214877025040892_dp, & & -21.72462936947634304_dp, 71.00751554077180572_dp, & & -27.20553396293055570_dp, 69.28213523998356038_dp, & & -31.39257932570032139_dp, 67.27265449907143591_dp, & & -34.46283411458643542_dp, 65.05952649321666570_dp, & & -36.61111913565844134_dp, 62.70556553816149403_dp, & & -38.01204327449661946_dp, 60.25870541573158334_dp, & & -38.80901819915838047_dp, 57.75559962131574565_dp, & & -39.11518954384547442_dp, 55.22475594928482678_dp, & & -39.01828312042781022_dp, 52.68892136253921876_dp, & & -38.58594887503603843_dp, 50.16679739569313057_dp, & & -37.87042151784342536_dp, 47.67424596736638165_dp, & & -36.91224672041000332_dp, 45.22513042719018728_dp, & & -35.74315247327935197_dp, 42.83190113242096686_dp, & & -34.38823113470313331_dp, 40.50600232791481403_dp, & & -32.86759479425493424_dp, 38.25815259440047811_dp, & & -31.19763777135731431_dp, 36.09853399317180589_dp, & & -29.39200856069424717_dp, 34.03691345955112979_dp, & & -27.46236656888655858_dp, 32.08271233019573287_dp, & & -25.41897786066869003_dp, 30.24503490367624536_dp, & & -23.27118821325056786_dp, 28.53266378578365448_dp, & & -21.02780000980340702_dp, 26.95402789495272344_dp, & & -18.69737092792362887_dp, 25.51714801636046204_dp, & & -16.28844626261448170_dp, 24.22956442487017270_dp, & & -13.80973254683687301_dp, 23.09825113914619976_dp, & & -11.27021753707411023_dp, 22.12952164603582617_dp, & & -8.67924035399200910_dp, 21.32893128539882710_dp, & & -6.04651538477557171_dp, 20.70118176192879389_dp, & & -3.38211423303386916_dp, 20.25003331964319031_dp, & & -0.69641128035123179_dp, 19.97822987242788884_dp, & & 1.99999999999999556_dp, 16.92397162343523931_dp, & & 4.91799021933642688_dp, 17.02221659443387480_dp, & & 7.82460955068693664_dp, 17.31631470128239059_dp, & & 10.70865569654628935_dp, 17.80437171431731258_dp, & & 13.55924980839402139_dp, 18.48328342761256238_dp, & & 16.36596498789072385_dp, 19.34881113120014362_dp, & & 19.11891780975726363_dp, 20.39568010883170501_dp, & & 21.80881477377849720_dp, 21.61769490666709714_dp, & & 24.42694818201795570_dp, 23.00786444863284430_dp, & & 26.96513811238917668_dp, 24.55852997196867804_dp, & & 29.41561850057672345_dp, 26.26148912292403637_dp, & & 31.77086550998261671_dp, 28.10811023218007065_dp, & & 34.02336509370287132_dp, 30.08943159705432180_dp, & & 36.16531367916817175_dp, 32.19624133969532664_dp, & & 38.18824090805679106_dp, 34.41913389699745807_dp, & & 40.08253581911941410_dp, 36.74853923728758076_dp, & & 41.83684688715484867_dp, 39.17472027603687934_dp, & & 43.43731045663169255_dp, 41.68773239540036002_dp, & & 44.86653894639825779_dp, 44.27733603455973110_dp, & & 46.10226597817306526_dp, 46.93284831506371546_dp, & & 47.11549450115907689_dp, 49.64291142290011294_dp, & & 47.86791757533286074_dp, 52.39514197266055362_dp, & & 48.30826862884845241_dp, 55.17560336970880286_dp, & & 48.36709840828364548_dp, 57.96800639328736793_dp, & & 47.94927569193657035_dp, 60.75248226909388904_dp, & & 46.92334661203305046_dp, 63.50367340927639503_dp, & & 45.10709162054252630_dp, 66.18773615332521842_dp, & & 42.25028545833759352_dp, 68.75766348420629015_dp, & & 38.02194152863378918_dp, 71.14627956201685777_dp, & & 32.02735732207737840_dp, 73.25702648451857613_dp, & & 23.91726594386999949_dp, 74.95625177955801632_dp, & & 13.67376613190776347_dp, 76.07990221077976400_dp, & & 1.99999999999999600_dp, 76.47602837656475572_dp, & & -9.67376613190777235_dp, 76.07990221077976400_dp, & & -19.91726594387001015_dp, 74.95625177955801632_dp, & & -28.02735732207739261_dp, 73.25702648451857613_dp, & & -34.02194152863380339_dp, 71.14627956201685777_dp, & & -38.25028545833759352_dp, 68.75766348420629015_dp, & & -41.10709162054251919_dp, 66.18773615332521842_dp, & & -42.92334661203306467_dp, 63.50367340927639503_dp, & & -43.94927569193657746_dp, 60.75248226909388904_dp, & & -44.36709840828364548_dp, 57.96800639328736793_dp, & & -44.30826862884845241_dp, 55.17560336970880286_dp, & & -43.86791757533286784_dp, 52.39514197266055362_dp, & & -43.11549450115907689_dp, 49.64291142290011294_dp, & & -42.10226597817306526_dp, 46.93284831506371546_dp, & & -40.86653894639826490_dp, 44.27733603455973110_dp, & & -39.43731045663169965_dp, 41.68773239540036002_dp, & & -37.83684688715485578_dp, 39.17472027603687934_dp, & & -36.08253581911941410_dp, 36.74853923728758076_dp, & & -34.18824090805679816_dp, 34.41913389699745807_dp, & & -32.16531367916818596_dp, 32.19624133969532664_dp, & & -30.02336509370288908_dp, 30.08943159705432180_dp, & & -27.77086550998262737_dp, 28.10811023218007065_dp, & & -25.41561850057673766_dp, 26.26148912292403637_dp, & & -22.96513811238918734_dp, 24.55852997196867804_dp, & & -20.42694818201796636_dp, 23.00786444863284430_dp, & & -17.80881477377850075_dp, 21.61769490666709714_dp, & & -15.11891780975727251_dp, 20.39568010883170501_dp, & & -12.36596498789073095_dp, 19.34881113120014362_dp, & & -9.55924980839402849_dp, 18.48328342761256238_dp, & & -6.70865569654630001_dp, 17.80437171431731258_dp, & & -3.82460955068694464_dp, 17.31631470128239059_dp, & & -0.91799021933643477_dp, 17.02221659443387480_dp, & & 1.99999999999999556_dp, 13.81592192037676625_dp, & & 5.14299451095195792_dp, 13.92173872937387102_dp, & & 8.27378779825514954_dp, 14.23846093632396581_dp, & & 11.38041466390652978_dp, 14.76392503362292530_dp, & & 14.45136393475438119_dp, 15.49459412897926924_dp, & & 17.47576217013061139_dp, 16.42565612762181004_dp, & & 20.44350999615491205_dp, 17.55115134744557182_dp, & & 23.34536199373425092_dp, 18.86412113035556004_dp, & & 26.17294518233925160_dp, 20.35676830949395466_dp, & & 28.91871466466540141_dp, 22.02062053097340311_dp, & & 31.57584735879237314_dp, 23.84668824688734645_dp, & & 34.13807554901531205_dp, 25.82561045671564415_dp, & & 36.59946098845733786_dp, 27.94778271439298933_dp, & & 38.95410730493897233_dp, 30.20346328869342756_dp, & & 41.19580324741197330_dp, 32.58285444844916867_dp, & & 43.31758136998609388_dp, 35.07615645611910082_dp, & & 45.31116510791174079_dp, 37.67359181604234664_dp, & & 47.16626004327477517_dp, 40.36539642075791789_dp, & & 48.86961926381131605_dp, 43.14177213432515856_dp, & & 50.40377244059970963_dp, 45.99279145534946167_dp, & & 51.74524369263552614_dp, 48.90823812634897649_dp, & & 52.86197695521289575_dp, 51.87735585154322138_dp, & & 53.70950811496559396_dp, 54.88845672164920586_dp, & & 54.22511448054331140_dp, 57.92830381592441569_dp, & & 54.31863539857850753_dp, 60.98111330635318694_dp, & & 53.85773173662808233_dp, 64.02688869849654907_dp, & & 52.64383991815974895_dp, 67.03853932314146391_dp, & & 50.37308749001456931_dp, 69.97672254379963874_dp, & & 46.57629367046713753_dp, 72.78039612779683409_dp, & & 40.54693285975561423_dp, 75.34972495190746145_dp, & & 31.34759503822197502_dp, 77.51867325981696411_dp, & & 18.24038894313819625_dp, 79.03039199474196153_dp, & & 1.99999999999999489_dp, 79.58407807962322522_dp, & & -14.24038894313820691_dp, 79.03039199474196153_dp, & & -27.34759503822198567_dp, 77.51867325981696411_dp, & & -36.54693285975562134_dp, 75.34972495190746145_dp, & & -42.57629367046714464_dp, 72.78039612779683409_dp, & & -46.37308749001456931_dp, 69.97672254379963874_dp, & & -48.64383991815974895_dp, 67.03853932314146391_dp, & & -49.85773173662808233_dp, 64.02688869849654907_dp, & & -50.31863539857852174_dp, 60.98111330635318694_dp, & & -50.22511448054332561_dp, 57.92830381592441569_dp, & & -49.70950811496560817_dp, 54.88845672164920586_dp, & & -48.86197695521290996_dp, 51.87735585154322138_dp, & & -47.74524369263553325_dp, 48.90823812634897649_dp, & & -46.40377244059971673_dp, 45.99279145534946167_dp, & & -44.86961926381133026_dp, 43.14177213432515856_dp, & & -43.16626004327478938_dp, 40.36539642075791789_dp, & & -41.31116510791174079_dp, 37.67359181604234664_dp, & & -39.31758136998610098_dp, 35.07615645611910082_dp, & & -37.19580324741197330_dp, 32.58285444844916867_dp, & & -34.95410730493897233_dp, 30.20346328869342756_dp, & & -32.59946098845735207_dp, 27.94778271439298933_dp, & & -30.13807554901532626_dp, 25.82561045671564415_dp, & & -27.57584735879238025_dp, 23.84668824688734645_dp, & & -24.91871466466541207_dp, 22.02062053097340311_dp, & & -22.17294518233926226_dp, 20.35676830949395466_dp, & & -19.34536199373426157_dp, 18.86412113035556004_dp, & & -16.44350999615492626_dp, 17.55115134744557182_dp, & & -13.47576217013062028_dp, 16.42565612762181004_dp, & & -10.45136393475438830_dp, 15.49459412897926924_dp, & & -7.38041466390653600_dp, 14.76392503362292530_dp, & & -4.27378779825515931_dp, 14.23846093632396581_dp, & & -1.14299451095196680_dp, 13.92173872937387102_dp, & & 1.99999999999999600_dp, 10.54173785529561158_dp, & & 5.37360609917943943_dp, 10.65531494200142149_dp, & & 8.73395228112667965_dp, 10.99520717814031556_dp, & & 12.06809948196381832_dp, 11.55892497220584403_dp, & & 15.36372621835939078_dp, 12.34240842556757656_dp, & & 18.60937983520193129_dp, 13.34015413091371016_dp, & & 21.79466590782825364_dp, 14.54537904344351951_dp, & & 24.91036566952359266_dp, 15.95020993686824440_dp, & & 27.94847769357361855_dp, 17.54588626406437513_dp, & & 30.90218550361061745_dp, 19.32296477964253967_dp, & & 33.76575655354334060_dp, 21.27151577227775903_dp, & & 36.53437968696508875_dp, 23.38130282889363798_dp, & & 39.20394762734519389_dp, 25.64194033091114022_dp, & & 41.77078829775712165_dp, 28.04302503644353095_dp, & & 44.23134383513491485_dp, 30.57423988556289629_dp, & & 46.58178882927892772_dp, 33.22542941501406233_dp, & & 48.81756887288366187_dp, 35.98664677769215103_dp, & & 50.93282539675248444_dp, 38.84817223653458029_dp, & & 52.91964996438991164_dp, 41.80050199367043007_dp, & & 54.76707508825830928_dp, 44.83430401955754974_dp, & & 56.45964870701569538_dp, 47.94033352757975308_dp, & & 57.97533567047437231_dp, 51.10929358481432416_dp, & & 59.28230228211859298_dp, 54.33161333463023368_dp, & & 60.33378767939932885_dp, 57.59709162031844443_dp, & & 61.05957379451161415_dp, 60.89430451319173443_dp, & & 61.35114187047394552_dp, 64.20957103321013903_dp, & & 61.03453256694833584_dp, 67.52503657439316953_dp, & & 59.81801935702030448_dp, 70.81486693953938527_dp, & & 57.18593275925195485_dp, 74.03707888623914357_dp, & & 52.17735288037920327_dp, 77.11452529323830163_dp, & & 42.96575654499160635_dp, 79.88802174923021937_dp, & & 26.58690897303194944_dp, 82.01033616765489853_dp, & & 1.99999999999999756_dp, 82.85826214470436923_dp, & & -22.58690897303195300_dp, 82.01033616765489853_dp, & & -38.96575654499161345_dp, 79.88802174923021937_dp, & & -48.17735288037921748_dp, 77.11452529323830163_dp, & & -53.18593275925197617_dp, 74.03707888623914357_dp, & & -55.81801935702031159_dp, 70.81486693953938527_dp, & & -57.03453256694834295_dp, 67.52503657439316953_dp, & & -57.35114187047395262_dp, 64.20957103321013903_dp, & & -57.05957379451162836_dp, 60.89430451319173443_dp, & & -56.33378767939933596_dp, 57.59709162031844443_dp, & & -55.28230228211860720_dp, 54.33161333463023368_dp, & & -53.97533567047437231_dp, 51.10929358481432416_dp, & & -52.45964870701570248_dp, 47.94033352757975308_dp, & & -50.76707508825830928_dp, 44.83430401955754974_dp, & & -48.91964996438991875_dp, 41.80050199367043007_dp, & & -46.93282539675249154_dp, 38.84817223653458029_dp, & & -44.81756887288366897_dp, 35.98664677769215103_dp, & & -42.58178882927894193_dp, 33.22542941501406233_dp, & & -40.23134383513492196_dp, 30.57423988556289629_dp, & & -37.77078829775713587_dp, 28.04302503644353095_dp, & & -35.20394762734520100_dp, 25.64194033091114022_dp, & & -32.53437968696510296_dp, 23.38130282889363798_dp, & & -29.76575655354335481_dp, 21.27151577227775903_dp, & & -26.90218550361062810_dp, 19.32296477964253967_dp, & & -23.94847769357362921_dp, 17.54588626406437513_dp, & & -20.91036566952360332_dp, 15.95020993686824440_dp, & & -17.79466590782826074_dp, 14.54537904344351951_dp, & & -14.60937983520194017_dp, 13.34015413091371016_dp, & & -11.36372621835939611_dp, 12.34240842556757656_dp, & & -8.06809948196382898_dp, 11.55892497220584403_dp, & & -4.73395228112668676_dp, 10.99520717814031556_dp, & & -1.37360609917944720_dp, 10.65531494200142149_dp, & & 1.99999999999999600_dp, 7.07694595379052860_dp, & & 5.61234064761578821_dp, 7.19855645524002874_dp, & & 9.21005712455210990_dp, 7.56241362477411005_dp, & & 12.77895396829166508_dp, 8.16563014493928208_dp, & & 16.30566013367806733_dp, 9.00350968307661859_dp, & & 19.77796305903054730_dp, 10.06971032975657643_dp, & & 23.18506021909562875_dp, 11.35645428598743756_dp, & & 26.51771689758311723_dp, 12.85476800444172873_dp, & & 29.76832854644279536_dp, 14.55473640862136264_dp, & & 32.93089420696504277_dp, 16.44575600838861362_dp, & & 36.00091302933286386_dp, 18.51677422677156315_dp, & & 38.97521854780546846_dp, 20.75650546647104022_dp, & & 41.85176517580521960_dp, 23.15361781597290403_dp, & & 44.62937881000767248_dp, 25.69688738457191945_dp, & & 47.30747895739604303_dp, 28.37531978094453677_dp, & & 49.88577372237665486_dp, 31.17824007995576707_dp, & & 52.36392121341754091_dp, 34.09535373993877982_dp, & & 54.74114071516557090_dp, 37.11678138456306897_dp, & & 57.01574257648425004_dp, 40.23307020728445593_dp, & & 59.18452370168172649_dp, 43.43518400478754415_dp, & & 61.24193904387428233_dp, 46.71447239476329827_dp, & & 63.17889520468774833_dp, 50.06261728049957327_dp, & & 64.98089250042356468_dp, 53.47155025248752480_dp, & & 66.62500642980749888_dp, 56.93332642202356197_dp, & & 68.07470870335012592_dp, 60.43992354246547904_dp, & & 69.27043361461275595_dp, 63.98289851135272244_dp, & & 70.11115128919476547_dp, 67.55274433031233627_dp, & & 70.41516373728855172_dp, 71.13755089586132385_dp, & & 69.82714145548160900_dp, 74.71983727667713993_dp, & & 67.56415050352947560_dp, 78.26774990878784877_dp, & & 61.58131858106001033_dp, 81.70467639663139892_dp, & & 45.28583165458982052_dp, 84.76914638163376026_dp, & & 1.99999999999999689_dp, 86.32305404620944955_dp, & & -41.28583165458982762_dp, 84.76914638163376026_dp, & & -57.58131858106002454_dp, 81.70467639663139892_dp, & & -63.56415050352948981_dp, 78.26774990878784877_dp, & & -65.82714145548162321_dp, 74.71983727667713993_dp, & & -66.41516373728856593_dp, 71.13755089586132385_dp, & & -66.11115128919477968_dp, 67.55274433031233627_dp, & & -65.27043361461275595_dp, 63.98289851135272244_dp, & & -64.07470870335011170_dp, 60.43992354246547904_dp, & & -62.62500642980752019_dp, 56.93332642202356197_dp, & & -60.98089250042357889_dp, 53.47155025248752480_dp, & & -59.17889520468775544_dp, 50.06261728049957327_dp, & & -57.24193904387428233_dp, 46.71447239476329827_dp, & & -55.18452370168172649_dp, 43.43518400478754415_dp, & & -53.01574257648425004_dp, 40.23307020728445593_dp, & & -50.74114071516557800_dp, 37.11678138456306897_dp, & & -48.36392121341754802_dp, 34.09535373993877982_dp, & & -45.88577372237665486_dp, 31.17824007995576707_dp, & & -43.30747895739605013_dp, 28.37531978094453677_dp, & & -40.62937881000767959_dp, 25.69688738457191945_dp, & & -37.85176517580522670_dp, 23.15361781597290403_dp, & & -34.97521854780547557_dp, 20.75650546647104022_dp, & & -32.00091302933286386_dp, 18.51677422677156315_dp, & & -28.93089420696504632_dp, 16.44575600838861362_dp, & & -25.76832854644280957_dp, 14.55473640862136264_dp, & & -22.51771689758312434_dp, 12.85476800444172873_dp, & & -19.18506021909564296_dp, 11.35645428598743756_dp, & & -15.77796305903055796_dp, 10.06971032975657643_dp, & & -12.30566013367807621_dp, 9.00350968307661859_dp, & & -8.77895396829167574_dp, 8.16563014493928208_dp, & & -5.21005712455211967_dp, 7.56241362477411005_dp, & & -1.61234064761579732_dp, 7.19855645524002874_dp, & & 1.99999999999999556_dp, 3.39364961385021102_dp, & & 5.86220744039440511_dp, 3.52366797872772963_dp, & & 9.70801791517021506_dp, 3.91258202080964379_dp, & & 13.52160273595813145_dp, 4.55701513369246314_dp, & & 17.28822357553576694_dp, 5.45149036402020482_dp, & & 20.99466904928960531_dp, 6.58864168091601599_dp, & & 24.62957864939021135_dp, 7.95948261839810378_dp, & & 28.18364165385172626_dp, 9.55371034930797514_dp, & & 31.64967308093981302_dp, 11.36002298851478365_dp, & & 35.02258051120933402_dp, 13.36643021383323671_dp, & & 38.29924330014637235_dp, 15.56054131855884215_dp, & & 41.47832910130669859_dp, 17.92981963683195801_dp, & & 44.56007232189227807_dp, 20.46179708442632261_dp, & & 47.54603622089215520_dp, 23.14424674184217068_dp, & & 50.43887601207384819_dp, 25.96531465470105005_dp, & & 53.24211556220075892_dp, 28.91361425616476666_dp, & & 55.95994579953058690_dp, 31.97828811153562256_dp, & & 58.59704918478396252_dp, 35.14904222145087687_dp, & & 61.15845171105517863_dp, 38.41615810166681655_dp, & & 63.64940188352277062_dp, 41.77048747717444854_dp, & & 66.07527488350018530_dp, 45.20343384586799118_dp, & & 68.44149951448399349_dp, 48.70692450151241104_dp, & & 70.75350545455997064_dp, 52.27337593848611164_dp, & & 73.01668877482984499_dp, 55.89565494125256606_dp, & & 75.23639478559491067_dp, 59.56703711552955127_dp, & & 77.41791965045068480_dp, 63.28116415502339720_dp, & & 79.56653776770211550_dp, 67.03200075627316323_dp, & & 81.68757690134610527_dp, 70.81379178736983704_dp, & & 83.78661098119418682_dp, 74.62102007313750107_dp, & & 85.87003129087820241_dp, 78.44836496333871878_dp, & & 87.94730489861872513_dp, 82.29066165213365025_dp, & & 90.04670840724658376_dp, 86.14286053253557895_dp, & & -177.99999999999985789_dp, 89.99364961382421768_dp, & & -86.04670840724655534_dp, 86.14286053253557895_dp, & & -83.94730489861876777_dp, 82.29066165213365025_dp, & & -81.87003129087820241_dp, 78.44836496333871878_dp, & & -79.78661098119418682_dp, 74.62102007313750107_dp, & & -77.68757690134613370_dp, 70.81379178736983704_dp, & & -75.56653776770212971_dp, 67.03200075627316323_dp, & & -73.41791965045068480_dp, 63.28116415502339720_dp, & & -71.23639478559491067_dp, 59.56703711552955127_dp, & & -69.01668877482984499_dp, 55.89565494125256606_dp, & & -66.75350545455997064_dp, 52.27337593848611164_dp, & & -64.44149951448400770_dp, 48.70692450151241104_dp, & & -62.07527488350018530_dp, 45.20343384586799118_dp, & & -59.64940188352277772_dp, 41.77048747717444854_dp, & & -57.15845171105518574_dp, 38.41615810166681655_dp, & & -54.59704918478396252_dp, 35.14904222145087687_dp, & & -51.95994579953059400_dp, 31.97828811153562256_dp, & & -49.24211556220076602_dp, 28.91361425616476666_dp, & & -46.43887601207384819_dp, 25.96531465470105005_dp, & & -43.54603622089217652_dp, 23.14424674184217068_dp, & & -40.56007232189229228_dp, 20.46179708442632261_dp, & & -37.47832910130670570_dp, 17.92981963683195801_dp, & & -34.29924330014637945_dp, 15.56054131855884215_dp, & & -31.02258051120933402_dp, 13.36643021383323671_dp, & & -27.64967308093982012_dp, 11.36002298851478365_dp, & & -24.18364165385173337_dp, 9.55371034930797514_dp, & & -20.62957864939022556_dp, 7.95948261839810378_dp, & & -16.99466904928961597_dp, 6.58864168091601599_dp, & & -13.28822357553577582_dp, 5.45149036402020482_dp, & & -9.52160273595813855_dp, 4.55701513369246314_dp, & & -5.70801791517022572_dp, 3.91258202080964379_dp, & & -1.86220744039441377_dp, 3.52366797872772963_dp, & & 1.99999999999999556_dp, -0.54003843869415158_dp, & & 6.12693783758673050_dp, -0.40111214042971866_dp, & & 10.23515434659413970_dp, 0.01431798130226895_dp, & & 14.30668142622436534_dp, 0.70226688878527466_dp, & & 18.32499114491300674_dp, 1.65629062554436168_dp, & & 22.27556116130715580_dp, 2.86776143955401341_dp, & & 26.14628266758476016_dp, 4.32621388981252775_dp, & & 29.92769777693464306_dp, 6.01973109672258033_dp, & & 33.61307490596014702_dp, 7.93534078498540918_dp, & & 37.19834740797973183_dp, 10.05939491571368904_dp, & & 40.68195074563112712_dp, 12.37791309905906267_dp, & & 44.06459700293881809_dp, 14.87687712846902421_dp, & & 47.34902402209520034_dp, 17.54247066454981052_dp, & & 50.53975201683548590_dp, 20.36126354510683711_dp, & & 53.64287529119709319_dp, 23.32034407821754485_dp, & & 56.66591251909911620_dp, 26.40740499964346455_dp, & & 59.61773745881250619_dp, 29.61078975921034129_dp, & & 62.50861449014276872_dp, 32.91950572923986584_dp, & & 65.35037195723165837_dp, 36.32321005821969351_dp, & & 68.15676435641833564_dp, 39.81217237539897269_dp, & & 70.94410839385281520_dp, 43.37721635788606989_dp, & & 73.73234087330637010_dp, 47.00963897352001908_dp, & & 76.54676554128549526_dp, 50.70110112272598712_dp, & & 79.42099130058548440_dp, 54.44347433172315220_dp, & & 82.40205491898922219_dp, 58.22861013398449614_dp, & & 85.55981529780903827_dp, 62.04795929474799721_dp, & & 89.00534896567364740_dp, 65.89187266757559769_dp, & & 92.93011750021230455_dp, 69.74815881341463353_dp, & & 97.69886509454546797_dp, 73.59868574092438109_dp, & & 104.10346048738931302_dp, 77.40995108179684792_dp, & & 114.19717929614462548_dp, 81.10053146183234674_dp, & & 134.58473801020662108_dp, 84.39088722277180921_dp, & & -178.00000000000002842_dp, 86.05996156130575514_dp, & & -130.58473801020659266_dp, 84.39088722277180921_dp, & & -110.19717929614465390_dp, 81.10053146183234674_dp, & & -100.10346048738929881_dp, 77.40995108179684792_dp, & & -93.69886509454548218_dp, 73.59868574092438109_dp, & & -88.93011750021230455_dp, 69.74815881341463353_dp, & & -85.00534896567366161_dp, 65.89187266757559769_dp, & & -81.55981529780905248_dp, 62.04795929474799721_dp, & & -78.40205491898922219_dp, 58.22861013398449614_dp, & & -75.42099130058548440_dp, 54.44347433172315220_dp, & & -72.54676554128549526_dp, 50.70110112272598712_dp, & & -69.73234087330638431_dp, 47.00963897352001908_dp, & & -66.94410839385282941_dp, 43.37721635788606989_dp, & & -64.15676435641836406_dp, 39.81217237539897269_dp, & & -61.35037195723166548_dp, 36.32321005821969351_dp, & & -58.50861449014277582_dp, 32.91950572923986584_dp, & & -55.61773745881251330_dp, 29.61078975921034129_dp, & & -52.66591251909913041_dp, 26.40740499964346455_dp, & & -49.64287529119710030_dp, 23.32034407821754485_dp, & & -46.53975201683548590_dp, 20.36126354510683711_dp, & & -43.34902402209520034_dp, 17.54247066454981052_dp, & & -40.06459700293881809_dp, 14.87687712846902421_dp, & & -36.68195074563112712_dp, 12.37791309905906267_dp, & & -33.19834740797973893_dp, 10.05939491571368904_dp, & & -29.61307490596015057_dp, 7.93534078498540918_dp, & & -25.92769777693465727_dp, 6.01973109672258033_dp, & & -22.14628266758476371_dp, 4.32621388981252775_dp, & & -18.27556116130716291_dp, 2.86776143955401341_dp, & & -14.32499114491301384_dp, 1.65629062554436168_dp, & & -10.30668142622437244_dp, 0.70226688878527466_dp, & & -6.23515434659414680_dp, 0.01431798130226895_dp, & & -2.12693783758673804_dp, -0.40111214042971866_dp, & & 1.99999999999999556_dp, -4.76061810234754645_dp, & & 6.41132592404072454_dp, -4.61212239418478553_dp, & & 10.80084816537008230_dp, -4.16824654595516098_dp, & & 15.14776873556008674_dp, -3.43374257581263365_dp, & & 19.43320327160517635_dp, -2.41626750988024641_dp, & & 23.64091162595336115_dp, -1.12602081331554671_dp, & & 27.75780275269853448_dp, 0.42470591907018324_dp, & & 31.77420154083880988_dp, 2.22202305439570047_dp, & & 35.68389780238808129_dp, 4.25093893994811900_dp, & & 39.48402100288601702_dp, 6.49582334126627270_dp, & & 43.17479644658765636_dp, 8.94081392759362181_dp, & & 46.75924066894447861_dp, 11.57015196980179894_dp, & & 50.24284889074777283_dp, 14.36844259642658628_dp, & & 53.63331929525543273_dp, 17.32084182287968943_dp, & & 56.94035098918124049_dp, 20.41317688045037571_dp, & & 60.17554747078253286_dp, 23.63200834498020697_dp, & & 63.35245736607463840_dp, 26.96464262473803686_dp, & & 66.48679120890342631_dp, 30.39910192691253599_dp, & & 69.59687011372152199_dp, 33.92405612452549946_dp, & & 72.70439458886511375_dp, 37.52871692222674938_dp, & & 75.83567948061782715_dp, 41.20268880474466755_dp, & & 79.02360369831293951_dp, 44.93576201889581512_dp, & & 82.31071017117776023_dp, 48.71761719224470966_dp, & & 85.75424365182692554_dp, 52.53738247574246856_dp, & & 89.43460651588344490_dp, 56.38292751246400769_dp, & & 93.47013935832343634_dp, 60.23965935256792648_dp, & & 98.04421718085441739_dp, 64.08831694761893516_dp, & & 103.45761459629794388_dp, 67.90061168070626252_dp, & & 110.23509874248559015_dp, 71.62987479878029262_dp, & & 119.34882263320223217_dp, 75.18923730101467129_dp, & & 132.64763601333982024_dp, 78.39753950634394641_dp, & & 153.15668933029419918_dp, 80.85560324742944260_dp, & & -178.00000000000002842_dp, 81.83938189765245852_dp, & & -149.15668933029422760_dp, 80.85560324742944260_dp, & & -128.64763601333984866_dp, 78.39753950634394641_dp, & & -115.34882263320223217_dp, 75.18923730101467129_dp, & & -106.23509874248556173_dp, 71.62987479878029262_dp, & & -99.45761459629794388_dp, 67.90061168070626252_dp, & & -94.04421718085441739_dp, 64.08831694761893516_dp, & & -89.47013935832346476_dp, 60.23965935256792648_dp, & & -85.43460651588344490_dp, 56.38292751246400769_dp, & & -81.75424365182695396_dp, 52.53738247574246856_dp, & & -78.31071017117777444_dp, 48.71761719224470966_dp, & & -75.02360369831293951_dp, 44.93576201889581512_dp, & & -71.83567948061784136_dp, 41.20268880474466755_dp, & & -68.70439458886511375_dp, 37.52871692222674938_dp, & & -65.59687011372152199_dp, 33.92405612452549946_dp, & & -62.48679120890344052_dp, 30.39910192691253599_dp, & & -59.35245736607464551_dp, 26.96464262473803686_dp, & & -56.17554747078254707_dp, 23.63200834498020697_dp, & & -52.94035098918124760_dp, 20.41317688045037571_dp, & & -49.63331929525544695_dp, 17.32084182287968943_dp, & & -46.24284889074779414_dp, 14.36844259642658628_dp, & & -42.75924066894448572_dp, 11.57015196980179894_dp, & & -39.17479644658766347_dp, 8.94081392759362181_dp, & & -35.48402100288603123_dp, 6.49582334126627270_dp, & & -31.68389780238809195_dp, 4.25093893994811900_dp, & & -27.77420154083882409_dp, 2.22202305439570047_dp, & & -23.75780275269854513_dp, 0.42470591907018324_dp, & & -19.64091162595336826_dp, -1.12602081331554671_dp, & & -15.43320327160518879_dp, -2.41626750988024641_dp, & & -11.14776873556009562_dp, -3.43374257581263365_dp, & & -6.80084816537009029_dp, -4.16824654595516098_dp, & & -2.41132592404073298_dp, -4.61212239418478553_dp, & & 1.99999999999999556_dp, -9.30985393851741705_dp, & & 6.72175805422021355_dp, -9.15091230991673932_dp, & & 11.41756212661333514_dp, -8.67603648837036268_dp, & & 16.06282113870961226_dp, -7.89096231408012549_dp, & & 20.63552097548324227_dp, -6.80489321395614688_dp, & & 25.11717183125960418_dp, -5.43001433583735604_dp, & & 29.49342310484702523_dp, -3.78089833576525836_dp, & & 33.75433817509684786_dp, -1.87386656696368870_dp, & & 37.89437094593881028_dp, 0.27363605581641121_dp, & & 41.91211819209927114_dp, 2.64360721178193936_dp, & & 45.80993456941938291_dp, 5.21796518077428217_dp, & & 49.59349430020984784_dp, 7.97892504367260536_dp, & & 53.27137129744098587_dp, 10.90927185095380025_dp, & & 56.85469423510678411_dp, 13.99253787292509266_dp, & & 60.35691981311008192_dp, 17.21309536720795919_dp, & & 63.79375952890369206_dp, 20.55617726580611659_dp, & & 67.18329478816110623_dp, 24.00783659504739731_dp, & & 70.54632400302143935_dp, 27.55485191826680236_dp, & & 73.90700610430916129_dp, 31.18458087158649406_dp, & & 77.29390254081907585_dp, 34.88475663106463287_dp, & & 80.74158359961856490_dp, 38.64321178012773572_dp, & & 84.29307189810650414_dp, 42.44749809608728697_dp, & & 88.00357763838491110_dp, 46.28434439562811065_dp, & & 91.94629406419836926_dp, 50.13884820321026581_dp, & & 96.22157019495104180_dp, 53.99321139219064491_dp, & & 100.97173315822050199_dp, 57.82466562901674934_dp, & & 106.40541928769302160_dp, 61.60190913800559542_dp, & & 112.83745587761723073_dp, 65.27873175231235336_dp, & & 120.75095597801100666_dp, 68.78228497629521598_dp, & & 130.87442892417493567_dp, 71.99165187875782124_dp, & & 144.18467631053604805_dp, 74.70285264202438213_dp, & & 161.47781378002883912_dp, 76.59572545663588983_dp, & & -178.00000000000002842_dp, 77.29014606148258792_dp, & & -157.47781378002883912_dp, 76.59572545663588983_dp, & & -140.18467631053607647_dp, 74.70285264202438213_dp, & & -126.87442892417496410_dp, 71.99165187875782124_dp, & & -116.75095597801099245_dp, 68.78228497629521598_dp, & & -108.83745587761723073_dp, 65.27873175231235336_dp, & & -102.40541928769302160_dp, 61.60190913800559542_dp, & & -96.97173315822051620_dp, 57.82466562901674934_dp, & & -92.22157019495104180_dp, 53.99321139219064491_dp, & & -87.94629406419838347_dp, 50.13884820321026581_dp, & & -84.00357763838492531_dp, 46.28434439562811065_dp, & & -80.29307189810650414_dp, 42.44749809608728697_dp, & & -76.74158359961857911_dp, 38.64321178012773572_dp, & & -73.29390254081907585_dp, 34.88475663106463287_dp, & & -69.90700610430916129_dp, 31.18458087158649406_dp, & & -66.54632400302145356_dp, 27.55485191826680236_dp, & & -63.18329478816111333_dp, 24.00783659504739731_dp, & & -59.79375952890369916_dp, 20.55617726580611659_dp, & & -56.35691981311008192_dp, 17.21309536720795919_dp, & & -52.85469423510679832_dp, 13.99253787292509266_dp, & & -49.27137129744098587_dp, 10.90927185095380025_dp, & & -45.59349430020986915_dp, 7.97892504367260536_dp, & & -41.80993456941939712_dp, 5.21796518077428217_dp, & & -37.91211819209929246_dp, 2.64360721178193936_dp, & & -33.89437094593881739_dp, 0.27363605581641121_dp, & & -29.75433817509685497_dp, -1.87386656696368870_dp, & & -25.49342310484703944_dp, -3.78089833576525836_dp, & & -21.11717183125961128_dp, -5.43001433583735604_dp, & & -16.63552097548325293_dp, -6.80489321395614688_dp, & & -12.06282113870962114_dp, -7.89096231408012549_dp, & & -7.41756212661334136_dp, -8.67603648837036268_dp, & & -2.72175805422022110_dp, -9.15091230991673932_dp, & & 1.99999999999999556_dp, -14.23540593490034212_dp, & & 7.06707279862612125_dp, -14.06484423228424063_dp, & & 12.10248720394761790_dp, -13.55555303790300137_dp, & & 17.07647532339787944_dp, -12.71455858250937965_dp, & & 21.96281473836326015_dp, -11.55307649365679623_dp, & & 26.74006765897273041_dp, -10.08584610023314454_dp, & & 31.39231472226267528_dp, -8.33033151194296018_dp, & & 35.90939060170597230_dp, -6.30588392466837799_dp, & & 40.28670514160292271_dp, -4.03294710811188395_dp, & & 44.52477649322944586_dp, -1.53236497867592791_dp, & & 48.62861191461712451_dp, 1.17517574056815022_dp, & & 52.60705694133754662_dp, 4.06955754229703981_dp, & & 56.47220691225791711_dp, 7.13150506907926562_dp, & & 60.23894695811429756_dp, 10.34273467530950796_dp, & & 63.92466443809040300_dp, 13.68599977255221845_dp, & & 67.54916484650964037_dp, 17.14505004500527363_dp, & & 71.13481958525987636_dp, 20.70451849248449960_dp, & & 74.70698212428176532_dp, 24.34974403770184992_dp, & & 78.29472888122153051_dp, 28.06652943785655552_dp, & & 81.93201512996088809_dp, 31.84082402428231973_dp, & & 85.65938939021744147_dp, 35.65830698143167155_dp, & & 89.52649044264860834_dp, 39.50382677214758331_dp, & & 93.59567144235911940_dp, 43.36062114047545890_dp, & & 97.94726867835277062_dp, 47.20919167069192923_dp, & & 102.68725830824645584_dp, 51.02562354013957702_dp, & & 107.95825647893494192_dp, 54.77900423463843538_dp, & & 113.95470750710921948_dp, 58.42738157410047961_dp, & & 120.94158660124729465_dp, 61.91142197433249095_dp, & & 129.26976638159490562_dp, 65.14478524125112813_dp, & & 139.36255278451201889_dp, 68.00113945880528377_dp, & & 151.60742176334809983_dp, 70.30250402254893061_dp, & & 166.05644118521092878_dp, 71.82662636485281382_dp, & & -178.00000000000002842_dp, 72.36459406509962378_dp, & & -162.05644118521095720_dp, 71.82662636485281382_dp, & & -147.60742176334812825_dp, 70.30250402254893061_dp, & & -135.36255278451201889_dp, 68.00113945880528377_dp, & & -125.26976638159489141_dp, 65.14478524125112813_dp, & & -116.94158660124729465_dp, 61.91142197433249095_dp, & & -109.95470750710923369_dp, 58.42738157410047961_dp, & & -103.95825647893495614_dp, 54.77900423463843538_dp, & & -98.68725830824647005_dp, 51.02562354013957702_dp, & & -93.94726867835278483_dp, 47.20919167069192923_dp, & & -89.59567144235911940_dp, 43.36062114047545890_dp, & & -85.52649044264862255_dp, 39.50382677214758331_dp, & & -81.65938939021744147_dp, 35.65830698143167155_dp, & & -77.93201512996090230_dp, 31.84082402428231973_dp, & & -74.29472888122153051_dp, 28.06652943785655552_dp, & & -70.70698212428177953_dp, 24.34974403770184992_dp, & & -67.13481958525987636_dp, 20.70451849248449960_dp, & & -63.54916484650964748_dp, 17.14505004500527363_dp, & & -59.92466443809041010_dp, 13.68599977255221845_dp, & & -56.23894695811430466_dp, 10.34273467530950796_dp, & & -52.47220691225792422_dp, 7.13150506907926562_dp, & & -48.60705694133756083_dp, 4.06955754229703981_dp, & & -44.62861191461713162_dp, 1.17517574056815022_dp, & & -40.52477649322946007_dp, -1.53236497867592791_dp, & & -36.28670514160293692_dp, -4.03294710811188395_dp, & & -31.90939060170597941_dp, -6.30588392466837799_dp, & & -27.39231472226267883_dp, -8.33033151194296018_dp, & & -22.74006765897273752_dp, -10.08584610023314454_dp, & & -17.96281473836326725_dp, -11.55307649365679623_dp, & & -13.07647532339788832_dp, -12.71455858250937965_dp, & & -8.10248720394762678_dp, -13.55555303790300137_dp, & & -3.06707279862612925_dp, -14.06484423228424063_dp, & & 1.99999999999999600_dp, -19.59134287356426896_dp, & & 7.46002704385165138_dp, -19.40755726556070115_dp, & & 12.88033504279662367_dp, -18.85919797266866738_dp, & & 18.22391622121717347_dp, -17.95503295463869264_dp, & & 23.45879403720244127_dp, -16.70896594558295334_dp, & & 28.55966518490710015_dp, -15.13909773195327269_dp, & & 33.50874372060166451_dp, -13.26662598462660014_dp, & & 38.29585521437642370_dp, -11.11472732454917889_dp, & & 42.91794868987490474_dp, -8.70753794849322738_dp, & & 47.37824652323212149_dp, -6.06930752874207258_dp, & & 51.68524592294901510_dp, -3.22375917751487107_dp, & & 55.85174332801063457_dp, -0.19365520038940884_dp, & & 59.89399913408759346_dp, 2.99945241539763385_dp, & & 63.83111160507218784_dp, 6.33531713186757095_dp, & & 67.68463403089357655_dp, 9.79496963802120746_dp, & & 71.47845003929684538_dp, 13.36059248585088000_dp, & & 75.23891693218338617_dp, 17.01531594444593409_dp, & & 78.99529352424693229_dp, 20.74294398926493699_dp, & & 82.78048521884274180_dp, 24.52760876444716587_dp, & & 86.63216392910494790_dp, 28.35333972343855180_dp, & & 90.59435351833845118_dp, 32.20351875194761959_dp, & & 94.71961138434764393_dp, 36.06017287638244539_dp, & & 99.07197748871266185_dp, 39.90302877816822757_dp, & & 103.73088255981890882_dp, 43.70821480745370025_dp, & & 108.79614728017998004_dp, 47.44644408577498496_dp, & & 114.39390839554110357_dp, 51.08045112569507751_dp, & & 120.68240638414135901_dp, 54.56141374911145192_dp, & & 127.85428534979440940_dp, 57.82417470821775396_dp, & & 136.12712761110682891_dp, 60.78156430751474204_dp, & & 145.70578215046154469_dp, 63.31961305621511116_dp, & & 156.69437932769909594_dp, 65.29863962109097031_dp, & & 168.95645291666264143_dp, 66.56908368553207822_dp, & & -178.00000000000002842_dp, 67.00865712643570760_dp, & & -164.95645291666266985_dp, 66.56908368553207822_dp, & & -152.69437932769912436_dp, 65.29863962109097031_dp, & & -141.70578215046154469_dp, 63.31961305621511116_dp, & & -132.12712761110685733_dp, 60.78156430751474204_dp, & & -123.85428534979440940_dp, 57.82417470821775396_dp, & & -116.68240638414138743_dp, 54.56141374911145192_dp, & & -110.39390839554111778_dp, 51.08045112569507751_dp, & & -104.79614728017998004_dp, 47.44644408577498496_dp, & & -99.73088255981890882_dp, 43.70821480745370025_dp, & & -95.07197748871266185_dp, 39.90302877816822757_dp, & & -90.71961138434764393_dp, 36.06017287638244539_dp, & & -86.59435351833845118_dp, 32.20351875194761959_dp, & & -82.63216392910496211_dp, 28.35333972343855180_dp, & & -78.78048521884275601_dp, 24.52760876444716587_dp, & & -74.99529352424694650_dp, 20.74294398926493699_dp, & & -71.23891693218340038_dp, 17.01531594444593409_dp, & & -67.47845003929684538_dp, 13.36059248585088000_dp, & & -63.68463403089357655_dp, 9.79496963802120746_dp, & & -59.83111160507219495_dp, 6.33531713186757095_dp, & & -55.89399913408760057_dp, 2.99945241539763385_dp, & & -51.85174332801064168_dp, -0.19365520038940884_dp, & & -47.68524592294902931_dp, -3.22375917751487107_dp, & & -43.37824652323212860_dp, -6.06930752874207258_dp, & & -38.91794868987490474_dp, -8.70753794849322738_dp, & & -34.29585521437643081_dp, -11.11472732454917889_dp, & & -29.50874372060167872_dp, -13.26662598462660014_dp, & & -24.55966518490711081_dp, -15.13909773195327269_dp, & & -19.45879403720245193_dp, -16.70896594558295334_dp, & & -14.22391622121718058_dp, -17.95503295463869264_dp, & & -8.88033504279663255_dp, -18.85919797266866738_dp, & & -3.46002704385166071_dp, -19.40755726556070115_dp, & & 1.99999999999999556_dp, -25.43837119820565817_dp, & & 7.91994134356474966_dp, -25.23910692117755517_dp, & & 13.78834011215970534_dp, -24.64516330804185529_dp, & & 19.55772615655293478_dp, -23.66774871950556403_dp, & & 25.18808138888132220_dp, -22.32448212182583092_dp, & & 30.64904852681420522_dp, -20.63801909702449677_dp, & & 35.92081946357622968_dp, -18.63449057116782726_dp, & & 40.99386004100856695_dp, -16.34197985921977292_dp, & & 45.86781613095838850_dp, -13.78920372951385431_dp, & & 50.54999441522539882_dp, -11.00448645615600007_dp, & & 55.05375641269628773_dp, -8.01504773475606846_dp, & & 59.39706222812605318_dp, -4.84657887125735343_dp, & & 63.60129716846514469_dp, -1.52305829704237761_dp, & & 67.69043463465565935_dp, 1.93324784086672352_dp, & & 71.69053915995505122_dp, 5.50164833296652045_dp, & & 75.62959031828353318_dp, 9.16278970353470612_dp, & & 79.53760403336076479_dp, 12.89834287286321945_dp, & & 83.44703540885105042_dp, 16.69062430422997068_dp, & & 87.39346098727328638_dp, 20.52214967325533124_dp, & & 91.41655400532015108_dp, 24.37510563102819106_dp, & & 95.56137924921046078_dp, 28.23071203179445021_dp, & & 99.88003723653145016_dp, 32.06843215731343832_dp, & & 104.43366587620558050_dp, 35.86497163386139420_dp, & & 109.29473060869932510_dp, 39.59298941537671368_dp, & & 114.54933935400606515_dp, 43.21943287866469774_dp, & & 120.29889149413013172_dp, 46.70342202473147353_dp, & & 126.65952710171551132_dp, 49.99368843662634987_dp, & & 133.75638606304184464_dp, 53.02581189527145256_dp, & & 141.70772458687929429_dp, 55.72003625021812923_dp, & & 150.59283359977422379_dp, 57.98141283097308474_dp, & & 160.40162010671977555_dp, 59.70518607312001080_dp, & & 170.97884079028881388_dp, 60.79043532425958318_dp, & & -178.00000000000002842_dp, 61.16162880179432193_dp, & & -166.97884079028878546_dp, 60.79043532425958318_dp, & & -156.40162010671977555_dp, 59.70518607312001080_dp, & & -146.59283359977425221_dp, 57.98141283097308474_dp, & & -137.70772458687926587_dp, 55.72003625021812923_dp, & & -129.75638606304184464_dp, 53.02581189527145256_dp, & & -122.65952710171549711_dp, 49.99368843662634987_dp, & & -116.29889149413011751_dp, 46.70342202473147353_dp, & & -110.54933935400606515_dp, 43.21943287866469774_dp, & & -105.29473060869931089_dp, 39.59298941537671368_dp, & & -100.43366587620559471_dp, 35.86497163386139420_dp, & & -95.88003723653146437_dp, 32.06843215731343832_dp, & & -91.56137924921046078_dp, 28.23071203179445021_dp, & & -87.41655400532016529_dp, 24.37510563102819106_dp, & & -83.39346098727328638_dp, 20.52214967325533124_dp, & & -79.44703540885105042_dp, 16.69062430422997068_dp, & & -75.53760403336077900_dp, 12.89834287286321945_dp, & & -71.62959031828354739_dp, 9.16278970353470612_dp, & & -67.69053915995506543_dp, 5.50164833296652045_dp, & & -63.69043463465567356_dp, 1.93324784086672352_dp, & & -59.60129716846515180_dp, -1.52305829704237761_dp, & & -55.39706222812606740_dp, -4.84657887125735343_dp, & & -51.05375641269629483_dp, -8.01504773475606846_dp, & & -46.54999441522540593_dp, -11.00448645615600007_dp, & & -41.86781613095839560_dp, -13.78920372951385431_dp, & & -36.99386004100857406_dp, -16.34197985921977292_dp, & & -31.92081946357624389_dp, -18.63449057116782726_dp, & & -26.64904852681421943_dp, -20.63801909702449677_dp, & & -21.18808138888132575_dp, -22.32448212182583092_dp, & & -15.55772615655294011_dp, -23.66774871950556403_dp, & & -9.78834011215971245_dp, -24.64516330804185529_dp, & & -3.91994134356475854_dp, -25.23910692117755517_dp, & & 1.99999999999999600_dp, -31.84350531560286868_dp, & & 8.47781225763252877_dp, -31.62546289221583962_dp, & & 14.88583393507996711_dp, -30.97645080660140593_dp, & & 21.16079514785619509_dp, -29.91127029421520689_dp, & & 27.25113130544622919_dp, -28.45290874783366775_dp, & & 33.11998552133064067_dp, -26.63042818683534207_dp, & & 38.74589265732009835_dp, -24.47666503668304117_dp, & & 44.12160159745183563_dp, -22.02610835172851722_dp, & & 49.25177543408178593_dp, -19.31318879069118211_dp, & & 54.15029168417838434_dp, -16.37106576006627279_dp, & & 58.83767345094592827_dp, -13.23089104518995640_dp, & & 63.33895182482493169_dp, -9.92146778444056032_dp, & & 67.68207179147748320_dp, -6.46920580966549874_dp, & & 71.89683474738467339_dp, -2.89828246890513030_dp, & & 76.01431282527251199_dp, 0.76906207426874740_dp, & & 80.06665407661380129_dp, 4.51214364124032485_dp, & & 84.08720437104130951_dp, 8.31139512800513636_dp, & & 88.11088800598805904_dp, 12.14788119395578647_dp, & & 92.17480572169650088_dp, 16.00276430383186366_dp, & & 96.31902021078285259_dp, 19.85670886149115333_dp, & & 100.58749981932022877_dp, 23.68920100972959730_dp, & & 105.02917319698201482_dp, 27.47775433026597725_dp, & & 109.69899804029304846_dp, 31.19696754875727152_dp, & & 114.65884380836618561_dp, 34.81740352705595143_dp, & & 119.97779813807069615_dp, 38.30427835916992052_dp, & & 125.73118977168117283_dp, 41.61600262291977259_dp, & & 131.99715883033749719_dp, 44.70273256173389598_dp, & & 138.84908760812987794_dp, 47.50530446205828383_dp, & & 146.34201331309026273_dp, 49.95526128262041254_dp, & & 154.49218436749947614_dp, 51.97706503648645082_dp, & & 163.25249258416440057_dp, 53.49372597518600259_dp, & & 172.49285933765111167_dp, 54.43639475342744305_dp, & & -178.00000000000002842_dp, 54.75649468439711143_dp, & & -168.49285933765114009_dp, 54.43639475342744305_dp, & & -159.25249258416440057_dp, 53.49372597518600259_dp, & & -150.49218436749950456_dp, 51.97706503648645082_dp, & & -142.34201331309029115_dp, 49.95526128262041254_dp, & & -134.84908760812987794_dp, 47.50530446205828383_dp, & & -127.99715883033752561_dp, 44.70273256173389598_dp, & & -121.73118977168118704_dp, 41.61600262291977259_dp, & & -115.97779813807071037_dp, 38.30427835916992052_dp, & & -110.65884380836619982_dp, 34.81740352705595143_dp, & & -105.69899804029306267_dp, 31.19696754875727152_dp, & & -101.02917319698202903_dp, 27.47775433026597725_dp, & & -96.58749981932022877_dp, 23.68920100972959730_dp, & & -92.31902021078286680_dp, 19.85670886149115333_dp, & & -88.17480572169651509_dp, 16.00276430383186366_dp, & & -84.11088800598805904_dp, 12.14788119395578647_dp, & & -80.08720437104132372_dp, 8.31139512800513636_dp, & & -76.06665407661381550_dp, 4.51214364124032485_dp, & & -72.01431282527251199_dp, 0.76906207426874740_dp, & & -67.89683474738467339_dp, -2.89828246890513030_dp, & & -63.68207179147749031_dp, -6.46920580966549874_dp, & & -59.33895182482493880_dp, -9.92146778444056032_dp, & & -54.83767345094592827_dp, -13.23089104518995640_dp, & & -50.15029168417838434_dp, -16.37106576006627279_dp, & & -45.25177543408179304_dp, -19.31318879069118211_dp, & & -40.12160159745184274_dp, -22.02610835172851722_dp, & & -34.74589265732009835_dp, -24.47666503668304117_dp, & & -29.11998552133064422_dp, -26.63042818683534207_dp, & & -23.25113130544624340_dp, -28.45290874783366775_dp, & & -17.16079514785620930_dp, -29.91127029421520689_dp, & & -10.88583393507997776_dp, -30.97645080660140593_dp, & & -4.47781225763253765_dp, -31.62546289221583962_dp, & & 1.99999999999999556_dp, -38.87875527701606160_dp, & & 9.18708125479774296_dp, -38.63683300066042392_dp, & & 16.27414365511956262_dp, -37.91820250638208734_dp, & & 23.17259140539264095_dp, -36.74332279213027164_dp, & & 29.81377244810832394_dp, -35.14341202945125531_dp, & & 36.15298964123235947_dp, -33.15698491696964822_dp, & & 42.16912769952225659_dp, -30.82630445836842625_dp, & & 47.86123932751760179_dp, -28.19436585459229860_dp, & & 53.24376548995357439_dp, -25.30271165018388402_dp, & & 58.34173321358173325_dp, -22.19009961204914561_dp, & & 63.18670666190918439_dp, -18.89188184637555779_dp, & & 67.81377209114522486_dp, -15.43989911427395079_dp, & & 72.25952713288313589_dp, -11.86270778471484277_dp, & & 76.56090370117536281_dp, -8.18599910867232694_dp, & & 80.75462189677602964_dp, -4.43311662588922584_dp, & & 84.87709425893895343_dp, -0.62561625366165385_dp, & & 88.96463936229967828_dp, 3.21615784383777248_dp, & & 93.05390189983984328_dp, 7.07248905242842518_dp, & & 97.18240447461120368_dp, 10.92369066480776318_dp, & & 101.38917071735671982_dp, 14.74948212031801020_dp, & & 105.71535755141637480_dp, 18.52832281285148497_dp, & & 110.20481280215611264_dp, 22.23668760419472790_dp, & & 114.90442707083992957_dp, 25.84827445710250160_dp, & & 119.86406835852177721_dp, 29.33315055785289260_dp, & & 125.13576891124590418_dp, 32.65687547770472321_dp, & & 130.77168357131759535_dp, 35.77969749489390949_dp, & & 136.82020109922683559_dp, 38.65601141858346068_dp, & & 143.31958252044233859_dp, 41.23439386179803279_dp, & & 150.28885177460347222_dp, 43.45866798082070659_dp, & & 157.71667350566602295_dp, 45.27051130755705799_dp, & & 165.55074086228117380_dp, 46.61395503653132977_dp, & & 173.69217969866488716_dp, 47.44157216781382402_dp, & & -178.00000000000002842_dp, 47.72124472298391851_dp, & & -169.69217969866491558_dp, 47.44157216781382402_dp, & & -161.55074086228117380_dp, 46.61395503653132977_dp, & & -153.71667350566602295_dp, 45.27051130755705799_dp, & & -146.28885177460344380_dp, 43.45866798082070659_dp, & & -139.31958252044236701_dp, 41.23439386179803279_dp, & & -132.82020109922686402_dp, 38.65601141858346068_dp, & & -126.77168357131760956_dp, 35.77969749489390949_dp, & & -121.13576891124588997_dp, 32.65687547770472321_dp, & & -115.86406835852179142_dp, 29.33315055785289260_dp, & & -110.90442707083994378_dp, 25.84827445710250160_dp, & & -106.20481280215612685_dp, 22.23668760419472790_dp, & & -101.71535755141638901_dp, 18.52832281285148497_dp, & & -97.38917071735673403_dp, 14.74948212031801020_dp, & & -93.18240447461121789_dp, 10.92369066480776318_dp, & & -89.05390189983984328_dp, 7.07248905242842518_dp, & & -84.96463936229969249_dp, 3.21615784383777248_dp, & & -80.87709425893895343_dp, -0.62561625366165385_dp, & & -76.75462189677602964_dp, -4.43311662588922584_dp, & & -72.56090370117536281_dp, -8.18599910867232694_dp, & & -68.25952713288315010_dp, -11.86270778471484277_dp, & & -63.81377209114523907_dp, -15.43989911427395079_dp, & & -59.18670666190919150_dp, -18.89188184637555779_dp, & & -54.34173321358174746_dp, -22.19009961204914561_dp, & & -49.24376548995358149_dp, -25.30271165018388402_dp, & & -43.86123932751760890_dp, -28.19436585459229860_dp, & & -38.16912769952225659_dp, -30.82630445836842625_dp, & & -32.15298964123236658_dp, -33.15698491696964822_dp, & & -25.81377244810833815_dp, -35.14341202945125531_dp, & & -19.17259140539265516_dp, -36.74332279213027164_dp, & & -12.27414365511956973_dp, -37.91820250638208734_dp, & & -5.18708125479775184_dp, -38.63683300066042392_dp, & & 1.99999999999999600_dp, -46.61822072475269607_dp, & & 10.68839576675080139_dp, -46.30624543028367412_dp, & & 19.18911080317679918_dp, -45.38412606933155757_dp, & & 27.34546416576215222_dp, -43.89055765093645078_dp, & & 35.05142021090807702_dp, -41.88213387768103502_dp, & & 42.25559890256131013_dp, -39.42480136235260346_dp, & & 48.95316063861176303_dp, -36.58631299796493153_dp, & & 55.17233704527057370_dp, -33.43100825355479344_dp, & & 60.96117255247643385_dp, -30.01702215459737744_dp, & & 66.37723925505210332_dp, -26.39536140379773599_dp, & & 71.48085419487965453_dp, -22.61014977269736548_dp, & & 76.33122188826865795_dp, -18.69947873486282575_dp, & & 80.98463901984250413_dp, -14.69649678201448850_dp, & & 85.49400025392819202_dp, -10.63053585607601548_dp, & & 89.90905356028720519_dp, -6.52818370181667884_dp, & & 94.27704477012913742_dp, -2.41427530523583922_dp, & & 98.64352777118510573_dp, 1.68719008387413583_dp, & & 103.05319914949195947_dp, 5.75218534812640314_dp, & & 107.55065416712271542_dp, 9.75582109859903390_dp, & & 112.18096363521173942_dp, 13.67152082594358298_dp, & & 116.98994385952536845_dp, 17.47019805768939449_dp, & & 122.02393786261990272_dp, 21.11944988981388605_dp, & & 127.32885205682140395_dp, 24.58281725765701253_dp, & & 132.94811747553873715_dp, 27.81921722445741807_dp, & & 138.91921485966395267_dp, 30.78272820361294038_dp, & & 145.26850797118672176_dp, 33.42299645292871446_dp, & & 152.00450116657361832_dp, 35.68659928106228563_dp, & & 159.11038900941949237_dp, 37.51968052984662449_dp, & & 166.53782868216146085_dp, 38.87197455633482690_dp, & & 174.20476659278793363_dp, 39.70189618399167131_dp, & & -178.00000000000002842_dp, 39.98177927524729114_dp, & & -170.20476659278793363_dp, 39.70189618399167131_dp, & & -162.53782868216148927_dp, 38.87197455633482690_dp, & & -155.11038900941949237_dp, 37.51968052984662449_dp, & & -148.00450116657361832_dp, 35.68659928106228563_dp, & & -141.26850797118672176_dp, 33.42299645292871446_dp, & & -134.91921485966395267_dp, 30.78272820361294038_dp, & & -128.94811747553873715_dp, 27.81921722445741807_dp, & & -123.32885205682140395_dp, 24.58281725765701253_dp, & & -118.02393786261990272_dp, 21.11944988981388605_dp, & & -112.98994385952538266_dp, 17.47019805768939449_dp, & & -108.18096363521175363_dp, 13.67152082594358298_dp, & & -103.55065416712271542_dp, 9.75582109859903390_dp, & & -99.05319914949195947_dp, 5.75218534812640314_dp, & & -94.64352777118511995_dp, 1.68719008387413583_dp, & & -90.27704477012913742_dp, -2.41427530523583922_dp, & & -85.90905356028720519_dp, -6.52818370181667884_dp, & & -81.49400025392820623_dp, -10.63053585607601548_dp, & & -76.98463901984250413_dp, -14.69649678201448850_dp, & & -72.33122188826867216_dp, -18.69947873486282575_dp, & & -67.48085419487966874_dp, -22.61014977269736548_dp, & & -62.37723925505212463_dp, -26.39536140379773599_dp, & & -56.96117255247644096_dp, -30.01702215459737744_dp, & & -51.17233704527057370_dp, -33.43100825355479344_dp, & & -44.95316063861176303_dp, -36.58631299796493153_dp, & & -38.25559890256131723_dp, -39.42480136235260346_dp, & & -31.05142021090808413_dp, -41.88213387768103502_dp, & & -23.34546416576216643_dp, -43.89055765093645078_dp, & & -15.18911080317680451_dp, -45.38412606933155757_dp, & & -6.68839576675080849_dp, -46.30624543028367412_dp, & & 1.99999999999999556_dp, -55.13279625041899834_dp, & & 12.21451927262477000_dp, -54.76594013073760436_dp, & & 22.09872704828332957_dp, -53.68864395349499574_dp, & & 31.39769711559288012_dp, -51.96422430511800883_dp, & & 39.96960367385942448_dp, -49.68040408706254141_dp, & & 47.77902429867568657_dp, -46.93247902115530223_dp, & & 54.86545369174167064_dp, -43.81109166541020983_dp, & & 61.30916699148811233_dp, -40.39598469592146301_dp, & & 67.20576633421555357_dp, -36.75430974219705860_dp, & & 72.65107920217620574_dp, -32.94148403257256064_dp, & & 77.73388667252179118_dp, -29.00309226896920123_dp, & & 82.53343020155088539_dp, -24.97699141478170048_dp, & & 87.11942634360052296_dp, -20.89524495725207132_dp, & & 91.55320457280680557_dp, -16.78577161643180204_dp, & & 95.88921878897831164_dp, -12.67371220268469578_dp, & & 100.17656220612792595_dp, -8.58256192749418645_dp, & & 104.46031614728221371_dp, -4.53512373056416251_dp, & & 108.78265670493264849_dp, -0.55433148808729282_dp, & & 113.18367588372132104_dp, 3.33602075627005812_dp, & & 117.70187269530691765_dp, 7.11062322287716331_dp, & & 122.37425008640165913_dp, 10.74206713847307348_dp, & & 127.23592626204656142_dp, 14.20035582641498806_dp, & & 132.31914580979304219_dp, 17.45256126555711873_dp, & & 137.65157520973264127_dp, 20.46271167815037728_dp, & & 143.25381666270027381_dp, 23.19203115841211726_dp, & & 149.13620894054645305_dp, 25.59967735271198208_dp, & & 155.29523174265207786_dp, 27.64411687523928052_dp, & & 161.71017626763577368_dp, 29.28521267182223298_dp, & & 168.34108626834759548_dp, 30.48695101230148552_dp, & & 175.12910064316457692_dp, 31.22051723140710777_dp, & & -178.00000000000002842_dp, 31.46720374958099598_dp, & & -171.12910064316457692_dp, 31.22051723140710777_dp, & & -164.34108626834762390_dp, 30.48695101230148552_dp, & & -157.71017626763574526_dp, 29.28521267182223298_dp, & & -151.29523174265207786_dp, 27.64411687523928052_dp, & & -145.13620894054648147_dp, 25.59967735271198208_dp, & & -139.25381666270027381_dp, 23.19203115841211726_dp, & & -133.65157520973266969_dp, 20.46271167815037728_dp, & & -128.31914580979304219_dp, 17.45256126555711873_dp, & & -123.23592626204654721_dp, 14.20035582641498806_dp, & & -118.37425008640165913_dp, 10.74206713847307348_dp, & & -113.70187269530693186_dp, 7.11062322287716331_dp, & & -109.18367588372132104_dp, 3.33602075627005812_dp, & & -104.78265670493266271_dp, -0.55433148808729282_dp, & & -100.46031614728222792_dp, -4.53512373056416251_dp, & & -96.17656220612794016_dp, -8.58256192749418645_dp, & & -91.88921878897831164_dp, -12.67371220268469578_dp, & & -87.55320457280681978_dp, -16.78577161643180204_dp, & & -83.11942634360052296_dp, -20.89524495725207132_dp, & & -78.53343020155088539_dp, -24.97699141478170048_dp, & & -73.73388667252179118_dp, -29.00309226896920123_dp, & & -68.65107920217620574_dp, -32.94148403257256064_dp, & & -63.20576633421556068_dp, -36.75430974219705860_dp, & & -57.30916699148811233_dp, -40.39598469592146301_dp, & & -50.86545369174167774_dp, -43.81109166541020983_dp, & & -43.77902429867569367_dp, -46.93247902115530223_dp, & & -35.96960367385943158_dp, -49.68040408706254141_dp, & & -27.39769711559288723_dp, -51.96422430511800883_dp, & & -18.09872704828334022_dp, -53.68864395349499574_dp, & & -8.21451927262477888_dp, -54.76594013073760436_dp, & & 1.99999999999999556_dp, -64.48162218246979194_dp, & & 17.98037118830460201_dp, -63.76341508305665684_dp, & & 32.62428016143745424_dp, -61.72174914658255318_dp, & & 45.26325159838053480_dp, -58.62403790832021144_dp, & & 55.90710322947827393_dp, -54.76300160634040992_dp, & & 64.89227178252910733_dp, -50.38082342875218700_dp, & & 72.61131289029847835_dp, -45.65520975111467550_dp, & & 79.40370496549049051_dp, -40.71192873614232610_dp, & & 85.53547443418635510_dp, -35.64102404029981841_dp, & & 91.20945067766287195_dp, -30.50961194071436822_dp, & & 96.58116190590757810_dp, -25.37072818781791383_dp, & & 101.77265218881962028_dp, -20.26925377022463337_dp, & & 106.88281945262124850_dp, -15.24594064653214254_dp, & & 111.99465974304708027_dp, -10.34025377357217934_dp, & & 117.18006743498143862_dp, -5.59247248797413388_dp, & & 122.50268451884780063_dp, -1.04529436283411115_dp, & & 128.01906809075350679_dp, 3.25496397729521814_dp, & & 133.77826339847268855_dp, 7.25759760165333567_dp, & & 139.81978239976487544_dp, 10.90731557455581502_dp, & & 146.17005258660478262_dp, 14.14485028302005709_dp, & & 152.83768595785250000_dp, 16.90865838596959492_dp, & & 159.80845268931921055_dp, 19.13794220572577487_dp, & & 167.04151372648448159_dp, 20.77699151155475477_dp, & & 174.46891148411387462_dp, 21.78042227826199806_dp, & & -178.00000000000002842_dp, 22.11837781753019527_dp, & & -170.46891148411387462_dp, 21.78042227826199806_dp, & & -163.04151372648448159_dp, 20.77699151155475477_dp, & & -155.80845268931921055_dp, 19.13794220572577487_dp, & & -148.83768595785252842_dp, 16.90865838596959492_dp, & & -142.17005258660481104_dp, 14.14485028302005709_dp, & & -135.81978239976487544_dp, 10.90731557455581502_dp, & & -129.77826339847271697_dp, 7.25759760165333567_dp, & & -124.01906809075352101_dp, 3.25496397729521814_dp, & & -118.50268451884778642_dp, -1.04529436283411115_dp, & & -113.18006743498142441_dp, -5.59247248797413388_dp, & & -107.99465974304709448_dp, -10.34025377357217934_dp, & & -102.88281945262124850_dp, -15.24594064653214254_dp, & & -97.77265218881962028_dp, -20.26925377022463337_dp, & & -92.58116190590757810_dp, -25.37072818781791383_dp, & & -87.20945067766288616_dp, -30.50961194071436822_dp, & & -81.53547443418635510_dp, -35.64102404029981841_dp, & & -75.40370496549049051_dp, -40.71192873614232610_dp, & & -68.61131289029849256_dp, -45.65520975111467550_dp, & & -60.89227178252910733_dp, -50.38082342875218700_dp, & & -51.90710322947828104_dp, -54.76300160634040992_dp, & & -41.26325159838054901_dp, -58.62403790832021144_dp, & & -28.62428016143746490_dp, -61.72174914658255318_dp, & & -13.98037118830460557_dp, -63.76341508305665684_dp, & & 1.99999999999999489_dp, -74.69966643845165777_dp, & & 26.73698176730493614_dp, -73.50791820369254026_dp, & & 46.45208209886830275_dp, -70.36956689494368788_dp, & & 60.80977159628141493_dp, -66.05660901455554779_dp, & & 71.45848324441054444_dp, -61.11675773175377913_dp, & & 79.80759217874309286_dp, -55.85321872580696834_dp, & & 86.74466514239207982_dp, -50.43213273346431436_dp, & & 92.80540124701705906_dp, -44.95206516381727369_dp, & & 98.32251211109890221_dp, -39.47845092120540045_dp, & & 103.51336526710929320_dp, -34.06038011063251503_dp, & & 108.52834753231333309_dp, -28.73921107936183361_dp, & & 113.47768355265095863_dp, -23.55333544230752452_dp, & & 118.44656525084906207_dp, -18.54103902309049090_dp, & & 123.50369215326308847_dp, -13.74234202233444968_dp, & & 128.70582948829564884_dp, -9.20020460514958671_dp, & & 134.09971622507504208_dp, -4.96122571165357140_dp, & & 139.72202213508867885_dp, -1.07580930674149267_dp, & & 145.59779383610683112_dp, 2.40232548413760538_dp, & & 151.73783261271663036_dp, 5.41746050329454931_dp, & & 158.13565186637288207_dp, 7.91416414131925894_dp, & & 164.76497871932286898_dp, 9.84055665005913660_dp, & & 171.57900980622665088_dp, 11.15231427016150256_dp, & & 178.51254101195692670_dp, 11.81685015330946520_dp, & & -174.51254101195692670_dp, 11.81685015330946520_dp, & & -167.57900980622667930_dp, 11.15231427016150256_dp, & & -160.76497871932289740_dp, 9.84055665005913660_dp, & & -154.13565186637291049_dp, 7.91416414131925894_dp, & & -147.73783261271665879_dp, 5.41746050329454931_dp, & & -141.59779383610683112_dp, 2.40232548413760538_dp, & & -135.72202213508870727_dp, -1.07580930674149267_dp, & & -130.09971622507501365_dp, -4.96122571165357140_dp, & & -124.70582948829567727_dp, -9.20020460514958671_dp, & & -119.50369215326308847_dp, -13.74234202233444968_dp, & & -114.44656525084907628_dp, -18.54103902309049090_dp, & & -109.47768355265095863_dp, -23.55333544230752452_dp, & & -104.52834753231334730_dp, -28.73921107936183361_dp, & & -99.51336526710929320_dp, -34.06038011063251503_dp, & & -94.32251211109890221_dp, -39.47845092120540045_dp, & & -88.80540124701705906_dp, -44.95206516381727369_dp, & & -82.74466514239209403_dp, -50.43213273346431436_dp, & & -75.80759217874310707_dp, -55.85321872580696834_dp, & & -67.45848324441054444_dp, -61.11675773175377913_dp, & & -56.80977159628143625_dp, -66.05660901455554779_dp, & & -42.45208209886832407_dp, -70.36956689494368788_dp, & & -22.73698176730495391_dp, -73.50791820369254026_dp, & & 1.99999999999999600_dp, -85.78166687108885924_dp, & & 61.87324357773108119_dp, -82.33459525449173100_dp, & & 80.27768438369075454_dp, -76.54083121758115738_dp, & & 89.42499621356618889_dp, -70.41813086842684299_dp, & & 95.82057104813959825_dp, -64.24948773689966686_dp, & & 101.11263541171206271_dp, -58.11928872485961506_dp, & & 105.90317607822892398_dp, -52.07335345715245722_dp, & & 110.47134737780878311_dp, -46.14758359783259323_dp, & & 114.97138028465980142_dp, -40.37601690822956613_dp, & & 119.50120511177952665_dp, -34.79395188944583595_dp, & & 124.13018318901127657_dp, -29.43951176908901957_dp, & & 128.91129483912590104_dp, -24.35455549606792047_dp, & & 133.88641650799124250_dp, -19.58517424494497305_dp, & & 139.08801440266989857_dp, -15.18177122287804615_dp, & & 144.53869867413172301_dp, -11.19861017149900739_dp, & & 150.24942513902280439_dp, -7.69266569608215622_dp, & & 156.21700460768090579_dp, -4.72161659803606781_dp, & & 162.42170502724644621_dp, -2.34091526057735644_dp, & & 168.82591385569978115_dp, -0.60006016949341945_dp, & & 175.37485237931247184_dp, 0.46152536384328491_dp, & & -178.00000000000002842_dp, 0.81833312891115950_dp, & & -171.37485237931247184_dp, 0.46152536384328491_dp, & & -164.82591385569978115_dp, -0.60006016949341945_dp, & & -158.42170502724641779_dp, -2.34091526057735644_dp, & & -152.21700460768090579_dp, -4.72161659803606781_dp, & & -146.24942513902283281_dp, -7.69266569608215622_dp, & & -140.53869867413175143_dp, -11.19861017149900739_dp, & & -135.08801440266989857_dp, -15.18177122287804615_dp, & & -129.88641650799127092_dp, -19.58517424494497305_dp, & & -124.91129483912591525_dp, -24.35455549606792047_dp, & & -120.13018318901130499_dp, -29.43951176908901957_dp, & & -115.50120511177955507_dp, -34.79395188944583595_dp, & & -110.97138028465980142_dp, -40.37601690822956613_dp, & & -106.47134737780881153_dp, -46.14758359783259323_dp, & & -101.90317607822893820_dp, -52.07335345715245722_dp, & & -97.11263541171207692_dp, -58.11928872485961506_dp, & & -91.82057104813961246_dp, -64.24948773689966686_dp, & & -85.42499621356618889_dp, -70.41813086842684299_dp, & & -76.27768438369074033_dp, -76.54083121758115738_dp, & & -57.87324357773110250_dp, -82.33459525449173100_dp, & & -178.00000000000002842_dp, -82.33591847955618448_dp, & & 143.22778135121401988_dp, -79.54230031515010069_dp, & & 128.60898756414843547_dp, -73.87349687490876704_dp, & & 124.32470998353319658_dp, -67.47696540683871547_dp, & & 124.01228809564837263_dp, -60.93070105520797597_dp, & & 125.59604777627561134_dp, -54.43751136022503090_dp, & & 128.28024682035200499_dp, -48.10755262562803125_dp, & & 131.71480673751509016_dp, -42.02394287044850074_dp, & & 135.73150447900647464_dp, -36.26259289105208694_dp, & & 140.24424010430539056_dp, -30.89942669541638764_dp, & & 145.20455296082769792_dp, -26.01294410398721979_dp, & & 150.57821873782722832_dp, -21.68429473238734317_dp, & & 156.33085313434582986_dp, -17.99542592709287092_dp, & & 162.41822389716838870_dp, -15.02545489467462048_dp, & & 168.78020742076520833_dp, -12.84550987892848184_dp, & & 175.33870321353637678_dp, -11.51266748991093536_dp, & & -178.00000000000002842_dp, -11.06408152044378745_dp, & & -171.33870321353637678_dp, -11.51266748991093536_dp, & & -164.78020742076523675_dp, -12.84550987892848184_dp, & & -158.41822389716841712_dp, -15.02545489467462048_dp, & & -152.33085313434585828_dp, -17.99542592709287092_dp, & & -146.57821873782722832_dp, -21.68429473238734317_dp, & & -141.20455296082769792_dp, -26.01294410398721979_dp, & & -136.24424010430539056_dp, -30.89942669541638764_dp, & & -131.73150447900647464_dp, -36.26259289105208694_dp, & & -127.71480673751507595_dp, -42.02394287044850074_dp, & & -124.28024682035203341_dp, -48.10755262562803125_dp, & & -121.59604777627559713_dp, -54.43751136022503090_dp, & & -120.01228809564838684_dp, -60.93070105520797597_dp, & & -120.32470998353322500_dp, -67.47696540683871547_dp, & & -124.60898756414846389_dp, -73.87349687490876704_dp, & & -139.22778135121404830_dp, -79.54230031515010069_dp, & & -178.00000000000002842_dp, -69.79343463141964321_dp, & & 167.63134400963940607_dp, -68.62304968612301082_dp, & & 156.89871695437406629_dp, -65.48334135458465255_dp, & & 150.54736374030309776_dp, -61.10651485448561715_dp, & & 147.65287319536125210_dp, -56.10790502157546911_dp, & & 147.17953110490091717_dp, -50.89570090116846757_dp, & & 148.39568884343808008_dp, -45.73876083396746139_dp, & & 150.83543074335267420_dp, -40.82969132012996027_dp, & & 154.20166627754437627_dp, -36.32066069380964279_dp, & & 158.29374271187359113_dp, -32.34078423455542861_dp, & & 162.96127389198957758_dp, -29.00296899656860816_dp, & & 168.07572003197756771_dp, -26.40464312032804983_dp, & & 173.51374580520655400_dp, -24.62495078480371546_dp, & & 179.14909640850049755_dp, -23.72034636702055366_dp, & & -175.14909640850046912_dp, -23.72034636702055366_dp, & & -169.51374580520655400_dp, -24.62495078480371546_dp, & & -164.07572003197756771_dp, -26.40464312032806049_dp, & & -158.96127389198957758_dp, -29.00296899656861527_dp, & & -154.29374271187361956_dp, -32.34078423455542861_dp, & & -150.20166627754440469_dp, -36.32066069380964279_dp, & & -146.83543074335270262_dp, -40.82969132012996027_dp, & & -144.39568884343808008_dp, -45.73876083396746139_dp, & & -143.17953110490091717_dp, -50.89570090116849599_dp, & & -143.65287319536128052_dp, -56.10790502157547621_dp, & & -146.54736374030309776_dp, -61.10651485448561715_dp, & & -152.89871695437406629_dp, -65.48334135458465255_dp, & & -163.63134400963940607_dp, -68.62304968612301082_dp, & & -178.00000000000002842_dp, -56.85258712078380228_dp, & & 176.37525674045562596_dp, -56.23764348052387874_dp, & & 171.72066876086481102_dp, -54.50623527875517738_dp, & & 168.62326642581021474_dp, -51.94637057143930292_dp, & & 167.22080918295719698_dp, -48.91487419033276751_dp, & & 167.36621335583259906_dp, -45.75617589753742465_dp, & & 168.79950790329058918_dp, -42.76713193279059766_dp, & & 171.24101930549505823_dp, -40.18968456371464271_dp, & & 174.42266827604018431_dp, -38.21219158951615213_dp, & & 178.09065200901943626_dp, -36.97055608025468132_dp, & & -178.00000000000002842_dp, -36.54741287921620341_dp, & & -174.09065200901946469_dp, -36.97055608025468132_dp, & & -170.42266827604018431_dp, -38.21219158951615213_dp, & & -167.24101930549508666_dp, -40.18968456371464271_dp, & & -164.79950790329061761_dp, -42.76713193279059766_dp, & & -163.36621335583262749_dp, -45.75617589753742465_dp, & & -163.22080918295719698_dp, -48.91487419033276751_dp, & & -164.62326642581024316_dp, -51.94637057143930292_dp, & & -167.72066876086483944_dp, -54.50623527875517738_dp, & & -172.37525674045562596_dp, -56.23764348052387874_dp ] grid = atlas_ReducedGaussianGrid(nx, projection=atlas_RotatedSchmidtProjection(stretch,centre,angle) ) jglo = 0 do j = 1, grid%ny() do i = 1, grid%nx(j) FCTEST_CHECK_CLOSE( grid%lonlat(i,j), ([lonlat(2*jglo+1),lonlat(2*jglo+2)]), 1.e-9_dp ); jglo = jglo + 1 enddo enddo bounding_box = grid%lonlat_bounding_box() FCTEST_CHECK_CLOSE( bounding_box%west(), 0._dp, 1.e-10_dp ) FCTEST_CHECK_CLOSE( bounding_box%east(), 360._dp, 1.e-10_dp ) FCTEST_CHECK_CLOSE( bounding_box%south(), -90._dp, 1.e-10_dp ) FCTEST_CHECK_CLOSE( bounding_box%north(), 90._dp, 1.e-10_dp ) call bounding_box%final() call grid%final() END_TEST ! ----------------------------------------------------------------------------- END_TESTSUITE atlas-0.46.0/src/tests/grid/fctest_griddistribution.F900000664000175000017500000000622015160212070023156 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. ! This File contains Unit Tests for testing the ! C++ / Fortran Interfaces to the Mesh Datastructure ! @author Willem Deconinck #include "fckit/fctest.h" ! ----------------------------------------------------------------------------- TESTSUITE(fctest_GridDist) ! ----------------------------------------------------------------------------- TESTSUITE_INIT use atlas_module call atlas_library%initialise() END_TESTSUITE_INIT ! ----------------------------------------------------------------------------- TESTSUITE_FINALIZE use atlas_module call atlas_library%finalise() END_TESTSUITE_FINALIZE ! ----------------------------------------------------------------------------- TEST( test_griddist ) #if 1 use atlas_module implicit none type(atlas_StructuredGrid) :: grid type(atlas_Mesh) :: mesh type(atlas_Output) :: gmsh type(atlas_MeshGenerator) :: meshgenerator type(atlas_GridDistribution) :: griddistribution integer, allocatable :: part(:) integer(ATLAS_KIND_IDX) :: jnode grid = atlas_StructuredGrid("O16") allocate( part(grid%size()) ) do jnode=1,grid%size()/3 part(jnode) = 1 enddo do jnode=grid%size()/3+1,grid%size() part(jnode) = 1 enddo griddistribution = atlas_GridDistribution(part, part0=1) FCTEST_CHECK_EQUAL( griddistribution%nb_partitions(), 1 ) FCTEST_CHECK_EQUAL( griddistribution%nb_pts(), [ int(grid%size(),ATLAS_KIND_IDX) ] ) FCTEST_CHECK_EQUAL( grid%owners(), 1 ) meshgenerator = atlas_MeshGenerator() mesh = meshgenerator%generate(grid,griddistribution) FCTEST_CHECK_EQUAL( mesh%owners(), 1 ) deallocate(part) FCTEST_CHECK_EQUAL( grid%owners(), 2 ) gmsh = atlas_output_Gmsh("testf3.msh") call gmsh%write(mesh) do jnode=1,grid%size() FCTEST_CHECK_EQUAL( griddistribution%partition(jnode), 0 ) enddo call griddistribution%final() call mesh%final() FCTEST_CHECK_EQUAL( grid%owners(), 1 ) call gmsh%final() call grid%final() call meshgenerator%final() #endif END_TEST ! ----------------------------------------------------------------------------- TEST( test_griddist_from_partitioner ) use atlas_module implicit none type(atlas_StructuredGrid) :: grid type(atlas_Partitioner) :: partitioner type(atlas_GridDistribution) :: griddistribution integer(ATLAS_KIND_IDX) :: jnode grid = atlas_StructuredGrid("O16") griddistribution = atlas_GridDistribution(grid,atlas_Partitioner("serial")) FCTEST_CHECK_EQUAL( griddistribution%nb_partitions(), 1 ) FCTEST_CHECK_EQUAL( griddistribution%nb_pts(), [ int(grid%size(),ATLAS_KIND_IDX) ] ) do jnode=1,grid%size() FCTEST_CHECK_EQUAL( griddistribution%partition(jnode), 0 ) enddo call griddistribution%final() call grid%final() END_TEST ! ----------------------------------------------------------------------------- END_TESTSUITE atlas-0.46.0/src/tests/grid/test_field.cc0000664000175000017500000001347615160212070020405 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "eckit/runtime/Tool.h" #include "eckit/value/CompositeParams.h" #include "atlas/array.h" #include "atlas/field/FieldSet.h" #include "atlas/grid.h" #include "atlas/grid/Grid.h" #include "atlas/option.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/runtime/Log.h" #include "atlas/util/ObjectHandle.h" #include "tests/AtlasTestEnvironment.h" using namespace std; using namespace eckit; using namespace atlas; using namespace atlas::grid; //----------------------------------------------------------------------------- namespace atlas { namespace test { //----------------------------------------------------------------------------- void take_array(const array::Array& arr) { EXPECT(arr.size() == 20); } class TakeArray { public: TakeArray(const array::Array& arr) { EXPECT(arr.size() == 20); } }; //----------------------------------------------------------------------------- CASE("test_fieldcreator") { Field field(util::Config("creator", "ArraySpec")("shape", array::make_shape(10, 2))( "datatype", array::DataType::real32().str())("name", "myfield")); EXPECT(field.datatype() == array::DataType::real32()); EXPECT(field.name() == "myfield"); Grid g("O6"); Field arr(util::Config("creator", "ArraySpec")("shape", array::make_shape(10, 2))); EXPECT(arr.shape(0) == 10); EXPECT(arr.shape(1) == 2); EXPECT(arr.datatype() == array::DataType::real64()); util::Config ifs_parameters = util::Config("creator", "IFS")("nlev", 137)("nproma", 10)("ngptot", g.size()); Log::info() << "Creating IFS field " << std::endl; Field ifs(util::Config(ifs_parameters)("name", "myfield")("datatype", array::DataType::int32().str())("nvar", 8)); ATLAS_DEBUG_VAR(ifs); EXPECT(ifs.shape(0) == 36); EXPECT(ifs.shape(1) == 8); EXPECT(ifs.shape(2) == 137); EXPECT(ifs.shape(3) == 10); Log::flush(); } CASE("test_implicit_conversion") { Field field("tmp", array::make_datatype(), array::make_shape(10, 2)); const array::Array& const_array = field; array::Array& array = field; array::ArrayView arrv = array::make_view(array); arrv(0, 0) = 8.; array::ArrayView carrv = array::make_view(const_array); EXPECT(carrv(0, 0) == 8.); array::ArrayView cfieldv = array::make_view(field); EXPECT(cfieldv(0, 0) == 8.); take_array(field); TakeArray ta(field); const Field& f = field; TakeArray cta(f); } CASE("test_clone") { Field org("origin", array::make_datatype(), array::make_shape(10, 2)); array::ArrayView orgv = array::make_view(org); double zz = 0.0; for (size_t ii = 0; ii < org.shape()[0]; ++ii) { for (size_t jj = 0; jj < org.shape()[1]; ++jj) { zz += 1.0; orgv(ii, jj) = zz; } } Field dst = org.clone(); for (size_t ii = 0; ii < org.shape()[0]; ++ii) { for (size_t jj = 0; jj < org.shape()[1]; ++jj) { orgv(ii, jj) = -999.999; } } EXPECT(dst.rank() == 2); EXPECT(dst.shape()[0] == 10); EXPECT(dst.shape()[1] == 2); array::ArrayView dstv = array::make_view(dst); zz = 0.0; for (size_t ii = 0; ii < dst.shape()[0]; ++ii) { for (size_t jj = 0; jj < dst.shape()[1]; ++jj) { zz += 1.0; EXPECT(dstv(ii, jj) == zz); } } } CASE("test_wrap_rawdata_through_array") { std::vector rawdata(20, 8.); util::ObjectHandle array(array::Array::wrap(rawdata.data(), array::make_shape(10, 2))); Field field("wrapped", array.get()); EXPECT(array->owners() == 2); const array::ArrayView cfieldv = array::make_view(field); EXPECT(cfieldv(9, 1) == 8.); } CASE("test_wrap_rawdata_direct") { std::vector rawdata(10 * 2, 8.); Field field("wrapped", rawdata.data(), array::make_shape(10, 2)); EXPECT(field.array().owners() == 1); const array::ArrayView cfieldv = array::make_view(field); EXPECT(cfieldv(9, 1) == 8.); } CASE("test_wrap_rawdata_through_field") { std::vector rawdata(10 * 2, 8.); Field field("name", rawdata.data(), array::make_shape(10, 2)); } CASE("test_field_aligned") { using namespace array; auto check_field = [](const Field& field) { EXPECT_EQ(field.shape()[0], 10); EXPECT_EQ(field.shape()[1], 5); EXPECT_EQ(field.shape()[2], 3); EXPECT_EQ(field.size(), 10 * 5 * 3); EXPECT_EQ(field.contiguous(), false); EXPECT_EQ(field.strides()[0], 5 * 4); EXPECT_EQ(field.strides()[1], 4); EXPECT_EQ(field.strides()[2], 1); }; SECTION("field(name,datatype,spec)") { Field field("name", array::make_datatype(), ArraySpec{make_shape(10, 5, 3), ArrayAlignment(4)}); check_field(field); } SECTION("field(config)") { Field field(util::Config("creator", "ArraySpec") | // util::Config("datatype", array::make_datatype().str()) | // option::shape({10, 5, 3}) | // option::alignment(4)); check_field(field); } } //----------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/grid/test_distribution_regular_bands.cc0000664000175000017500000001635615160212070024731 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include #include #include #include #include "atlas/array.h" #include "atlas/field.h" #include "atlas/functionspace.h" #include "atlas/grid.h" #include "atlas/grid/detail/distribution/BandsDistribution.h" #include "atlas/grid/detail/distribution/SerialDistribution.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/parallel/omp/omp.h" #include "atlas/util/Config.h" #include "tests/AtlasTestEnvironment.h" using Grid = atlas::Grid; using Config = atlas::util::Config; namespace atlas { namespace test { atlas::FieldSet getIJ(const atlas::functionspace::StructuredColumns& fs) { atlas::FieldSet ij; // auto i = atlas::array::make_view( fs.index_i() ); // auto j = atlas::array::make_view( fs.index_j() ); ij.add(fs.index_i()); ij.add(fs.index_j()); return ij; } //----------------------------------------------------------------------------- CASE("test_bands") { int nproc = mpi::size(); StructuredGrid grid = Grid("L200x101"); grid::Distribution dist(grid, atlas::util::Config("type", "checkerboard") | Config("bands", nproc)); for (int i = 1; i < grid.size(); i++) { EXPECT(dist.partition(i - 1) <= dist.partition(i)); } } CASE("test_regular_bands") { int nproc = mpi::size(); std::vector gridnames = {"L40x21", "L40x20", "Slat100x50"}; for (auto gridname : gridnames) { SECTION(gridname) { auto grid = RegularGrid(gridname); const int nx = grid.nx(); const int ny = grid.ny(); bool equivalent_with_checkerboard = (ny % nproc) == 0; // Compare regular band & checkerboard distributions when possible grid::Distribution checkerboard(grid, grid::Partitioner("checkerboard", Config("bands", nproc))); grid::Distribution regularbands(grid, grid::Partitioner("regular_bands")); EXPECT(regularbands.footprint() < 100); const auto& nb_pts2 = regularbands.nb_pts(); int count = 0; for (int i = 0; i < grid.size(); i++) { if (regularbands.partition(i) == mpi::rank()) { count++; } } EXPECT_EQ(count, nb_pts2[mpi::rank()]); if (equivalent_with_checkerboard) { for (int i = 0; i < grid.size(); i++) { EXPECT_EQ(checkerboard.partition(i), regularbands.partition(i)); } } else { Log::warning() << "WARNING: checkerboard not expected to be equal!" << std::endl; } // Expect each points with constant latitude to be on the same partition as the first on each latitude. for (int iy = 0, jglo = 0; iy < ny; iy++) { int jglo0 = jglo; for (int ix = 0; ix < nx; ix++, jglo++) { EXPECT_EQ(regularbands.partition(jglo), regularbands.partition(jglo0)); } } functionspace::StructuredColumns fs_checkerboard(grid, checkerboard, Config("halo", 1) | Config("periodic_points", true)); functionspace::StructuredColumns fs_regularbands(grid, regularbands, Config("halo", 1) | Config("periodic_points", true)); auto ij1 = getIJ(fs_checkerboard); auto ij2 = getIJ(fs_regularbands); fs_checkerboard.haloExchange(ij1); fs_regularbands.haloExchange(ij2); if (equivalent_with_checkerboard) { EXPECT_EQ(fs_regularbands.size(), fs_checkerboard.size()); EXPECT_EQ(fs_regularbands.sizeOwned(), fs_checkerboard.sizeOwned()); auto i1 = array::make_view(ij1[0]); auto j1 = array::make_view(ij1[1]); auto i2 = array::make_view(ij2[0]); auto j2 = array::make_view(ij2[1]); for (int k = 0; k < fs_checkerboard.sizeOwned(); k++) { EXPECT_EQ(i1[k], i2[k]); EXPECT_EQ(j1[k], j2[k]); } } for (int j = fs_regularbands.j_begin_halo(); j < fs_regularbands.j_end_halo(); j++) { EXPECT_EQ(fs_regularbands.i_begin_halo(j), -1); EXPECT_EQ(fs_regularbands.i_end_halo(j), nx + 2); } } } } CASE("test regular_bands performance test") { // auto grid = StructuredGrid( "L40000x20000" ); //-- > test takes too long( less than 15 seconds ) // Example timings for L40000x20000: // └─test regular_bands performance test │ 1 │ 11.90776s // ├─inline access │ 1 │ 3.52238s // ├─virtual access │ 1 │ 4.11881s // └─virtual cached access │ 1 │ 4.02159s auto grid = StructuredGrid("L5000x2500"); // -> negligible time. auto dist = grid::Distribution(grid, grid::Partitioner("regular_bands")); auto& dist_direct = dynamic_cast&>(*dist.get()); // Trick to prevent the compiler from compiling out a loop if it sees the result was not used. struct DoNotCompileOut { int x = 0; ~DoNotCompileOut() { Log::debug() << x << std::endl; } ATLAS_ALWAYS_INLINE void operator()(int p) { x = p; } } do_not_compile_out; gidx_t size = grid.size(); ATLAS_TRACE_SCOPE("inline access") { for (gidx_t n = 0; n < size; ++n) { // inline function call do_not_compile_out(dist_direct.function(n)); } } ATLAS_TRACE_SCOPE("virtual access") { for (gidx_t n = 0; n < size; ++n) { // virtual function call do_not_compile_out(dist.partition(n)); } } ATLAS_TRACE_SCOPE("virtual cached access") { gidx_t n = 0; grid::Distribution::partition_t part(grid.nxmax()); for (idx_t j = 0; j < grid.ny(); ++j) { const idx_t nx = grid.nx(j); dist.partition(n, n + nx, part); // this is one virtual call, which in turn has inline-access for nx(j) evaluations int* P = part.data(); for (idx_t i = 0; i < nx; ++i) { do_not_compile_out(P[i]); } n += grid.nx(j); } } } CASE("test regular_bands with a very large grid") { auto grid = StructuredGrid(sizeof(atlas::idx_t) == 4 ? "L40000x20000" : "L160000x80000"); auto dist = grid::Distribution(grid, grid::Partitioner("regular_bands")); } //----------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/grid/test_grid_hash.cc0000664000175000017500000000355015160212070021242 0ustar alastairalastair/* * (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/domain.h" #include "atlas/grid/Grid.h" #include "atlas/runtime/Log.h" #include "atlas/util/Config.h" #include "eckit/utils/MD5.h" #include "tests/AtlasTestEnvironment.h" using Hash = eckit::MD5; namespace atlas { namespace test { //---------------------------------------------------------------------------------------------------------------------- CASE("test_global") { auto grids = { std::make_tuple(Grid("O32"), "e8d76a652ea937615276383aba43c912"), std::make_tuple(Grid("O32", Domain()), "e8d76a652ea937615276383aba43c912"), std::make_tuple(Grid("O32", RectangularDomain({0, 360}, {90, -90})), "e8d76a652ea937615276383aba43c912"), std::make_tuple(Grid("O32", ZonalBandDomain({90, -90})), "e8d76a652ea937615276383aba43c912"), }; int c{0}; for (auto entry : grids) { Grid grid = std::get<0>(entry); std::string hash = std::get<1>(entry); SECTION("grid: " + std::to_string(c)) { Hash h; grid.hash(h); if (hash.empty()) { Log::info() << "grid " << c << " hash = " << std::string(h) << std::endl; } EXPECT(std::string(h) == hash); } c++; } } //----------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/grid/fctest_regional_grids.F900000664000175000017500000001407615160212070022571 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. ! @author Willem Deconinck #include "fckit/fctest.h" module fixture_reg_grid use atlas_module use, intrinsic :: iso_c_binding, only : c_double implicit none contains subroutine print_spec( grid ) type(atlas_Grid) :: grid type(atlas_Config) :: spec spec = grid%spec() write(0,*) spec%json() call spec%final() end subroutine end module ! ----------------------------------------------------------------------------- TESTSUITE_WITH_FIXTURE(fctest_regional_Grid, fixture_reg_grid) ! ----------------------------------------------------------------------------- TESTSUITE_INIT call atlas_library%initialise() END_TESTSUITE_INIT ! ----------------------------------------------------------------------------- TESTSUITE_FINALIZE call atlas_library%finalise() END_TESTSUITE_FINALIZE TEST( simple_regional_grid ) #if 1 type(atlas_Grid) :: grid1, grid2 grid1 = atlas_RegionalGrid( nx=11, ny=11, xy_min=[0._dp,-5._dp], xy_max=[10._dp,5._dp] ) grid2 = atlas_RegionalGrid( nx=11, ny=11, north=5._dp, west=0._dp, south=-5._dp, east=10._dp ) call print_spec(grid1) call print_spec(grid2) FCTEST_CHECK_EQUAL( grid1%uid() , "0aae0dfb8141e8c70ef63108e6c59ac1" ) FCTEST_CHECK_EQUAL( grid1%uid() , grid2%uid() ) call grid1%final() call grid2%final() #endif END_TEST TEST( rotated_regional_grid ) #if 1 type(atlas_Grid) :: grid1, grid2 grid1 = atlas_RegionalGrid( nx=11, ny=11, xy_min=[0._dp,-5._dp], xy_max=[10._dp,5._dp], & & projection=atlas_RotatedLonLatProjection([40._dp,20._dp]) ) grid2 = atlas_RegionalGrid( nx=11, ny=11, north=5._dp, west=0._dp, south=-5._dp, east=10._dp, & & projection=atlas_RotatedLonLatProjection([40._dp,20._dp]) ) call print_spec(grid1) FCTEST_CHECK_EQUAL( grid1%uid() , grid2%uid() ) call grid1%final() call grid2%final() #endif END_TEST TEST( lambert_grid ) #if 1 type(atlas_Grid) :: grid grid = atlas_RegionalGrid( nx=11, ny=11, dx=10000._dp,dy=10000._dp, & & xy_min=[-50000._dp,-50000._dp], & & projection=atlas_LambertConformalConicProjection(4.0_dp,50._dp) ) call print_spec(grid) call grid%final() #endif END_TEST TEST( test_regional_lonlat_grid_MF ) #if 1 ! Grid provided by Philippe Marguinaud, Meteo France type(atlas_StructuredGrid) :: grid type(atlas_LonLatRectangularDomain) :: bounding_box real(c_double), parameter :: zlonw = -20., zlone = +10., zlats = 10., zlatn = 50. integer(c_int), parameter :: ilons = 30, ilats = 40 real(c_double), parameter :: tol = 1.e-5_dp grid = atlas_RegionalGrid( nx=ilons, ny=ilats, north=zlatn, west=zlonw, south=zlats, east=zlone ) FCTEST_CHECK_EQUAL( grid%size(), ilons*ilats ) FCTEST_CHECK_CLOSE( grid%lonlat(1,1), ([-0.2000000000E+02_dp, 0.1000000000E+02_dp]), tol) FCTEST_CHECK_CLOSE( grid%lonlat(2,1), ([-0.1896551724E+02_dp, 0.1000000000E+02_dp]), tol) FCTEST_CHECK_CLOSE( grid%lonlat(ilons,1), ([ 0.1000000000E+02_dp, 0.1000000000E+02_dp]), tol) FCTEST_CHECK_CLOSE( grid%lonlat(1,2), ([-0.2000000000E+02_dp, 0.1102564103E+02_dp]), tol) FCTEST_CHECK_CLOSE( grid%lonlat(ilons,ilats), ([ 0.1000000000E+02_dp, 0.5000000000E+02_dp]), tol) bounding_box = grid%lonlat_bounding_box() FCTEST_CHECK_CLOSE( bounding_box%west(), -20.0_dp, tol ) FCTEST_CHECK_CLOSE( bounding_box%east(), +10.0_dp, tol ) FCTEST_CHECK_CLOSE( bounding_box%south(), +10.0_dp, tol ) FCTEST_CHECK_CLOSE( bounding_box%north(), +50.0_dp, tol ) FCTEST_CHECK_EQUAL( bounding_box%owners(), 2 ) call bounding_box%final() call grid%final() #endif END_TEST TEST( test_regional_lambert_grid_MF ) #if 1 ! Grid provided by Philippe Marguinaud, Meteo France type(atlas_StructuredGrid) :: grid type(atlas_LonLatRectangularDomain) :: bounding_box integer(c_int), parameter :: ndlon=64 integer(c_int), parameter :: ndglg=64 integer(c_int), parameter :: nux=53 integer(c_int), parameter :: nuy=53 real(c_double), parameter :: dxinmetres=50000. real(c_double), parameter :: dyinmetres=50000. real(c_double), parameter :: xmin = int(real(-nux,c_double)/2.) * dxinmetres real(c_double), parameter :: ymin = int(real(-nuy,c_double)/2.) * dyinmetres real(c_double), parameter :: ladindegrees=46.2 real(c_double), parameter :: latin1indegrees=46.2 real(c_double), parameter :: latin2indegrees=46.2 real(c_double), parameter :: lovindegrees=2.0 real(c_double), parameter :: tol = 1.e-5_dp grid = atlas_RegionalGrid( nx=ndlon, ny=ndglg, xy_min=[xmin,ymin], & & dx=dxinmetres, dy=dyinmetres, & & projection=atlas_LambertConformalConicProjection(lovindegrees,ladindegrees, & & latin1indegrees,latin2indegrees) ) FCTEST_CHECK_EQUAL( grid%size(), ndglg*ndlon ) FCTEST_CHECK_CLOSE( grid%lonlat(1,1), ([-0.1178699484E+02_dp, 0.3358923512E+02_dp]), tol) FCTEST_CHECK_CLOSE( grid%lonlat(2,1), ([-0.1126673471E+02_dp, 0.3366377557E+02_dp]), tol) FCTEST_CHECK_CLOSE( grid%lonlat(ndlon,1), ([ 0.2142256121E+02_dp, 0.3258651702E+02_dp]), tol) FCTEST_CHECK_CLOSE( grid%lonlat(1,2), ([-0.1187876836E+02_dp, 0.3402241700E+02_dp]), tol) FCTEST_CHECK_CLOSE( grid%lonlat(ndlon,ndglg), ([ 0.3452466369E+02_dp, 0.5925747619E+02_dp]), tol) bounding_box = grid%lonlat_bounding_box() FCTEST_CHECK_CLOSE( bounding_box%west(), -21.513504_dp, tol ) FCTEST_CHECK_CLOSE( bounding_box%east(), 34.524664_dp, tol ) FCTEST_CHECK_CLOSE( bounding_box%south(), 32.586516_dp, tol ) FCTEST_CHECK_CLOSE( bounding_box%north(), 62.587002_dp, tol ) FCTEST_CHECK_EQUAL( bounding_box%owners(), 1 ) call bounding_box%final() call grid%final() #endif END_TEST ! ----------------------------------------------------------------------------- END_TESTSUITE atlas-0.46.0/src/tests/grid/test_grid_cropping.cc0000664000175000017500000002065415160212070022144 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include #include #include #include "atlas/grid/Iterator.h" #include "atlas/grid/StructuredGrid.h" #include "atlas/mesh/Mesh.h" #include "atlas/meshgenerator.h" #include "atlas/output/Gmsh.h" #include "atlas/runtime/Log.h" #include "atlas/util/Config.h" #include "tests/AtlasTestEnvironment.h" using Config = atlas::util::Config; namespace atlas { namespace test { //----------------------------------------------------------------------------- namespace { struct Increments { double dx; double dy; Increments(double _dx, double _dy): dx(_dx), dy(_dy) {} }; static Grid RegularLL(const Increments& increments, const Domain& _domain) { // As MIR defines a "RegularLL" grid: // - If the domain is global or zonal band, the grid is periodic, with the domain's West defining the West boundary // and the East-most point is at (East-dx) where (East = West+360) // - If the domain is not a zonal band or global, the East-most point is at (East) double dx = increments.dx; double dy = increments.dy; RectangularDomain domain{_domain}; double n = domain.ymax(); double s = domain.ymin(); double w = domain.xmin(); double e = domain.xmax(); long nj = long((n - s) / dy) + 1; long ni = long((e - w) / dx) + long(domain.zonal_band() ? 0 : 1); using atlas::grid::LinearSpacing; StructuredGrid::XSpace xspace(LinearSpacing(w, e, ni, not domain.zonal_band())); StructuredGrid::YSpace yspace(LinearSpacing(n, s, nj)); return StructuredGrid(xspace, yspace, StructuredGrid::Projection(), domain); } static Grid RegularLL(const Increments& increments) { return RegularLL(increments, RectangularDomain{{0., 360.}, {-90., 90.}}); } static Grid RegularLL11(const Domain& domain) { return RegularLL(Increments{1., 1.}, domain); } static Grid RegularLL11() { return RegularLL(Increments{1., 1.}, RectangularDomain{{0., 360.}, {-90., 90.}}); } } // namespace CASE("Test number of points for global grids") { // --- Global grids --- // EXPECT(Grid("O16").size() == 1600); EXPECT(Grid("O1280").size() == 6599680); EXPECT(RegularLL(Increments{1., 1.}).size() == 65160); } CASE("Test number of points for non-global grids") { Grid global = RegularLL11(); auto test_regional_size = [&](const Domain& domain, idx_t size) -> bool { if (RegularLL11(domain).size() != size) { Log::error() << "RegularLL11( domain ).size() != size ---> [ " << RegularLL11(domain).size() << " != " << size << " ]" << std::endl; Log::error() << " with Domain = " << domain << std::endl; return false; } return true; }; auto test_cropping_size = [&](const Domain& domain, idx_t size) -> bool { if (Grid(global, domain).size() != size) { Log::error() << "Grid( global, domain ).size() != size ---> [ " << Grid(global, domain).size() << " != " << size << " ]" << std::endl; Log::error() << " with Domain = " << domain << std::endl; return false; } return true; }; // -- Parallels { // North Pole latitude EXPECT(test_regional_size(RectangularDomain{{0., 360.}, {90., 90.}}, 360)); EXPECT(test_cropping_size(RectangularDomain{{0., 360.}, {90., 90.}}, 360)); // North Pole latitude plus one extra EXPECT(test_regional_size(RectangularDomain{{0., 360.}, {89., 90.}}, 720)); EXPECT(test_cropping_size(RectangularDomain{{0., 360.}, {89., 90.}}, 720)); // Equator latitude EXPECT(test_regional_size(RectangularDomain{{0., 360.}, {0., 0.}}, 360)); EXPECT(test_cropping_size(RectangularDomain{{0., 360.}, {0., 0.}}, 360)); // South Pole latitude EXPECT(test_regional_size(RectangularDomain{{0., 360.}, {-90., -90.}}, 360)); EXPECT(test_cropping_size(RectangularDomain{{0., 360.}, {-90., -90.}}, 360)); // South Pole latitude plus one extra EXPECT(test_regional_size(RectangularDomain{{0., 360.}, {-90., -89.}}, 720)); EXPECT(test_cropping_size(RectangularDomain{{0., 360.}, {-90., -89.}}, 720)); } // -- Meridians { // Greenwich Meridian EXPECT(test_regional_size(RectangularDomain{{0., 0.}, {-90., 90.}}, 181)); EXPECT(test_cropping_size(RectangularDomain{{0., 0.}, {-90., 90.}}, 181)); // Greenwich Meridian + one to East EXPECT(test_regional_size(RectangularDomain{{0., 1.}, {-90., 90.}}, 2 * 181)); EXPECT(test_cropping_size(RectangularDomain{{0., 1.}, {-90., 90.}}, 2 * 181)); // Greenwich Meridian + one to West EXPECT(test_regional_size(RectangularDomain{{-1., 0.}, {-90., 90.}}, 2 * 181)); EXPECT(test_cropping_size(RectangularDomain{{-1., 0.}, {-90., 90.}}, 2 * 181)); // Meridian, one degree to the East of Greenwich EXPECT(test_regional_size(RectangularDomain{{-1., -1.}, {-90., 90.}}, 181)); EXPECT(test_cropping_size(RectangularDomain{{-1., -1.}, {-90., 90.}}, 181)); // Date line at 180 degrees East EXPECT(test_regional_size(RectangularDomain{{180., 180.}, {-90., 90.}}, 181)); EXPECT(test_cropping_size(RectangularDomain{{180., 180.}, {-90., 90.}}, 181)); // Date line at 180 degrees West EXPECT(test_regional_size(RectangularDomain{{-180., -180.}, {-90., 90.}}, 181)); EXPECT(test_cropping_size(RectangularDomain{{-180., -180.}, {-90., 90.}}, 181)); } // Crop to single point {0.,0.} EXPECT(test_cropping_size(RectangularDomain{{-0.5, 0.5}, {-0.5, 0.5}}, 1)); EXPECT(test_cropping_size(RectangularDomain{{0., 0.}, {0., 0.}}, 1)); // Regional grid with same domain leads to bounds given by domain --> 4 points EXPECT(test_regional_size(RectangularDomain{{-0.5, 0.5}, {-0.5, 0.5}}, 4)); EXPECT(test_regional_size(RectangularDomain{{0., 0.}, {0., 0.}}, 1)); } CASE("Test first point of cropped global grids (MIR-374)") { auto test_first_point = [](const std::string& name, const Domain& domain, const PointLonLat& p) -> bool { double eps = 1.e-6; Grid global_grid{name}; Grid cropped_grid{global_grid, domain}; PointLonLat first_point = *cropped_grid.lonlat().begin(); if (not is_approximately_equal(first_point.lon(), p.lon(), eps) || not is_approximately_equal(first_point.lat(), p.lat(), eps)) { auto old_precision = Log::error().precision(16); Log::error() << "First point doesn't match for Grid " << name << " with domain " << domain << std::endl; Log::error() << " first_point = " << first_point << std::endl; Log::error() << " expected = " << p << std::endl; Log::error().precision(old_precision); return false; } return true; }; // clang-format off EXPECT( test_first_point( "O16", RectangularDomain{ {-180.,180.}, {-90.,90.} }, PointLonLat{-180.,85.76058712044382} ) ); EXPECT( test_first_point( "O640", RectangularDomain{ {-180.,180.}, {-90.,90.} }, PointLonLat{-180.,89.89239644558999} ) ); EXPECT( test_first_point( "O16", RectangularDomain{ {-180.,180.}, {-60.,90.} }, PointLonLat{-180.,85.76058712044382} ) ); EXPECT( test_first_point( "O640", RectangularDomain{ {-180.,180.}, {-60.,90.} }, PointLonLat{-180.,89.89239644558999} ) ); EXPECT( test_first_point( "O16", RectangularDomain{ {-180.,180.}, {-90.,60.} }, PointLonLat{-180.,58.14295404920328} ) ); EXPECT( test_first_point( "O640", RectangularDomain{ {-180.,180.}, {-90.,60.} }, PointLonLat{-180.,59.953135752239} ) ); EXPECT( test_first_point( "O16", RectangularDomain{ {-10. ,10. }, {-10.,10.} }, PointLonLat{-9.473684210526358,8.306702856518804} ) ); EXPECT( test_first_point( "O640", RectangularDomain{ {-10. ,10. }, {-10.,10.} }, PointLonLat{-9.878048780487802,9.910190568389} ) ); // clang-format on } //----------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/grid/CMakeLists.txt0000664000175000017500000000502315160212070020501 0ustar alastairalastair# (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. if( atlas_HAVE_ATLAS_FUNCTIONSPACE ) if( HAVE_FCTEST ) foreach(test fctest_griddistribution fctest_grids fctest_regional_grids fctest_stretchedrotatedgaussiangrid fctest_state ) add_fctest( TARGET atlas_${test} SOURCES ${test}.F90 LINKER_LANGUAGE Fortran LIBS atlas_f ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) endforeach() add_fctest( TARGET atlas_fctest_unstructuredgrid SOURCES fctest_unstructuredgrid.F90 LINKER_LANGUAGE Fortran LIBS atlas_f CONDITION HAVE_TESSELATION ) endif() endif() foreach(test test_domain test_grid_iterator test_stretchedrotatedgaussian test_grid_cropping test_vertical test_spacing test_largegrid test_grid_hash ) ecbuild_add_test( TARGET atlas_${test} SOURCES ${test}.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) endforeach() if( atlas_HAVE_ATLAS_FUNCTIONSPACE ) foreach(test test_field test_grids test_state test_cubedsphere test_cubedsphere_2 ) ecbuild_add_test( TARGET atlas_${test} SOURCES ${test}.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) endforeach() endif() set( _WITH_MPI ) if( eckit_HAVE_MPI ) set( _WITH_MPI MPI 4 ) endif() ecbuild_add_test( TARGET atlas_test_distribution_regular_bands ${_WITH_MPI} SOURCES test_distribution_regular_bands.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} CONDITION atlas_HAVE_ATLAS_FUNCTIONSPACE AND MPI_SLOTS GREATER_EQUAL 4 ) 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 ) ecbuild_list_exclude_pattern( LIST grids REGEX regional_polar_stereographic_1.yml ) endif() foreach( grid ${grids} ) get_filename_component( grid_name ${grid} NAME_WE ) set( test_name atlas_test_grid_${grid_name} ) ecbuild_add_test( TARGET ${test_name} COMMAND atlas-grids ARGS ${grid} --check --check-uid --check-boundingbox ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) endforeach() atlas-0.46.0/src/tests/array/0000775000175000017500000000000015160212070016132 5ustar alastairalastairatlas-0.46.0/src/tests/array/test_array_view_util.cc0000664000175000017500000000346015160212070022710 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "atlas/array/Array.h" #include "atlas/array/ArrayViewUtil.h" #include "atlas/array_fwd.h" #include "tests/AtlasTestEnvironment.h" using namespace atlas::array; namespace atlas { namespace test { //----------------------------------------------------------------------------- CASE("test_var_size") { std::unique_ptr ds{Array::create(4ul, 5ul, 7ul, 9ul)}; auto arrv = make_host_view(*ds); EXPECT(ds->size() == 1260); EXPECT(get_var_size<0>(arrv) == 315); EXPECT(get_var_size<1>(arrv) == 252); EXPECT(get_var_size<2>(arrv) == 180); EXPECT(get_var_size<3>(arrv) == 140); } CASE("test_get_parallel_dim") { std::unique_ptr ds{Array::create(4ul, 5ul, 7ul, 9ul)}; auto arrv = make_host_view(*ds); EXPECT(ds->size() == 1260); EXPECT(get_parallel_dim(arrv) == 0); EXPECT(get_parallel_dim(arrv) == 3); EXPECT(get_parallel_dim>(arrv) == 1); EXPECT(get_parallel_dim>(arrv) == 2); } CASE("test mixed index types") { std::unique_ptr ds{Array::create(4ul, 5ul, 7ul, 9ul)}; auto arrv = make_host_view(*ds); arrv(long(0), size_t(0), int(0), unsigned(0)) = 0.; } //----------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/array/test_array_kernel.hic0000664000175000017500000000763015160212070022342 0ustar alastairalastair/* * (C) Copyright 1996-2016 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 "hic/hic.h" #include "tests/AtlasTestEnvironment.h" #include "atlas/array.h" #include "atlas/array/MakeView.h" #include "atlas/runtime/Log.h" using namespace atlas::array; namespace atlas { namespace test { // -------------------------------------------------------------------------------------------------------- #define REQUIRE_HIC_SUCCESS(msg) \ do { \ hicError_t err = hicPeekAtLastError(); \ if (err != hicSuccess ) { \ throw eckit::testing::TestException("REQUIRE_CUDA_SUCCESS ["+std::string(msg)+"] failed:\n"\ + std::string(hicGetErrorString(err)) , Here()); \ } \ } while(false) static int get_device_count() { static int _devices = [](){ int num_devices = 0; auto err = hicGetDeviceCount(&num_devices); if (err) { num_devices = 0; } return num_devices; }(); return _devices; } // -------------------------------------------------------------------------------------------------------- template __global__ void kernel_ex(array::ArrayView dv) { dv(3, 3, 3) += dv.shape(0) * dv.shape(1) * dv.shape(2); } template __global__ void loop_kernel_ex(array::ArrayView dv) { for(int i=0; i < dv.shape(0); i++) { for(int j=0; j < dv.shape(1); j++) { for(int k=0; k < dv.shape(2); k++) { dv(i,j,k) += i*10+j*100+k*1000; } } } } // -------------------------------------------------------------------------------------------------------- CASE( "test_array" ) { REQUIRE(get_device_count() > 0); constexpr unsigned int dx = 5; constexpr unsigned int dy = 6; constexpr unsigned int dz = 7; auto ds = std::unique_ptr(Array::create(dx, dy, dz)); auto hv = make_host_view(*ds); hv(3, 3, 3) = 4.5; ds->updateDevice(); REQUIRE_HIC_SUCCESS("updateDevice"); auto cv = make_device_view(*ds); REQUIRE_HIC_SUCCESS("make_device_view"); kernel_ex<<<1,1>>>(cv); REQUIRE_HIC_SUCCESS("kernel_ex"); hicDeviceSynchronize(); ds->updateHost(); REQUIRE_HIC_SUCCESS("updateHost"); EXPECT( hv(3, 3, 3) == 4.5 + dx*dy*dz ); } // -------------------------------------------------------------------------------------------------------- CASE( "test_array_loop" ) { REQUIRE(get_device_count() > 0); constexpr unsigned int dx = 5; constexpr unsigned int dy = 6; constexpr unsigned int dz = 7; auto ds = std::unique_ptr(Array::create(dx, dy, dz)); array::ArrayView hv = make_host_view(*ds); for(int i=0; i < dx; i++) { for(int j=0; j < dy; j++) { for(int k=0; k < dz; k++) { hv(i,j,k) = 0; } } } ds->allocateDevice(); ds->updateDevice(); REQUIRE_HIC_SUCCESS("updateDevice"); auto cv = make_device_view(*ds); REQUIRE_HIC_SUCCESS("make_device_view"); loop_kernel_ex<<<1,1>>>(cv); REQUIRE_HIC_SUCCESS("loop_kernel_ex"); hicDeviceSynchronize(); ds->updateHost(); REQUIRE_HIC_SUCCESS("updateHost"); for(int i=0; i < dx; i++) { for(int j=0; j < dy; j++) { for(int k=0; k < dz; k++) { EXPECT( hv(i,j,k) == i*10+j*100+k*1000 ); } } } } // -------------------------------------------------------------------------------------------------------- } } // -------------------------------------------------------------------------------------------------------- int main(int argc, char **argv) { return atlas::test::run( argc, argv ); } atlas-0.46.0/src/tests/array/test_indexview.cc0000664000175000017500000001630615160212070021510 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "atlas/array.h" #include "atlas/array/ArrayView.h" #include "atlas/array/IndexView.h" #include "atlas/array/MakeView.h" #include "atlas/library/defines.h" #include "atlas/parallel/mpi/mpi.h" #include "tests/AtlasTestEnvironment.h" #if ATLAS_HAVE_FORTRAN #define IN_FORTRAN #else #define IN_FORTRAN -1 #endif using namespace atlas::array; namespace atlas { namespace test { //----------------------------------------------------------------------------- template std::string pos(Iterator& it) { std::stringstream ss; for (size_t i = 0; i < it.pos().size(); ++i) { ss << it.pos()[i] << " "; } return ss.str(); } //----------------------------------------------------------------------------- /* CASE( "test_array" ) { array::ArrayT _array (3,1,4); array::ArrayView array(_array); EXPECT( array.shape(0) == 3 ); EXPECT( array.shape(1) == 1 ); EXPECT( array.shape(2) == 4 ); EXPECT( array.size() == 12 ); EXPECT( array.stride(0) == 4 ); EXPECT( array.stride(1) == 4 ); EXPECT( array.stride(2) == 1 ); for( size_t j=0; j array(5,4,2); size_t strides[2] = {8,1}; size_t extents[2] = {5,2}; array::ArrayView aview(array.data(),2,extents,strides); array::ArrayView const const_aview(array); std::cout << "aview.size() = " << aview.size() << std::endl; std::cout << "const_.size() = " << const_aview.size() << std::endl; array::ArrayView::iterator it; array::ArrayView::const_iterator const_it; int i(0); for(it = aview.begin(); it!=aview.end(); ++it, ++i) { std::cout << "set at pos " << test::pos(it) << " : " << i << std::endl; *it = i; } for( const_it = const_aview.begin(); const_it!=const_aview.end(); ++const_it) { std::cout << "read at pos " << test::pos(const_it) << " : " << *const_it << std::endl; } } */ #if ATLAS_HAVE_GRIDTOOLS_STORAGE CASE("test_indexview_1d") { // array::ArrayT array( 10 ); Array* array = Array::create(10); array::ArrayView aview = make_host_view(*array); aview(0) = 1 IN_FORTRAN; EXPECT(aview(0) == 1 IN_FORTRAN); /* array::ArrayView aview(array); IndexView iview(array); const IndexView const_iview(array); aview(0) = 1 IN_FORTRAN; EXPECT( aview(0) == 1 IN_FORTRAN ); EXPECT( const_iview(0) == 0 ); EXPECT( int(iview(0)) == 0 ); iview(2) = 2; EXPECT( aview(2) == 3 IN_FORTRAN ); EXPECT( const_iview(2) == 2 ); EXPECT( int(iview(2)) == 2 ); iview(3) = iview(2); EXPECT( aview(3) == 3 IN_FORTRAN ); EXPECT( const_iview(3) == 2 ); EXPECT( int(iview(3)) == 2 ); iview(3) = iview(2) + 1; EXPECT( aview(3) == 4 IN_FORTRAN ); EXPECT( const_iview(3) == 3 ); EXPECT( int(iview(3)) == 3 ); iview(4) = iview(3); ++iview(4); EXPECT( aview(4) == 5 IN_FORTRAN ); EXPECT( const_iview(4) == 4 ); EXPECT( int(iview(4)) == 4 ); int val = iview(4); EXPECT( val == 4 ); val = iview(4)+1; EXPECT( val == 5 ); */ delete array; } #else CASE("test_indexview_1d") { Array* array = Array::create(10); array::ArrayView aview = array::make_view(*array); IndexView iview = make_indexview(*array); const IndexView const_iview = make_indexview(*array); aview(0) = 1 IN_FORTRAN; EXPECT(aview(0) == 1 IN_FORTRAN); EXPECT(const_iview(0) == 0); EXPECT(int(iview(0)) == 0); iview(2) = 2; EXPECT(aview(2) == 3 IN_FORTRAN); EXPECT(const_iview(2) == 2); EXPECT(int(iview(2)) == 2); iview(3) = iview(2); EXPECT(aview(3) == 3 IN_FORTRAN); EXPECT(const_iview(3) == 2); EXPECT(int(iview(3)) == 2); iview(3) = iview(2) + 1; EXPECT(aview(3) == 4 IN_FORTRAN); EXPECT(const_iview(3) == 3); EXPECT(int(iview(3)) == 3); iview(4) = iview(3); ++iview(4); EXPECT(aview(4) == 5 IN_FORTRAN); EXPECT(const_iview(4) == 4); EXPECT(int(iview(4)) == 4); int val = iview(4); EXPECT(val == 4); val = iview(4) + 1; EXPECT(val == 5); delete array; } #endif /* CASE( "test_indexview_2d" ) { array::ArrayT array( 5, 10 ); array::ArrayView aview(array); IndexView iview(array); const IndexView const_iview(array); int i=2; aview(i,0) = 1 IN_FORTRAN; EXPECT( aview(i,0) == 1 IN_FORTRAN ); EXPECT( const_iview(i,0) == 0 ); EXPECT( int(iview(i,0)) == 0 ); iview(i,2) = 2; EXPECT( aview(i,2) == 3 IN_FORTRAN ); EXPECT( const_iview(i,2) == 2 ); EXPECT( int(iview(i,2)) == 2 ); iview(i,3) = iview(i,2); EXPECT( aview(i,3) == 3 IN_FORTRAN ); EXPECT( const_iview(i,3) == 2 ); EXPECT( int(iview(i,3)) == 2 ); iview(i,3) = iview(i,2) + 1; EXPECT( aview(i,3) == 4 IN_FORTRAN ); EXPECT( const_iview(i,3) == 3 ); EXPECT( int(iview(i,3)) == 3 ); iview(i,4) = iview(i,3); ++iview(i,4); EXPECT( aview(i,4) == 5 IN_FORTRAN ); EXPECT( const_iview(i,4) == 4 ); EXPECT( int(iview(i,4)) == 4 ); int val = iview(i,4); EXPECT( val == 4 ); val = iview(i,4)+1; EXPECT( val == 5 ); } CASE( "test_indexview_3d" ) { array::ArrayT array( 5, 7, 10 ); array::ArrayView aview(array); IndexView iview(array); const IndexView const_iview(array); int i=2; int k=4; aview(i,0,k) = 1 IN_FORTRAN; EXPECT( aview(i,0,k) == 1 IN_FORTRAN ); EXPECT( const_iview(i,0,k) == 0 ); EXPECT( int(iview(i,0,k)) == 0 ); iview(i,2,k) = 2; EXPECT( aview(i,2,k) == 3 IN_FORTRAN ); EXPECT( const_iview(i,2,k) == 2 ); EXPECT( int(iview(i,2,k)) == 2 ); iview(i,3,k) = iview(i,2,k); EXPECT( aview(i,3,k) == 3 IN_FORTRAN ); EXPECT( const_iview(i,3,k) == 2 ); EXPECT( int(iview(i,3,k)) == 2 ); iview(i,3,k) = iview(i,2,k) + 1; EXPECT( aview(i,3,k) == 4 IN_FORTRAN ); EXPECT( const_iview(i,3,k) == 3 ); EXPECT( int(iview(i,3,k)) == 3 ); iview(i,4,k) = iview(i,3,k); ++iview(i,4,k); EXPECT( aview(i,4,k) == 5 IN_FORTRAN ); EXPECT( const_iview(i,4,k) == 4 ); EXPECT( int(iview(i,4,k)) == 4 ); int val = iview(i,4,k); EXPECT( val == 4 ); val = iview(i,4,k)+1; EXPECT( val == 5 ); } */ //----------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/array/test_array_slicer.cc0000664000175000017500000003142615160212070022165 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "atlas/array.h" #include "atlas/array/MakeView.h" #include "atlas/array/helpers/ArraySlicer.h" #include "atlas/library/config.h" #include "tests/AtlasTestEnvironment.h" using namespace atlas::array; using namespace atlas::array::helpers; template struct Slice { using type = typename std::conditional<(Rank == 0), Value, std::array>::type; }; struct Slicer { template static typename Slice::value>::type apply(Args... args) { typename Slice::value>::type a; } }; namespace atlas { namespace test { //----------------------------------------------------------------------------- CASE("test_SliceRank") { static_assert(SliceRank_impl<1, int>::value == 0, "failed"); static_assert(SliceRank_impl<1, Range>::value == 1, "failed"); static_assert(SliceRank_impl<2, int, int>::value == 0, "failed"); static_assert(SliceRank_impl<2, Range, int>::value == 1, "failed"); static_assert(SliceRank_impl<2, Range, Range>::value == 2, "failed"); static_assert(SliceRank_impl<3, int, int, int>::value == 0, "failed"); static_assert(SliceRank_impl<3, Range, int, int>::value == 1, "failed"); static_assert(SliceRank_impl<3, Range, Range, int>::value == 2, "failed"); static_assert(SliceRank_impl<3, int, int, Range>::value == 1, "failed"); static_assert(SliceRank_impl<3, Range, int, Range>::value == 2, "failed"); static_assert(SliceRank_impl<3, Range, Range, Range>::value == 3, "failed"); static_assert(SliceRank_impl<3, Range, int, Range>::value == 2, "failed"); static_assert(SliceRank_impl<3, int, int, Range>::value == 1, "failed"); static_assert(SliceRank_impl<3, int, Range, Range>::value == 2, "failed"); } #if 1 CASE("test_array_slicer_1d") { ArrayT arr(10); auto view = make_view(arr); view.assign({0, 1, 2, 3, 4, 5, 6, 7, 8, 9}); using View = decltype(view); ArraySlicer slicer(view); { auto slice = slicer.apply(Range{0, 2}); static_assert(std::is_same>::value, "failed"); EXPECT(slice.rank() == 1); EXPECT(slice.shape(0) == 2); EXPECT(slice(0) == 0); EXPECT(slice(1) == 1); } { auto slice = slicer.apply(Range{5, 10}); static_assert(std::is_same>::value, "failed"); EXPECT(slice.rank() == 1); EXPECT(slice.shape(0) == 5); EXPECT(slice(0) == 5); EXPECT(slice(1) == 6); EXPECT(slice(2) == 7); EXPECT(slice(3) == 8); EXPECT(slice(4) == 9); } { auto slice = slicer.apply(5); EXPECT(slice == 5); static_assert(std::is_same>::value, "failed"); } { auto slice = slicer.apply(Range::all(), Range::dummy()); static_assert(std::is_same>::value, "failed"); EXPECT(slice.rank() == 2); EXPECT(slice.shape(0) == 10); EXPECT(slice.shape(1) == 1); EXPECT(slice(0, 0) == 0); EXPECT(slice(1, 0) == 1); } } #endif CASE("test_array_slicer_2d") { ArrayT arr(3, 5); auto view = make_view(arr); view.assign({ 11, 12, 13, 14, 15, 21, 22, 23, 24, 25, 31, 32, 33, 34, 35, }); using View = decltype(view); ArraySlicer slicer(view); // ArraySlicer slicer(view); { auto slice = slicer.apply(Range{0, 2}, 2); EXPECT(slice.rank() == 1); EXPECT(slice.shape(0) == 2); EXPECT(slice(0) == 13); EXPECT(slice(1) == 23); } { const double& slice = slicer.apply(1, 3); EXPECT(slice == 24); // static_assert( std::is_same< decltype(slice) , double& >::value, "failed" // ); } { auto slice = slicer.apply(Range{0, 2}, Range::dummy(), Range{1, 3}); static_assert(std::is_same>::value, "failed"); EXPECT(slice.rank() == 3); EXPECT(slice.shape(0) == 2); EXPECT(slice.shape(1) == 1); EXPECT(slice.shape(2) == 2); EXPECT(slice(0, 0, 0) == 12); EXPECT(slice(0, 0, 1) == 13); EXPECT(slice(1, 0, 0) == 22); EXPECT(slice(1, 0, 1) == 23); } } CASE("test_array_slicer_3d") { ArrayT arr(2, 3, 5); auto view = make_view(arr); view.assign({111, 112, 113, 114, 115, 121, 122, 123, 124, 125, 131, 132, 133, 134, 135, 211, 212, 213, 214, 215, 221, 222, 223, 224, 225, 231, 232, 233, 234, 235}); using View = decltype(view); ArraySlicer slicer(view); { auto slice = slicer.apply(Range{0, 2}, 2, Range{2, 5}); EXPECT(slice.rank() == 2); EXPECT(slice.shape(0) == 2); EXPECT(slice.shape(1) == 3); EXPECT(slice(0, 0) == 133); EXPECT(slice(0, 1) == 134); EXPECT(slice(0, 2) == 135); EXPECT(slice(1, 0) == 233); EXPECT(slice(1, 1) == 234); EXPECT(slice(1, 2) == 235); } { auto slice = slicer.apply(0, 0, Range::from(3)); EXPECT(slice.rank() == 1); EXPECT(slice.shape(0) == 2); EXPECT(slice(0) == 114); EXPECT(slice(1) == 115); } { auto slice = slicer.apply(1, Range::all(), 3); EXPECT(slice.rank() == 1); EXPECT(slice.shape(0) == 3); EXPECT(slice(0) == 214); EXPECT(slice(1) == 224); EXPECT(slice(2) == 234); } { const double& slice = slicer.apply(1, 2, 3); EXPECT(slice == 234); } } CASE("test_array_slicer_of_slice") { ArrayT arr(2, 3, 5); auto view = make_view(arr); view.assign({111, 112, 113, 114, 115, 121, 122, 123, 124, 125, 131, 132, 133, 134, 135, 211, 212, 213, 214, 215, 221, 222, 223, 224, 225, 231, 232, 233, 234, 235}); using View = decltype(view); ArraySlicer slicer(view); auto slice = slicer.apply(Range{0, 2}, 2, Range{2, 5}); using Slice = decltype(slice); ArraySlicer subslicer(slice); auto subslice1 = subslicer.apply(0, 0); auto subslice2 = subslicer.apply(Range::all(), Range::all()); auto subslice3 = subslicer.apply(Range::all(), 0); auto subslice4 = subslicer.apply(0, Range::to(2)); EXPECT(is_approximately_equal(double(subslice1), 133.)); EXPECT(subslice2.size() == slice.size()); EXPECT(subslice3.size() == slice.shape(0)); EXPECT(subslice4.size() == 2); } CASE("test_arrayview_slice_type") { ArrayT arr(2, 3, 5); // Assign { auto view = make_view(arr); view.assign({111, 112, 113, 114, 115, 121, 122, 123, 124, 125, 131, 132, 133, 134, 135, 211, 212, 213, 214, 215, 221, 222, 223, 224, 225, 231, 232, 233, 234, 235}); } { auto read_write_view = make_view(arr); auto slice1 = read_write_view.slice(Range{0, 2}, 2, Range{2, 5}); EXPECT(slice1(0, 0) == 133); EXPECT(slice1(0, 1) == 134); EXPECT(slice1(0, 2) == 135); EXPECT(slice1(1, 0) == 233); EXPECT(slice1(1, 1) == 234); EXPECT(slice1(1, 2) == 235); auto slice2 = read_write_view.slice(Range::all(), Range::to(2), Range::from(3)); EXPECT(slice2(0, 0, 0) == 114); EXPECT(slice2(0, 0, 1) == 115); EXPECT(slice2(0, 1, 0) == 124); EXPECT(slice2(0, 1, 1) == 125); EXPECT(slice2(1, 0, 0) == 214); EXPECT(slice2(1, 0, 1) == 215); EXPECT(slice2(1, 1, 0) == 224); EXPECT(slice2(1, 1, 1) == 225); auto slice3 = read_write_view.slice(0, 1, 2); EXPECT(slice3 == 123); static_assert(std::is_same>::value, "failed"); static_assert(std::is_same>::value, "failed"); static_assert(std::is_same>::value, "failed"); } { auto read_only_view = make_view(arr); auto slice1 = read_only_view.slice(Range{0, 2}, 2, Range{2, 5}); EXPECT(slice1(0, 0) == 133); EXPECT(slice1(0, 1) == 134); EXPECT(slice1(0, 2) == 135); EXPECT(slice1(1, 0) == 233); EXPECT(slice1(1, 1) == 234); EXPECT(slice1(1, 2) == 235); auto slice2 = read_only_view.slice(Range::all(), Range::to(2), Range::from(3)); EXPECT(slice2(0, 0, 0) == 114); EXPECT(slice2(0, 0, 1) == 115); EXPECT(slice2(0, 1, 0) == 124); EXPECT(slice2(0, 1, 1) == 125); EXPECT(slice2(1, 0, 0) == 214); EXPECT(slice2(1, 0, 1) == 215); EXPECT(slice2(1, 1, 0) == 224); EXPECT(slice2(1, 1, 1) == 225); auto slice3 = read_only_view.slice(0, 1, 2); EXPECT(slice3 == 123); const double& slice4 = read_only_view.slice(0, 1, 2); EXPECT(slice4 == 123); static_assert(std::is_same>::value, "failed"); static_assert(std::is_same>::value, "failed"); static_assert(std::is_same>::value, "failed"); static_assert(std::is_same::value, "failed"); } { const auto const_read_write_view = make_view(arr); auto slice1 = const_read_write_view.slice(Range{0, 2}, 2, Range{2, 5}); EXPECT(slice1(0, 0) == 133); EXPECT(slice1(0, 1) == 134); EXPECT(slice1(0, 2) == 135); EXPECT(slice1(1, 0) == 233); EXPECT(slice1(1, 1) == 234); EXPECT(slice1(1, 2) == 235); auto slice2 = const_read_write_view.slice(Range::all(), Range::to(2), Range::from(3)); EXPECT(slice2(0, 0, 0) == 114); EXPECT(slice2(0, 0, 1) == 115); EXPECT(slice2(0, 1, 0) == 124); EXPECT(slice2(0, 1, 1) == 125); EXPECT(slice2(1, 0, 0) == 214); EXPECT(slice2(1, 0, 1) == 215); EXPECT(slice2(1, 1, 0) == 224); EXPECT(slice2(1, 1, 1) == 225); auto slice3 = const_read_write_view.slice(0, 1, 2); EXPECT(slice3 == 123); static_assert(std::is_same>::value, "failed"); static_assert(std::is_same>::value, "failed"); static_assert(std::is_same>::value, "failed"); } { const auto const_read_write_view = make_view(arr); auto slice1 = const_read_write_view.slice(Range{0, 2}, 2, Range{2, 5}).as_mdspan(); #define INDEX(...) std::array{__VA_ARGS__} EXPECT(slice1[INDEX(0, 0)] == 133); EXPECT(slice1[INDEX(0, 1)] == 134); EXPECT(slice1[INDEX(0, 2)] == 135); EXPECT(slice1[INDEX(1, 0)] == 233); EXPECT(slice1[INDEX(1, 1)] == 234); EXPECT(slice1[INDEX(1, 2)] == 235); auto slice2 = const_read_write_view.slice(Range::all(), Range::to(2), Range::from(3)).as_mdspan(); EXPECT(slice2[INDEX(0, 0, 0)] == 114); EXPECT(slice2[INDEX(0, 0, 1)] == 115); EXPECT(slice2[INDEX(0, 1, 0)] == 124); EXPECT(slice2[INDEX(0, 1, 1)] == 125); EXPECT(slice2[INDEX(1, 0, 0)] == 214); EXPECT(slice2[INDEX(1, 0, 1)] == 215); EXPECT(slice2[INDEX(1, 1, 0)] == 224); EXPECT(slice2[INDEX(1, 1, 1)] == 225); auto slice2_view = LocalView(slice2); EXPECT(slice2_view(0, 0, 0) == 114); EXPECT(slice2_view(0, 0, 1) == 115); EXPECT(slice2_view(0, 1, 0) == 124); EXPECT(slice2_view(0, 1, 1) == 125); EXPECT(slice2_view(1, 0, 0) == 214); EXPECT(slice2_view(1, 0, 1) == 215); EXPECT(slice2_view(1, 1, 0) == 224); EXPECT(slice2_view(1, 1, 1) == 225); static_assert(std::is_same, layout_stride>>::value, "failed"); static_assert(std::is_same, layout_stride>>::value, "failed"); } } //----------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/array/test_array.cc0000664000175000017500000004171415160212070020625 0ustar alastairalastair/* * (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/array.h" #include "atlas/array/MakeView.h" #include "atlas/library/config.h" #include "tests/AtlasTestEnvironment.h" #if ATLAS_HAVE_GRIDTOOLS_STORAGE #include "atlas/array/gridtools/GridToolsMakeView.h" #endif #include "atlas/parallel/acc/acc.h" using namespace atlas::array; namespace atlas { namespace test { //----------------------------------------------------------------------------- CASE("test_array") { Array* ds = Array::create(4ul); #if ATLAS_HAVE_GRIDTOOLS_STORAGE auto hv = atlas::array::gridtools::make_gt_host_view(*ds); #else auto hv = atlas::array::make_host_view(*ds); #endif hv(3) = 4.5; atlas::array::ArrayView atlas_hv = make_host_view(*ds); EXPECT(hv(3) == 4.5); EXPECT(atlas_hv(3) == 4.5); delete ds; } CASE("test_array_zero_size") { Array* ds = Array::create(0); EXPECT(ds->size() == 0); delete ds; } CASE("test_create") { Array* ds = Array::create(array::DataType::create(), ArrayShape({4, 3})); #if ATLAS_HAVE_GRIDTOOLS_STORAGE auto hv = atlas::array::gridtools::make_gt_host_view(*ds); #else auto hv = atlas::array::make_host_view(*ds); #endif hv(3, 2) = 4; atlas::array::ArrayView atlas_hv = make_host_view(*ds); EXPECT(hv(3, 2) == 4); EXPECT(atlas_hv(3, 2) == 4); delete ds; } CASE("test_make_view") { Array* ds = Array::create(4ul); #if ATLAS_HAVE_GRIDTOOLS_STORAGE auto hv = atlas::array::gridtools::make_gt_host_view(*ds); #else auto hv = atlas::array::make_host_view(*ds); #endif hv(3) = 4.5; atlas::array::ArrayView atlas_hv = make_view(*ds); EXPECT(hv(3) == 4.5); EXPECT(atlas_hv(3) == 4.5); delete ds; } CASE("test_localview") { Array* ds = Array::create(8ul, 4ul, 2ul); auto hv = make_host_view(*ds); EXPECT(hv.shape(0) == 8ul); EXPECT(hv.shape(1) == 4ul); EXPECT(hv.shape(2) == 2ul); EXPECT(hv.size() == 8ul * 4ul * 2ul); // Initialize fields for (idx_t i = 0; i < ds->shape(0); ++i) { for (idx_t j = 0; j < ds->shape(1); ++j) { for (idx_t k = 0; k < ds->shape(2); ++k) { hv(i, j, k) = (i * 100) + (j * 10) + (k); } } } // Check values for (idx_t i = 0; i < ds->shape(0); ++i) { LocalView lv = hv.slice(i, Range::all(), Range::all()); for (idx_t j = 0; j < lv.shape(0); ++j) { for (idx_t k = 0; k < lv.shape(1); ++k) { EXPECT(lv(j, k) == (i * 100) + (j * 10) + (k)); } } } delete ds; } #if ATLAS_HAVE_GRIDTOOLS_STORAGE CASE("test_array_shape") { ArrayShape as{2, 3}; Array* ds = Array::create(as); auto gt_hv = atlas::array::gridtools::make_gt_host_view(*ds); atlas::array::ArrayView atlas_hv = make_host_view(*ds); gt_hv(1, 1) = 4.5; EXPECT(gt_hv(1, 1) == 4.5); EXPECT(atlas_hv(1, 1) == 4.5); EXPECT_EQ(ds->size(), 6); EXPECT_EQ(ds->rank(), 2); EXPECT_EQ(ds->stride(0), gt_hv.storage_info().stride<0>()); EXPECT_EQ(ds->stride(1), gt_hv.storage_info().stride<1>()); EXPECT(ds->contiguous()); delete ds; } #endif CASE("test_spec") { Array* ds = Array::create(4, 5, 6); EXPECT(ds->spec().rank() == 3); EXPECT(ds->spec().size() == 4 * 5 * 6); EXPECT(ds->spec().shape()[0] == 4); EXPECT(ds->spec().shape()[1] == 5); EXPECT(ds->spec().shape()[2] == 6); EXPECT(ds->spec().shapef()[0] == 6); EXPECT(ds->spec().shapef()[1] == 5); EXPECT(ds->spec().shapef()[2] == 4); EXPECT_EQ(ds->spec().strides()[0], 6 * 5); EXPECT_EQ(ds->spec().strides()[1], 6); EXPECT_EQ(ds->spec().strides()[2], 1); EXPECT(ds->spec().hasDefaultLayout() == true); delete ds; } CASE("test_spec_layout") { Array* ds = Array::create(make_shape(4, 5, 6), make_layout(0, 1, 2)); EXPECT(ds->spec().rank() == 3); EXPECT(ds->spec().size() == 4 * 5 * 6); EXPECT(ds->spec().shape()[0] == 4); EXPECT(ds->spec().shape()[1] == 5); EXPECT(ds->spec().shape()[2] == 6); EXPECT(ds->spec().shapef()[0] == 6); EXPECT(ds->spec().shapef()[1] == 5); EXPECT(ds->spec().shapef()[2] == 4); EXPECT(ds->spec().strides()[0] == 6 * 5); EXPECT(ds->spec().strides()[1] == 6); EXPECT(ds->spec().strides()[2] == 1); EXPECT(ds->spec().size() == ds->spec().allocatedSize()); EXPECT(ds->spec().hasDefaultLayout() == true); EXPECT(ds->spec().layout()[0] == 0); EXPECT(ds->spec().layout()[1] == 1); EXPECT(ds->spec().layout()[2] == 2); delete ds; } #if ATLAS_HAVE_GRIDTOOLS_STORAGE CASE("test_spec_layout_rev") { Array* ds = Array::create(make_shape(4, 5, 6), make_layout(2, 1, 0)); EXPECT(ds->spec().rank() == 3); EXPECT(ds->spec().size() == 4 * 5 * 6); EXPECT(ds->spec().shape()[0] == 4); EXPECT(ds->spec().shape()[1] == 5); EXPECT(ds->spec().shape()[2] == 6); EXPECT(ds->spec().shapef()[0] == 4); EXPECT(ds->spec().shapef()[1] == 5); EXPECT(ds->spec().shapef()[2] == 6); EXPECT(ds->spec().strides()[0] == 1); EXPECT(ds->spec().strides()[1] == 4); EXPECT(ds->spec().strides()[2] == 4 * 5); EXPECT(ds->spec().hasDefaultLayout() == false); EXPECT(ds->spec().layout()[0] == 2); EXPECT(ds->spec().layout()[1] == 1); EXPECT(ds->spec().layout()[2] == 0); delete ds; EXPECT_THROWS_AS(Array::create(make_shape(4, 5, 6, 2), make_layout(0, 1, 3, 2)), eckit::Exception); } #endif CASE("test_resize_throw") { Array* ds = Array::create(32, 5, 33); EXPECT_NO_THROW(ds->resize(32, 5, 33)); EXPECT_NO_THROW(ds->resize(32, 4, 33)); EXPECT_NO_THROW(ds->resize(32, 5, 32)); EXPECT_THROWS_AS(ds->resize(32, 5, 33, 4), eckit::Exception); delete ds; } CASE("test_copy_ctr") { Array* ds = Array::create(3, 2); atlas::array::ArrayView hv = make_host_view(*ds); hv(2, 1) = 4; hv(1, 1) = 7; atlas::array::ArrayView hv2(hv); EXPECT(hv2(2, 1) == 4); EXPECT(hv2(1, 1) == 7); delete ds; } #if ATLAS_HAVE_GRIDTOOLS_STORAGE CASE("test_copy_gt_ctr") { Array* ds = Array::create(3, 2); atlas::array::ArrayView hv = make_host_view(*ds); hv(2, 1) = 4; hv(1, 1) = 7; atlas::array::ArrayView hv2(hv); EXPECT(hv2(2, 1) == 4); EXPECT(hv2(1, 1) == 7); auto dims = hv.data_view().storage_info().total_lengths(); EXPECT_EQ(dims[0], 3); EXPECT_EQ(dims[1], 2); delete ds; } #endif CASE("test_resize") { { Array* ds = Array::create(0); EXPECT(ds->size() == 0); ds->resize(0); delete ds; } { Array* ds = Array::create(1); EXPECT(ds->size() == 1); ds->resize(2); delete ds; } { Array* ds = Array::create(0); EXPECT(ds->size() == 0); ds->resize(make_shape(5)); delete ds; } { Array* ds = Array::create(2, 3, 4); { atlas::array::ArrayView hv = make_host_view(*ds); hv(1, 1, 1) = 4.5; hv(1, 2, 2) = 7.5; } ds->resize(3, 4, 5); atlas::array::ArrayView hv = make_host_view(*ds); EXPECT(ds->spec().shape()[0] == 3); EXPECT(ds->spec().shape()[1] == 4); EXPECT(ds->spec().shape()[2] == 5); EXPECT(ds->spec().rank() == 3); EXPECT(ds->spec().size() == 3 * 4 * 5); EXPECT(hv(1, 1, 1) == 4.5); EXPECT(hv(1, 2, 2) == 7.5); delete ds; } { Array* ds = Array::create(3, 2); { atlas::array::ArrayView hv = make_host_view(*ds); hv(2, 1) = 4; hv(1, 1) = 7; } ds->resize(6, 2); atlas::array::ArrayView hv = make_host_view(*ds); EXPECT(ds->spec().shape()[0] == 6); EXPECT(ds->spec().shape()[1] == 2); EXPECT(ds->spec().rank() == 2); EXPECT(ds->spec().size() == 6 * 2); EXPECT(hv(2, 1) == 4); EXPECT(hv(1, 1) == 7); delete ds; } // test the resize with wrap { int vals[6] = {3, 4, 6, 7, 5, 4}; Array* ds = Array::wrap(vals, array::ArrayShape{3, 2}); { atlas::array::ArrayView hv = make_host_view(*ds); hv(2, 1) = 4; hv(1, 1) = 7; } ds->resize(6, 2); atlas::array::ArrayView hv = make_host_view(*ds); EXPECT(ds->spec().shape()[0] == 6); EXPECT(ds->spec().shape()[1] == 2); EXPECT(ds->spec().rank() == 2); EXPECT(ds->spec().size() == 6 * 2); EXPECT(hv(2, 1) == 4); EXPECT(hv(1, 1) == 7); delete ds; } } CASE("test_resize_shape") { Array* ds = Array::create(7, 5, 8); { atlas::array::ArrayView hv = make_host_view(*ds); hv(3, 3, 3) = 4.5; hv(6, 4, 7) = 7.5; } ds->resize(ArrayShape{32, 5, 33}); atlas::array::ArrayView hv = make_host_view(*ds); EXPECT(ds->spec().shape()[0] == 32); EXPECT(ds->spec().shape()[1] == 5); EXPECT(ds->spec().shape()[2] == 33); EXPECT(ds->spec().rank() == 3); EXPECT(ds->spec().size() == 32 * 5 * 33); EXPECT(hv(3, 3, 3) == 4.5); EXPECT(hv(6, 4, 7) == 7.5); delete ds; } CASE("test_insert") { Array* ds = Array::create(7, 5, 8); EXPECT(ds->hostNeedsUpdate() == false); auto hv = make_host_view(*ds); hv.assign(-1.); EXPECT(hv(0, 0, 0) == -1.); hv(1, 3, 3) = 1.5; hv(2, 3, 3) = 2.5; hv(3, 3, 3) = 3.5; hv(6, 4, 7) = 6.5; ds->insert(3, 2); EXPECT(ds->spec().shape()[0] == 9); EXPECT(ds->spec().shape()[1] == 5); EXPECT(ds->spec().shape()[2] == 8); EXPECT(ds->spec().rank() == 3); EXPECT(ds->spec().size() == 9 * 5 * 8); auto hv2 = make_host_view(*ds); // currently we have no mechanism to invalidate the old views after an insertion // into the Array // The original gt data store is deleted and replaced, but the former // atlas::array::ArrayView keeps a pointer to it // wihtout noticing it has been deleted #if ATLAS_HAVE_GRIDTOOLS_STORAGE // Following statement seems to contradict previous comment EXPECT(hv.valid() == false); #endif EXPECT(hv2.valid() == true); EXPECT(hv2(1, 3, 3) == 1.5); EXPECT(hv2(2, 3, 3) == 2.5); EXPECT(hv2(5, 3, 3) == 3.5); EXPECT(hv2(8, 4, 7) == 6.5); delete ds; } CASE("test_insert_throw") { Array* ds = Array::create(7, 5, 8); EXPECT_THROWS_AS(ds->insert(8, 2), eckit::Exception); delete ds; } CASE("test_wrap_storage") { { Array* ds = Array::create(4, 5, 6); atlas::array::ArrayView hv = make_host_view(*ds); hv(2, 3, 3) = 2.5; Array* ds_ext = Array::wrap(hv.data(), ds->spec()); atlas::array::ArrayView hv_ext = make_host_view(*ds_ext); EXPECT(hv_ext(2, 3, 3) == 2.5); delete ds; delete ds_ext; } { std::vector v(4 * 5 * 6, 0.); v[2 * (5 * 6) + 3 * 6 + 3] = 2.5; ArrayShape shape{4, 5, 6}; Array* ds_ext = Array::wrap(v.data(), shape); atlas::array::ArrayView hv_ext = make_host_view(*ds_ext); EXPECT(hv_ext(2, 3, 3) == 2.5); delete ds_ext; } } CASE("test_assign") { Array* ds = Array::create(2ul, 3ul, 4ul); auto hv = make_host_view(*ds); hv.assign(2.5); EXPECT(hv(1, 2, 3) == 2.5); auto lv = hv.slice(1, Range::all(), Range::all()); lv.assign(5.0); EXPECT(hv(0, 2, 3) == 2.5); EXPECT(hv(1, 2, 3) == 5.0); EXPECT(lv(2, 3) == 5.0); delete ds; } CASE("test_ArrayT") { { ArrayT ds(2, 3, 4); EXPECT(ds.size() == 2 * 3 * 4); EXPECT(ds.stride(0) == 3 * 4); EXPECT(ds.stride(1) == 4); EXPECT(ds.stride(2) == 1); EXPECT(ds.shape(0) == 2); EXPECT(ds.shape(1) == 3); EXPECT(ds.shape(2) == 4); } { ArrayT ds(make_shape(2, 3, 4)); EXPECT(ds.size() == 2 * 3 * 4); EXPECT(ds.stride(0) == 3 * 4); EXPECT(ds.stride(1) == 4); EXPECT(ds.stride(2) == 1); EXPECT(ds.shape(0) == 2); EXPECT(ds.shape(1) == 3); EXPECT(ds.shape(2) == 4); } { ArrayT ds(ArraySpec(make_shape(2, 3, 4))); EXPECT(ds.size() == 2 * 3 * 4); EXPECT(ds.stride(0) == 3 * 4); EXPECT(ds.stride(1) == 4); EXPECT(ds.stride(2) == 1); EXPECT(ds.shape(0) == 2); EXPECT(ds.shape(1) == 3); EXPECT(ds.shape(2) == 4); } } CASE("test_valid") { { Array* ds = Array::create(2, 3, 4); atlas::array::ArrayView hv = make_host_view(*ds); hv(1, 1, 1) = 4.5; hv(1, 2, 2) = 7.5; EXPECT(hv.valid() == true); ds->resize(3, 4, 5); // Only implemented using gridtools storage for now #if ATLAS_HAVE_GRIDTOOLS_STORAGE EXPECT(hv.valid() == false); #endif delete ds; } } CASE("test_wrap") { array::ArrayT arr_t(3, 2); EXPECT(arr_t.shape(0) == 3); EXPECT(arr_t.stride(0) == 2); EXPECT(arr_t.shape(1) == 2); EXPECT(arr_t.stride(1) == 1); EXPECT(arr_t.rank() == 2); array::ArrayView arrv_t = array::make_view(arr_t); for (idx_t i = 0; i < arrv_t.shape(0); ++i) { for (idx_t j = 0; j < arrv_t.shape(1); ++j) { arrv_t(i, j) = i * 10 + j - 1; } } std::unique_ptr arr(array::Array::wrap( arrv_t.data(), array::ArraySpec{array::make_shape(3), array::make_strides(arr_t.stride(0))})); EXPECT(arr->shape(0) == 3); EXPECT(arr->stride(0) == arr_t.stride(0)); EXPECT(arr->rank() == 1); auto view = make_host_view(*arr); EXPECT(view.shape(0) == 3); EXPECT(view.stride(0) == arr_t.stride(0)); EXPECT(view.rank() == 1); EXPECT(view(0) == -1); EXPECT(view(1) == 9); EXPECT(view(2) == 19); } CASE("test_acc_map") { Array* ds = Array::create(2, 3, 4); EXPECT_NO_THROW(ds->allocateDevice()); if( ds->deviceAllocated() ) { EXPECT_NO_THROW(ds->accMap()); EXPECT_EQ(ds->accMapped(), std::min(acc::devices(),1)); } else { Log::warning() << "WARNING: Array could not be allocated on device, so acc_map could not be tested" << std::endl; } delete ds; } CASE("test_aligned_ArraySpec") { auto spec = ArraySpec(make_shape(10, 5, 3), ArrayAlignment(4)); EXPECT_EQ(spec.shape()[0], 10); EXPECT_EQ(spec.shape()[1], 5); EXPECT_EQ(spec.shape()[2], 3); EXPECT_EQ(spec.size(), 10 * 5 * 3); EXPECT_EQ(spec.allocatedSize(), 10 * 5 * 4); EXPECT_EQ(spec.contiguous(), false); EXPECT_EQ(spec.strides()[0], 5 * 4); EXPECT_EQ(spec.strides()[1], 4); EXPECT_EQ(spec.strides()[2], 1); } CASE("test_aligned_Array") { auto shape = make_shape(10, 5, 3); auto alignment = ArrayAlignment(4); auto datatype = make_datatype(); auto check_array = [](const Array& array) { EXPECT_EQ(array.shape()[0], 10); EXPECT_EQ(array.shape()[1], 5); EXPECT_EQ(array.shape()[2], 3); EXPECT_EQ(array.size(), 10 * 5 * 3); EXPECT_EQ(array.contiguous(), false); EXPECT_EQ(array.strides()[0], 5 * 4); EXPECT_EQ(array.strides()[1], 4); EXPECT_EQ(array.strides()[2], 1); }; SECTION("ArrayT(shape,alignment)") { ArrayT array{shape, alignment}; check_array(array); } SECTION("Array::create(spec)") { std::unique_ptr array{Array::create(datatype, ArraySpec(shape, alignment))}; check_array(*array); } } //----------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/array/test_svector_kernel.hic0000664000175000017500000000470415160212070022710 0ustar alastairalastair/* * (C) Copyright 1996-2016 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 "hic/hic.h" #include "atlas/library/config.h" #include "tests/AtlasTestEnvironment.h" #include "atlas/array/SVector.h" using namespace atlas::array; namespace atlas { namespace test { __global__ void kernel_exe(int* list_ints_ptr, size_t size, int offset, bool* result ) { SVector list_ints(list_ints_ptr, size); *result = *result && (list_ints[offset] == 3); *result = *result && (list_ints[offset+1] == 4); list_ints[offset]++; list_ints[offset+1]++; } CASE( "test_svector" ) { SVector list_ints(2); list_ints[0] = 3; list_ints[1] = 4; EXPECT( list_ints[0] == 3 ); EXPECT( list_ints[1] == 4 ); EXPECT( list_ints.size() == 2); bool *result; hicError_t err = hicMallocManaged(&result, sizeof(bool)); if(err != hicSuccess) throw_AssertionFailed("failed to allocate GPU memory"); *result=true; kernel_exe<<<1,1>>>(list_ints.data(), list_ints.size(), 0, result); hicDeviceSynchronize(); err = hicGetLastError(); if(err != hicSuccess) throw_AssertionFailed("failed to execute kernel"); EXPECT( *result ); EXPECT( list_ints[0] == 4); EXPECT( list_ints[1] == 5); } CASE( "test_svector_resize" ) { SVector list_ints(2); list_ints[0] = 3; list_ints[1] = 4; EXPECT( list_ints[0] == 3 ); EXPECT( list_ints[1] == 4 ); EXPECT( list_ints.size() == 2); list_ints.resize(5); EXPECT( list_ints.size() == 5); bool *result; hicError_t err = hicMallocManaged(&result, sizeof(bool)); if(err != hicSuccess) throw_AssertionFailed("failed to allocate GPU memory"); *result=true; list_ints[3] = 3; list_ints[4] = 4; kernel_exe<<<1,1>>>(list_ints.data(), list_ints.size(), 3, result); hicDeviceSynchronize(); err = hicGetLastError(); if(err != hicSuccess) throw_AssertionFailed("failed to execute kernel"); EXPECT( *result ); EXPECT( list_ints[3] == 4); EXPECT( list_ints[4] == 5); } } } int main(int argc, char **argv) { return atlas::test::run( argc, argv ); } atlas-0.46.0/src/tests/array/test_vector_kernel.hic0000664000175000017500000000523215160212070022522 0ustar alastairalastair/* * (C) Copyright 1996-2016 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 "hic/hic.h" #include "tests/AtlasTestEnvironment.h" #include "atlas/array/Vector.h" #include "atlas/array.h" #include "atlas/util/GPUClonable.h" #include "atlas/array/MakeView.h" #include "atlas/runtime/Log.h" using namespace atlas::array; namespace atlas { namespace test { struct int_gpu { int_gpu() = delete; int_gpu(int val) : val_(val), gpu_clone_(this) {} int_gpu* gpu_object_ptr() {return gpu_clone_.gpu_object_ptr();} void updateDevice(){ gpu_clone_.updateDevice();} void updateHost(){ gpu_clone_.updateHost();} int val_; private: util::GPUClonable gpu_clone_; }; __global__ void kernel_ex(VectorView* list_ints ) { for(size_t i=0; i < list_ints->size() ; ++i) { (*list_ints)[i]->val_ += 5; } } CASE( "test_resize" ) { Vector list_ints(2); VectorView list_ints_h = make_host_vector_view(list_ints); list_ints_h[0] = new int_gpu(3); list_ints_h[1] = new int_gpu(4); EXPECT( list_ints_h[0]->val_ == 3 ); EXPECT( list_ints_h[1]->val_ == 4 ); list_ints.resize(6); EXPECT( list_ints_h.is_valid(list_ints) == false ); VectorView list_ints_h2 = make_host_vector_view(list_ints); EXPECT( list_ints_h2[0]->val_ == 3 ); EXPECT( list_ints_h2[1]->val_ == 4 ); EXPECT( list_ints_h2.size() == 6 ); } CASE( "test_vector_kernel" ) { Vector list_ints(4); VectorView list_ints_h = make_host_vector_view(list_ints); list_ints_h[0] = new int_gpu(3); list_ints_h[1] = new int_gpu(4); list_ints_h[2] = new int_gpu(5); list_ints_h[3] = new int_gpu(6); list_ints.updateDevice(); VectorView list_ints_d = make_device_vector_view(list_ints); VectorView* list_ints_dp; hicMalloc((void**)(&list_ints_dp), sizeof(VectorView)); hicMemcpy(list_ints_dp, &list_ints_d, sizeof(VectorView), hicMemcpyHostToDevice); kernel_ex<<<1,1>>>(list_ints_dp); if( hicPeekAtLastError() != hicSuccess) std::cout << "ERROR " << std::endl; list_ints.updateHost(); EXPECT( list_ints_h[0]->val_ == 8 ); EXPECT_THROWS_AS( list_ints.resize(6), eckit::AssertionFailed ); } } } int main(int argc, char **argv) { return atlas::test::run( argc, argv ); } atlas-0.46.0/src/tests/array/test_table.cc0000664000175000017500000001363015160212070020572 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "atlas/array/Table.h" #include "atlas/library/defines.h" #include "atlas/runtime/Log.h" #include "tests/AtlasTestEnvironment.h" using namespace atlas::array; namespace atlas { namespace test { //----------------------------------------------------------------------------- #if ATLAS_HAVE_FORTRAN #define IN_FORTRAN -1 #else #define IN_FORTRAN #endif CASE("test_table") { Table table; EXPECT(table.rows() == 0); EXPECT(table.maxcols() == 0); constexpr idx_t vals[4] = {2, 3, 5, 6}; table.add(1, 4, vals, /* fortran-array = */ false); EXPECT(table.rows() == 1); EXPECT(table.cols(0) == 4); EXPECT(table.mincols() == 4); EXPECT(table.maxcols() == 4); auto trv = make_table_view(table); EXPECT(trv(0, 0) == 2); EXPECT(trv(0, 1) == 3); EXPECT(trv(0, 2) == 5); EXPECT(trv(0, 3) == 6); EXPECT(trv.row(0)(0) == 2); EXPECT(trv.row(0)(1) == 3); EXPECT(trv.row(0)(2) == 5); EXPECT(trv.row(0)(3) == 6); constexpr idx_t vals2[6] = {1, 3, 4, 3, 7, 8}; table.add(2, 3, vals2, /* fortran-array = */ true); table.dump(Log::info()); Log::info() << std::endl; EXPECT(table.rows() == 3); EXPECT(table.cols(1) == 3); EXPECT(table.cols(2) == 3); EXPECT(table.mincols() == 3); EXPECT(table.maxcols() == 4); trv = make_table_view(table); EXPECT(trv(1, 0) == 1 IN_FORTRAN); EXPECT(trv(1, 1) == 3 IN_FORTRAN); EXPECT(trv(1, 2) == 4 IN_FORTRAN); EXPECT(trv.row(2)(0) == 3 IN_FORTRAN); EXPECT(trv.row(2)(1) == 7 IN_FORTRAN); EXPECT(trv.row(2)(2) == 8 IN_FORTRAN); auto twv = make_table_view(table); twv(1, 1) = 9 IN_FORTRAN; EXPECT(twv(1, 0) == 1 IN_FORTRAN); EXPECT(twv(1, 1) == 9 IN_FORTRAN); EXPECT(twv(1, 2) == 4 IN_FORTRAN); constexpr idx_t vals3[3] = {6 IN_FORTRAN, 7 IN_FORTRAN, 5 IN_FORTRAN}; twv.row(2) = vals3; EXPECT(twv(2, 0) == 6 IN_FORTRAN); EXPECT(twv(2, 1) == 7 IN_FORTRAN); EXPECT(twv(2, 2) == 5 IN_FORTRAN); // TODO: following statement should not be allowed to compile! trv.row(2) = vals3; constexpr idx_t vals4[8] = {2, 11, 51, 12, 4, 13, 55, 78}; table.insert(1, 2, 4, vals4, /* fortran-array = */ false); EXPECT(table.mincols() == 3); EXPECT(table.maxcols() == 4); EXPECT(table.rows() == 5); EXPECT(table.cols(0) == 4); EXPECT(table.cols(1) == 4); EXPECT(table.cols(2) == 4); EXPECT(table.cols(3) == 3); EXPECT(table.cols(4) == 3); trv = make_table_view(table); EXPECT(trv(1, 0) == 2); EXPECT(trv(1, 1) == 11); EXPECT(trv(1, 2) == 51); EXPECT(trv(1, 3) == 12); EXPECT(trv(2, 0) == 4); EXPECT(trv(2, 1) == 13); EXPECT(trv(2, 2) == 55); EXPECT(trv(2, 3) == 78); EXPECT(trv(3, 0) == 1 IN_FORTRAN); EXPECT(trv(3, 1) == 9 IN_FORTRAN); EXPECT(trv(3, 2) == 4 IN_FORTRAN); constexpr idx_t vals5[2] = {3, 6}; table.insert(3, 1, 2, vals5, true); EXPECT(table.mincols() == 2); EXPECT(table.maxcols() == 4); EXPECT(table.rows() == 6); EXPECT(table.cols(3), 2); EXPECT(table.cols(4), 3); trv = make_table_view(table); EXPECT(trv(3, 0) == 3 IN_FORTRAN); EXPECT(trv(3, 1) == 6 IN_FORTRAN); EXPECT(trv(4, 0) == 1 IN_FORTRAN); EXPECT(trv(4, 1) == 9 IN_FORTRAN); EXPECT(trv(4, 2) == 4 IN_FORTRAN); // insert 3 rows with 1 column table.insert(4, 3, 1); EXPECT(table.rows() == 9); EXPECT(table.cols(4) == 1); EXPECT(table.cols(5) == 1); EXPECT(table.mincols() == 1); EXPECT(table.maxcols() == 4); trv = make_table_view(table); EXPECT(trv(7, 0) == 1 IN_FORTRAN); EXPECT(trv(7, 1) == 9 IN_FORTRAN); EXPECT(trv(7, 2) == 4 IN_FORTRAN); constexpr size_t cols[3] = {3, 7, 1}; EXPECT(table.cols(2), 4); // insert in position 2, 3 rows with cols[3] number of columns table.insert(2, 3, cols); EXPECT(table.mincols() == 1); EXPECT(table.maxcols() == 7); EXPECT(table.rows() == 12); EXPECT(table.cols(2) == 3); EXPECT(table.cols(3) == 7); EXPECT(table.cols(4) == 1); EXPECT(table.cols(5) == 4); trv = make_table_view(table); EXPECT(trv(5, 0) == 4); EXPECT(trv(5, 1) == 13); EXPECT(trv(5, 2) == 55); EXPECT(trv(5, 3) == 78); } CASE("test_irregular_insert") { Table table; EXPECT(table.rows() == 0); EXPECT(table.maxcols() == 0); constexpr idx_t vals[4] = {2, 3, 5, 6}; table.insert(0, 1, 4, vals, false); EXPECT(table.rows() == 1); EXPECT(table.cols(0) == 4); EXPECT(table.mincols() == 4); EXPECT(table.maxcols() == 4); auto trv = make_table_view(table); EXPECT(trv(0, 0) == 2); EXPECT(trv(0, 1) == 3); EXPECT(trv(0, 2) == 5); EXPECT(trv(0, 3) == 6); EXPECT(trv.row(0)(0) == 2); EXPECT(trv.row(0)(1) == 3); EXPECT(trv.row(0)(2) == 5); EXPECT(trv.row(0)(3) == 6); } CASE("test_table_row") { Table table; EXPECT(table.rows() == 0); EXPECT(table.maxcols() == 0); constexpr idx_t vals[4] = {2, 3, 5, 6}; table.insert(0, 1, 4, vals, /*fortran_array =*/false); auto twv = make_table_view(table); auto trv = make_table_view(table); auto row_write = twv.row(0); auto row_read = trv.row(0); EXPECT(row_read(1) == 3); EXPECT(row_write(1) == 3); row_write(1) = 5; row_write(1) += 2; EXPECT(row_read(1) == 7); EXPECT(row_write(1) == 7); } //----------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/array/test_array_foreach.cc0000664000175000017500000004061115160212070022307 0ustar alastairalastair/* * (C) Crown Copyright 2024 Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ #include #include #include #include "atlas/array.h" #include "atlas/array/MakeView.h" #include "atlas/array/helpers/ArrayForEach.h" #include "atlas/array/helpers/ArraySlicer.h" #include "atlas/util/Config.h" #include "tests/AtlasTestEnvironment.h" using namespace atlas::array; using namespace atlas::array::helpers; namespace atlas { namespace test { CASE("test_array_foreach_1_view") { const auto arr = ArrayT(2, 3); const auto view = make_view(arr); // Test slice shapes. const auto loopFunctorDim0 = [](auto&& slice) { EXPECT_EQ(slice.rank(), 1); EXPECT_EQ(slice.shape(0), 3); }; ArrayForEach<0>::apply(std::tie(view), loopFunctorDim0); const auto loopFunctorDim1 = [](auto&& slice) { EXPECT_EQ(slice.rank(), 1); EXPECT_EQ(slice.shape(0), 2); }; ArrayForEach<1>::apply(std::tie(view), loopFunctorDim1); // Test that slice resolves to double. const auto loopFunctorDimAll = [](auto&& slice) { static_assert(std::is_convertible_v); }; ArrayForEach<0, 1>::apply(std::tie(view), loopFunctorDimAll); // Test ghost functionality. auto ghost = ArrayT(2); auto ghostView = make_view(ghost); ghostView.assign({0, 1}); auto count = int {}; const auto countNonGhosts = [&count](auto&&...) { ++count; }; ArrayForEach<0>::apply(execution::seq, std::tie(view), ghostView, countNonGhosts); EXPECT_EQ(count, 1); count = 0; const auto ghostWrap = [&ghostView](idx_t idx, auto&&...) { // Wrap ghostView to use correct number of indices. return ghostView(idx); }; ArrayForEach<0, 1>::apply(execution::seq, std::tie(view), ghostWrap, countNonGhosts); EXPECT_EQ(count, 3); } CASE("test_array_foreach_2_views") { const auto arr1 = ArrayT(2, 3); const auto view1 = make_view(arr1); const auto arr2 = ArrayT(2, 3, 4); const auto view2 = make_view(arr2); // Test slice shapes. const auto loopFunctorDim0 = [](auto&& slice1, auto&& slice2) { EXPECT_EQ(slice1.rank(), 1); EXPECT_EQ(slice1.shape(0), 3); EXPECT_EQ(slice2.rank(), 2); EXPECT_EQ(slice2.shape(0), 3); EXPECT_EQ(slice2.shape(1), 4); }; ArrayForEach<0>::apply(std::tie(view1, view2), loopFunctorDim0); const auto loopFunctorDim1 = [](auto&& slice1, auto&& slice2) { EXPECT_EQ(slice1.rank(), 1); EXPECT_EQ(slice1.shape(0), 2); EXPECT_EQ(slice2.rank(), 2); EXPECT_EQ(slice2.shape(0), 2); EXPECT_EQ(slice2.shape(1), 4); }; ArrayForEach<1>::apply(std::tie(view1, view2), loopFunctorDim1); // Test that slice resolves to double. const auto loopFunctorDimAll = [](auto&& slice2) { static_assert(std::is_convertible_v); }; ArrayForEach<0, 1, 2>::apply(std::tie(view2), loopFunctorDimAll); // Test ghost functionality. auto ghost = ArrayT(2); auto ghostView = make_view(ghost); ghostView.assign({0, 1}); auto count = int {}; const auto countNonGhosts = [&count](auto&&...) { ++count; }; ArrayForEach<0>::apply(execution::seq, std::tie(view2), ghostView, countNonGhosts); EXPECT_EQ(count, 1); count = 0; const auto ghostWrap = [&ghostView](idx_t idx, auto&&...) { // Wrap ghostView to use correct number of indices. return ghostView(idx); }; ArrayForEach<0, 1>::apply(execution::seq, std::tie(view2), ghostWrap, countNonGhosts); EXPECT_EQ(count, 3); count = 0; ArrayForEach<0, 1, 2>::apply(execution::seq, std::tie(view2), ghostWrap, countNonGhosts); EXPECT_EQ(count, 12); } CASE("test_array_foreach_3_views") { const auto arr1 = ArrayT(2, 3); const auto view1 = make_view(arr1); const auto arr2 = ArrayT(2, 3, 4); const auto view2 = make_view(arr2); const auto arr3 = ArrayT(2, 3, 4, 5); const auto view3 = make_view(arr3); // Test slice shapes. const auto loopFunctorDim0 = [](auto&& slice1, auto&& slice2, auto&& slice3) { EXPECT_EQ(slice1.rank(), 1); EXPECT_EQ(slice1.shape(0), 3); EXPECT_EQ(slice2.rank(), 2); EXPECT_EQ(slice2.shape(0), 3); EXPECT_EQ(slice2.shape(1), 4); EXPECT_EQ(slice3.rank(), 3); EXPECT_EQ(slice3.shape(0), 3); EXPECT_EQ(slice3.shape(1), 4); EXPECT_EQ(slice3.shape(2), 5); }; ArrayForEach<0>::apply(std::tie(view1, view2, view3), loopFunctorDim0); const auto loopFunctorDim1 = [](auto&& slice1, auto&& slice2, auto&& slice3) { EXPECT_EQ(slice1.rank(), 1); EXPECT_EQ(slice1.shape(0), 2); EXPECT_EQ(slice2.rank(), 2); EXPECT_EQ(slice2.shape(0), 2); EXPECT_EQ(slice2.shape(1), 4); EXPECT_EQ(slice3.rank(), 3); EXPECT_EQ(slice3.shape(0), 2); EXPECT_EQ(slice3.shape(1), 4); EXPECT_EQ(slice3.shape(2), 5); }; ArrayForEach<1>::apply(std::tie(view1, view2, view3), loopFunctorDim1); // Test that slice resolves to double. const auto loopFunctorDimAll = [](auto&& slice3) { static_assert(std::is_convertible_v); }; ArrayForEach<0, 1, 2, 3>::apply(std::tie(view3), loopFunctorDimAll); // Test ghost functionality. auto ghost = ArrayT(2); auto ghostView = make_view(ghost); ghostView.assign({0, 1}); auto count = int {}; const auto countNonGhosts = [&count](auto&&...) { ++count; }; ArrayForEach<0>::apply(execution::seq, std::tie(view3), ghostView, countNonGhosts); EXPECT_EQ(count, 1); count = 0; const auto ghostWrap = [&ghostView](idx_t idx, auto&&...) { // Wrap ghostView to use correct number of indices. return ghostView(idx); }; ArrayForEach<0, 1>::apply(execution::seq, std::tie(view3), ghostWrap, countNonGhosts); EXPECT_EQ(count, 3); count = 0; ArrayForEach<0, 1, 2>::apply(execution::seq, std::tie(view3), ghostWrap, countNonGhosts); EXPECT_EQ(count, 12); count = 0; ArrayForEach<0, 1, 2, 3>::apply(execution::seq, std::tie(view3), ghostWrap, countNonGhosts); EXPECT_EQ(count, 60); } CASE("test_array_foreach_integer_sequence") { const auto arr1 = ArrayT(2, 3); const auto view1 = make_view(arr1); const auto arr2 = ArrayT(2, 3, 4); const auto view2 = make_view(arr2); const auto arr3 = ArrayT(2, 3, 4, 5); const auto view3 = make_view(arr3); const auto none = std::integer_sequence{}; const auto zero = std::integer_sequence{}; const auto one = std::integer_sequence{}; const auto zeroOneTwoThree = std::make_integer_sequence{}; // Test slice shapes. const auto loopFunctorDimNone = [](auto&& slice1, auto&& slice2, auto&& slice3) { EXPECT_EQ(slice1.rank(), 2); EXPECT_EQ(slice1.shape(0), 2); EXPECT_EQ(slice1.shape(1), 3); EXPECT_EQ(slice2.rank(), 3); EXPECT_EQ(slice2.shape(0), 2); EXPECT_EQ(slice2.shape(1), 3); EXPECT_EQ(slice2.shape(2), 4); EXPECT_EQ(slice3.rank(), 4); EXPECT_EQ(slice3.shape(0), 2); EXPECT_EQ(slice3.shape(1), 3); EXPECT_EQ(slice3.shape(2), 4); EXPECT_EQ(slice3.shape(3), 5); }; // No iterations. Apply functor directly to unsliced ArrayViews. arrayForEachDim(none, std::tie(view1, view2, view3), loopFunctorDimNone); const auto loopFunctorDim0 = [](auto&& slice1, auto&& slice2, auto&& slice3) { EXPECT_EQ(slice1.rank(), 1); EXPECT_EQ(slice1.shape(0), 3); EXPECT_EQ(slice2.rank(), 2); EXPECT_EQ(slice2.shape(0), 3); EXPECT_EQ(slice2.shape(1), 4); EXPECT_EQ(slice3.rank(), 3); EXPECT_EQ(slice3.shape(0), 3); EXPECT_EQ(slice3.shape(1), 4); EXPECT_EQ(slice3.shape(2), 5); }; arrayForEachDim(zero, std::tie(view1, view2, view3), loopFunctorDim0); const auto loopFunctorDim1 = [](auto&& slice1, auto&& slice2, auto&& slice3) { EXPECT_EQ(slice1.rank(), 1); EXPECT_EQ(slice1.shape(0), 2); EXPECT_EQ(slice2.rank(), 2); EXPECT_EQ(slice2.shape(0), 2); EXPECT_EQ(slice2.shape(1), 4); EXPECT_EQ(slice3.rank(), 3); EXPECT_EQ(slice3.shape(0), 2); EXPECT_EQ(slice3.shape(1), 4); EXPECT_EQ(slice3.shape(2), 5); }; arrayForEachDim(one, std::tie(view1, view2, view3), loopFunctorDim1); // Test that slice resolves to double. const auto loopFunctorDimAll = [](auto&& slice3) { static_assert(std::is_convertible_v); }; arrayForEachDim(zeroOneTwoThree, std::tie(view3), loopFunctorDimAll); } CASE("test_array_foreach_forwarding") { const auto arr1 = ArrayT(2, 3); const auto view1 = make_view(arr1); auto arr2 = ArrayT(2, 3, 4); auto view2 = make_view(arr2); const auto loopFunctorDim0 = [](auto&& slice1, auto&& slice2) { EXPECT_EQUAL(slice1.rank(), 1); EXPECT_EQUAL(slice1.shape(0), 3); EXPECT_EQUAL(slice2.rank(), 2); EXPECT_EQUAL(slice2.shape(0), 3); EXPECT_EQUAL(slice2.shape(1), 4); }; ArrayForEach<0>::apply(std::make_tuple(view1, view2), loopFunctorDim0); ArrayForEach<0>::apply(std::tie(view1, view2), loopFunctorDim0); ArrayForEach<0>::apply(std::forward_as_tuple(view1, view2), loopFunctorDim0); } CASE("test_array_foreach_data_integrity") { auto arr1 = ArrayT(200, 3); auto view1 = make_view(arr1); auto arr2 = ArrayT(200, 3, 4); auto view2 = make_view(arr2); for (auto idx = size_t{}; idx < arr1.size(); ++idx) { static_cast(arr1.data())[idx] = idx; } for (auto idx = size_t{}; idx < arr2.size(); ++idx) { static_cast(arr2.data())[idx] = idx; } const auto scaleDataDim0 = [](auto&& slice1, auto&& slice2) { static_assert(std::is_convertible_v); slice1 *= 2.; const auto scaleDataDim1 = [](auto&& slice) { static_assert(std::is_convertible_v); slice *= 3.; }; ArrayForEach<0>::apply(execution::seq, std::tie(slice2), scaleDataDim1); }; ArrayForEach<0, 1>::apply(std::tie(view1, view2), scaleDataDim0); for (auto idx = size_t{}; idx < arr1.size(); ++idx) { EXPECT_EQ(static_cast(arr1.data())[idx], 2. * idx); } for (auto idx = size_t{}; idx < arr2.size(); ++idx) { EXPECT_EQ(static_cast(arr2.data())[idx], 3. * idx); } } template double timeLoop(const IterationMethod& iterationMethod, int num_iter, int num_first, const Operation& operation, double baseline, const std::string& output) { double time{0}; for (int i=0; i{stop - start}; if (i>=num_first) { time += duration.count(); } } time /= double(num_iter); Log::info() << "Elapsed time: " + output + "= " << time << "s"; if (baseline != 0) { Log::info() << "\t; relative to baseline : " << 100.*time/baseline << "%"; } Log::info() << std::endl; return time; } CASE("test_array_foreach_performance") { int ni = 50000; int nj = 100; int num_iter = 20; int num_first = 3; if( ATLAS_ARRAYVIEW_BOUNDS_CHECKING ) { ni = 5000; nj = 20; num_iter = 1; num_first = 0; Log::info() << "WARNING: Following timings contain very expensive bounds checking. Compile with -DENABLE_BOUNDSCHECKING=OFF for fair comparison" << std::endl; } auto arr1 = ArrayT(ni, nj); auto view1 = make_view(arr1); auto arr2 = ArrayT(ni, nj); auto view2 = make_view(arr2); for (auto idx = size_t{}; idx < arr2.size(); ++idx) { static_cast(arr2.data())[idx] = 2 * idx + 1; } auto arr3 = ArrayT(ni, nj); auto view3 = make_view(arr2); for (auto idx = size_t{}; idx < arr3.size(); ++idx) { static_cast(arr3.data())[idx] = 3 * idx + 1; } const auto add = [](double& a1, const double& a2, const double& a3) { a1 = a2 + a3; }; const auto trig = [](double& a1, const double& a2, const double& a3) { a1 = std::sin(a2) + std::cos(a3); }; const auto rawPointer = [&](const auto& operation) { const size_t size = arr1.size(); auto* p1 = view1.data(); const auto* p2 = view2.data(); const auto* p3 = view3.data(); for (size_t idx = 0; idx < size; ++idx) { operation(p1[idx], p2[idx], p3[idx]); } }; const auto ijLoop = [&](const auto& operation) { const idx_t ni = view1.shape(0); const idx_t nj = view1.shape(1); for (idx_t i = 0; i < ni; ++i) { for (idx_t j = 0; j < nj; ++j) { operation(view1(i, j), view2(i, j), view3(i, j)); } } }; const auto jiLoop = [&](const auto& operation) { const idx_t ni = view1.shape(0); const idx_t nj = view1.shape(1); for (idx_t j = 0; j < nj; ++j) { for (idx_t i = 0; i < ni; ++i) { operation(view1(i, j), view2(i, j), view3(i, j)); } } }; const auto forEachCol = [&](const auto& operation) { const auto function = [&](auto&& slice1, auto&& slice2, auto&& slice3) { const idx_t size = slice1.shape(0); for (idx_t idx = 0; idx < size; ++idx) { operation(slice1(idx), slice2(idx), slice3(idx)); } }; ArrayForEach<0>::apply(execution::seq, std::tie(view1, view2, view3), function); }; const auto forEachLevel = [&](const auto& operation) { const auto function = [&](auto&& slice1, auto&& slice2, auto&& slice3) { const idx_t size = slice1.shape(0); for (idx_t idx = 0; idx < size; ++idx) { operation(slice1(idx), slice2(idx), slice3(idx)); } }; ArrayForEach<1>::apply(execution::seq, std::tie(view1, view2, view3), function); }; const auto forEachAll = [&](const auto& operation) { ArrayForEach<0, 1>::apply(execution::seq, std::tie(view1, view2, view3), operation); }; const auto forEachNested = [&](const auto& operation) { const auto function = [&](auto&& slice1, auto&& slice2, auto&& slice3) { ArrayForEach<0>::apply(execution::seq, std::tie(slice1, slice2, slice3), operation); }; ArrayForEach<0>::apply(execution::seq, std::tie(view1, view2, view3), function); }; const auto forEachConf = [&](const auto& operation) { const auto function = [&](auto&& slice1, auto&& slice2, auto&& slice3) { ArrayForEach<0>::apply(option::execution_policy(execution::seq), std::tie(slice1, slice2, slice3), operation); }; ArrayForEach<0>::apply(option::execution_policy(execution::seq), std::tie(view1, view2, view3), function); }; double baseline; baseline = timeLoop(rawPointer, num_iter, num_first, add, 0, "Addition; raw pointer "); timeLoop(ijLoop, num_iter, num_first, add, baseline, "Addition; for loop (i, j) "); timeLoop(jiLoop, num_iter, num_first, add, baseline, "Addition; for loop (j, i) "); timeLoop(forEachCol, num_iter, num_first, add, baseline, "Addition; for each (columns) "); timeLoop(forEachLevel, num_iter, num_first, add, baseline, "Addition; for each (levels) "); timeLoop(forEachAll, num_iter, num_first, add, baseline, "Addition; for each (all elements) "); timeLoop(forEachNested, num_iter, num_first, add, baseline, "Addition; for each (nested) "); timeLoop(forEachConf, num_iter, num_first, add, baseline, "Addition; for each (nested, config) "); Log::info() << std::endl; num_first = 2; num_iter = 5; baseline = timeLoop(rawPointer, num_iter, num_first, trig, 0, "Trig ; raw pointer "); timeLoop(ijLoop, num_iter, num_first, trig, baseline, "Trig ; for loop (i, j) "); timeLoop(jiLoop, num_iter, num_first, trig, baseline, "Trig ; for loop (j, i) "); timeLoop(forEachCol, num_iter, num_first, trig, baseline, "Trig ; for each (columns) "); timeLoop(forEachLevel, num_iter, num_first, trig, baseline, "Trig ; for each (levels) "); timeLoop(forEachAll, num_iter, num_first, trig, baseline, "Trig ; for each (all elements) "); timeLoop(forEachNested, num_iter, num_first, trig, baseline, "Trig ; for each (nested) "); timeLoop(forEachConf, num_iter, num_first, trig, baseline, "Trig ; for each (nested, config) "); } } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/array/test_svector.cc0000664000175000017500000000270315160212070021167 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "atlas/array/SVector.h" #include "atlas/library/config.h" #include "tests/AtlasTestEnvironment.h" using namespace atlas::array; namespace atlas { namespace test { CASE("test_svector") { SVector list_ints(2); list_ints[0] = 3; list_ints[1] = 4; EXPECT(list_ints[0] == 3); EXPECT(list_ints[1] == 4); EXPECT(list_ints.size() == 2); list_ints[0]++; list_ints[1]++; EXPECT(list_ints[0] == 4); EXPECT(list_ints[1] == 5); } CASE("test_svector_resize") { SVector list_ints(2); list_ints[0] = 3; list_ints[1] = 4; EXPECT(list_ints[0] == 3); EXPECT(list_ints[1] == 4); EXPECT(list_ints.size() == 2); list_ints.resize(5); EXPECT(list_ints.size() == 5); list_ints[3] = 5; list_ints[4] = 6; list_ints[3]++; list_ints[4]++; EXPECT(list_ints[0] == 3); EXPECT(list_ints[1] == 4); EXPECT(list_ints[3] == 6); EXPECT(list_ints[4] == 7); } } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/array/test_array_view_variant.cc0000664000175000017500000000746015160212070023403 0ustar alastairalastair/* * (C) Crown Copyright 2024 Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ #include #include #include "atlas/array.h" #include "atlas/array/ArrayViewVariant.h" #include "eckit/utils/Overloaded.h" #include "tests/AtlasTestEnvironment.h" namespace atlas { namespace test { using namespace array; CASE("test variant assignment") { auto array1 = array::ArrayT(2); auto array2 = array::ArrayT(2, 3); auto array3 = array::ArrayT(2, 3, 4); const auto& arrayRef = array1; array1.allocateDevice(); array2.allocateDevice(); array3.allocateDevice(); auto view1 = make_view_variant(array1); auto view2 = make_view_variant(array2); auto view3 = make_view_variant(array3); auto view4 = make_view_variant(arrayRef); const auto hostView1 = make_host_view_variant(array1); const auto hostView2 = make_host_view_variant(array2); const auto hostView3 = make_host_view_variant(array3); const auto hostView4 = make_host_view_variant(arrayRef); auto deviceView1 = make_device_view_variant(array1); auto deviceView2 = make_device_view_variant(array2); auto deviceView3 = make_device_view_variant(array3); auto deviceView4 = make_device_view_variant(arrayRef); const auto visitVariants = [](auto& var1, auto& var2, auto var3, auto var4) { std::visit( [](auto view) { EXPECT((is_rank<1>(view))); EXPECT((is_value_type(view))); EXPECT((is_non_const_value_type(view))); }, var1); std::visit( [](auto view) { EXPECT((is_rank<2>(view))); EXPECT((is_value_type(view))); EXPECT((is_non_const_value_type(view))); }, var2); std::visit( [](auto view) { EXPECT((is_rank<3>(view))); EXPECT((is_value_type(view))); EXPECT((is_non_const_value_type(view))); }, var3); std::visit( [](auto view) { EXPECT((is_rank<1>(view))); EXPECT((is_value_type(view))); EXPECT((is_non_const_value_type(view))); }, var4); }; visitVariants(view1, view2, view3, view4); visitVariants(hostView1, hostView2, hostView3, hostView4); visitVariants(deviceView1, deviceView2, deviceView3, deviceView4); } CASE("test std::visit") { auto array1 = ArrayT(10); make_view(array1).assign({0, 1, 2, 3, 4, 5, 6, 7, 8, 9}); auto array2 = ArrayT(5, 2); make_view(array2).assign({0, 1, 2, 3, 4, 5, 6, 7, 8, 9}); const auto var1 = make_view_variant(array1); const auto var2 = make_view_variant(array2); auto rank1Tested = false; auto rank2Tested = false; const auto visitor = [&](auto view) { if constexpr (is_rank<1>(view)) { EXPECT((is_value_type(view))); auto testValue = int{0}; for (auto i = size_t{0}; i < view.size(); ++i) { const auto value = view(i); EXPECT_EQ(value, static_cast(testValue++)); } rank1Tested = true; } else if constexpr (is_rank<2>(view)) { EXPECT((is_value_type(view))); auto testValue = int{0}; for (auto i = idx_t{0}; i < view.shape(0); ++i) { for (auto j = idx_t{0}; j < view.shape(1); ++j) { const auto value = view(i, j); EXPECT_EQ(value, static_cast(testValue++)); } } rank2Tested = true; } else { // Test should not reach here. EXPECT(false); } }; std::visit(visitor, var1); EXPECT(rank1Tested); std::visit(visitor, var2); EXPECT(rank2Tested); } } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/array/CMakeLists.txt0000664000175000017500000000451115160212070020673 0ustar alastairalastair# (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. ecbuild_add_test( TARGET atlas_test_array_slicer SOURCES test_array_slicer.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) ecbuild_add_test( TARGET atlas_test_array_foreach SOURCES test_array_foreach.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} CONDITION NOT SKIP_TEST_atlas_test_array_foreach ) ecbuild_add_test( TARGET atlas_test_array SOURCES test_array.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) #ecbuild_add_test( TARGET atlas_test_table # SOURCES test_table.cc # LIBS atlas # ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} #) ecbuild_add_test( TARGET atlas_test_array_vector SOURCES test_array_vector.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} CONDITION atlas_HAVE_GRIDTOOLS_STORAGE ) ecbuild_add_test( TARGET atlas_test_svector SOURCES test_svector.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) ecbuild_add_test( TARGET atlas_test_array_view_util SOURCES test_array_view_util.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) ecbuild_add_test( TARGET atlas_test_array_indexview SOURCES test_indexview.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) if( CMAKE_BUILD_TYPE MATCHES "DEBUG" ) set ( CMAKE_NVCC_FLAGS "-G" ) endif() atlas_add_hic_test( TARGET atlas_test_array_kernel SOURCES test_array_kernel.hic LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) atlas_add_hic_test( TARGET atlas_test_vector_kernel SOURCES test_vector_kernel.hic LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) atlas_add_hic_test( TARGET atlas_test_svector_kernel SOURCES test_svector_kernel.hic LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) if(eckit_VERSION VERSION_GREATER_EQUAL 1.27.0) # The test uses a header file from eckit "Overloaded.h" which only is available in v 1.27.0 ecbuild_add_test( TARGET atlas_test_array_view_variant SOURCES test_array_view_variant.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) endif() atlas-0.46.0/src/tests/array/test_array_vector.cc0000664000175000017500000000266415160212070022210 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "atlas/array/Vector.h" #include "tests/AtlasTestEnvironment.h" using namespace atlas::array; namespace atlas { namespace test { //----------------------------------------------------------------------------- CASE("test_vector") { Vector vec(3); VectorView vec_view = make_host_vector_view(vec); vec_view[0] = 3; vec_view[1] = -3; vec_view[2] = 1; EXPECT(vec_view.size() == 3); EXPECT(vec_view[0] == 3); EXPECT(vec_view[1] == -3); EXPECT(vec_view[2] == 1); vec.resize(5); // TODO invalidate preview views VectorView vec_viewb = make_host_vector_view(vec); vec_viewb[3] = 5; vec_viewb[4] = 6; EXPECT(vec_viewb[0] == 3); EXPECT(vec_viewb[1] == -3); EXPECT(vec_viewb[2] == 1); EXPECT(vec_viewb[3] == 5); EXPECT(vec_viewb[4] == 6); } //----------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/field/0000775000175000017500000000000015160212070016077 5ustar alastairalastairatlas-0.46.0/src/tests/field/fctest_multifield_ifs.F900000664000175000017500000002312715160212070022733 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. ! This File contains Unit Tests for testing the ! C++ / Fortran Interfaces to the Mesh Datastructure ! @author Willem Deconinck ! @author Slavko Brdar #include "fckit/fctest.h" ! ----------------------------------------------------------------------------- module fcta_MultiField_fixture use atlas_module use atlas_multifield_module use pluto_module, only : pluto use, intrinsic :: iso_c_binding implicit none end module ! ----------------------------------------------------------------------------- TESTSUITE_WITH_FIXTURE(fctest_atlas_MultiField, fcta_MultiField_fixture) ! ----------------------------------------------------------------------------- TESTSUITE_INIT call atlas_library%initialise() END_TESTSUITE_INIT ! ----------------------------------------------------------------------------- TESTSUITE_FINALIZE call atlas_library%finalise() END_TESTSUITE_FINALIZE ! ----------------------------------------------------------------------------- TEST( test_multifield ) implicit none type(atlas_MultiField) :: mfield_1, mfield_2 type(atlas_FieldSet) :: fieldset_1, fieldset_2 type(atlas_Field) :: field type(atlas_config) :: config integer, pointer :: fdata_int_2d(:,:) real(c_float), pointer :: fdata_f2d(:,:), fdata_f3d(:,:,:) real(c_double), pointer :: fdata_d3d(:,:,:) integer, parameter :: nproma = 16; integer, parameter :: nlev = 1; integer, parameter :: ngptot = 2000; integer, parameter :: nblk = (ngptot + nproma - 1) / nproma type(atlas_Config), allocatable :: field_configs(:) integer :: i character(len=64), parameter, dimension(5) :: var_names = [ character(64) :: & "temperature", "pressure", "density", "clv", "wind_u" ] config = atlas_Config() call config%set("type", "MultiFieldCreatorIFS"); call config%set("ngptot", ngptot); call config%set("nproma", nproma); allocate(field_configs(size(var_names))) do i = 1, size(var_names) field_configs(i) = atlas_Config() call field_configs(i)%set("name", trim(var_names(i))) end do call field_configs(4)%set("nvar", 4) ! clv has four subvariables call config%set("fields", field_configs) call config%set("nlev", 0); ! surface fields call config%set("datatype", "real32"); mfield_1 = atlas_MultiField(config) call config%set("nlev", 4); ! fields are 3d call config%set("datatype", "real64"); mfield_2 = atlas_MultiField(config) fieldset_1 = mfield_1%fieldset() FCTEST_CHECK_EQUAL(mfield_1%size(), 5) FCTEST_CHECK_EQUAL(fieldset_1%size(), 5) fieldset_2 = atlas_FieldSet() call fieldset_2%add(mfield_1%fieldset()) field = fieldset_2%field("density") call field%data(fdata_f2d) fdata_f2d(1,1) = 2. call field%rename("dens") ! check data access directly though multifield call fieldset_1%data("dens", fdata_f2d) fdata_f2d(1,1) = 3. ! check access to the renamed variable field = fieldset_1%field("dens") call field%data(fdata_f2d) FCTEST_CHECK_EQUAL(fdata_f2d(1,1), 3._c_float) ! check dimesionality fieldset_2 = mfield_2%fieldset() call fieldset_2%data("density", fdata_d3d) fdata_d3d(1,1,1) = 4. fieldset_2 = atlas_FieldSet() call fieldset_2%add(mfield_2%fieldset()) field = fieldset_2%field("density") call field%data(fdata_d3d) FCTEST_CHECK_EQUAL(fdata_d3d(1,1,1), 4._c_double) END_TEST TEST( test_device_strides_on_cpu ) implicit none type(atlas_MultiField) :: mfield type(atlas_FieldSet) :: fieldset type(atlas_Field) :: field real(c_double), pointer :: fdata_d3d(:,:,:), fdata_d3d_ddata(:,:,:) integer, parameter :: nproma = 6; integer, parameter :: nlev = 5; integer, parameter :: ngptot = 18; integer, parameter :: nblk = (ngptot + nproma - 1) / nproma integer :: i, j, k character(len=64), parameter, dimension(2) :: var_names_dp = [ character(64) :: & "clv", "wind_u" ] mfield = atlas_MultiField(atlas_real(c_double), [nproma, nlev, -1, nblk], var_names_dp) FCTEST_CHECK_EQUAL(mfield%size(), 2) fieldset = mfield%fieldset() field = fieldset%field("clv") call field%data(fdata_d3d) do i = 1, field%shape(1) do j = 1, field%shape(2) do k = 1, field%shape(3) fdata_d3d(i,j,k) = real(1000*i + 100*j + 10*1 + k, c_double) end do end do end do call field%update_device() if (pluto%devices() == 0) then call field%device_data(fdata_d3d_ddata) ! We expect this to be host-resident, so we can read it do i = 1, field%shape(1) do j = 1, field%shape(2) do k = 1, field%shape(3) FCTEST_CHECK_EQUAL(fdata_d3d_ddata(i,j,k), fdata_d3d(i,j,k)) fdata_d3d_ddata(i,j,k) = fdata_d3d_ddata(i,j,k) + 1_c_double end do end do end do call field%update_host() do i = 1, field%shape(1) do j = 1, field%shape(2) do k = 1, field%shape(3) FCTEST_CHECK_EQUAL(fdata_d3d(i,j,k), real(1000*i + 100*j + 10*1 + k + 1, c_double)) end do end do end do endif END_TEST TEST( test_multifield_array_direct_constructor ) implicit none type(atlas_MultiField) :: mfield_1, mfield_2 type(atlas_FieldSet) :: fieldset_1, fieldset_2, fieldset_3 type(atlas_Field) :: field type(atlas_config) :: config real(c_float), pointer :: fdata_f2d(:,:) real(c_double), pointer :: fdata_d3d(:,:,:) integer, parameter :: nproma = 6; integer, parameter :: nlev = 5; integer, parameter :: ngptot = 18; integer, parameter :: nblk = (ngptot + nproma - 1) / nproma integer :: i, j, k character(len=64), parameter, dimension(3) :: var_names_sp = [ character(64) :: & "temperature ", "pressure", "density" ] character(len=64), parameter, dimension(2) :: var_names_dp = [ character(64) :: & "clv", "wind_u" ] mfield_1 = atlas_MultiField(atlas_real(c_float), [nproma, -1, nblk], var_names_sp) mfield_2 = atlas_MultiField(atlas_real(c_double), [nproma, nlev, -1, nblk], var_names_dp) FCTEST_CHECK_EQUAL(mfield_1%size(), 3) FCTEST_CHECK_EQUAL(mfield_2%size(), 2) fieldset_1 = mfield_1%fieldset() FCTEST_CHECK_EQUAL(fieldset_1%size(), 3) call fieldset_1%data("density", fdata_f2d) fdata_f2d(1,1) = 3._c_float fieldset_2 = atlas_FieldSet() call fieldset_2%add(mfield_2%fieldset()) call fieldset_2%data("wind_u", fdata_d3d) fdata_d3d(1,1,1) = 4._c_double fieldset_3 = atlas_FieldSet() call fieldset_3%add(mfield_1%fieldset()) call fieldset_3%add(mfield_2%fieldset()) call fieldset_3%data("density", fdata_f2d) call fieldset_3%data("wind_u", fdata_d3d) FCTEST_CHECK_EQUAL(fdata_f2d(1,1), 3._c_float) FCTEST_CHECK_EQUAL(fdata_d3d(1,1,1), 4._c_double) END_TEST TEST( test_multifield_array_config_constuctor ) implicit none type(atlas_MultiField) :: mfield_1, mfield_2 type(atlas_FieldSet) :: fieldset_1, fieldset_2 type(atlas_Field) :: field type(atlas_config) :: config integer, pointer :: fdata_int_2d(:,:) real(c_float), pointer :: fdata_f2d(:,:), fdata_f3d(:,:,:) real(c_double), pointer :: fdata_d3d(:,:,:) integer, parameter :: nproma = 16; integer, parameter :: nlev = 1; integer, parameter :: nblk = 200; type(atlas_Config), allocatable :: field_configs(:) integer :: i character(len=64), parameter, dimension(5) :: var_names = [ character(64) :: & "temperature", "pressure", "density", "clv", "wind_u" ] config = atlas_Config() call config%set("type", "MultiFieldCreatorArray"); allocate(field_configs(size(var_names))) do i = 1, size(var_names) field_configs(i) = atlas_Config() call field_configs(i)%set("name", trim(var_names(i))) end do call field_configs(4)%set("nvar", 5) ! clv has four subvariables call config%set("fields", field_configs) ! surface fields call config%set("shape", [nproma, -1, nblk]); call config%set("datatype", "real32"); mfield_1 = atlas_MultiField(config) ! fields are 3d call config%set("shape", [nproma, nlev, -1, nblk]); call config%set("datatype", "real64"); mfield_2 = atlas_MultiField(config) fieldset_1 = mfield_1%fieldset() FCTEST_CHECK_EQUAL(mfield_1%size(), 9) FCTEST_CHECK_EQUAL(fieldset_1%size(), 9) fieldset_2 = atlas_FieldSet() call fieldset_2%add(mfield_1%fieldset()) field = fieldset_2%field("density") call field%data(fdata_f2d) fdata_f2d(1,1) = 2. call field%rename("dens") ! check data access directly though multifield call fieldset_1%data("dens", fdata_f2d) fdata_f2d(1,1) = 3. ! check access to the renamed variable field = fieldset_1%field("dens") call field%data(fdata_f2d) FCTEST_CHECK_EQUAL(fdata_f2d(1,1), 3._c_float) ! check dimesionality fieldset_2 = mfield_2%fieldset() call fieldset_2%data("density", fdata_d3d) fdata_d3d(1,1,1) = 4. fieldset_2 = atlas_FieldSet() call fieldset_2%add(mfield_2%fieldset()) field = fieldset_2%field("density") call field%data(fdata_d3d) FCTEST_CHECK_EQUAL(fdata_d3d(1,1,1), 4._c_double) END_TEST ! ----------------------------------------------------------------------------- END_TESTSUITE atlas-0.46.0/src/tests/field/fctest_field_wrap_gpu.F900000664000175000017500000001715615160212070022730 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. ! This File contains Unit Tests for testing the ! C++ / Fortran Interfaces to the Mesh Datastructure ! @author Willem Deconinck #include "fckit/fctest.h" ! ----------------------------------------------------------------------------- module fcta_Field_wrap_device_fixture use atlas_module use, intrinsic :: iso_c_binding implicit none end module ! ----------------------------------------------------------------------------- TESTSUITE_WITH_FIXTURE(fctest_atlas_Field_wrap_device,fcta_Field_wrap_device_fixture) ! ----------------------------------------------------------------------------- TESTSUITE_INIT call atlas_library%initialise() END_TESTSUITE_INIT ! ----------------------------------------------------------------------------- TESTSUITE_FINALIZE call atlas_library%finalise() END_TESTSUITE_FINALIZE ! ----------------------------------------------------------------------------- subroutine kernel_1(view, N) real(c_double) :: view(:,:,:) integer, intent(in) :: N !$acc data present(view) !$acc parallel loop do j=1,N view(1,1,j) = real(j, c_double) view(2,1,j) = -3_c_double enddo !$acc end data end subroutine kernel_1 subroutine kernel_2(view, Ni, Nj, Nl) ! The OpenACC standard states that a deviceptr (here 'view') ! shall not have the POINTER attribute, hence this subroutine ! is required rather than have the code inlined at call site. real(c_double) :: view(:,:,:) integer, intent(in) :: Ni, Nj, Nl !$acc data deviceptr(view) !$acc parallel loop do i = 1, Ni do j = 1, Nj do l = 1, Nl view(i,j,l) = -view(i,j,l) enddo enddo enddo !$acc end data end subroutine kernel_2 subroutine kernel_3(view, Ni, Nj, Nk) logical :: view(:,:,:) integer, intent(in) :: Ni, Nj, Nk !$acc data present(view) !$acc parallel loop do i=1,Ni do j=1,Nj do k=1,Nk view(i,j,k) = (mod(k,3) == 0 ) enddo enddo enddo !$acc end data end subroutine kernel_3 subroutine try_offload(field) type(atlas_Field), intent(inout) :: field call field%allocate_device() call field%update_device() call field%update_host() call field%deallocate_device() end subroutine try_offload TEST( test_field_wrapdata ) implicit none real(c_double), allocatable :: existing_data(:,:,:) real(c_double), pointer :: fview(:,:,:) type(atlas_Field) :: field integer(c_int) :: N=20 integer(c_int) :: j write(0,*) "test_field_wrapdata" allocate( existing_data(2,10,N) ) existing_data(:,:,:) = -1._c_double field = atlas_Field("wrapped", existing_data) call field%data(fview) do j=1,N fview(1,1,j) = -2._c_double enddo call field%allocate_device() call field%update_device() call kernel_1(fview, N) j = N/2 FCTEST_CHECK_EQUAL( existing_data(1,1,j), -2._c_double ) FCTEST_CHECK_EQUAL( existing_data(2,1,j), -1._c_double) call field%update_host() call field%deallocate_device() call field%final() ! Existing data is not deleted after field%final() FCTEST_CHECK_EQUAL( existing_data(1,1,j), real(j, c_double) ) FCTEST_CHECK_EQUAL( existing_data(2,1,j), -3._c_double ) END_TEST ! ----------------------------------------------------------------------------- TEST( test_field_wrapdataslice ) implicit none real(c_double), allocatable :: existing_data(:,:,:,:) real(c_double), pointer :: dview(:,:,:), hview(:,:,:) type(atlas_Field) :: field integer(c_int) :: i, j, k, l integer(c_int), parameter :: Ni = 8, Nj = 2, Nk = 3, Nl = 4, fix_id = 2 real(c_double) :: a write(0,*) "test_field_wrapdataslice" allocate( existing_data(Ni, Nj, Nk, Nl) ) do i = 1, Ni do j = 1, Nj do k = 1, Nk do l = 1, Nl existing_data(i,j,k,l) = real(1000 * i+100 * j + 10*k + l, c_double) enddo enddo enddo enddo field = atlas_Field(existing_data(:,:,fix_id,:)) FCTEST_CHECK_EQUAL(field%shape() , ([Ni, Nj, Nl])) FCTEST_CHECK_EQUAL(field%strides() , ([1, Ni, Ni*Nj*Nk])) call field%allocate_device() call field%update_device() call field%data(hview) call field%device_data(dview) FCTEST_CHECK_EQUAL(shape(hview), ([Ni, Nj, Nl])) FCTEST_CHECK_EQUAL(shape(dview), ([Ni, Nj, Nl])) call kernel_2(dview, Ni, Nj, Nl) call field%update_host() call field%deallocate_device() do i = 1, Ni do j = 1, Nj do k = 1, Nk a = 1._c_double; if (k == fix_id) a = -1._c_double; do l = 1, Nl FCTEST_CHECK_EQUAL(existing_data(i, j, k, l), a * real(1000*i + 100*j + 10*k + l, c_double)) enddo enddo enddo enddo field = atlas_Field(existing_data(fix_id:fix_id, fix_id, :, :)) FCTEST_CHECK_EQUAL(field%strides(), ([1, 16, 48])) #if HAVE_NOREPACK field = atlas_Field(existing_data(fix_id, fix_id, :, :)) FCTEST_CHECK_EQUAL(field%strides(), ([16, 48])) call try_offload(field) field = atlas_Field(existing_data(fix_id, :, fix_id, :)) FCTEST_CHECK_EQUAL(field%strides(), ([8, 48])) field = atlas_Field(existing_data(fix_id, :, :, fix_id)) FCTEST_CHECK_EQUAL(field%strides(), ([8, 16])) call try_offload(field) #endif field = atlas_Field(existing_data( :, fix_id, fix_id, :)) FCTEST_CHECK_EQUAL(field%strides(), ([1, 48])) call try_offload(field) field = atlas_Field(existing_data( :, fix_id, :, fix_id)) FCTEST_CHECK_EQUAL(field%strides(), ([1, 16])) call try_offload(field) field = atlas_Field(existing_data( :, :, fix_id, fix_id)) FCTEST_CHECK_EQUAL(field%strides(), ([1, 8])) call try_offload(field) field = atlas_Field(existing_data( :, fix_id, fix_id, fix_id)) FCTEST_CHECK_EQUAL(field%strides(), ([1])) call try_offload(field) #if HAVE_NOREPACK field = atlas_Field(existing_data(fix_id, :, fix_id, fix_id)) FCTEST_CHECK_EQUAL(field%strides(), ([8])) call try_offload(field) field = atlas_Field(existing_data(fix_id, fix_id, :, fix_id)) FCTEST_CHECK_EQUAL(field%strides(), ([16])) call try_offload(field) field = atlas_Field(existing_data(fix_id, fix_id, fix_id, :)) FCTEST_CHECK_EQUAL(field%strides(), ([48])) call try_offload(field) #endif call field%final() END_TEST ! ----------------------------------------------------------------------------- TEST( test_field_wrap_logical) implicit none logical, allocatable :: existing_data(:,:,:) logical, pointer :: fview(:,:,:) type(atlas_Field) :: field integer(c_int) :: Ni=1 integer(c_int) :: Nj=1 integer(c_int) :: Nk=6 integer(c_int) :: i,j,k write(0,*) "test_field_wrap_logical" allocate( existing_data(Ni,Nj,Nk) ) do i=1,Ni do j=1,Nj do k=1,Nk existing_data(i,j,k) = (mod(k,2) == 0 ) enddo enddo enddo ! Work with fields from here field = atlas_Field("wrapped",existing_data) call field%data(fview) call field%allocate_device() call field%update_device() call kernel_3(fview, Ni, Nj, Nk) FCTEST_CHECK_EQUAL( fview(1,1,1), .false. ) FCTEST_CHECK_EQUAL( fview(1,1,2), .true. ) call field%update_host() call field%deallocate_device() call field%final() ! ... until here ! Existing data is not deleted do i=1,Ni do j=1,Nj do k=1,Nk FCTEST_CHECK_EQUAL( fview(i,j,k), (mod(k,3) == 0) ) enddo enddo enddo END_TEST ! ----------------------------------------------------------------------------- END_TESTSUITE atlas-0.46.0/src/tests/field/test_field_acc.cc0000664000175000017500000001541015160212070021337 0ustar alastairalastair/* * (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 "hic/hic.h" #ifdef _OPENACC #include #endif #include "atlas/field/Field.h" #include "atlas/field/FieldSet.h" #include "atlas/field/MultiField.h" #include "atlas/parallel/acc/acc.h" #include "atlas/runtime/Log.h" #include "tests/AtlasTestEnvironment.h" using namespace std; using namespace eckit; //----------------------------------------------------------------------------- namespace atlas { namespace test { constexpr bool using_acc() { #ifdef _OPENACC return atlas::acc::devices() > 0; #else return false; #endif } //----------------------------------------------------------------------------- CASE("Information") { #ifdef _OPENACC Log::info() << "Compiled with OpenACC : 1" << std::endl; #else Log::info() << "Compiled with OpenACC : 0" << std::endl; #endif Log::info() << "OpenACC devices : " << acc::devices() << std::endl; Log::info() << "Using OpenACC : " << using_acc() << std::endl; } CASE("test_acc") { int* c_ptr = new int(); *c_ptr = 5; [[maybe_unused]] int* d_ptr{nullptr}; if (using_acc()) { HIC_CALL(hicMalloc(&d_ptr, sizeof(int))); acc::map(c_ptr, d_ptr, sizeof(int)); HIC_CALL(hicMemcpy(d_ptr, c_ptr, sizeof(int), hicMemcpyHostToDevice)); } atlas_acc_pragma( acc kernels present(c_ptr) ) { *c_ptr -= 3.; } if (using_acc()) { EXPECT_EQ( *c_ptr, 5. ); HIC_CALL(hicMemcpy(c_ptr, d_ptr, sizeof(int), hicMemcpyDeviceToHost)); } EXPECT_EQ( *c_ptr, 2. ); if (using_acc()) { HIC_CALL(hicFree(d_ptr)); } delete c_ptr; } CASE("test_field_acc") { auto field = Field("0", make_datatype(), array::make_shape(10,4)); auto view = array::make_view(field); double* cpu_ptr = static_cast(view.data()); view(3,2) = 1.; #if ! ATLAS_HAVE_GRIDTOOLS_STORAGE // TODO: gridtools storage does not implement view.index(...) at the moment cpu_ptr[view.index(3,2)] = 2.; EXPECT_EQ( view(3,2), 2. ); if (using_acc()) { field.updateDevice(); } atlas_acc_pragma( acc kernels present(cpu_ptr) ) { cpu_ptr[view.index(3,2)] = 3.; } if (using_acc()) { field.updateHost(); } EXPECT_EQ( view(3,2), 3. ); auto dview = array::make_device_view(field); double* dptr = using_acc() ? dview.data() : cpu_ptr; atlas_acc_pragma( acc parallel deviceptr(dptr) ) { dptr[dview.index(3,2)] = 4.; } if (using_acc()) { field.updateHost(); } EXPECT_EQ( view(3,2), 4. ); #endif } CASE("test_fieldset_acc") { auto init_field_on_host = [](Field& field, double seed) { auto hview = array::make_host_view(field); for (int i=0; i(field); for (int i=0; i(field) : array::make_host_view(field); double* dptr = dview.data(); atlas_acc_pragma (acc parallel deviceptr(dptr)) { for (int i=0; i(field); for (int i=0; i vshape = std::vector({4, -1, 3}); const std::vector var_names = {"temperature", "pressure", "density"}; field::MultiField mfield(array::make_datatype(), vshape, var_names); FieldSet fieldset; fieldset.add(mfield); fieldset.add(Field("contiguous", make_datatype(), array::make_shape(4,3))); return fieldset; }; SECTION("all fields in fieldset") { auto fieldset = create_fieldset(); double seed = 0.; for( auto field : fieldset ) { init_field_on_host(field, seed); seed += 100.; } fieldset.allocateDevice(); fieldset.updateDevice(); for( auto field : fieldset ) { edit_field_on_device(field); } if (using_acc()) { fieldset.updateHost(); } fieldset.deallocateDevice(); seed = 0.; for( auto field : fieldset ) { verify_field_on_host(field, seed); seed += 100.; } } SECTION("field 'temperature' in fieldset") { auto fieldset = create_fieldset(); auto field = fieldset.field("temperature"); double seed = 0.; init_field_on_host(field, seed); fieldset.allocateDevice({"temperature"}); fieldset.updateDevice({"temperature"}); edit_field_on_device(field); if (using_acc()) { fieldset.updateHost({"temperature"}); } fieldset.deallocateDevice({"temperature"}); verify_field_on_host(field, seed); } SECTION("field [0] in fieldset") { auto fieldset = create_fieldset(); auto field = fieldset.field(0); double seed = 0.; init_field_on_host(field, seed); fieldset.allocateDevice({0}); fieldset.updateDevice({0}); edit_field_on_device(field); if (using_acc()) { fieldset.updateHost({0}); } fieldset.deallocateDevice({0}); verify_field_on_host(field, seed); } } //----------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/field/fctest_field_host.F900000664000175000017500000000331515160212070022051 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. ! This File contains Unit Tests for testing the ! C++ / Fortran Interfaces to the State Datastructure ! @author Willem Deconinck #include "fckit/fctest.h" #include "atlas/atlas_f.h" ! ----------------------------------------------------------------------------- module fcta_Field_fxt use atlas_module use, intrinsic :: iso_c_binding implicit none contains end module ! ----------------------------------------------------------------------------- TESTSUITE_WITH_FIXTURE(fcta_Field,fcta_Field_fxt) ! ----------------------------------------------------------------------------- TESTSUITE_INIT call atlas_initialize() END_TESTSUITE_INIT ! ----------------------------------------------------------------------------- TESTSUITE_FINALIZE call atlas_finalise() END_TESTSUITE_FINALIZE ! ----------------------------------------------------------------------------- TEST( test_host_data ) type(atlas_Field) :: field real(8), pointer :: view(:,:) field = atlas_Field(kind=atlas_real(8),shape=[10,5]) call field%data(view) FCTEST_CHECK( .not. field%host_needs_update() ) #if ! ATLAS_HAVE_GPU FCTEST_CHECK( .not. field%device_needs_update() ) #endif call field%update_device() FCTEST_CHECK( .not. field%device_needs_update() ) call field%final() END_TEST ! ----------------------------------------------------------------------------- END_TESTSUITE atlas-0.46.0/src/tests/field/test_field_halo.cc0000664000175000017500000000613415160212070021537 0ustar alastairalastair/* * (C) Copyright 2025 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 "tests/AtlasTestEnvironment.h" #include "atlas/grid.h" #include "atlas/meshgenerator.h" #include "atlas/mesh.h" #include "atlas/functionspace.h" #include "atlas/field.h" #include "atlas/option.h" namespace atlas { namespace test { //----------------------------------------------------------------------------- CASE("test ghost at end") { auto g = Grid("O32"); auto mg = MeshGenerator("structured", util::Config("ghost_at_end", true)); auto m = mg.generate(g); auto fs = functionspace::NodeColumns(m, option::halo(1)); auto f = fs.createField(); auto h = f.halo(); EXPECT(h.contiguous()); EXPECT(h.appended()); if (mpi::size() == 1) { EXPECT_EQ(h.size(), 192); EXPECT_EQ(h.begin(), 5248); EXPECT_EQ(h.end(), 5440); } if (mpi::size() == 4) { if (mpi::rank() == 0) { EXPECT_EQ(h.size(), 284); EXPECT_EQ(h.begin(), 1312); EXPECT_EQ(h.end(), 1596); } } EXPECT(not h.updated()); h.updated(true); EXPECT(h.updated()); } //----------------------------------------------------------------------------- CASE("test ghost interleaved") { auto g = Grid("O32"); auto mg = MeshGenerator("structured", util::Config("ghost_at_end", false)); auto m = mg.generate(g); auto fs = functionspace::NodeColumns(m, option::halo(1)); auto f = fs.createField(); auto h = f.halo(); EXPECT(not h.contiguous()); EXPECT(not h.appended()); if (mpi::size() == 1) { EXPECT_EQ(h.size(), 192); EXPECT_EQ(h.begin(), 20); EXPECT_EQ(h.end(), 5440); } if (mpi::size() == 4) { if (mpi::rank() == 0) { EXPECT_EQ(h.size(), 284); EXPECT_EQ(h.begin(), 20); EXPECT_EQ(h.end(), 1596); } } EXPECT(not h.updated()); h.updated(true); EXPECT(h.updated()); } //----------------------------------------------------------------------------- CASE("test halo exchange") { auto fs = functionspace::NodeColumns{Mesh{Grid{"O32"}}, option::halo(1)}; auto f = fs.createField(); EXPECT(not f.halo().updated()); f.halo().update(); EXPECT(f.halo().updated()); f.halo().invalidate(); EXPECT(not f.halo().updated()); } //----------------------------------------------------------------------------- CASE("test halo exchange on device") { auto fs = functionspace::NodeColumns{Mesh{Grid{"O32"}}, option::halo(1)}; auto f = fs.createField(); EXPECT(not f.halo().updated()); f.updateDevice(); f.halo().update(option::on_device()); EXPECT(f.halo().updated()); f.halo().invalidate(); EXPECT(not f.halo().updated()); } //----------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/field/fctest_field_view.F900000664000175000017500000000452015160212070022045 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. ! This File contains Unit Tests for testing the ! C++ / Fortran Interfaces to the Mesh Datastructure ! @author Willem Deconinck #include "fckit/fctest.h" ! ----------------------------------------------------------------------------- module fcta_Field_view_fixture use atlas_module use, intrinsic :: iso_c_binding use, intrinsic :: iso_fortran_env implicit none end module ! ----------------------------------------------------------------------------- TESTSUITE_WITH_FIXTURE(fctest_atlas_Field_view,fcta_Field_view_fixture) ! ----------------------------------------------------------------------------- TESTSUITE_INIT call atlas_library%initialise() END_TESTSUITE_INIT ! ----------------------------------------------------------------------------- TESTSUITE_FINALIZE call atlas_library%finalise() END_TESTSUITE_FINALIZE ! ----------------------------------------------------------------------------- TEST( test_field_data_view) use omp_lib implicit none integer, parameter :: lon = 20, lev = 16, blocks = 138 type(atlas_Field) :: field real(kind=REAL64), pointer :: global(:,:,:), local(:,:) integer :: i, j, k, pid, num_threads field = atlas_Field(kind=atlas_real(kind=REAL64), shape=[lon, lev, blocks]) FCTEST_CHECK_EQUAL( field%rank() , 3 ) FCTEST_CHECK_EQUAL( field%shape(1) , lon ) FCTEST_CHECK_EQUAL( field%shape(2) , lev ) FCTEST_CHECK_EQUAL( field%shape(3) , blocks ) !$OMP PARALLEL PRIVATE(k, global, local) SHARED(field) num_threads = omp_get_num_threads() !$OMP DO SCHEDULE(DYNAMIC,1) do k=1, blocks pid = omp_get_thread_num() call field%data(global) local => global(:,:,k) local(:,:) = real(pid, REAL64) end do !$OMP END DO !$OMP END PARALLEL call field%data(global) do k=1, blocks do j=1, lev do i=1, lon FCTEST_CHECK( global(i,j,k) >= 0.0 ) FCTEST_CHECK( global(i,j,k) <= REAL(num_threads, REAL64) ) end do end do end do END_TEST ! ----------------------------------------------------------------------------- END_TESTSUITE atlas-0.46.0/src/tests/field/fctest_field.F900000664000175000017500000001773015160212070021022 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. ! This File contains Unit Tests for testing the ! C++ / Fortran Interfaces to the Mesh Datastructure ! @author Willem Deconinck #include "fckit/fctest.h" ! ----------------------------------------------------------------------------- module fcta_Field_fixture use atlas_module use, intrinsic :: iso_c_binding implicit none end module ! ----------------------------------------------------------------------------- TESTSUITE_WITH_FIXTURE(fctest_atlas_Field,fcta_Field_fixture) ! ----------------------------------------------------------------------------- TESTSUITE_INIT call atlas_library%initialise() END_TESTSUITE_INIT ! ----------------------------------------------------------------------------- TESTSUITE_FINALIZE call atlas_library%finalise() END_TESTSUITE_FINALIZE ! ----------------------------------------------------------------------------- TEST( test_field_name ) implicit none type(atlas_Field) :: field field = atlas_Field("field",atlas_real(c_double),(/10/)) FCTEST_CHECK_EQUAL( field%name() , "field" ) call field%final() ! memory leak if not finalized! END_TEST ! ----------------------------------------------------------------------------- TEST( test_field_owners) implicit none type(atlas_Field) :: f type(atlas_Field) :: f2 type(atlas_State) :: state type(atlas_FieldSet) :: fields write(0,*) "test_field_owners" f = atlas_Field("field_test_owners",atlas_real(c_double),(/10/) ) FCTEST_CHECK_EQUAL( f%owners() , 1 ) state = atlas_State() call state%add(f) FCTEST_CHECK_EQUAL( f%owners() , 2 ) f2 = state%field("field_test_owners") FCTEST_CHECK_EQUAL( f%owners() , 3 ) call f2%final() FCTEST_CHECK_EQUAL( f%owners() , 2 ) call state%remove("field_test_owners") FCTEST_CHECK_EQUAL( f%owners() , 1 ) fields = atlas_FieldSet("fields") FCTEST_CHECK_EQUAL( fields%name() , "fields" ) call fields%add(f) FCTEST_CHECK_EQUAL( f%owners() , 2 ) call fields%final() FCTEST_CHECK_EQUAL( f%owners() , 1 ) call f%final() ! memory leak if not finalized! call state%final() END_TEST ! ----------------------------------------------------------------------------- TEST( test_field_size ) implicit none integer, pointer :: fdata_int(:,:) real(c_float), pointer :: fdata_real32(:,:) real(c_double), pointer :: fdata_real64(:,:) type(atlas_Field) :: field write(*,*) "test_field_size starting" field = atlas_Field("field_0",atlas_integer(),(/0,10/)) FCTEST_CHECK_EQUAL( field%owners() , 1 ) call field%data(fdata_int) FCTEST_CHECK_EQUAL( field%datatype() , "int32" ) FCTEST_CHECK_EQUAL( size(fdata_int) , 0 ) call field%final() ! Not necessary, following "=" will handle it write(0,*) "finalized field0" field = atlas_Field("field_1",atlas_real(c_float),(/1,10/)) call field%data(fdata_real32) FCTEST_CHECK_EQUAL( field%datatype() , "real32" ) FCTEST_CHECK_EQUAL( size(fdata_real32) , 10 ) call field%final() !Not necessary, following "=" will handle it field = atlas_Field("field_2",atlas_real(c_double),(/2,10/)) FCTEST_CHECK_EQUAL( field%owners() , 1 ) call field%data(fdata_real64) FCTEST_CHECK_EQUAL( field%name(), "field_2" ) FCTEST_CHECK_EQUAL( field%datatype() , "real64" ) FCTEST_CHECK_EQUAL( size(fdata_real64) , 20 ) write(0,*) "Owners = ", field%owners() call field%attach() write(0,*) "Owners = ", field%owners() call field%attach() write(0,*) "Owners = ", field%owners() call field%detach() write(0,*) "Owners = ", field%owners() call field%detach() write(0,*) "Owners = ", field%owners() field = field write(0,*) "Owners = ", field%owners() call field%final() END_TEST ! ----------------------------------------------------------------------------- TEST( test_fieldset ) implicit none type(atlas_FieldSet) :: fieldset, fieldset_2 type(atlas_Field) :: field, field_2 write(*,*) "test_fieldset starting" fieldset = atlas_FieldSet() field = atlas_Field("field_0",atlas_integer(),(/0,10/)) call fieldset%add( field ) field = atlas_Field("field_1",atlas_integer(),(/1,10/)) call fieldset%add( field ) field = atlas_Field("field_2",atlas_integer(),(/2,10/)) call fieldset%add( field ) FCTEST_CHECK_EQUAL( fieldset%size(), 3 ) field = fieldset%field(1) FCTEST_CHECK_EQUAL( field%name(), "field_0" ) field = fieldset%field(2) FCTEST_CHECK_EQUAL( field%name(), "field_1" ) field = fieldset%field(3) FCTEST_CHECK_EQUAL( field%name(), "field_2" ) fieldset_2 = atlas_FieldSet() call fieldset_2%add(fieldset) field_2 = fieldset_2%field("field_0") call field_2%rename("field_00") FCTEST_CHECK(fieldset%has("field_00")) field = fieldset%field("field_00") FCTEST_CHECK_EQUAL(field%name(), "field_00") call fieldset%final() write(0,*) "test_fieldset end" END_TEST TEST( test_field_aligned ) implicit none type(atlas_Field) :: field real(c_double), pointer :: view(:,:,:) field = atlas_Field("field_3",atlas_real(c_double),(/3,5,10/),alignment=4) call field%data(view) FCTEST_CHECK_EQUAL( size(view,1) , 3 ) FCTEST_CHECK_EQUAL( size(view,2) , 5 ) FCTEST_CHECK_EQUAL( size(view,3) , 10 ) FCTEST_CHECK_EQUAL( field%shape(1), 3 ) FCTEST_CHECK_EQUAL( field%shape(2), 5 ) FCTEST_CHECK_EQUAL( field%shape(3), 10 ) FCTEST_CHECK_EQUAL( field%stride(1), 1 ) FCTEST_CHECK_EQUAL( field%stride(2), 4 ) FCTEST_CHECK_EQUAL( field%stride(3), 4*5 ) call field%final() END_TEST TEST( test_fieldset_slice ) implicit none type(atlas_FieldSet) :: fieldset type(atlas_Field) :: field integer, pointer :: view1d(:), view3d(:,:,:) integer, pointer :: slice0d, slice2d(:,:) ! slicing of a three-dimensional field field = atlas_Field("field_4",atlas_integer(),(/3,5,10/)) call field%data(view3d) view3d(1,2,3) = 123 call field%data(slice2d,3) FCTEST_CHECK_EQUAL( size(slice2d,1) , 3 ) FCTEST_CHECK_EQUAL( size(slice2d,2) , 5 ) FCTEST_CHECK_EQUAL( slice2d(1,2), 123 ) slice2d(1,2) = slice2d(1,2) * 2 call field%data(view3d) FCTEST_CHECK_EQUAL( view3d(1,2,3), 246 ) ! slicing of a one-dimensional field field = atlas_Field("field_5",atlas_integer(),(/3/)) call field%data(view1d) view1d(2) = 123 call field%data(slice0d,2) FCTEST_CHECK_EQUAL( slice0d, 123 ) slice0d = slice0d * 2 call field%data(view1d) FCTEST_CHECK_EQUAL( view1d(2), 246 ) call field%final() ! slicing of a three-dimensional field through a fieldset by name and by idx fieldset = atlas_FieldSet() field = atlas_Field("field_6",atlas_integer(),(/3,5,10/)) call fieldset%add(field) field = atlas_Field("field_7",atlas_integer(),(/3/)) call fieldset%add(field) call fieldset%data("field_6", view3d) view3d(1,2,3) = 122 call fieldset%data(1, view3d) view3d(1,2,3) = 123 call fieldset%data("field_6", slice2d, 3) slice2d(1,2) = slice2d(1,2) + 1 call fieldset%data(1, slice2d, 3) slice2d(1,2) = slice2d(1,2) - 1 FCTEST_CHECK_EQUAL( size(slice2d,1) , 3 ) FCTEST_CHECK_EQUAL( size(slice2d,2) , 5 ) FCTEST_CHECK_EQUAL( slice2d(1,2), 123 ) slice2d(1,2) = slice2d(1,2) * 2 call fieldset%data('field_6', view3d) FCTEST_CHECK_EQUAL( view3d(1,2,3), 246 ) ! slicing of a one-dimensional field through a fieldset by name and by idx call fieldset%data('field_7', view1d) view1d(2) = 122 call fieldset%data(2, view1d) view1d(2) = 123 call field%data(slice0d, 2) FCTEST_CHECK_EQUAL( slice0d, 123 ) slice0d = slice0d * 2 call field%data(view1d) FCTEST_CHECK_EQUAL( view1d(2), 246 ) call field%final() call fieldset%final() END_TEST ! ----------------------------------------------------------------------------- END_TESTSUITE atlas-0.46.0/src/tests/field/test_fieldset.cc0000664000175000017500000001024515160212070021246 0ustar alastairalastair/* * (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/field/Field.h" #include "atlas/field/FieldSet.h" #include "tests/AtlasTestEnvironment.h" using namespace std; using namespace eckit; //----------------------------------------------------------------------------- namespace atlas { namespace test { //----------------------------------------------------------------------------- CASE("test_rename") { FieldSet fieldset; auto field_0 = fieldset.add(Field("0", make_datatype(), array::make_shape(10,4))); auto field_1 = fieldset.add(Field("1", make_datatype(), array::make_shape(10,5))); auto field_2 = fieldset.add(Field("2", make_datatype(), array::make_shape(10,6))); EXPECT(fieldset.has("0")); EXPECT(fieldset.has("1")); EXPECT(fieldset.has("2")); field_0.rename("field_0"); field_1.rename("field_1"); field_2.rename("field_2"); EXPECT(fieldset.has("field_0")); EXPECT(fieldset.has("field_1")); EXPECT(fieldset.has("field_2")); EXPECT(!fieldset.has("0")); EXPECT(!fieldset.has("1")); EXPECT(!fieldset.has("2")); EXPECT_EQ(fieldset.field(0).name(),"field_0"); EXPECT_EQ(fieldset.field(1).name(),"field_1"); EXPECT_EQ(fieldset.field(2).name(),"field_2"); EXPECT_EQ(fieldset.field("field_0").shape(1),4); EXPECT_EQ(fieldset.field("field_1").shape(1),5); EXPECT_EQ(fieldset.field("field_2").shape(1),6); field_1.rename(""); EXPECT(!fieldset.has("field_1")); EXPECT_EQ(fieldset.field(1).name(),std::string("")); EXPECT(fieldset.has("[1]")); } CASE("test_fieldset_concatenation") { FieldSet fieldset_1; FieldSet fieldset_2; auto field_1 = fieldset_1.add(Field("0", make_datatype(), array::make_shape(10,4))); auto field_1_v = array::make_view(field_1); field_1_v(1,1) = 2.; fieldset_2.add(fieldset_1); auto field_2 = fieldset_2.field("0"); field_2.rename(""); auto field_2_v = array::make_view(field_2); field_2_v(1,1) = 1.; EXPECT(!fieldset_2.has("")); EXPECT_EQ(fieldset_2.field(0).name(), ""); EXPECT_EQ(field_1_v(1,1), 1.); } CASE("test_duplicate_name_throws") { FieldSet fieldset; [[maybe_unused]] auto field_0 = fieldset.add(Field("0", make_datatype(), array::make_shape(10,4))); [[maybe_unused]] auto field_1 = fieldset.add(Field("0", make_datatype(), array::make_shape(10,5))); // same name as field_0, uh-oh ! [[maybe_unused]] auto field_2 = fieldset.add(Field("2", make_datatype(), array::make_shape(10,6))); Field f; EXPECT_NO_THROW(f = fieldset["2"]); // OK EXPECT_EQ(f.shape(1), 6); EXPECT_THROWS(f = fieldset["0"]); // ambiguous because field_0 and field_1 have same name, should throw field_1.rename("1"); // fix ambiguity between field_0 and field_1 EXPECT_NO_THROW(f = fieldset["0"]); // no longer ambiguous EXPECT_EQ(f.shape(1), 4); // to be sure that we got the right field EXPECT_NO_THROW(f = fieldset["1"]); // no longer ambiguous EXPECT_EQ(f.shape(1), 5); // to be sure that we got the right field field_2.rename("0"); // Introduce new ambiguity between field_0 and field_2 EXPECT_THROWS(f = fieldset["0"]); // ambiguous because field_0 and field_2 have same name, should throw field_2.rename("2"); // fix ambiguity EXPECT_NO_THROW(f = fieldset["0"]); // no longer ambiguous EXPECT_EQ(f.shape(1), 4); // to be sure we got the right field EXPECT_NO_THROW(f = fieldset["2"]); // no longer ambiguous EXPECT_EQ(f.shape(1), 6); // to be sure we got the right field } //----------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/field/test_field_memory_resource.cc0000664000175000017500000000765215160212070024041 0ustar alastairalastair/* * (C) Copyright 2025- 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 "pluto/pluto.h" #include "atlas/field.h" #include "tests/AtlasTestEnvironment.h" namespace atlas { namespace test { CASE("default resource") { std::stringstream pluto_trace_stream; pluto::trace::enable(true); pluto::trace::set(pluto_trace_stream); Field field_1("field_1", make_datatype(), array::make_shape(20, 5)); { Field field_2("field_2", make_datatype(), array::make_shape(200, 5)); } Field field_3("field_3", make_datatype(), array::make_shape(20, 10)); EXPECT_EQ( pluto::memory::host.total_allocations() , 3 ); EXPECT_EQ( pluto::memory::host.allocations() , 2 ); EXPECT_EQ( pluto::memory::host.bytes() , 2400 ); EXPECT_EQ( pluto::memory::host.high_watermark() , 8800 ); auto pluto_trace = pluto_trace_stream.str(); std::cout << "pluto_trace:\n" << pluto_trace << std::endl; auto pluto_trace_contains = [&pluto_trace](const std::string& str) { return (pluto_trace.find(str) != std::string::npos); }; EXPECT(pluto_trace_contains("PLUTO_TRACE pluto::host_resource::allocate(label=field_1, bytes=800.00B, alignment=256)")); EXPECT(pluto_trace_contains("PLUTO_TRACE pluto::host_resource::allocate(label=field_2, bytes=7.81K, alignment=256)")); EXPECT(pluto_trace_contains("PLUTO_TRACE pluto::host_resource::deallocate(label=field_2")); EXPECT(pluto_trace_contains("PLUTO_TRACE pluto::host_resource::allocate(label=field_3, bytes=1.56K, alignment=256)")); pluto::trace::enable(false); } CASE("pinned_pool_resource") { pluto::host::set_default_resource("pluto::pinned_pool_resource"); std::stringstream pluto_trace_stream; pluto::trace::enable(true); pluto::trace::set(pluto_trace_stream); Field field_1("field_1", make_datatype(), array::make_shape(20, 5)); { Field field_2("field_2", make_datatype(), array::make_shape(200, 5)); } Field field_3("field_3", make_datatype(), array::make_shape(20, 10)); EXPECT_EQ( pluto::memory::pinned.total_allocations() , 1 ); EXPECT_EQ( pluto::memory::pinned.allocations() , 1 ); EXPECT_EQ( pluto::memory::pinned.bytes() , 256*1024*1024 ); // 256 mb EXPECT_EQ( pluto::memory::pinned.high_watermark() , 256*1024*1024 ); EXPECT_EQ( pluto::memory::pinned_pool.total_allocations() , 3 ); EXPECT_EQ( pluto::memory::pinned_pool.allocations() , 2 ); EXPECT_EQ( pluto::memory::pinned_pool.bytes() , 2400 ); EXPECT_EQ( pluto::memory::pinned_pool.high_watermark() , 8800 ); auto pluto_trace = pluto_trace_stream.str(); std::cout << "pluto_trace:\n" << pluto_trace << std::endl; auto pluto_trace_contains = [&pluto_trace](const std::string& str) { return (pluto_trace.find(str) != std::string::npos); }; EXPECT(pluto_trace_contains("PLUTO_TRACE pluto::pinned_resource::allocate(label=pool_chunk, bytes=256.00M, alignment=256)")); EXPECT(pluto_trace_contains("PLUTO_TRACE pluto::pinned_pool_resource::allocate(label=field_1, bytes=800.00B, alignment=256)")); EXPECT(pluto_trace_contains("PLUTO_TRACE pluto::pinned_pool_resource::allocate(label=field_2, bytes=7.81K, alignment=256)")); EXPECT(pluto_trace_contains("PLUTO_TRACE pluto::pinned_pool_resource::deallocate(label=field_2")); EXPECT(pluto_trace_contains("PLUTO_TRACE pluto::pinned_pool_resource::allocate(label=field_3, bytes=1.56K, alignment=256)")); pluto::trace::enable(false); } } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/field/external_acc_routine.F900000664000175000017500000000113615160212070022555 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. subroutine external_acc_routine(view) implicit none real(4), intent(inout) :: view(:,:) !$acc data present(view) !$acc kernels view(1,1) = 4. !$acc end kernels !$acc end data end subroutine external_acc_routine atlas-0.46.0/src/tests/field/fctest_field_wrap.F900000664000175000017500000001076715160212070022056 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. ! This File contains Unit Tests for testing the ! C++ / Fortran Interfaces to the Mesh Datastructure ! @author Willem Deconinck #include "fckit/fctest.h" ! ----------------------------------------------------------------------------- module fcta_Field_wrap_fixture use atlas_module use, intrinsic :: iso_c_binding implicit none end module ! ----------------------------------------------------------------------------- TESTSUITE_WITH_FIXTURE(fctest_atlas_Field_wrap,fcta_Field_wrap_fixture) ! ----------------------------------------------------------------------------- TESTSUITE_INIT call atlas_library%initialise() END_TESTSUITE_INIT ! ----------------------------------------------------------------------------- TESTSUITE_FINALIZE call atlas_library%finalise() END_TESTSUITE_FINALIZE ! ----------------------------------------------------------------------------- TEST( test_field_wrapdata) implicit none real(c_double), allocatable :: existing_data(:,:,:) real(c_double), pointer :: data(:,:,:) type(atlas_Field) :: field integer(c_int) :: N=20 integer(c_int) :: j write(0,*) "test_field_wrapdata" allocate( existing_data(2,10,N) ) write(0,*) "line ", __LINE__ ! Work with fields from here field = atlas_Field("wrapped",existing_data) FCTEST_CHECK_EQUAL( field%rank() , 3 ) FCTEST_CHECK_EQUAL( field%size() , 2*10*N ) FCTEST_CHECK_EQUAL( field%shape(1) , 2 ) FCTEST_CHECK_EQUAL( field%shape(2) , 10 ) FCTEST_CHECK_EQUAL( field%shape(3) , N ) FCTEST_CHECK_EQUAL( field%owners() , 1 ) call field%data(data) do j=1,N data(1,1,j) = j enddo call field%final() ! ... until here ! Existing data is not deleted do j=1,N FCTEST_CHECK_EQUAL( existing_data(1,1,j), real(j,c_double) ) enddo END_TEST ! ----------------------------------------------------------------------------- TEST( test_field_wrapdataslice) implicit none real(c_double), allocatable :: existing_data(:,:,:,:) real(c_double), pointer :: data(:,:,:) type(atlas_Field) :: field integer(c_int) :: i,j,k,l write(0,*) "test_field_wrapdataslice" allocate( existing_data(4,3,2,5) ) do i=1,4 do j=1,3 do k=1,2 do l=1,5 existing_data(i,j,k,l) = 1000*i+100*j+10*k+l enddo enddo enddo enddo field = atlas_Field(existing_data(:,:,1,:)) FCTEST_CHECK_EQUAL( field%rank() , 3 ) FCTEST_CHECK_EQUAL( field%size() , 4*3*5 ) FCTEST_CHECK_EQUAL( field%shape(1) , 4 ) FCTEST_CHECK_EQUAL( field%shape(2) , 3 ) FCTEST_CHECK_EQUAL( field%shape(3) , 5 ) call field%data(data) write(0,*) "Shape of field = ",shape(data) k=1 do i=1,4 do j=1,3 do l=1,5 FCTEST_CHECK_EQUAL(data(i,j,l) , real(1000*i+100*j+10*k+l,c_double) ) enddo enddo enddo call field%final() END_TEST ! ----------------------------------------------------------------------------- TEST( test_field_wrap_logical) implicit none logical, allocatable :: existing_data(:,:,:) logical, pointer :: data(:,:,:) type(atlas_Field) :: field integer(c_int) :: Ni=1 integer(c_int) :: Nj=1 integer(c_int) :: Nk=6 integer(c_int) :: i,j,k write(0,*) "test_field_wrap_logical" allocate( existing_data(Ni,Nj,Nk) ) do i=1,Ni do j=1,Nj do k=1,Nk existing_data(i,j,k) = (mod(k,2) == 0 ) enddo enddo enddo ! Work with fields from here field = atlas_Field("wrapped",existing_data) FCTEST_CHECK_EQUAL( field%rank() , 3 ) FCTEST_CHECK_EQUAL( field%size() , Ni*Nj*Nk ) FCTEST_CHECK_EQUAL( field%shape(1) , Ni ) FCTEST_CHECK_EQUAL( field%shape(2) , Nj ) FCTEST_CHECK_EQUAL( field%shape(3) , Nk ) FCTEST_CHECK_EQUAL( field%owners() , 1 ) call field%data(data) do i=1,Ni do j=1,Nj do k=1,Nk FCTEST_CHECK_EQUAL( data(i,j,k), (mod(k,2) == 0) ) data(i,j,k) = (mod(k,3) == 0 ) enddo enddo enddo call field%final() ! ... until here ! Existing data is not deleted do i=1,Ni do j=1,Nj do k=1,Nk FCTEST_CHECK_EQUAL( data(i,j,k), (mod(k,3) == 0) ) enddo enddo enddo END_TEST ! ----------------------------------------------------------------------------- END_TESTSUITE atlas-0.46.0/src/tests/field/fctest_fieldset_gpu.F900000664000175000017500000002360015160212070022402 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. ! This File contains Unit Tests for testing the ! C++ / Fortran Interfaces to the State Datastructure ! @author Willem Deconinck ! @author Slavko Brdar #include "fckit/fctest.h" ! ----------------------------------------------------------------------------- module fcta_FieldSet_gpu_fxt use atlas_module use, intrinsic :: iso_c_binding, only: c_ptr implicit none contains subroutine kernel_host_ptr(view) implicit none real(4), intent(inout) :: view(:,:) !$acc data present(view) !$acc kernels present(view) view(2,1) = 4. !$acc end kernels !$acc end data end subroutine kernel_host_ptr subroutine kernel_device_ptr(dview) implicit none real(4) :: dview(:,:) !$acc data deviceptr(dview) !$acc kernels dview(2,1) = 5. !$acc end kernels !$acc end data end subroutine kernel_device_ptr ! ----------------------------------------------------------------------------- end module ! ----------------------------------------------------------------------------- ! ----------------------------------------------------------------------------- TESTSUITE_WITH_FIXTURE(fcta_FieldSet_gpu,fcta_FieldSet_gpu_fxt) ! ----------------------------------------------------------------------------- TESTSUITE_INIT call atlas_initialize() END_TESTSUITE_INIT ! ----------------------------------------------------------------------------- TESTSUITE_FINALIZE call atlas_finalise() END_TESTSUITE_FINALIZE ! ----------------------------------------------------------------------------- TEST( test_fieldset_cummulative_gpu_calls_all_fields ) implicit none type(atlas_FieldSet) :: fset type(atlas_Field) :: field real(4), pointer :: fview(:,:) real(4), pointer :: fview_dev(:,:) print *, "test_fieldset_cummulative_gpu_calls_all_fields" fset = atlas_FieldSet() call fset%add(atlas_Field(name="f1", kind=atlas_real(4), shape=[1,2])) call fset%add(atlas_Field(name="f2", kind=atlas_real(4), shape=[2,2])) call fset%add(atlas_Field(name="f3", kind=atlas_real(4), shape=[2,1])) print *, "... by idx" field = fset%field(2) call field%data(fview) fview(:,:) = 1. fview(2,1) = 2. call fset%set_host_needs_update(.false.) call fset%allocate_device() FCTEST_CHECK_EQUAL(field%device_allocated(), .true.) call fset%update_device() !$acc kernels present(fview) fview(2,1) = 3. !$acc end kernels FCTEST_CHECK_EQUAL( fview(2,1), 2. ) call fset%update_host() FCTEST_CHECK_EQUAL( fview(2,1), 3. ) call kernel_host_ptr(fview) FCTEST_CHECK_EQUAL( fview(2,1), 3. ) call fset%update_host() FCTEST_CHECK_EQUAL( fview(2,1), 4. ) call field%device_data(fview_dev) call kernel_device_ptr(fview_dev) FCTEST_CHECK_EQUAL( fview(2,1), 4. ) call fset%update_host() FCTEST_CHECK_EQUAL( fview(2,1), 5. ) call fset%deallocate_device() print *, "... by name" field = fset%field("f3") call field%data(fview) fview(:,:) = 3. fview(2,1) = 4. call fset%allocate_device() call fset%update_device() !$acc kernels present(fview) fview(2,1) = 7. !$acc end kernels FCTEST_CHECK_EQUAL( fview(2,1), 4. ) call fset%update_host() FCTEST_CHECK_EQUAL( fview(2,1), 7. ) call fset%final() END_TEST ! ----------------------------------------------------------------------------- TEST( test_fieldset_cummulative_gpu_calls_subset_field) implicit none type(atlas_FieldSet) :: fset type(atlas_Field) :: field real(4), pointer :: fview(:,:) print *, "fieldset_cummulative_gpu_calls subset fields" fset = atlas_FieldSet() call fset%add(atlas_Field(name="f1", kind=atlas_real(4), shape=[1,2])) call fset%add(atlas_Field(name="f2", kind=atlas_real(4), shape=[2,2])) call fset%add(atlas_Field(name="f3", kind=atlas_real(4), shape=[2,1])) print *, "... by idx" field = fset%field(2) call field%data(fview) fview(:,:) = 1. fview(2,1) = 2. call fset%set_host_needs_update((/2, 3/), .false.) call fset%allocate_device((/ 2, 3 /)) ! device allocate only for field 2 and 3 call fset%sync_host_device((/ 2, 3 /)) ! device-memcpy for field 2 and 3 !$acc kernels present(fview) fview(2,1) = 5. !$acc end kernels call fset%set_host_needs_update((/ 2 /)) FCTEST_CHECK_EQUAL( fview(2,1), 2. ) call fset%sync_host_device() FCTEST_CHECK_EQUAL( fview(2,1), 5. ) call fset%deallocate_device() print *, "... by name" field = fset%field("f3") call field%data(fview) fview(:,:) = 3. fview(2,1) = 4. call fset%allocate_device((/ "f2", "f3" /)) call fset%sync_host_device((/ "f2", "f3" /)) !$acc kernels present(fview) fview(2,1) = 7. !$acc end kernels call fset%set_host_needs_update((/ "f3" /)) FCTEST_CHECK_EQUAL( fview(2,1), 4. ) call fset%sync_host_device() FCTEST_CHECK_EQUAL( fview(2,1), 7. ) call fset%final() END_TEST ! ----------------------------------------------------------------------------- TEST( test_fieldset_cummulative_gpu_calls_with_sync_host_device) implicit none type(atlas_FieldSet) :: fset type(atlas_Field) :: field real(4), pointer :: fview(:,:) print *, "test_fieldset_cummulative_gpu_calls_with_sync_host_device" fset = atlas_FieldSet() call fset%add(atlas_Field(name="f1", kind=atlas_real(4), shape=[1,2])) call fset%add(atlas_Field(name="f2", kind=atlas_real(4), shape=[2,2])) call fset%add(atlas_Field(name="f3", kind=atlas_real(4), shape=[2,1])) print *, "... by idx" field = fset%field(2) call field%data(fview) fview(:,:) = 1. fview(2,1) = 2. call fset%set_host_needs_update(.false.) call fset%allocate_device((/ 2, 3 /)) ! device allocate only for field 2 and 3 call fset%set_device_needs_update((/ 2 /)) call fset%sync_host_device((/ 2 /)) ! device-memcpy for field 2 and 3 !$acc kernels present(fview) fview(2,1) = 5. !$acc end kernels call fset%set_host_needs_update((/ 2 /)) FCTEST_CHECK_EQUAL( fview(2,1), 2. ) call fset%sync_host_device() FCTEST_CHECK_EQUAL( fview(2,1), 5. ) call fset%deallocate_device() print *, "... by name" field = fset%field("f2") call field%data(fview) fview(:,:) = 3. fview(2,1) = 4. call fset%set_host_needs_update(.false.) call fset%allocate_device((/ "f2", "f3" /)) ! device allocate only for field 2 and 3 call fset%set_device_needs_update((/ "f2" /)) ! set fields for sync_host_device call fset%sync_host_device((/ "f2" /)) ! device-memcpy for field 'f2' !$acc kernels present(fview) fview(2,1) = 7. !$acc end kernels call fset%set_host_needs_update((/ "f2" /)) FCTEST_CHECK_EQUAL( fview(2,1), 4. ) call fset%sync_host_device() FCTEST_CHECK_EQUAL( fview(2,1), 7. ) call fset%final() END_TEST ! ----------------------------------------------------------------------------- TEST( test_fieldset_cummulative_gpu_calls_subset_field_with_sync_host_sync_device) implicit none type(atlas_FieldSet) :: fset type(atlas_Field) :: field real(4), pointer :: fview(:,:) print *, "fieldset_cummulative_gpu_calls subset fields" fset = atlas_FieldSet() call fset%add(atlas_Field(name="f1", kind=atlas_real(4), shape=[1,2])) call fset%add(atlas_Field(name="f2", kind=atlas_real(4), shape=[2,2])) call fset%add(atlas_Field(name="f3", kind=atlas_real(4), shape=[2,1])) print *, "... by idx" field = fset%field(2) call field%data(fview) fview(:,:) = 1. fview(2,1) = 2. call fset%set_host_needs_update((/2, 3/), .false.) call fset%allocate_device((/ 2, 3 /)) ! device allocate only for field 2 and 3 call fset%sync_device((/ 2, 3 /)) ! device-memcpy for field 2 and 3 !$acc kernels present(fview) fview(2,1) = 5. !$acc end kernels call fset%set_host_needs_update((/ 2 /)) FCTEST_CHECK_EQUAL( fview(2,1), 2. ) call fset%sync_host() FCTEST_CHECK_EQUAL( fview(2,1), 5. ) call fset%deallocate_device() print *, "... by name" field = fset%field("f3") call field%data(fview) fview(:,:) = 3. fview(2,1) = 4. call fset%allocate_device((/ "f2", "f3" /)) call fset%sync_device((/ "f2", "f3" /)) !$acc kernels present(fview) fview(2,1) = 7. !$acc end kernels call fset%set_host_needs_update((/ "f3" /)) FCTEST_CHECK_EQUAL( fview(2,1), 4. ) call fset%sync_host() FCTEST_CHECK_EQUAL( fview(2,1), 7. ) call fset%final() END_TEST ! ----------------------------------------------------------------------------- TEST( test_fieldset_cummulative_gpu_calls_with_sync_host_sync_device) implicit none type(atlas_FieldSet) :: fset type(atlas_Field) :: field real(4), pointer :: fview(:,:) print *, "test_fieldset_cummulative_gpu_calls_with_sync_host_device" fset = atlas_FieldSet() call fset%add(atlas_Field(name="f1", kind=atlas_real(4), shape=[1,2])) call fset%add(atlas_Field(name="f2", kind=atlas_real(4), shape=[2,2])) call fset%add(atlas_Field(name="f3", kind=atlas_real(4), shape=[2,1])) print *, "... by idx" field = fset%field(2) call field%data(fview) fview(:,:) = 1. fview(2,1) = 2. call fset%set_host_needs_update(.false.) call fset%allocate_device((/ 2, 3 /)) ! device allocate only for field 2 and 3 call fset%set_device_needs_update((/ 2 /)) call fset%sync_device((/ 2 /)) ! device-memcpy for field 2 and 3 !$acc kernels present(fview) fview(2,1) = 5. !$acc end kernels call fset%set_host_needs_update((/ 2 /)) FCTEST_CHECK_EQUAL( fview(2,1), 2. ) call fset%sync_host() FCTEST_CHECK_EQUAL( fview(2,1), 5. ) call fset%deallocate_device() print *, "... by name" field = fset%field("f2") call field%data(fview) fview(:,:) = 3. fview(2,1) = 4. call fset%set_host_needs_update(.false.) call fset%allocate_device((/ "f2", "f3" /)) ! device allocate only for field 2 and 3 call fset%set_device_needs_update((/ "f2" /)) ! set fields for sync_host_device call fset%sync_device((/ "f2" /)) ! device-memcpy for field 'f2' !$acc kernels present(fview) fview(2,1) = 7. !$acc end kernels call fset%set_host_needs_update((/ "f2" /)) FCTEST_CHECK_EQUAL( fview(2,1), 4. ) call fset%sync_host() FCTEST_CHECK_EQUAL( fview(2,1), 7. ) call fset%final() END_TEST ! ----------------------------------------------------------------------------- END_TESTSUITE atlas-0.46.0/src/tests/field/fctest_field_gpu.F900000664000175000017500000000734015160212070021671 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. ! This File contains Unit Tests for testing the ! C++ / Fortran Interfaces to the State Datastructure ! @author Willem Deconinck ! @author Slavko Brdar #include "fckit/fctest.h" ! ----------------------------------------------------------------------------- module fcta_Field_gpu_fxt use atlas_module use, intrinsic :: iso_c_binding implicit none contains subroutine module_acc_routine(view) implicit none real(4), intent(inout) :: view(:,:) !$acc data present(view) !$acc kernels view(1,1) = 4. !$acc end kernels !$acc end data end subroutine module_acc_routine subroutine kernel_host_ptr(view) implicit none real(4), intent(inout) :: view(:,:) !$acc data present(view) !$acc kernels present(view) view(2,1) = 4. !$acc end kernels !$acc end data end subroutine kernel_host_ptr subroutine kernel_device_ptr(dview) implicit none real(4), intent(inout) :: dview(:,:) !$acc data deviceptr(dview) !$acc kernels dview(2,1) = 5. !$acc end kernels !$acc end data end subroutine kernel_device_ptr end module ! ----------------------------------------------------------------------------- ! ----------------------------------------------------------------------------- TESTSUITE_WITH_FIXTURE(fcta_Field_gpu,fcta_Field_gpu_fxt) ! ----------------------------------------------------------------------------- TESTSUITE_INIT call atlas_initialize() END_TESTSUITE_INIT ! ----------------------------------------------------------------------------- TESTSUITE_FINALIZE call atlas_finalise() END_TESTSUITE_FINALIZE ! ----------------------------------------------------------------------------- TEST( test_host_data ) implicit none type(atlas_Field) :: field real(4), pointer :: view(:,:) !!! WARNING !!! Without this interface, there is a runtime error !!! interface subroutine external_acc_routine(view) real(4), intent(inout) :: view(:,:) end subroutine external_acc_routine end interface field = atlas_Field(kind=atlas_real(4),shape=[5,3]) call field%data(view) view(:,:) = 0 view(1,1) = 1 call field%update_device() !$acc data present(view) !$acc kernels view(1,1) = 2. !$acc end kernels !$acc end data ! FCTEST_CHECK_EQUAL( view(1,1), 1. ) ! update_host may not be needed when field uses managed memory! call field%update_host() FCTEST_CHECK_EQUAL( view(1,1), 2. ) view(1,1) = 3. call field%update_device() write(0,*) "Calling module_acc_routine ..." call module_acc_routine(view) write(0,*) "Calling module_acc_routine ... done" write(0,*) "Calling external_acc_routine ..." call external_acc_routine(view) write(0,*) "Calling external_acc_routine ... done" ! FCTEST_CHECK_EQUAL( view(1,1), 3. ) ! update_host may not be needed when field uses managed memory! call field%update_host() FCTEST_CHECK_EQUAL( view(1,1), 4. ) call field%final() END_TEST ! ----------------------------------------------------------------------------- TEST( test_device_data ) implicit none type(atlas_Field) :: field real(4), pointer :: view(:,:) real(4), pointer :: dview(:,:) field = atlas_Field(kind=atlas_real(4),shape=[5,3]) call field%data(view) view(:,:) = 0 call field%allocate_device() call field%update_device() call field%device_data(dview) call kernel_device_ptr(dview) call field%update_host() FCTEST_CHECK_EQUAL( view(2,1), 5. ) call field%final() END_TEST ! ----------------------------------------------------------------------------- END_TESTSUITE atlas-0.46.0/src/tests/field/test_multifield_ifs.cc0000664000175000017500000003702615160212070022454 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include #include #include #include "eckit/config/YAMLConfiguration.h" #include "atlas/array/ArrayView.h" #include "atlas/array/MakeView.h" #include "atlas/field/Field.h" #include "atlas/field/MultiField.h" #include "atlas/field/MultiFieldCreatorArray.h" #include "atlas/field/detail/MultiFieldImpl.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Log.h" #include "tests/AtlasTestEnvironment.h" using namespace atlas::field; namespace atlas { namespace test { const std::vector make_shape(const std::initializer_list& list) { return std::vector(list); } CASE("multifield_generator") { EXPECT(MultiFieldCreatorFactory::has("MultiFieldCreatorIFS")); std::unique_ptr MultiFieldCreatorIFS(MultiFieldCreatorFactory::build("MultiFieldCreatorIFS")); EXPECT(MultiFieldCreatorFactory::has("MultiFieldCreatorArray")); std::unique_ptr MultiFieldCreatorArray(MultiFieldCreatorFactory::build("MultiFieldCreatorArray")); } CASE("multifield_ifs_create") { using Value = float; int nproma = 16; int nlev = 100; int ngptot = 2000; auto json = [&]() -> std::string { util::Config p; p.set("type", "MultiFieldCreatorIFS"); p.set("ngptot", ngptot); p.set("nproma", nproma); p.set("nlev", nlev); p.set("datatype", array::make_datatype().str()); p.set("fields", std::vector{ util::Config("name", "temperature"), util::Config("name", "pressure"), util::Config("name", "density"), util::Config("name", "clv")("nvar", 5), // 'clv' with 5 subvariables util::Config("name", "wind_u") }); return p.json(); }; SECTION("Print configuration") { Log::info() << "json = " << json() << std::endl; } SECTION("test") { MultiField multifield{eckit::YAMLConfiguration{json()}}; const auto nblk = multifield.array().shape(0); const auto nvar = multifield.array().shape(1); const auto nfld = multifield.size(); EXPECT_EQ(nfld, 5); EXPECT_EQ(nvar, 9); EXPECT_EQ(multifield.size(), 5); EXPECT(multifield.has("temperature")); EXPECT(multifield.has("pressure")); EXPECT(multifield.has("density")); EXPECT(multifield.has("clv")); EXPECT(multifield.has("wind_u")); Log::info() << multifield.field("temperature") << std::endl; Log::info() << multifield.field("pressure") << std::endl; Log::info() << multifield.field("density") << std::endl; Log::info() << multifield.field("clv") << std::endl; Log::info() << multifield.field("wind_u") << std::endl; auto temp = array::make_view(multifield.field("temperature")); auto pres = array::make_view(multifield.field("pressure")); auto dens = array::make_view(multifield.field("density")); auto clv = array::make_view(multifield.field("clv")); // note rank 4 auto wind_u = array::make_view(multifield.field("wind_u")); EXPECT_EQ(multifield[0].name(), "temperature"); EXPECT_EQ(multifield[1].name(), "pressure"); EXPECT_EQ(multifield[2].name(), "density"); EXPECT_EQ(multifield[3].name(), "clv"); EXPECT_EQ(multifield[4].name(), "wind_u"); auto block_stride = multifield.array().stride(0); auto field_stride = nproma * nlev; auto level_stride = nproma; auto nproma_stride = 1; temp(1, 2, 3) = 4; pres(5, 6, 7) = 8; dens(9, 10, 11) = 12; clv(13, 2, 14, 15) = 16; wind_u(17, 18, 3) = 19; EXPECT_EQ(temp.stride(0), block_stride); EXPECT_EQ(temp.stride(1), level_stride); EXPECT_EQ(temp.stride(2), nproma_stride); EXPECT_EQ(temp.size(), nblk * nlev * nproma); EXPECT_EQ(clv.stride(0), block_stride); EXPECT_EQ(clv.stride(1), field_stride); EXPECT_EQ(clv.stride(2), level_stride); EXPECT_EQ(clv.stride(3), nproma_stride); EXPECT_EQ(clv.size(), nblk * 5 * nlev * nproma); // Advanced usage, to access underlying array. This should only be used // in a driver and not be exposed to algorithms. { auto multiarray = array::make_view(multifield); EXPECT_EQ(multiarray.stride(0), block_stride); EXPECT_EQ(multiarray.stride(1), field_stride); EXPECT_EQ(multiarray.stride(2), level_stride); EXPECT_EQ(multiarray.stride(3), nproma_stride); EXPECT_EQ(multiarray(1, 0, 2, 3), 4.); EXPECT_EQ(multiarray(5, 1, 6, 7), 8.); EXPECT_EQ(multiarray(9, 2, 10, 11), 12.); EXPECT_EQ(multiarray(13, 5, 14, 15), 16.); EXPECT_EQ(multiarray(17, 8, 18, 3), 19.); EXPECT_EQ(multiarray.size(), nblk * nvar * nlev * nproma); } // access FieldSet through MultiField auto fieldset = multifield->fieldset(); auto field_v = array::make_view(fieldset.field("temperature")); EXPECT_EQ(fieldset.size(), 5); EXPECT(fieldset.has("temperature")); EXPECT(fieldset.has("wind_u")); EXPECT_EQ(field_v(1,2,3), 4); } SECTION("test registry") { { Field field = MultiField {eckit::YAMLConfiguration{json()}}.field("temperature"); auto temp = array::make_view(field); EXPECT_EQ(temp.rank(), 3); } } } //----------------------------------------------------------------------------- CASE("multifield_array_create") { using Value = float; int nproma = 16; int nlev = 100; int ngptot = 2000; const int nblks = (ngptot + nproma - 1) / nproma; const std::vector var_names = {"temperature", "pressure", "density", "clv", "wind_u"}; auto json = [&]() -> std::string { util::Config p; p.set("type", "MultiFieldCreatorArray"); p.set("shape", {nblks, -1, nlev, nproma}); p.set("datatype", array::make_datatype().str()); p.set("fields", std::vector{ util::Config("name", "temperature"), // util::Config("name", "pressure"), // util::Config("name", "density"), // util::Config("name", "clv")("nvar", 5), // util::Config("name", "wind_u") // }); return p.json(); }; SECTION("test_MultiFieldArray_noconfig_3d") { int nlev = 3; const std::vector vshape = make_shape({nblks, -1, nlev, nproma}); MultiField multifield(array::make_datatype(), vshape, var_names); const auto nblk = multifield.array().shape(0); const auto nvar = multifield.array().shape(1); nlev = multifield.array().shape(2); const auto nfld = multifield.size(); EXPECT_EQ(nfld, 5); EXPECT_EQ(nvar, 5); EXPECT_EQ(multifield.size(), 5); EXPECT(multifield.has("temperature")); EXPECT(multifield.has("clv")); Log::info() << multifield.field("temperature") << std::endl; Log::info() << multifield.field("clv") << std::endl; auto temp = array::make_view(multifield.field("temperature")); auto clv = array::make_view(multifield.field("clv")); EXPECT_EQ(multifield[0].name(), "temperature"); EXPECT_EQ(multifield[3].name(), "clv"); auto block_stride = multifield.array().stride(0); auto field_stride = nproma * nlev; auto level_stride = nproma; auto nproma_stride = 1; temp(1, 2, 3) = 4; clv(13, 2, 14) = 16; EXPECT_EQ(temp.stride(0), block_stride); EXPECT_EQ(temp.stride(1), level_stride); EXPECT_EQ(temp.stride(2), nproma_stride); EXPECT_EQ(temp.size(), nblk * nlev * nproma); // Advanced usage, to access underlying array. This should only be used // in a driver and not be exposed to algorithms. { auto multiarray = array::make_view(multifield); EXPECT_EQ(multiarray.stride(0), block_stride); EXPECT_EQ(multiarray.stride(1), field_stride); EXPECT_EQ(multiarray.stride(2), level_stride); EXPECT_EQ(multiarray.stride(3), nproma_stride); EXPECT_EQ(multiarray(1, 0, 2, 3), 4.); EXPECT_EQ(multiarray(13, 3, 2, 14), 16.); EXPECT_EQ(multiarray.size(), nblk * nvar * nlev * nproma); } // access FieldSet through MultiField auto fieldset = multifield->fieldset(); auto field_v = array::make_view(fieldset.field("temperature")); EXPECT_EQ(fieldset.size(), 5); EXPECT(fieldset.has("temperature")); EXPECT(fieldset.has("wind_u")); EXPECT_EQ(field_v(1,2,3), 4); } SECTION("test_MultiFieldArray_noconfig_2d") { const std::vector vshape = make_shape({nblks, -1, nproma}); MultiField multifield(array::make_datatype(), vshape, var_names); const auto nblk = multifield.array().shape(0); const auto nvar = multifield.array().shape(1); nlev = multifield.array().shape(2); const auto nfld = multifield.size(); EXPECT_EQ(nfld, 5); EXPECT_EQ(nvar, 5); EXPECT_EQ(multifield.size(), 5); EXPECT(multifield.has("temperature")); EXPECT(multifield.has("clv")); Log::info() << multifield.field("temperature") << std::endl; Log::info() << multifield.field("clv") << std::endl; auto temp = array::make_view(multifield.field("temperature")); auto clv = array::make_view(multifield.field("clv")); EXPECT_EQ(multifield[0].name(), "temperature"); EXPECT_EQ(multifield[3].name(), "clv"); auto block_stride = multifield.array().stride(0); auto field_stride = nproma; auto nproma_stride = 1; temp(1, 3) = 4; clv(13, 14) = 16; EXPECT_EQ(temp.stride(0), block_stride); EXPECT_EQ(temp.stride(1), nproma_stride); EXPECT_EQ(temp.size(), nblk * nproma); // Advanced usage, to access underlying array. This should only be used // in a driver and not be exposed to algorithms. { auto multiarray = array::make_view(multifield); EXPECT_EQ(multiarray.stride(0), block_stride); EXPECT_EQ(multiarray.stride(1), field_stride); EXPECT_EQ(multiarray.stride(2), nproma_stride); EXPECT_EQ(multiarray(1, 0, 3), 4.); EXPECT_EQ(multiarray(13, 3, 14), 16.); EXPECT_EQ(multiarray.size(), nblk * nvar * nproma); } // access FieldSet through MultiField auto fieldset = multifield->fieldset(); auto field_v = array::make_view(fieldset.field("temperature")); EXPECT_EQ(fieldset.size(), 5); EXPECT(fieldset.has("temperature")); EXPECT(fieldset.has("wind_u")); EXPECT_EQ(field_v(1,3), 4); } SECTION("Print configuration") { Log::info() << "json = " << json() << std::endl; } SECTION("test_MultiFieldArray_config") { MultiField multifield{eckit::YAMLConfiguration{json()}}; const auto nblk = multifield.array().shape(0); const auto nvar = multifield.array().shape(1); const auto nfld = multifield.size(); EXPECT_EQ(nfld, 9); EXPECT_EQ(nvar, 9); EXPECT_EQ(multifield.size(), 9); EXPECT(multifield.has("temperature")); EXPECT(multifield.has("pressure")); EXPECT(multifield.has("density")); EXPECT(multifield.has("clv_0")); EXPECT(multifield.has("wind_u")); Log::info() << multifield.field("temperature") << std::endl; Log::info() << multifield.field("pressure") << std::endl; Log::info() << multifield.field("density") << std::endl; Log::info() << multifield.field("clv_0") << std::endl; Log::info() << multifield.field("wind_u") << std::endl; auto temp = array::make_view(multifield.field("temperature")); auto pres = array::make_view(multifield.field("pressure")); auto dens = array::make_view(multifield.field("density")); auto clv = array::make_view(multifield.field("clv_0")); // note rank 4 auto wind_u = array::make_view(multifield.field("wind_u")); EXPECT_EQ(multifield[0].name(), "temperature"); EXPECT_EQ(multifield[1].name(), "pressure"); EXPECT_EQ(multifield[2].name(), "density"); EXPECT_EQ(multifield[3].name(), "clv_0"); EXPECT_EQ(multifield[8].name(), "wind_u"); auto block_stride = multifield.array().stride(0); auto field_stride = nproma * nlev; auto level_stride = nproma; auto nproma_stride = 1; temp(1, 2, 3) = 4; pres(5, 6, 7) = 8; dens(9, 10, 11) = 12; clv(13, 14, 15) = 16; wind_u(17, 18, 3) = 19; EXPECT_EQ(temp.stride(0), block_stride); EXPECT_EQ(temp.stride(1), level_stride); EXPECT_EQ(temp.stride(2), nproma_stride); EXPECT_EQ(temp.size(), nblk * nlev * nproma); EXPECT_EQ(clv.stride(0), block_stride); EXPECT_EQ(clv.stride(1), level_stride); EXPECT_EQ(clv.stride(2), nproma_stride); EXPECT_EQ(clv.size(), nblk * nlev * nproma); // Advanced usage, to access underlying array. This should only be used // in a driver and not be exposed to algorithms. { auto multiarray = array::make_view(multifield); EXPECT_EQ(multiarray.stride(0), block_stride); EXPECT_EQ(multiarray.stride(1), field_stride); EXPECT_EQ(multiarray.stride(2), level_stride); EXPECT_EQ(multiarray.stride(3), nproma_stride); EXPECT_EQ(multiarray(1, 0, 2, 3), 4.); EXPECT_EQ(multiarray(5, 1, 6, 7), 8.); EXPECT_EQ(multiarray(9, 2, 10, 11), 12.); EXPECT_EQ(multiarray(13, 3, 14, 15), 16.); EXPECT_EQ(multiarray(17, 8, 18, 3), 19.); EXPECT_EQ(multiarray.size(), nblk * nvar * nlev * nproma); } // access FieldSet through MultiField auto fieldset = multifield->fieldset(); auto field_v = array::make_view(fieldset.field("temperature")); EXPECT_EQ(fieldset.size(), 9); EXPECT(fieldset.has("temperature")); EXPECT(fieldset.has("wind_u")); EXPECT_EQ(field_v(1,2,3), 4); } SECTION("test registry") { { Field field = MultiField {eckit::YAMLConfiguration{json()}}.field("temperature"); auto temp = array::make_view(field); EXPECT_EQ(temp.rank(), 3); } } } //----------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/field/CMakeLists.txt0000664000175000017500000001060715160212070020643 0ustar alastairalastair# (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. ecbuild_add_test( TARGET atlas_test_fieldset SOURCES test_fieldset.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) ecbuild_add_test( TARGET atlas_test_field_missingvalue SOURCES test_field_missingvalue.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) ecbuild_add_test( TARGET atlas_test_field_memory_resource SOURCES test_field_memory_resource.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) ecbuild_add_test( TARGET atlas_test_field_foreach SOURCES test_field_foreach.cc LIBS atlas OMP 4 ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} CONDITION NOT SKIP_TEST_atlas_test_field_foreach ) set( _WITH_MPI ) if( eckit_HAVE_MPI ) set( _WITH_MPI MPI 4 CONDITION MPI_SLOTS GREATER_EQUAL 4) endif() ecbuild_add_test( TARGET atlas_test_field_halo SOURCES test_field_halo.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ${_WITH_MPI} ) set( HAVE_ACC_CXX FALSE ) if( TARGET OpenACC::OpenACC_CXX AND atlas_HAVE_ACC ) set( HAVE_ACC_CXX TRUE ) endif() ecbuild_add_test( TARGET atlas_test_field_acc SOURCES test_field_acc.cc LIBS atlas OpenACC::OpenACC_CXX ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ATLAS_RUN_NGPUS=1 CONDITION HAVE_ACC_CXX ) ecbuild_add_test( TARGET atlas_test_field_acc_cpu SOURCES test_field_acc.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) if( TEST atlas_test_field_acc ) set_tests_properties ( atlas_test_field_acc PROPERTIES LABELS "gpu;acc") endif() ecbuild_add_test( TARGET atlas_test_multifield_ifs SOURCES test_multifield_ifs.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) if( HAVE_FCTEST ) add_fctest( TARGET atlas_fctest_multifield_ifs LINKER_LANGUAGE Fortran SOURCES fctest_multifield_ifs.F90 LIBS atlas_f pluto_f ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) add_fctest( TARGET atlas_fctest_field LINKER_LANGUAGE Fortran SOURCES fctest_field.F90 LIBS atlas_f ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) add_fctest( TARGET atlas_fctest_field_wrap LINKER_LANGUAGE Fortran SOURCES fctest_field_wrap.F90 LIBS atlas_f ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) add_fctest( TARGET atlas_fctest_field_view LINKER_LANGUAGE Fortran SOURCES fctest_field_view.F90 LIBS atlas_f OpenMP::OpenMP_Fortran OMP 3 ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} CONDITION atlas_HAVE_OMP_Fortran ) add_fctest( TARGET atlas_fctest_field_host LINKER_LANGUAGE Fortran SOURCES fctest_field_host.F90 LIBS atlas_f ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) add_fctest( TARGET atlas_fctest_field_device CONDITION atlas_HAVE_ACC LINKER_LANGUAGE Fortran SOURCES fctest_field_gpu.F90 external_acc_routine.F90 LIBS atlas_f OpenACC::OpenACC_Fortran ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ATLAS_RUN_NGPUS=1 ) if( TARGET atlas_fctest_field_device ) set_tests_properties ( atlas_fctest_field_device PROPERTIES LABELS "gpu;acc") endif() add_fctest( TARGET atlas_fctest_fieldset_device CONDITION atlas_HAVE_ACC LINKER_LANGUAGE Fortran SOURCES fctest_fieldset_gpu.F90 LIBS atlas_f OpenACC::OpenACC_Fortran ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ATLAS_RUN_NGPUS=1 ) if( TARGET atlas_fctest_fieldset_device ) set_tests_properties ( atlas_fctest_fieldset_device PROPERTIES LABELS "gpu;acc") endif() add_fctest( TARGET atlas_fctest_field_wrap_device CONDITION atlas_HAVE_ACC LINKER_LANGUAGE Fortran SOURCES fctest_field_wrap_gpu.F90 external_acc_routine.F90 LIBS atlas_f OpenACC::OpenACC_Fortran DEFINITIONS HAVE_NOREPACK=${HAVE_NOREPACK} ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ATLAS_RUN_NGPUS=1 ) if( TARGET atlas_fctest_field_wrap_device ) set_tests_properties ( atlas_fctest_field_wrap_device PROPERTIES LABELS "gpu;acc") endif() endif() atlas-0.46.0/src/tests/field/test_field_foreach.cc0000664000175000017500000004420315160212070022222 0ustar alastairalastair/* * (C) Crown Copyright 2023 Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ #include "tests/AtlasTestEnvironment.h" #include "atlas/field/for_each.h" #include "atlas/option.h" using namespace atlas::array; using namespace atlas::array::helpers; namespace atlas { namespace test { auto split_index_3d = [] (int idx, auto shape) { int i = idx / (shape[2]*shape[1]); int j = (idx - i * (shape[2] * shape[1]))/shape[2]; int k = idx - i * (shape[2]*shape[1]) - j*shape[2]; return std::make_tuple(i,j,k); }; CASE( "test field::for_each_value" ) { Field f("name", array::make_datatype(), array::make_shape({4,2,8})); int v=0; field::for_each_value(f, [&](double& x) { auto [i,j,k] = split_index_3d(v,f.shape()); x = 100*i + 10*j + k; v++; }); v = 0; field::for_each_value(f, [&](double& x) { auto [i,j,k] = split_index_3d(v,f.shape()); double expected = 100*i + 10*j + k; EXPECT_EQ(x, expected); ++v; }); } CASE( "test field::for_each_value_masked; horizontal_dimension {0} (implicit)" ) { Field f("name", array::make_datatype(), array::make_shape({4,2,8})); array::make_view(f).assign(0.); Field ghost("ghost", array::make_datatype(), array::make_shape(4) ); array::make_view(ghost).assign(0); array::make_view(ghost)(2) = 1; field::for_each_value_masked(ghost, f, [&](double& x) { x = 1.; }); int v = 0; field::for_each_value(f, [&](double& x) { auto [i,j,k] = split_index_3d(v,f.shape()); double expected = 1.; if (i==2) { expected = 0.; } EXPECT_EQ(x, expected); ++v; }); } CASE( "test field::for_each_value_masked; horizontal_dimension {0,2} mask_rank1" ) { Field f("name", array::make_datatype(), array::make_shape({4,2,8})); f.set_horizontal_dimension({0,2}); array::make_view(f).assign(0.); Field ghost("ghost", array::make_datatype(), array::make_shape(f.shape(0)*f.shape(2)) ); array::make_view(ghost).assign(0); array::make_view(ghost)(2*f.shape(2)+3) = 1; field::for_each_value_masked(ghost, f, [&](double& x) { x = 1.; }); int v = 0; field::for_each_value(f, [&](double& x) { auto [i,j,k] = split_index_3d(v,f.shape()); double expected = 1.; if (i==2 && k==3) { expected = 0.; } EXPECT_EQ(x, expected); ++v; }); } CASE( "test field::for_each_value_masked; horizontal_dimension {0,2} mask_rank2" ) { Field f("name", array::make_datatype(), array::make_shape({4,2,8})); f.set_horizontal_dimension({0,2}); array::make_view(f).assign(0.); Field ghost("ghost", array::make_datatype(), array::make_shape(f.shape(0),f.shape(2)) ); array::make_view(ghost).assign(0); array::make_view(ghost)(2,3) = 1; field::for_each_value_masked(ghost, f, [&](double& x) { x = 1.; }); int v = 0; field::for_each_value(f, [&](double& x) { auto [i,j,k] = split_index_3d(v,f.shape()); double expected = 1.; if (i==2 && k==3) { expected = 0.; } EXPECT_EQ(x, expected); ++v; }); } CASE( "test field::for_each_column" ) { Field f("name", array::make_datatype(), array::make_shape({6,4,2})); int v=0; field::for_each_value(execution::seq, f, [&](double& x) { auto [i,j,k] = split_index_3d(v,f.shape()); x = 100*i + 10*j + k; v++; }); [[maybe_unused]] auto print_column_1d = [&](const array::View& column) { Log::info() << ""; for( idx_t jlev=0; jlev& column) { Log::info() << ""; for( idx_t jlev=0; jlev visited_values; field::for_each_column(f, [&visited_values](const array::View& column) { for( idx_t jlev=0; jlev expected_values = { 0, 1, // 10, 11, // 20, 21, // 30, 31, // 100, 101, // 110, 111, // 120, 121, // 130, 131, // 200, 201, // 210, 211, // 220, 221, // 230, 231, // 300, 301, // 310, 311, // 320, 321, // 330, 331, // 400, 401, // 410, 411, // 420, 421, // 430, 431, // 500, 501, // 510, 511, // 520, 521, // 530, 531 // }; EXPECT_EQ( visited_values, expected_values ); } SECTION( "horizontal_dimension = {0,2}") { f.set_horizontal_dimension({0,2}); std::vector visited_values; field::for_each_column(f, [&visited_values](const array::View& column) { for( idx_t jlev=0; jlev expected_values = { 0, 10, 20, 30, // 1, 11, 21, 31, // 100, 110, 120, 130, // 101, 111, 121, 131, // 200, 210, 220, 230, // 201, 211, 221, 231, // 300, 310, 320, 330, // 301, 311, 321, 331, // 400, 410, 420, 430, // 401, 411, 421, 431, // 500, 510, 520, 530, // 501, 511, 521, 531 // }; EXPECT_EQ( visited_values, expected_values ); } SECTION( "horizontal_dimension = {1,2}") { f.set_horizontal_dimension({1,2}); std::vector visited_values; field::for_each_column(f, [&visited_values](const array::View& column) { for( idx_t jlev=0; jlev expected_values = { 0, 100, 200, 300, 400, 500, // 1, 101, 201, 301, 401, 501, // 10, 110, 210, 310, 410, 510, // 11, 111, 211, 311, 411, 511, // 20, 120, 220, 320, 420, 520, // 21, 121, 221, 321, 421, 521, // 30, 130, 230, 330, 430, 530, // 31, 131, 231, 331, 431, 531 // }; EXPECT_EQ( visited_values, expected_values ); } SECTION( "horizontal_dimension = {0}") { f.set_horizontal_dimension({0}); std::vector visited_values; field::for_each_column(f, [&visited_values](const array::View& column) { for( idx_t jlev=0; jlev expected_values = { 0, 1, // 10, 11, // 20, 21, // 30, 31, // 100, 101, // 110, 111, // 120, 121, // 130, 131, // 200, 201, // 210, 211, // 220, 221, // 230, 231, // 300, 301, // 310, 311, // 320, 321, // 330, 331, // 400, 401, // 410, 411, // 420, 421, // 430, 431, // 500, 501, // 510, 511, // 520, 521, // 530, 531 // }; EXPECT_EQ( visited_values, expected_values ); } SECTION( "horizontal_dimension = {1}") { f.set_horizontal_dimension({1}); std::vector visited_values; field::for_each_column(f, [&visited_values](const array::View& column) { for( idx_t jlev=0; jlev expected_values = { 0, 1, // 100, 101, // 200, 201, // 300, 301, // 400, 401, // 500, 501, // 10, 11, // 110, 111, // 210, 211, // 310, 311, // 410, 411, // 510, 511, // 20, 21, // 120, 121, // 220, 221, // 320, 321, // 420, 421, // 520, 521, // 30, 31, // 130, 131, // 230, 231, // 330, 331, // 430, 431, // 530, 531 // }; EXPECT_EQ( visited_values, expected_values ); } SECTION( "horizontal_dimension = {2}") { f.set_horizontal_dimension({2}); std::vector visited_values; field::for_each_column(f, [&visited_values](const array::View& column) { for( idx_t jlev=0; jlev expected_values = { 0, 10, 20, 30, // 100, 110, 120, 130, // 200, 210, 220, 230, // 300, 310, 320, 330, // 400, 410, 420, 430, // 500, 510, 520, 530, // 1, 11, 21, 31, // 101, 111, 121, 131, // 201, 211, 221, 231, // 301, 311, 321, 331, // 401, 411, 421, 431, // 501, 511, 521, 531 // }; EXPECT_EQ( visited_values, expected_values ); } } CASE( "test field::for_each_column multiple" ) { Field f1("f1", array::make_datatype(),array::make_shape({6,4,2})); Field f2("f2", array::make_datatype(),array::make_shape({6,4,2})); Field f3("f2", array::make_datatype(),array::make_shape({6,4,2})); int v=0; field::for_each_value(f1, f2, [&](double& x1, double& x2) { auto [i,j,k] = split_index_3d(v,f1.shape()); x1 = 100*i + 10*j + k; x2 = 2*x1; v++; }); auto check_result = [&]() { auto view = array::make_view(f3); for (idx_t i=0; i(f3); for (idx_t i=0; i mask) { auto h_dim = f1.horizontal_dimension(); ATLAS_ASSERT(h_dim.size() == mask.size()); std::vector h_shape; for (auto h: h_dim) { h_shape.emplace_back(f1.shape(h)); } size_t h_size = 1; for (auto h: h_shape) { h_size *= h; } Field ghost("ghost", array::make_datatype(), array::make_shape(h_size)); auto ghost_v = array::make_view(ghost); ghost_v.assign(0); if( mask.size() == 1 ) { ghost_v(mask[0]) = 1; } if( mask.size() == 2 ) { ghost_v(h_shape[1]*mask[0] + mask[1]) = 1; } if( mask.size() == 3 ) { ghost_v(h_shape[2]*h_shape[1]*mask[0] + h_shape[1]*mask[1] + mask[2]) = 1; } return ghost; }; SECTION( "for_each_column; horizontal_dimension = {0,1}") { f1.set_horizontal_dimension({0,1}); array::make_view(f3).assign(0.); field::for_each_column(f1, f2, f3, []( array::View&& c1, array::View&& c2, array::View&& c3) { for( idx_t jlev=0; jlev(f3).assign(0.); field::for_each_column(f1, f2, f3, []( array::View&& c1, array::View&& c2, array::View&& c3) { for( idx_t jlev=0; jlev(f3).assign(0.); field::for_each_column(f1, f2, f3, []( array::View&& c1, array::View&& c2, array::View&& c3) { for( idx_t jlev=0; jlev(f3).assign(0.); field::for_each_column(f1, f2, f3, []( array::View&& c1, array::View&& c2, array::View&& c3) { for( idx_t jlev=0; jlev(f3).assign(0.); field::for_each_column(f1, f2, f3, []( array::View&& c1, array::View&& c2, array::View&& c3) { for( idx_t jlev=0; jlev(f3).assign(0.); field::for_each_column(f1, f2, f3, []( array::View&& c1, array::View&& c2, array::View&& c3) { for( idx_t jlev=0; jlev(f3).assign(0.); field::for_each_column_masked(ghost, f1, f2, f3, []( array::View&& c1, array::View&& c2, array::View&& c3) { for( idx_t jlev=0; jlev(f3).assign(0.); field::for_each_column_masked(ghost, f1, f2, f3, []( array::View&& c1, array::View&& c2, array::View&& c3) { for( idx_t jlev=0; jlev(),array::make_shape({ni,nj,nk})); Field f2("f2", array::make_datatype(),array::make_shape({ni,nj,nk})); Field f3("f2", array::make_datatype(),array::make_shape({ni,nj,nk})); int v=0; field::for_each_value(f1, f2, [&](double& x1, double& x2) { auto [i,j,k] = split_index_3d(v,f1.shape()); x1 = 100*i + 10*j + k; x2 = 2*x1; v++; }); auto time_function = [](const auto& function) -> double { runtime::trace::StopWatch stopwatch; for (size_t j=0; j<5; ++j) { function(); } size_t N=10; stopwatch.start(); for (size_t j=0; j(f1); auto v2 = array::make_view(f2); auto v3 = array::make_view(f3); for (size_t i=0; i::quiet_NaN(); using field::MissingValue; using util::Config; CASE("MissingValue (basic)") { SECTION("not defined") { auto mv = MissingValue(); EXPECT(!bool(mv)); mv = MissingValue("not defined", Config()); EXPECT(!bool(mv)); } SECTION("nan") { Config config; auto mv = MissingValue("nan", config); EXPECT(bool(mv)); EXPECT(mv(nan)); EXPECT(mv(missingValue) == false); config.set("type", "nan"); mv = MissingValue(config); EXPECT(bool(mv)); EXPECT(mv(nan)); EXPECT(mv(missingValue) == false); } SECTION("equals") { Config config; config.set("missing_value", missingValue); auto mv = MissingValue("equals", config); EXPECT(bool(mv)); EXPECT(mv(missingValue - 1) == false); EXPECT(mv(missingValue - missingValueEps / 2) == false); EXPECT(mv(missingValue)); EXPECT(mv(missingValue + missingValueEps / 2) == false); EXPECT(mv(missingValue + 1) == false); config.set("type", "equals"); mv = MissingValue(config); EXPECT(bool(mv)); EXPECT(mv(missingValue - 1) == false); EXPECT(mv(missingValue - missingValueEps / 2) == false); EXPECT(mv(missingValue)); EXPECT(mv(missingValue + missingValueEps / 2) == false); EXPECT(mv(missingValue + 1) == false); } SECTION("approximately-equals") { Config config; config.set("missing_value", missingValue); config.set("missing_value_epsilon", missingValueEps); auto mv = MissingValue("approximately-equals", config); EXPECT(bool(mv)); EXPECT(mv(missingValue - missingValueEps * 2) == false); EXPECT(mv(missingValue - missingValueEps / 2)); EXPECT(mv(missingValue)); EXPECT(mv(missingValue + missingValueEps / 2)); EXPECT(mv(missingValue + missingValueEps * 2) == false); config.set("type", "approximately-equals"); mv = MissingValue(config); EXPECT(bool(mv)); EXPECT(mv(missingValue - missingValueEps * 2) == false); EXPECT(mv(missingValue - missingValueEps / 2)); EXPECT(mv(missingValue)); EXPECT(mv(missingValue + missingValueEps / 2)); EXPECT(mv(missingValue + missingValueEps * 2) == false); } } CASE("MissingValue (DataType specialisations)") { SECTION("real64") { auto n = static_cast(missingValue); auto eps = static_cast(missingValueEps); auto nan = std::numeric_limits::quiet_NaN(); Config config; config.set("missing_value", n); config.set("missing_value_epsilon", eps); Log::info().indent(); for (std::string type : {"nan", "equals", "approximately-equals"}) { Log::info() << "type " << type << std::endl; Log::info().indent(); auto mv = MissingValue(type + "-real64", config); EXPECT(bool(mv)); EXPECT(mv(type == "nan" ? nan : n)); if (type == "nan") { EXPECT(mv(n) != mv(nan)); } EXPECT(mv(n + 1) == false); Log::info().unindent(); } Log::info().unindent(); } SECTION("real32") { auto n = static_cast(missingValue); auto eps = static_cast(missingValueEps); auto nan = std::numeric_limits::quiet_NaN(); Config config; config.set("missing_value", n); config.set("missing_value_epsilon", eps); Log::info().indent(); for (std::string type : {"nan", "equals", "approximately-equals"}) { Log::info() << "type " << type << std::endl; Log::info().indent(); auto mv = MissingValue(type + "-real32", config); EXPECT(bool(mv)); EXPECT(mv(type == "nan" ? nan : n)); if (type == "nan") { EXPECT(mv(n) != mv(nan)); } EXPECT(mv(n + 1) == false); Log::info().unindent(); } Log::info().unindent(); } SECTION("int32") { auto n = static_cast(missingValue); auto mv = MissingValue("equals-int32", Config("missing_value", n)); EXPECT(bool(mv)); EXPECT(mv(n)); EXPECT(mv(n + 1) == false); } SECTION("int64") { auto n = static_cast(missingValue); auto mv = MissingValue("equals-int64", Config("missing_value", n)); EXPECT(bool(mv)); EXPECT(mv(n)); EXPECT(mv(n + 1) == false); } SECTION("uint64") { auto n = static_cast(missingValue); auto mv = MissingValue("equals-uint64", Config("missing_value", n)); EXPECT(bool(mv)); EXPECT(mv(n)); EXPECT(mv(n + 1) == false); } } CASE("MissingValue from Field (basic)") { std::vector values{1., missingValue, missingValue, missingValue + missingValueEps / 2., 6., 7.}; Field field("field", values.data(), array::make_shape(values.size(), 1)); field.metadata().set("missing_value_type", "not defined"); field.metadata().set("missing_value", missingValue); field.metadata().set("missing_value_epsilon", missingValueEps); EXPECT(!bool(MissingValue(field))); SECTION("nan") { std::vector values_with_nan = values; values_with_nan.insert(values_with_nan.begin() + 1, nan); // missing value type from user EXPECT(std::count_if(values_with_nan.begin(), values_with_nan.end(), MissingValue("nan", field)) == 1); // missing value type from field field.metadata().set("missing_value_type", "nan"); EXPECT(std::count_if(values_with_nan.begin(), values_with_nan.end(), MissingValue(field)) == 1); } SECTION("equals") { // missing value type from user (value set from field) EXPECT(std::count_if(values.begin(), values.end(), MissingValue("equals", field)) == 2); // missing value type from field field.metadata().set("missing_value_type", "equals"); EXPECT(std::count_if(values.begin(), values.end(), MissingValue(field)) == 2); } SECTION("approximately-equals") { // missing value type from user (value set from field) EXPECT(std::count_if(values.begin(), values.end(), MissingValue("approximately-equals", field)) == 3); // missing value type from field field.metadata().set("missing_value_type", "approximately-equals"); EXPECT(std::count_if(values.begin(), values.end(), MissingValue(field)) == 3); } } CASE("MissingValue from Field (DataType specialisations)") { SECTION("real64") { std::vector values(3, 1.); Field field("field", array::make_datatype(), array::make_shape(values.size(), 1)); EXPECT(field.datatype().str() == array::DataType::real64().str()); EXPECT(!MissingValue(field)); field.metadata().set("missing_value_type", "nan"); EXPECT(MissingValue(field)); } SECTION("real32") { std::vector values(3, 1.); Field field("field", array::make_datatype(), array::make_shape(values.size(), 1)); EXPECT(field.datatype().str() == array::DataType::real32().str()); EXPECT(!MissingValue(field)); field.metadata().set("missing_value_type", "nan"); EXPECT(MissingValue(field)); } SECTION("int32") { std::vector values(3, 1); Field field("field", array::make_datatype(), array::make_shape(values.size(), 1)); EXPECT(field.datatype().str() == array::DataType::int32().str()); EXPECT(!MissingValue(field)); field.metadata().set("missing_value_type", "equals"); field.metadata().set("missing_value", static_cast(missingValue)); EXPECT(MissingValue(field)); } SECTION("int64") { std::vector values(3, 1); Field field("field", array::make_datatype(), array::make_shape(values.size(), 1)); EXPECT(field.datatype().str() == array::DataType::int64().str()); EXPECT(!MissingValue(field)); field.metadata().set("missing_value_type", "equals"); field.metadata().set("missing_value", static_cast(missingValue)); EXPECT(MissingValue(field)); } SECTION("uint64") { std::vector values(3, 1); Field field("field", array::make_datatype(), array::make_shape(values.size(), 1)); EXPECT(field.datatype().str() == array::DataType::uint64().str()); EXPECT(!MissingValue(field)); field.metadata().set("missing_value_type", "equals"); field.metadata().set("missing_value", static_cast(missingValue)); EXPECT(MissingValue(field)); } } } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/CMakeLists.txt0000664000175000017500000000714515160212070017563 0ustar alastairalastair# (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. if( NOT DEFINED MPI_SLOTS ) set( MPI_SLOTS 9999 ) endif() ###################################################### # Compiler bugs force us to disable some tests if( CMAKE_Fortran_COMPILER_ID MATCHES "PGI" AND CMAKE_Fortran_COMPILER_VERSION VERSION_LESS 16.8 ) set( atlas_fctest_parametrisation_DISABLED TRUE ) ecbuild_warn( "PGI Fortran compiler version tested up to 16.7 has a compiler bug " "that leads to a segfaults for atlas_fctest_parametrisation. " "Disabling this test..." ) endif() if( CMAKE_Fortran_COMPILER_ID MATCHES "Intel" ) ## ATLAS-170 workaround internal compiler error ## Other workaround that makes ecbuild_remove_fortran_flags work when variables are empty string( TOUPPER ${CMAKE_BUILD_TYPE} CMAKE_BUILD_TYPE_CAPS ) if( NOT CMAKE_Fortran_FLAGS ) set( CMAKE_Fortran_FLAGS " " ) endif() if( NOT CMAKE_Fortran_FLAGS_${CMAKE_BUILD_TYPE_CAPS} ) set( CMAKE_Fortran_FLAGS_${CMAKE_BUILD_TYPE_CAPS} " " ) endif() ecbuild_remove_fortran_flags( "-g" ) endif() ###################################################### # Macro atlas_add_hic_test macro( atlas_add_hic_test ) set( options "" ) set( single_value_args TARGET ) set( multi_value_args SOURCES LIBS ENVIRONMENT ) cmake_parse_arguments( _PAR "${options}" "${single_value_args}" "${multi_value_args}" ${_FIRST_ARG} ${ARGN} ) if(_PAR_UNPARSED_ARGUMENTS) ecbuild_critical("Unknown keywords given to atlas_add_hic_test(): \"${_PAR_UNPARSED_ARGUMENTS}\"") endif() if( HAVE_CUDA AND HAVE_TESTS ) ecbuild_debug("atlas_add_hic_test: Adding test ${_PAR_TARGET}") list( APPEND _PAR_ENVIRONMENT "ATLAS_RUN_NGPUS=1" ) list( REMOVE_DUPLICATES _PAR_ENVIRONMENT ) list( APPEND _libs ${_PAR_LIBS} ) if( _libs ) ecbuild_debug("atlas_add_hic_test: Test ${_PAR_TARGET} explicitly links with libraries ${_libs}") endif() if( HAVE_CUDA ) set_source_files_properties(${_PAR_SOURCES} PROPERTIES LANGUAGE CUDA) elseif( HAVE_HIP ) set_source_files_properties(${_PAR_SOURCES} PROPERTIES LANGUAGE HIP) endif() ecbuild_add_test( TARGET ${_PAR_TARGET} SOURCES ${_PAR_SOURCES} LIBS ${_libs} LINKER_LANGUAGE CXX ENVIRONMENT ${_PAR_ENVIRONMENT} ) set_tests_properties(${_PAR_TARGET} PROPERTIES LABELS "gpu") endif() endmacro() add_subdirectory( util ) add_subdirectory( runtime ) if (atlas_HAVE_ATLAS_GRID) add_subdirectory( projection ) add_subdirectory( grid ) endif() if (atlas_HAVE_ATLAS_FIELD) add_subdirectory( acc ) add_subdirectory( array ) add_subdirectory( field ) endif() if (atlas_HAVE_ATLAS_FUNCTIONSPACE) add_subdirectory( parallel ) add_subdirectory( mesh ) add_subdirectory( functionspace ) add_subdirectory( io ) add_subdirectory( output ) add_subdirectory( redistribution ) endif() if (atlas_HAVE_ATLAS_TRANS) add_subdirectory( trans ) endif() if (atlas_HAVE_ATLAS_INTERPOLATION) add_subdirectory( interpolation ) add_subdirectory( linalg ) endif() if (atlas_HAVE_ATLAS_NUMERICS) add_subdirectory( numerics ) endif() add_subdirectory( acceptance_tests ) add_subdirectory( export_tests ) atlas-0.46.0/src/tests/functionspace/0000775000175000017500000000000015160212070017655 5ustar alastairalastairatlas-0.46.0/src/tests/functionspace/test_cubedsphere_functionspace.cc0000664000175000017500000003727715160212070026455 0ustar alastairalastair/* * (C) Crown Copyright 2021 Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ #include "atlas/array.h" #include "atlas/field/FieldSet.h" #include "atlas/functionspace/CellColumns.h" #include "atlas/functionspace/CubedSphereColumns.h" #include "atlas/functionspace/NodeColumns.h" #include "atlas/grid.h" #include "atlas/grid/CubedSphereGrid.h" #include "atlas/grid/Tiles.h" #include "atlas/mesh.h" #include "atlas/meshgenerator.h" #include "atlas/meshgenerator/detail/cubedsphere/CubedSphereUtility.h" #include "atlas/option.h" #include "atlas/util/CoordinateEnums.h" #include "atlas/util/function/VortexRollup.h" #include "tests/AtlasTestEnvironment.h" namespace atlas { namespace test { // Allow small differences in the last few bits of a double aprroximately equal to 1 constexpr double epsilon = std::numeric_limits::epsilon() * 16; template void testFunctionSpace(const functionspace::CubedSphereColumns& functionspace) { // Make field. auto field = functionspace.template createField(util::Config("name", "test field")); auto fieldView = array::make_view(field); // Get view of lonlat. const auto lonLatView = array::make_view(functionspace.lonlat()); // Get view of ghost field if NodeColumns, halo field if CellColumns. const auto ghostView = functionspace.type() == "NodeColumns" ? array::make_view(functionspace.mesh().nodes().ghost()) : array::make_view(functionspace.mesh().nodes().halo()); // Loop over all non halo elements of test field. idx_t testFuncCallCount = 0; functionspace.parallel_for([&](idx_t index, idx_t t, idx_t i, idx_t j) { // Make sure index matches ijt. EXPECT(index == functionspace.index(t, i, j)); // Check that indices of "+" stencil are valid. const auto badIdx = functionspace.invalid_index(); EXPECT(functionspace.index(t, i - 1, j) != badIdx); EXPECT(functionspace.index(t, i + 1, j) != badIdx); EXPECT(functionspace.index(t, i, j - 1) != badIdx); EXPECT(functionspace.index(t, i, j + 1) != badIdx); // Make sure we're avoiding halos. EXPECT(!ghostView(index)); // Set field values. fieldView(index) = util::function::vortex_rollup(lonLatView(index, LON), lonLatView(index, LAT), 1.0); ++testFuncCallCount; }); // Make sure call count is less than functionspace.size() as we skipped halos. EXPECT(testFuncCallCount < functionspace.size()); // Perform halo exchange. functionspace.haloExchange(field); // Loop over elements including halo testFuncCallCount = 0; functionspace.parallel_for(util::Config("include_halo", true), [&](idx_t index, idx_t t, idx_t i, idx_t j) { // Make sure index matches ijt. EXPECT(index == functionspace.index(t, i, j)); // Set field values. EXPECT_APPROX_EQ(fieldView(index), util::function::vortex_rollup(lonLatView(index, LON), lonLatView(index, LAT), 1.0), epsilon); ++testFuncCallCount; }); // Make sure call count is equal to functionspace.size(). std::cout << testFuncCallCount << " " << functionspace.size() << std::endl; //EXPECT( testFuncCallCount == functionspace.size() ); // Test SFINAE for parallel_for. // Suggestions for more inventive tests are welcome. idx_t nLevels = 10; auto field2 = functionspace.template createField(util::Config("name", "test field") | util::Config("levels", nLevels)); auto fieldView2 = array::make_view(field2); functionspace.parallel_for(util::Config("levels", nLevels), [&](idx_t index, idx_t t, idx_t i, idx_t j, idx_t k) { fieldView2(index, k) = t * i * j * k; }); functionspace.parallel_for([&](idx_t index, idx_t t, idx_t i, idx_t j) { for (idx_t k = 0; k < nLevels; ++k) { EXPECT(static_cast(fieldView2(index, k)) == t * i * j * k); } }); functionspace.parallel_for(util::Config("levels", nLevels), [&](idx_t index, idx_t k) { fieldView2(index, k) = k; }); functionspace.parallel_for([&](idx_t index) { for (idx_t k = 0; k < nLevels; ++k) { EXPECT(static_cast(fieldView2(index, k)) == k); } }); } CASE("cubedsphere_mesh_functionspace") { // Set grid. const auto grid = Grid("CS-LFR-C-12"); // Set mesh config. const auto meshConfigEqualRegions = util::Config("partitioner", "equal_regions") | util::Config("halo", 1); const auto meshConfigCubedSphere = util::Config("partitioner", "cubedsphere") | util::Config("halo", 1); // Set mesh generator. const auto meshGenEqualRegions = MeshGenerator("cubedsphere", meshConfigEqualRegions); const auto meshGenCubedSphere = MeshGenerator("cubedsphere", meshConfigCubedSphere); // Set dual mesh generator. const auto dualMeshGenEqualRegions = MeshGenerator("cubedsphere_dual", meshConfigEqualRegions); const auto dualMeshGenCubedSphere = MeshGenerator("cubedsphere_dual", meshConfigCubedSphere); // Set mesh const auto meshEqualRegions = meshGenEqualRegions.generate(grid); const auto meshCubedSphere = meshGenCubedSphere.generate(grid); // Set dual mesh const auto dualMeshEqualRegions = dualMeshGenEqualRegions.generate(grid); const auto dualMeshCubedSphere = dualMeshGenCubedSphere.generate(grid); // Set functionspace. const auto equalRegionsCellColumns = functionspace::CubedSphereCellColumns(meshEqualRegions); const auto cubedSphereCellColumns = functionspace::CubedSphereCellColumns(meshCubedSphere); const auto equalRegionsNodeColumns = functionspace::CubedSphereNodeColumns(meshEqualRegions); const auto cubedSphereNodeColumns = functionspace::CubedSphereNodeColumns(meshCubedSphere); const auto equalRegionsDualNodeColumns = functionspace::CubedSphereNodeColumns(dualMeshEqualRegions); const auto cubedSphereDualNodeColumns = functionspace::CubedSphereNodeColumns(dualMeshCubedSphere); const auto equalRegionsDualCellColumns = functionspace::CubedSphereCellColumns(dualMeshEqualRegions); const auto cubedSphereDualCellColumns = functionspace::CubedSphereCellColumns(dualMeshCubedSphere); // test functionspaces. SECTION("CellColumns: equal_regions") { testFunctionSpace(equalRegionsCellColumns); } SECTION("CellColumns: cubedsphere") { testFunctionSpace(cubedSphereCellColumns); } SECTION("NodeColumns: equal_regions") { testFunctionSpace(equalRegionsNodeColumns); } SECTION("NodeColumns: cubedsphere") { testFunctionSpace(cubedSphereNodeColumns); } SECTION("CellColumns: dual mesh, equal_regions") { testFunctionSpace(equalRegionsDualCellColumns); } SECTION("CellColumns: dual mesh, cubedsphere") { testFunctionSpace(cubedSphereDualCellColumns); } SECTION("NodeColumns: dual mesh, equal_regions") { testFunctionSpace(equalRegionsDualNodeColumns); } SECTION("NodeColumns: dual mesh, cubedsphere") { testFunctionSpace(cubedSphereDualNodeColumns); } } CASE("test copies and up/down casting") { // IMPORTANT: The internal structure should be cached and not be recomputed at all costs // This can be verified with ATLAS_DEBUG=1 and ATLAS_TRACE_REPORT=1 // Set grid. const auto grid = Grid("CS-LFR-12"); const auto mesh = MeshGenerator(grid.meshgenerator() | option::halo(1)).generate(grid); functionspace::CubedSphereCellColumns cs_cellcolumns; functionspace::CubedSphereNodeColumns cs_nodecolumns; ATLAS_TRACE_SCOPE("create functionspaces with cs structure") { cs_cellcolumns = functionspace::CubedSphereCellColumns(mesh); cs_nodecolumns = functionspace::CubedSphereNodeColumns(mesh); } ATLAS_TRACE_SCOPE("casting cellcolumns up/down") { const auto columns = functionspace::CellColumns(cs_cellcolumns); const auto functionspace = FunctionSpace(columns); const functionspace::CubedSphereCellColumns back = functionspace; } ATLAS_TRACE_SCOPE("casting cellcolumns up/down from mesh") { const auto columns = functionspace::CellColumns(mesh); const auto functionspace = FunctionSpace(columns); const functionspace::CubedSphereCellColumns back = functionspace; } ATLAS_TRACE_SCOPE("casting nodecolumns up/down") { const auto columns = functionspace::NodeColumns(cs_nodecolumns); const auto functionspace = FunctionSpace(columns); const functionspace::CubedSphereNodeColumns back = functionspace; } ATLAS_TRACE_SCOPE("casting nodecolumns up/down from mesh") { const auto columns = functionspace::NodeColumns(mesh); const auto functionspace = FunctionSpace(columns); const functionspace::CubedSphereNodeColumns back = functionspace; } } CASE("Cubed sphere primal-dual equivalence") { // Created a LFRic layout N12 cubedsphere grid. // This is a global set of grid points with lonlats located at cell centres. const auto grid = Grid("CS-LFR-12"); // Create domain decomposed mesh. // We can generate two types of mesh, a primal and a dual. // The cell-centre (nodes) lonlats of the primal mesh are identical to the // node (cell-centre) lonlats of the dual mesh. const auto primalMesh = MeshGenerator("cubedsphere", util::Config("halo", 5) | util::Config("partitioner", "equal_regions")) .generate(grid); const auto dualMesh = MeshGenerator("cubedsphere_dual", util::Config("halo", 4) | util::Config("partitioner", "equal_regions")) .generate(grid); // Create cubed sphere function spaces (these have fancy features, such as // (t, i, j) indexing and parallel_for methods). The halo sizes of the primal // functionspaces are set to match that of the dual functionspaces. const auto primalNodes = functionspace::CubedSphereNodeColumns(primalMesh, util::Config("halo", 4)); const auto primalCells = functionspace::CubedSphereCellColumns(primalMesh, util::Config("halo", 5)); const auto dualNodes = functionspace::CubedSphereNodeColumns(dualMesh, util::Config("halo", 4)); const auto dualCells = functionspace::CubedSphereCellColumns(dualMesh, util::Config("halo", 4)); // Note, the functionspaces we are usually interested in are primalCells and // dualNodes. The others are there for completeness. // Field comparison function. Note that this upcasts everything to // to the base FunctionSpace class. const auto compareFields = [](const FunctionSpace& functionSpaceA, const FunctionSpace& functionSpaceB) { // Check that function spaces are the same size. EXPECT_EQ(functionSpaceA.size(), functionSpaceB.size()); // Make views to fields. const auto lonLatFieldA = array::make_view(functionSpaceA.lonlat()); const auto lonLatFieldB = array::make_view(functionSpaceB.lonlat()); const auto ghostFieldA = array::make_view(functionSpaceA.ghost()); const auto ghostFieldB = array::make_view(functionSpaceB.ghost()); // Loop over functionspaces. for (idx_t i = 0; i < functionSpaceA.size(); ++i) { EXPECT_EQ(lonLatFieldA(i, LON), lonLatFieldB(i, LON)); EXPECT_EQ(lonLatFieldA(i, LAT), lonLatFieldB(i, LAT)); EXPECT_EQ(ghostFieldA(i), ghostFieldB(i)); } }; // Check that primal cells and dual nodes are equivalent. compareFields(primalCells, dualNodes); // Check that dual cells and primal nodes are equivalent. compareFields(dualCells, primalNodes); } CASE("Variable halo size functionspaces (primal mesh)") { // Create a mesh with a large halo, and a few functionspaces with different // (smaller) halo sizes. These should create fields with a smaller memory // footprint. // Set grid. const auto grid = Grid("CS-LFR-C-12"); // Set mesh config. const auto meshConfig = util::Config("partitioner", "equal_regions") | util::Config("halo", 3); // Set mesh. const auto mesh = MeshGenerator("cubedsphere", meshConfig).generate(grid); // Set functionspaces. const auto nodeColumns0 = functionspace::CubedSphereNodeColumns(mesh, util::Config("halo", 0)); const auto nodeColumns1 = functionspace::CubedSphereNodeColumns(mesh, util::Config("halo", 1)); const auto nodeColumns2 = functionspace::CubedSphereNodeColumns(mesh, util::Config("halo", 2)); const auto cellColumns0 = functionspace::CubedSphereCellColumns(mesh, util::Config("halo", 0)); const auto cellColumns1 = functionspace::CubedSphereCellColumns(mesh, util::Config("halo", 1)); const auto cellColumns2 = functionspace::CubedSphereCellColumns(mesh, util::Config("halo", 2)); // Check functionspace sizes. EXPECT(nodeColumns0.size() < nodeColumns1.size()); EXPECT(nodeColumns1.size() < nodeColumns2.size()); EXPECT(nodeColumns2.size() < mesh.nodes().size()); EXPECT(cellColumns0.size() < cellColumns1.size()); EXPECT(cellColumns1.size() < cellColumns2.size()); EXPECT(cellColumns2.size() < mesh.cells().size()); // Make sure size of owned cell data matches grid. auto checkSize = [&](idx_t sizeOwned) { mpi::comm().allReduceInPlace(sizeOwned, eckit::mpi::Operation::SUM); EXPECT_EQ(sizeOwned, grid.size()); }; checkSize(cellColumns0.sizeOwned()); checkSize(cellColumns1.sizeOwned()); checkSize(cellColumns2.sizeOwned()); } CASE("Variable halo size functionspaces (dual mesh)") { // Create a mesh with a large halo, and a few functionspaces with different // (smaller) halo sizes. These should create fields with a smaller memory // footprint. // Set grid. const auto grid = Grid("CS-LFR-C-12"); // Set mesh config. const auto meshConfig = util::Config("partitioner", "equal_regions") | util::Config("halo", 3); // Set mesh. const auto mesh = MeshGenerator("cubedsphere_dual", meshConfig).generate(grid); // Set functionspaces. const auto nodeColumns0 = functionspace::CubedSphereNodeColumns(mesh, util::Config("halo", 0)); const auto nodeColumns1 = functionspace::CubedSphereNodeColumns(mesh, util::Config("halo", 1)); const auto nodeColumns2 = functionspace::CubedSphereNodeColumns(mesh, util::Config("halo", 2)); const auto cellColumns0 = functionspace::CubedSphereCellColumns(mesh, util::Config("halo", 0)); const auto cellColumns1 = functionspace::CubedSphereCellColumns(mesh, util::Config("halo", 1)); const auto cellColumns2 = functionspace::CubedSphereCellColumns(mesh, util::Config("halo", 2)); // Check functionspace sizes. EXPECT(nodeColumns0.size() < nodeColumns1.size()); EXPECT(nodeColumns1.size() < nodeColumns2.size()); EXPECT(nodeColumns2.size() < mesh.nodes().size()); EXPECT(cellColumns0.size() < cellColumns1.size()); EXPECT(cellColumns1.size() < cellColumns2.size()); EXPECT(cellColumns2.size() < mesh.cells().size()); // Make sure size of owned cell data matches grid. auto checkSize = [&](idx_t sizeOwned) { mpi::comm().allReduceInPlace(sizeOwned, eckit::mpi::Operation::SUM); EXPECT_EQ(sizeOwned, grid.size()); }; checkSize(nodeColumns0.sizeOwned()); checkSize(nodeColumns1.sizeOwned()); checkSize(nodeColumns2.sizeOwned()); } } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/functionspace/test_blockstructuredcolumns.cc0000664000175000017500000001525315160212070026051 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "atlas/array/ArrayView.h" #include "atlas/array/MakeView.h" #include "atlas/field/Field.h" #include "atlas/functionspace/BlockStructuredColumns.h" #include "atlas/grid/Partitioner.h" #include "atlas/grid/StructuredGrid.h" #include "atlas/parallel/mpi/mpi.h" #include "tests/AtlasTestEnvironment.h" using namespace eckit; using namespace atlas; using namespace atlas::functionspace; using namespace atlas::test; using namespace atlas::util; namespace { template void run_scatter_gather(const Grid& grid, const BlockStructuredColumns& fs, int nlev, int nvar) { auto glb_field = fs.createField( option::global() | option::levels(nlev) | option::variables(nvar) ); // Only allocated on rank 0 if( atlas::mpi::comm().rank() != 0 ) { EXPECT_EQ( glb_field.shape(0), 0); } else { EXPECT_EQ( glb_field.shape(0), grid.size()); } auto glb_field_v = array::make_view(glb_field); for (gidx_t p = 0; p < glb_field.shape(0); p++) { for (idx_t jlev = 0; jlev < nlev; ++jlev) { for (idx_t jvar = 0; jvar < nvar; ++jvar) { glb_field_v(p, jlev, jvar) = p*nvar*nlev + jlev*nvar + jvar; } } } auto field = fs.createField(glb_field); EXPECT_EQ(glb_field.horizontal_dimension(), (std::vector{0})); EXPECT_EQ(field.horizontal_dimension(), (std::vector{0,3})); fs.scatter(glb_field, field); auto glb_field_2 = fs.createField(glb_field , option::global(atlas::mpi::comm().size()-1)); // Only allocated on rank MPI_SIZE-1 if( atlas::mpi::comm().rank() != atlas::mpi::comm().size() - 1 ) { EXPECT_EQ( glb_field_2.shape(0), 0); } else { EXPECT_EQ( glb_field_2.shape(0), grid.size()); } fs.gather(field, glb_field_2); auto glb_field_2_v = array::make_view(glb_field_2); for (gidx_t p = 0; p < glb_field_2.shape(0); p++) { for (idx_t jlev = 0; jlev < nlev; ++jlev) { for (idx_t jvar = 0; jvar < nvar; ++jvar) { EXPECT_EQ(glb_field_2_v(p, jlev, jvar), p*nvar*nlev + jlev*nvar + jvar); } } } } } // namespace namespace atlas { namespace test { //----------------------------------------------------------------------------- CASE("test_BlockStructuredColumns") { std::string gridname = eckit::Resource("--grid", "O8"); idx_t halo = eckit::Resource("--halo", 0); idx_t nproma = eckit::Resource("--nproma", 12); idx_t nvar = 3; idx_t nlev = 4; auto grid = StructuredGrid(gridname); util::Config config; config.set("halo", halo); config.set("nproma", nproma); config.set("levels", nlev); auto fs = functionspace::BlockStructuredColumns(grid, config); SECTION("blocking indices parallel without halo") { ATLAS_DEBUG_VAR(mpi::comm().size()); ATLAS_DEBUG_VAR(halo); ATLAS_DEBUG_VAR(fs.size()); ATLAS_DEBUG_VAR(fs.nproma()); ATLAS_DEBUG_VAR(fs.nblks()); ATLAS_DEBUG_VAR(fs.block(fs.nblks()-1).size()); ATLAS_DEBUG_VAR(fs.levels()); ATLAS_DEBUG_VAR(nvar); idx_t ngptot = fs.size(); idx_t nblk = ngptot / nproma; idx_t nrof_last = nproma; if (ngptot % nproma > 0) { // Correction when ngptot is not divisible by nproma nrof_last = ngptot - nblk * nproma; ++nblk; } EXPECT_EQ(fs.nproma(), nproma); EXPECT_EQ(fs.levels(), nlev); EXPECT_EQ(fs.nblks(), nblk); EXPECT_EQ(fs.block(nblk-1).size(), nrof_last); ATLAS_DEBUG("Test cover full iteration space"); idx_t jpoint = 0; for (idx_t jblk = 0; jblk < nblk; ++jblk) { auto blk = fs.block(jblk); for (idx_t jrof = 0; jrof < blk.size(); ++jrof, ++jpoint) { idx_t h_idx = fs.index(jblk, jrof); idx_t hh_idx = blk.index(jrof); EXPECT_EQ(h_idx, jpoint); EXPECT_EQ(h_idx, hh_idx); } } EXPECT_EQ(jpoint, ngptot); Field field = fs.createField(option::name("field") | option::variables(nvar)); EXPECT_EQ(field.shape(0), nblk ); EXPECT_EQ(field.shape(1), nvar ); EXPECT_EQ(field.shape(2), nlev ); EXPECT_EQ(field.shape(3), nproma ); // When above is failing, we need to really stop here. REQUIRE( not current_test().failed() ); auto value = array::make_view(field); auto g = array::make_view(fs.global_index()); ATLAS_DEBUG("Set"); for (idx_t jblk = 0; jblk < nblk; ++jblk) { auto blk = fs.block(jblk); for (idx_t jvar = 0; jvar < nvar; ++jvar) { for (idx_t jlev = 0; jlev < nlev; ++jlev) { for (idx_t jrof = 0; jrof < blk.size(); ++jrof) { idx_t h_idx = blk.index(jrof); value(jblk,jvar,jlev,jrof) = g(h_idx); } } } } ATLAS_DEBUG("Check"); std::vector counters; counters.resize(nvar * nlev); std::fill(counters.begin(), counters.end(), 0); for (idx_t jblk = 0; jblk < nblk; ++jblk) { auto blk = fs.block(jblk); for (idx_t jvar = 0; jvar < nvar; ++jvar) { for (idx_t jlev = 0; jlev < nlev; ++jlev) { for (idx_t jrof = 0; jrof < blk.size(); ++jrof) { idx_t& icheck = counters[jvar*nlev + jlev]; EXPECT_EQ(value(jblk,jvar,jlev,jrof), g(icheck)); icheck++; } } } } } SECTION("test_BlockStructuredColumns scatter/gather") { auto fs = functionspace::BlockStructuredColumns(grid, config); run_scatter_gather(grid, fs, nlev, nvar); run_scatter_gather(grid, fs, nlev, nvar); run_scatter_gather(grid, fs, nlev, nvar); run_scatter_gather(grid, fs, nlev, nvar); } } //----------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/functionspace/test_reduced_halo.cc0000664000175000017500000000610515160212070023643 0ustar alastairalastair/* * (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/functionspace/EdgeColumns.h" #include "atlas/functionspace/NodeColumns.h" #include "atlas/grid/StructuredGrid.h" #include "atlas/mesh/HybridElements.h" #include "atlas/mesh/Mesh.h" #include "atlas/mesh/Nodes.h" #include "atlas/meshgenerator.h" #include "atlas/parallel/mpi/mpi.h" #include "tests/AtlasTestEnvironment.h" using namespace atlas::functionspace; using namespace atlas::grid; using namespace atlas::meshgenerator; namespace atlas { namespace test { template Container reversed(const Container& a) { Container a_reversed = a; std::reverse(a_reversed.begin(), a_reversed.end()); return a_reversed; } static std::array false_true{false, true}; //----------------------------------------------------------------------------- CASE("halo nodes") { Grid grid("O8"); std::vector halos{0, 1, 2, 3, 4}; std::vector nodes{560, 592, 624, 656, 688}; for (bool reduce : false_true) { SECTION(std::string(reduce ? "reduced" : "increased")) { Mesh mesh = StructuredMeshGenerator().generate(grid); EXPECT(mesh.nodes().size() == nodes[0]); for (int h : (reduce ? reversed(halos) : halos)) { NodeColumns fs(mesh, option::halo(h)); if (mpi::comm().size() == 1) { EXPECT(fs.nb_nodes() == nodes[h]); } } } } } //----------------------------------------------------------------------------- CASE("halo edges") { Grid grid("O8"); std::vector halos{0, 1, 2, 3, 4}; std::vector edges{1559, 1649, 1739, 1829, 1919}; for (bool reduce : false_true) { for (bool with_pole_edges : false_true) { int pole_edges = with_pole_edges ? StructuredGrid(grid).nx().front() : 0; SECTION(std::string(reduce ? "reduced " : "increased ") + std::string(with_pole_edges ? "with_pole_edges" : "without_pole_edges")) { Mesh mesh = StructuredMeshGenerator().generate(grid); EXPECT(mesh.edges().size() == 0); for (int h : (reduce ? reversed(halos) : halos)) { EdgeColumns fs(mesh, option::halo(h) | option::pole_edges(with_pole_edges)); if (mpi::comm().size() == 1) { EXPECT(fs.nb_edges() == edges[h] + pole_edges); } } } } } } //----------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/functionspace/test_structuredcolumns.cc0000664000175000017500000004102515160212070025032 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "eckit/log/Bytes.h" #include "eckit/types/Types.h" #include "atlas/array/ArrayView.h" #include "atlas/array/MakeView.h" #include "atlas/field/Field.h" #include "atlas/functionspace/NodeColumns.h" #include "atlas/functionspace/StructuredColumns.h" #include "atlas/grid/Partitioner.h" #include "atlas/grid/StructuredGrid.h" #include "atlas/mesh/Mesh.h" #include "atlas/meshgenerator.h" #include "atlas/output/Gmsh.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/util/CoordinateEnums.h" #include "atlas/util/MicroDeg.h" #include "tests/AtlasTestEnvironment.h" using namespace eckit; using namespace atlas::functionspace; using namespace atlas::util; namespace atlas { namespace test { //----------------------------------------------------------------------------- CASE("ATLAS-295 (Github PR 32): Fix StructuredColumns when domain is shifted by 180 degrees (Github PR 32)") { auto grid1 = Grid("S80x40", GlobalDomain{-180}); functionspace::StructuredColumns fs1(grid1, grid::Partitioner("checkerboard"), Config("halo", 1) | Config("periodic_points", true)); auto grid2 = Grid{"S80x40"}; functionspace::StructuredColumns fs2(grid2, grid::Partitioner("checkerboard"), Config("halo", 1) | Config("periodic_points", true)); EXPECT_EQ(grid1.size(), grid2.size()); EXPECT_EQ(fs1.size(), fs2.size()); EXPECT_EQ(fs1.sizeOwned(), fs2.sizeOwned()); } CASE("test_functionspace_StructuredColumns_no_halo") { size_t root = 0; std::string gridname = eckit::Resource("--grid", "O8"); Grid grid(gridname); util::Config config; config.set("halo", 0); config.set("periodic_points", true); functionspace::StructuredColumns fs(grid, grid::Partitioner("equal_regions"), config); ATLAS_DEBUG_VAR(fs.size()); ATLAS_DEBUG_VAR(eckit::Bytes(fs.footprint())); Field field = fs.createField(option::name("field")); Field field_glb = fs.createField(option::name("field_global") | option::global(root)); auto value = array::make_view(field); auto value_glb = array::make_view(field_glb); value.assign(mpi::comm().rank()); fs.gather(field, field_glb); Log::info() << "field checksum = " << fs.checksum(field) << std::endl; // for( size_t j=0; j check{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4}; EXPECT(value_glb.size() == idx_t(check.size())); for (idx_t j = 0; j < value_glb.size(); ++j) { EXPECT(value_glb(j) == check[j]); } } ATLAS_TRACE_SCOPE("output gmsh") { output::Gmsh gmsh("structured.msh"); gmsh.write(MeshGenerator("structured").generate(grid)); gmsh.write(field); } } CASE("test_functionspace_StructuredColumns_halo with output") { ATLAS_DEBUG_VAR(mpi::comm().size()); // grid::StructuredGrid grid( // grid::StructuredGrid::XSpace( {0.,360.} , {2,4,6,6,4,2} , false ), // grid::StructuredGrid::YSpace( grid::LinearSpacing( {80.,-80.}, 6 ) ), // Projection(), // Domain() ); std::string gridname = eckit::Resource("--grid", "O8"); StructuredGrid grid(gridname); int halo = eckit::Resource("--halo", 2); util::Config config; config.set("halo", halo); config.set("periodic_points", true); functionspace::StructuredColumns fs(grid, grid::Partitioner("equal_regions"), config); Field field = fs.createField(option::name("field")); auto value = array::make_view(field); auto xy = array::make_view(fs.xy()); auto g = array::make_view(fs.global_index()); auto r = array::make_view(fs.remote_index()); auto p = array::make_view(fs.partition()); for (idx_t j = fs.j_begin(); j < fs.j_end(); ++j) { for (idx_t i = fs.i_begin(j); i < fs.i_end(j); ++i) { idx_t n = fs.index(i, j); value(n) = util::microdeg(xy(n, XX)); } } // EXPECT( fs.checksum(field) == "cef2694016492d408fa157b7c59ce741" ); fs.haloExchange(field); // EXPECT( fs.checksum(field) == "cef2694016492d408fa157b7c59ce741" ); ATLAS_TRACE_SCOPE("Output python") { eckit::PathName filepath("test_functionspace_StructuredColumns_halo_p" + std::to_string(mpi::comm().rank()) + ".py"); std::ofstream f(filepath.asString().c_str(), std::ios::trunc); f << "\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" "" "\n" "fig = plt.figure(figsize=(20,10))" "\n" "ax = fig.add_subplot(111,aspect='equal')" "\n" ""; double xmin = std::numeric_limits::max(); double xmax = -std::numeric_limits::max(); double ymin = std::numeric_limits::max(); double ymax = -std::numeric_limits::max(); f << "\n" "x = ["; for (idx_t j = fs.j_begin_halo(); j < fs.j_end_halo(); ++j) { for (idx_t i = fs.i_begin_halo(j); i < fs.i_end_halo(j); ++i) { idx_t n = fs.index(i, j); f << xy(n, XX) << ", "; xmin = std::min(xmin, xy(n, XX)); xmax = std::max(xmax, xy(n, XX)); } } f << "]"; f << "\n" "y = ["; for (idx_t j = fs.j_begin_halo(); j < fs.j_end_halo(); ++j) { for (idx_t i = fs.i_begin_halo(j); i < fs.i_end_halo(j); ++i) { idx_t n = fs.index(i, j); f << xy(n, YY) << ", "; ymin = std::min(ymin, xy(n, YY)); ymax = std::max(ymax, xy(n, YY)); } } f << "]"; f << "\n" "g = ["; for (idx_t j = fs.j_begin_halo(); j < fs.j_end_halo(); ++j) { for (idx_t i = fs.i_begin_halo(j); i < fs.i_end_halo(j); ++i) { idx_t n = fs.index(i, j); f << g(n) << ", "; } } f << "]"; f << "\n" "p = ["; for (idx_t j = fs.j_begin_halo(); j < fs.j_end_halo(); ++j) { for (idx_t i = fs.i_begin_halo(j); i < fs.i_end_halo(j); ++i) { idx_t n = fs.index(i, j); f << p(n) << ", "; } } f << "]"; f << "\n" "r = ["; for (idx_t j = fs.j_begin_halo(); j < fs.j_end_halo(); ++j) { for (idx_t i = fs.i_begin_halo(j); i < fs.i_end_halo(j); ++i) { idx_t n = fs.index(i, j); f << r(n) << ", "; } } f << "]"; f << "\n" "" "\n" "c = [ cm.Paired( float(pp%13)/12. ) for pp in p ]" "\n" "ax.scatter(x, y, color=c, marker='o')" "\n" "for i in range(" << fs.size() << "):" "\n" " ax.annotate(g[i], (x[i],y[i]), fontsize=8)" "\n" ""; f << "\n" "ax.set_xlim( " << std::min(0., xmin) << "-5, " << std::max(360., xmax) << "+5)" "\n" "ax.set_ylim( " << std::min(-90., ymin) << "-5, " << std::max(90., ymax) << "+5)" "\n" "ax.set_xticks([0,45,90,135,180,225,270,315,360])" "\n" "ax.set_yticks([-90,-45,0,45,90])" "\n" "plt.grid()" "\n" "plt.show()" "\n"; } } //----------------------------------------------------------------------------- CASE("test_functionspace_StructuredColumns_halo checks without output") { std::string gridname = eckit::Resource("--grid", "O8"); StructuredGrid grid(gridname); int halo = eckit::Resource("--halo", 2); util::Config config; config.set("halo", halo); config.set("levels", 10); config.set("periodic_points", true); functionspace::StructuredColumns fs(grid, grid::Partitioner("equal_regions"), config); Field field = fs.createField(option::name("field")); auto value = array::make_view(field); auto xy = array::make_view(fs.xy()); for (idx_t j = fs.j_begin(); j < fs.j_end(); ++j) { for (idx_t i = fs.i_begin(j); i < fs.i_end(j); ++i) { idx_t n = fs.index(i, j); for (idx_t k = 0; k < fs.levels(); ++k) { value(n, k) = util::microdeg(xy(n, XX)); } } } ATLAS_TRACE_SCOPE("control each value ") fs.parallel_for([&](idx_t n, idx_t k) { EXPECT(value(n, k) == util::microdeg(xy(n, XX))); }); fs.parallel_for([&](idx_t n, idx_t i, idx_t j, idx_t k) { EXPECT(value(n, k) == util::microdeg(grid.x(i, j))); }); Field fieldg = fs.createField(field, option::global()); fs.gather(field, fieldg); ATLAS_TRACE_SCOPE("control_global") { auto valueg = array::make_view(fieldg); fs.parallel_for(option::global(), [&](idx_t n, idx_t i, idx_t j, idx_t k) { EXPECT(valueg(n, k) == util::microdeg(grid.x(i, j))); }); } } //----------------------------------------------------------------------------- CASE("test_functionspace_StructuredColumns halo exchange registration") { // Test by observing log with ATLAS_DEBUG=1 // The HaloExchange Cache should be created twice, already found 4 times, // erased twice. std::string gridname = eckit::Resource("--grid", "O8"); StructuredGrid grid(gridname); util::Config config; config.set("levels", 10); config.set("periodic_points", true); for (idx_t i = 0; i < 3; ++i) { config.set("halo", 2); functionspace::StructuredColumns fs1(grid, grid::Partitioner("equal_regions"), config); config.set("halo", 4); functionspace::StructuredColumns fs2(grid, grid::Partitioner("equal_regions"), config); Field field1 = fs1.createField(option::name("field")); Field field2 = fs2.createField(option::name("field")); field1.haloExchange(); field2.haloExchange(); } } //----------------------------------------------------------------------------- long innerproductwithhalo(const atlas::Field& f1, const atlas::Field& f2) { long sum(0); auto view1 = atlas::array::make_view(f1); auto view2 = atlas::array::make_view(f2); for (atlas::idx_t jn = 0; jn < f1.shape(0); ++jn) { for (atlas::idx_t jl = 0; jl < f1.levels(); ++jl) { sum += view1(jn, jl) * view2(jn, jl); } } atlas::mpi::comm().allReduceInPlace(sum, eckit::mpi::sum()); return sum; } CASE("test_functionspace_StructuredColumns halo exchange adjoint test 1") { // Adjoint test for fields std::string gridname = eckit::Resource("--grid", "S20x3"); StructuredGrid grid(gridname); util::Config config; config.set("levels", 1); config.set("halo", 1); long sum1(0), sum2(0); functionspace::StructuredColumns fs(grid, grid::Partitioner("checkerboard"), config); Field fieldInit = fs.createField(option::name("fieldInit")); // Field setup values 1 in interior and zeros in halos. auto view1 = atlas::array::make_view(fieldInit); auto i_index = atlas::array::make_view(fs.index_i()); auto j_index = atlas::array::make_view(fs.index_j()); for (atlas::idx_t jn = 0; jn < fs.sizeOwned(); ++jn) { for (atlas::idx_t jl = 0; jl < fieldInit.levels(); ++jl) { view1(jn, jl) = 1; std::cout << " initial interior regions:: " << " size = " << atlas::mpi::comm().size() << " " << " jn = " << jn << " " << " rank = " << atlas::mpi::comm().rank() << " " << " jn = " << jn << " " << " view1 = " << view1(jn, 0) << " " << " i_index = " << i_index(jn) << " " << " j_index = " << j_index(jn) << std::endl; } } Field fieldTmp = fs.createField(option::name("fieldTmp")); auto viewTmp = atlas::array::make_view(fieldTmp); for (atlas::idx_t jn = 0; jn < fieldTmp.shape(0); ++jn) { for (atlas::idx_t jl = 0; jl < fieldTmp.levels(); ++jl) { viewTmp(jn, jl) = view1(jn, jl); } } fieldTmp.haloExchange(); sum1 = innerproductwithhalo(fieldTmp, fieldTmp); fieldTmp.adjointHaloExchange(); sum2 = innerproductwithhalo(fieldInit, fieldTmp); atlas::Log::info() << "sum1 " << sum1 << "sum2 " << sum2 << std::endl; EXPECT(sum1 == sum2); // check that it is zero is in the halo sum1 = 0; for (atlas::idx_t jn = fs.sizeOwned(); jn < fieldTmp.shape(0); ++jn) { for (atlas::idx_t jl = 0; jl < fieldTmp.levels(); ++jl) { sum1 += viewTmp(jn, jl); } } atlas::mpi::comm().allReduceInPlace(sum1, eckit::mpi::sum()); EXPECT(sum1 == 0); } CASE("create_aligned_field") { std::string gridname = eckit::Resource("--grid", "S20x3"); Grid grid(gridname); functionspace::StructuredColumns fs(grid, option::levels(5)); Field field = fs.createField(option::variables(3) | option::alignment(4)); auto check_field = [&](const Field& field) { EXPECT_EQ(field.shape()[0], fs.size()); EXPECT_EQ(field.shape()[1], 5); EXPECT_EQ(field.shape()[2], 3); EXPECT_EQ(field.size(), fs.size() * 5 * 3); EXPECT_EQ(field.contiguous(), false); EXPECT_EQ(field.strides()[0], 5 * 4); EXPECT_EQ(field.strides()[1], 4); EXPECT_EQ(field.strides()[2], 1); }; check_field(field); } //----------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/functionspace/fctest_blockstructuredcolumns.F900000664000175000017500000001536515160212070026337 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. ! This File contains Unit Tests for testing the ! C++ / Fortran Interfaces to the State Datastructure ! ! @author Willem Deconinck ! @author Slavko Brdar #include "fckit/fctest.h" ! ----------------------------------------------------------------------------- module fcta_BlockStructuredColumns_fxt use atlas_module use, intrinsic :: iso_c_binding implicit none character(len=1024) :: msg contains end module ! ----------------------------------------------------------------------------- TESTSUITE_WITH_FIXTURE(fcta_BlockStructuredColumns,fcta_BlockStructuredColumns_fxt) ! ----------------------------------------------------------------------------- TESTSUITE_INIT call atlas_library%initialise() END_TESTSUITE_INIT ! ----------------------------------------------------------------------------- TESTSUITE_FINALIZE call atlas_library%finalise() END_TESTSUITE_FINALIZE TEST( test_blockstructuredcolumns ) use atlas_functionspace_blockstructuredcolumns_module implicit none type(atlas_StructuredGrid) :: grid type(atlas_functionspace_BlockStructuredColumns) :: fs type(atlas_functionspace) :: fs_base integer(ATLAS_KIND_IDX) :: i, j, jbegin, jblk, jrof character(len=256) str type(atlas_Field) :: field type(atlas_Field) :: field_xy type(atlas_Field) :: field_global_index type(atlas_Field) :: field_index_j real(8), pointer :: xy(:,:), x(:,:) integer(ATLAS_KIND_GIDX), pointer :: global_index(:) integer(ATLAS_KIND_IDX), pointer :: index_j(:) integer(ATLAS_KIND_IDX) :: nblks, blksize integer, parameter :: XX=1 grid = atlas_StructuredGrid("O16") fs = atlas_functionspace_BlockStructuredColumns(grid,halo=0,nproma=12) field = fs%create_field(name="field",kind=atlas_real(8)) FCTEST_CHECK_EQUAL( field%owners(), 1 ) field_xy = fs%xy() FCTEST_CHECK_EQUAL( field_xy%owners(), 2 ) call field%data(x) call field_xy%data(xy) field_global_index = fs%global_index() call field_global_index%data(global_index) field_index_j = fs%index_j() call field_index_j%data(index_j) nblks=fs%nblks() do jblk=1,nblks write(str,'(A,I4,A)') 'block', jblk, ' : ' call atlas_log%info(str,newl=.false.) jbegin = fs%block_begin(jblk) blksize = fs%block_size(jblk) do jrof=1,blksize write(str,'(I6)') global_index( jbegin+jrof-1 ) call atlas_log%info(str,newl=.false.) x(jrof,jblk) = xy(XX, jbegin+jrof-1) enddo call atlas_log%info("",newl=.true.) enddo !call fs%halo_exchange(field) FCTEST_CHECK_EQUAL( field_xy%owners(), 2 ) fs = atlas_functionspace_BlockStructuredColumns(grid,levels=5) field = fs%create_field(atlas_real(c_float)) FCTEST_CHECK_EQUAL( field%rank() , 3 ) FCTEST_CHECK_EQUAL( field%name() , "" ) FCTEST_CHECK_EQUAL( field%kind() , atlas_real(c_float) ) write(str,*) "before: name = ", fs%name(); call atlas_log%info(str) write(str,*) "before: owners = ", fs%owners(); call atlas_log%info(str) FCTEST_CHECK_EQUAL( fs%name(), "BlockStructuredColumns" ) fs_base = field%functionspace(); call atlas_log%info(str) write(str,*) "after: name = " , fs_base%name(); call atlas_log%info(str) write(str,*) "after: owners = " , fs_base%owners(); call atlas_log%info(str) FCTEST_CHECK_EQUAL( fs_base%name(), "BlockStructuredColumns" ) FCTEST_CHECK_EQUAL( field%owners(), 1 ) call field%final() FCTEST_CHECK_EQUAL( field_xy%owners(), 1 ) call field_xy%final() call field_global_index%final() call fs%final() call fs_base%final() call grid%final() END_TEST ! ----------------------------------------------------------------------------- TEST( test_collectives ) use fckit_mpi_module use atlas_functionspace_blockstructuredcolumns_module implicit none type(atlas_StructuredGrid) :: grid type(atlas_functionspace_BlockStructuredColumns) :: fs type(atlas_Field) :: field, global, scal type(atlas_Config) :: config type(atlas_Metadata) :: metadata type(fckit_mpi_comm) :: mpi integer(c_int) :: test_broadcast, halo_size, levels, nproma, nvar call atlas_log%info("test_collectives") mpi = fckit_mpi_comm() halo_size = 0 levels = 10 nproma = 12 nvar = 2 FCTEST_CHECK_EQUAL( halo_size, 0 ) grid = atlas_StructuredGrid("O16") config = atlas_Config() fs = atlas_functionspace_BlockStructuredColumns(grid,nproma=nproma,levels=levels,halo=halo_size) ! type c_int field = fs%create_field(kind=atlas_integer(c_int),variables=2) global = fs%create_field(field,global=.True.) scal = fs%create_field(kind=atlas_integer(c_int)) call fs%gather(field,global) metadata = global%metadata() if( mpi%rank() == 0 ) then call metadata%set("test_broadcast",123) endif call fs%scatter(global,field) metadata = field%metadata() call metadata%get("test_broadcast",test_broadcast) FCTEST_CHECK_EQUAL( test_broadcast, 123 ) ! type c_long field = fs%create_field(kind=c_long,variables=nvar) global = fs%create_field(field,global=.True.) scal = fs%create_field(kind=c_long) call fs%gather(field,global) metadata = global%metadata() if( mpi%rank() == 0 ) then call metadata%set("test_broadcast",123) endif call fs%scatter(global,field) metadata = field%metadata() call metadata%get("test_broadcast",test_broadcast) FCTEST_CHECK_EQUAL( test_broadcast, 123 ) ! type c_float field = fs%create_field(kind=atlas_real(c_float),variables=nvar) global = fs%create_field(field,global=.True.) scal = fs%create_field(kind=atlas_real(c_float)) write(msg,*) "field: rank",field%rank(), " size ", field%size(), " shape [",field%shape(), "]" call atlas_log%info(msg) write(msg,*) "nblk = ",fs%nblks(); call atlas_log%info(msg) write(msg,*) "global: rank",global%rank()," size ", global%size(), " shape [",global%shape(),"]" call atlas_log%info(msg) call fs%gather(field,global) !call fs%halo_exchange(field) metadata = global%metadata() if( mpi%rank() == 0 ) then call metadata%set("test_broadcast",123) FCTEST_CHECK_EQUAL(global%shape(3), grid%size() ) FCTEST_CHECK_EQUAL(global%shape(2), levels) FCTEST_CHECK_EQUAL(global%shape(1), nvar ) endif call fs%scatter(global,field) metadata = field%metadata() call metadata%get("test_broadcast",test_broadcast) FCTEST_CHECK_EQUAL( test_broadcast, 123 ) ! type c_double field = fs%create_field(kind=atlas_real(c_double),variables=2) global = fs%create_field(field,global=.True.) scal = fs%create_field(kind=atlas_real(c_double)) call fs%gather(field,global) metadata = global%metadata() if( mpi%rank() == 0 ) then call metadata%set("test_broadcast",123) endif call fs%scatter(global,field) metadata = field%metadata() call metadata%get("test_broadcast",test_broadcast) FCTEST_CHECK_EQUAL( test_broadcast, 123 ) END_TEST END_TESTSUITE atlas-0.46.0/src/tests/functionspace/test_stencil.cc0000664000175000017500000002234415160212070022671 0ustar alastairalastair/* * (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 "eckit/linalg/Vector.h" #include "eckit/types/Types.h" #include "atlas/array.h" #include "atlas/field/Field.h" #include "atlas/functionspace/NodeColumns.h" #include "atlas/functionspace/PointCloud.h" #include "atlas/functionspace/StructuredColumns.h" #include "atlas/grid/Partitioner.h" #include "atlas/grid/Stencil.h" #include "atlas/grid/StencilComputer.h" #include "atlas/grid/StructuredGrid.h" #include "atlas/mesh/Mesh.h" #include "atlas/meshgenerator.h" #include "atlas/output/Gmsh.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/util/CoordinateEnums.h" #include "atlas/util/MicroDeg.h" #include "tests/AtlasTestEnvironment.h" using namespace eckit; using namespace atlas::functionspace; using namespace atlas::util; using namespace atlas::grid; namespace atlas { namespace test { //----------------------------------------------------------------------------- std::vector IFS_vertical_coordinates(idx_t nlev) { std::vector zcoord(nlev); double dzcoord = 1. / double(nlev); for (idx_t jlev = 0; jlev < nlev; ++jlev) { zcoord[jlev] = 0.5 * dzcoord + jlev * dzcoord; } return zcoord; } std::vector zrange(idx_t nlev, double min, double max) { std::vector zcoord(nlev); double dzcoord = (max - min) / double(nlev - 1); for (idx_t jlev = 0; jlev < nlev; ++jlev) { zcoord[jlev] = min + jlev * dzcoord; } return zcoord; } double cubic(double x, double min, double max) { double x0 = min; double x1 = 0.5 * (max + min); double x2 = max; double xmax = 0.5 * (x0 + x1); return (x - x0) * (x - x1) * (x - x2) / ((xmax - x0) * (xmax - x1) * (xmax - x2)); } CASE("test finding of North-West grid point") { std::string gridname = eckit::Resource("--grid", "O8"); StructuredGrid grid(gridname); constexpr double tol = 0.5e-6; constexpr idx_t halo = 2; ComputeNorth compute_j_north(grid, halo); ComputeWest compute_i_west(grid, halo); struct IJ { idx_t i; idx_t j; }; idx_t ny = grid.ny(); if (mpi::comm().size() == 1) { auto entries = { std::make_tuple(PointXY{0. + 0.5 * tol, grid.y(0) + 0.5 * tol}, IJ{0, 0}), std::make_tuple(PointXY{0. - 0.5 * tol, grid.y(0) - 0.5 * tol}, IJ{0, 0}), std::make_tuple(PointXY{0. + 2.0 * tol, grid.y(0) + 2.0 * tol}, IJ{0, -1}), std::make_tuple(PointXY{0. - 2.0 * tol, grid.y(0) - 2.0 * tol}, IJ{-1, 0}), std::make_tuple(PointXY{360. + 0.5 * tol, grid.y(ny - 1) + 0.5 * tol}, IJ{grid.nx(ny - 1), ny - 1}), std::make_tuple(PointXY{360. - 0.5 * tol, grid.y(ny - 1) - 0.5 * tol}, IJ{grid.nx(ny - 1), ny - 1}), std::make_tuple(PointXY{360. + 2.0 * tol, grid.y(ny - 1) + 2.0 * tol}, IJ{grid.nx(ny - 2), ny - 2}), std::make_tuple(PointXY{360. - 2.0 * tol, grid.y(ny - 1) - 2.0 * tol}, IJ{grid.nx(ny - 1) - 1, ny - 1}), }; for (auto entry : entries) { auto p = std::get<0>(entry); auto index = std::get<1>(entry); EXPECT(compute_j_north(p.y()) == index.j); EXPECT(compute_i_west(p.x(), index.j) == index.i); } } } CASE("test horizontal stencil") { //if ( mpi::comm().size() == 1 ) { std::string gridname = eckit::Resource("--grid", "O8"); StructuredGrid grid(gridname); int halo = eckit::Resource("--halo", 2); util::Config config; config.set("halo", halo); config.set("levels", 9); config.set("periodic_points", true); functionspace::StructuredColumns fs(grid, grid::Partitioner("equal_regions"), config); HorizontalStencil<4> stencil; ComputeHorizontalStencil compute_stencil(grid, stencil.width()); auto departure_points = { PointXY(0., 90.), PointXY(0., -90.), PointXY(0., 0.), PointXY(360., 0.), }; for (auto p : departure_points) { Log::info() << p << std::endl; compute_stencil(p.x(), p.y(), stencil); for (idx_t j = 0; j < stencil.width(); ++j) { Log::info() << stencil.i(0, j) << " " << stencil.j(j) << " -- " << "x,y = " << fs.compute_xy(stencil.i(0, j), stencil.j(j)) << std::endl; } Log::info() << std::endl; } } CASE("test horizontal stencil linear") { //if ( mpi::comm().size() == 1 ) { std::string gridname = eckit::Resource("--grid", "O8"); StructuredGrid grid(gridname); //int halo = eckit::Resource( "--halo", 2 ); util::Config config; config.set("levels", 9); config.set("periodic_points", true); functionspace::StructuredColumns fs(grid, grid::Partitioner("equal_regions"), config); HorizontalStencil<2> stencil; ComputeHorizontalStencil compute_stencil(grid, stencil.width()); auto departure_points = { PointXY(0., 90.), PointXY(0., -90.), PointXY(0., 0.), PointXY(360., 0.), PointXY(359.9, 0.), }; for (auto p : departure_points) { Log::info() << p << std::endl; compute_stencil(p.x(), p.y(), stencil); for (idx_t j = 0; j < stencil.width(); ++j) { for (idx_t i = 0; i < stencil.width(); ++i) { Log::info() << stencil.i(i, j) << " " << stencil.j(j) << " -- " << "x,y = " << fs.compute_xy(stencil.i(i, j), stencil.j(j)) << std::endl; } Log::info() << std::endl; } } } //----------------------------------------------------------------------------- CASE("test vertical stencil") { SECTION("Initialize ComputeLower from raw data (as e.g. given from IFS)") { double z[] = {0.1, 0.3, 0.5, 0.7, 0.9}; EXPECT_NO_THROW(ComputeLower{Vertical(5, array::make_view(z, 5), std::vector{0., 1.})}); } idx_t nlev = 10; auto zcoord = IFS_vertical_coordinates(nlev); double dzcoord = 1. / double(nlev); auto vertical = Vertical(nlev, zcoord, std::vector{0., 1.}); SECTION("Test compute_lower works as expected ") { ComputeLower compute_lower(vertical); const double eps = 1.e-14; for (idx_t k = 0; k < nlev; ++k) { idx_t k_expected = std::max(0, std::min(nlev - 1, k)); EXPECT(compute_lower(zcoord[k]) == k_expected); EXPECT(compute_lower(zcoord[k] - eps) == k_expected); EXPECT(compute_lower(zcoord[k] + eps) == k_expected); EXPECT(compute_lower(zcoord[k] + 0.5 * dzcoord) == k_expected); } } SECTION("Compute vertical stencil") { ComputeVerticalStencil compute_vertical_stencil(vertical, 4); std::vector departure_points{0.0, 0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0}; for (auto p : departure_points) { VerticalStencil<4> stencil; compute_vertical_stencil(p, stencil); Log::info() << p << " : "; for (idx_t k = 0; k < stencil.width(); ++k) { Log::info() << stencil.k(k) << "[" << vertical[stencil.k(k)] << "] "; } Log::info() << " interval = " << stencil.k_interval() << std::endl; if (p < vertical[0]) { EXPECT(stencil.k_interval() == -1); } else if (p < vertical[1]) { EXPECT(stencil.k_interval() == 0); } else if (p > vertical[nlev - 1]) { EXPECT(stencil.k_interval() == 3); } else if (p > vertical[nlev - 2]) { EXPECT(stencil.k_interval() == 2); } else { EXPECT(stencil.k_interval() == 1); } } } } //----------------------------------------------------------------------------- #if 1 CASE("ifs method to find nearest grid point") { // see satrad/module/gaussgrid.F90 std::string gridname = eckit::Resource("--grid", "O8"); StructuredGrid grid(gridname); auto p = PointXY{0., grid.y(0)}; idx_t kgrib_lat, kgrib_lon; { double x = p.x(); double y = p.y(); std::vector pdlat(grid.ny()); for (idx_t j = 0; j < grid.ny(); ++j) { pdlat[j] = std::abs(y - grid.y(j)); } auto iterator = std::min_element(pdlat.begin(), pdlat.end()); kgrib_lat = static_cast(iterator - pdlat.begin()); double zfirstlon = grid.x(0, kgrib_lat); double zdlon = grid.x(1, kgrib_lat) - zfirstlon; double zsafelon = std::fmod(x - zfirstlon + 720., 360.); kgrib_lon = static_cast(std::fmod(std::round(zsafelon / zdlon), grid.nx(kgrib_lat))); } EXPECT(kgrib_lon == 0); EXPECT(kgrib_lat == 0); } #endif //----------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/functionspace/fctest_functionspace.F900000664000175000017500000010002315160212070024342 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. ! This File contains Unit Tests for testing the ! C++ / Fortran Interfaces to the State Datastructure ! @author Willem Deconinck #include "fckit/fctest.h" ! ----------------------------------------------------------------------------- module fcta_FunctionSpace_fxt use atlas_module use, intrinsic :: iso_c_binding implicit none character(len=1024) :: msg contains end module ! ----------------------------------------------------------------------------- TESTSUITE_WITH_FIXTURE(fcta_FunctionSpace,fcta_FunctionSpace_fxt) ! ----------------------------------------------------------------------------- TESTSUITE_INIT call atlas_library%initialise() END_TESTSUITE_INIT ! ----------------------------------------------------------------------------- TESTSUITE_FINALIZE call atlas_library%finalise() END_TESTSUITE_FINALIZE ! ----------------------------------------------------------------------------- TEST( test_nodes ) #if 1 type(atlas_StructuredGrid) :: grid type(atlas_MeshGenerator) :: meshgenerator type(atlas_Mesh) :: mesh type(atlas_functionspace_NodeColumns) :: fs type(atlas_Field) :: field, template type(atlas_mesh_Nodes) :: nodes integer :: halo_size, nb_nodes halo_size = 1 grid = atlas_StructuredGrid("N24") meshgenerator = atlas_MeshGenerator() mesh = meshgenerator%generate(grid) call meshgenerator%final() fs = atlas_functionspace_NodeColumns(mesh,halo_size) nodes = fs%nodes() nb_nodes = fs%nb_nodes() write(msg,*) "nb_nodes = ",nb_nodes; call atlas_log%info(msg) field = fs%create_field(atlas_real(c_float)) FCTEST_CHECK_EQUAL( field%rank() , 1 ) FCTEST_CHECK_EQUAL( field%name() , "" ) FCTEST_CHECK_EQUAL( field%kind() , atlas_real(c_float) ) call field%final() field = fs%create_field(name="field",kind=atlas_real(c_float)) FCTEST_CHECK_EQUAL( field%rank() , 1 ) FCTEST_CHECK_EQUAL( field%name() , "field" ) FCTEST_CHECK_EQUAL( field%kind() , atlas_real(c_float) ) call field%final() field = fs%create_field(atlas_real(c_float),variables=2) FCTEST_CHECK_EQUAL( field%rank() , 2 ) FCTEST_CHECK_EQUAL( field%name() , "" ) call field%final() field = fs%create_field(name="field",kind=atlas_integer(c_int),variables=2*2) FCTEST_CHECK_EQUAL( field%rank() , 2 ) FCTEST_CHECK_EQUAL( field%name() , "field" ) template = field field = fs%create_field(template) FCTEST_CHECK_EQUAL( field%rank() , 2 ) FCTEST_CHECK_EQUAL( field%name() , template%name() ) call field%final() field = fs%create_field(template,name="field") FCTEST_CHECK_EQUAL( field%rank() , 2 ) FCTEST_CHECK_EQUAL( field%name() , "field" ) call field%final() call template%final() field = fs%create_field(atlas_real(c_float),global=.True.) FCTEST_CHECK_EQUAL( field%rank() , 1 ) FCTEST_CHECK_EQUAL( field%name() , "" ) FCTEST_CHECK_EQUAL( field%kind() , atlas_real(c_float) ) call field%final() field = fs%create_field(name="field",kind=atlas_real(c_float),global=.True.) FCTEST_CHECK_EQUAL( field%rank() , 1 ) FCTEST_CHECK_EQUAL( field%name() , "field" ) FCTEST_CHECK_EQUAL( field%kind() , atlas_real(c_float) ) call field%final() field = fs%create_field(atlas_real(c_float),variables=2,global=.True.) FCTEST_CHECK_EQUAL( field%rank() , 2 ) FCTEST_CHECK_EQUAL( field%name() , "" ) call field%final() field = fs%create_field(name="field",kind=atlas_integer(c_int),variables=2*2,global=.True.) FCTEST_CHECK_EQUAL( field%rank() , 2 ) FCTEST_CHECK_EQUAL( field%name() , "field" ) template = field field = fs%create_field(template,global=.True.) FCTEST_CHECK_EQUAL( field%rank() , 2 ) FCTEST_CHECK_EQUAL( field%name() , template%name() ) call field%final() field = fs%create_field(template,name="field",global=.True.) FCTEST_CHECK_EQUAL( field%rank() , 2 ) FCTEST_CHECK_EQUAL( field%name() , "field" ) call field%final() call template%final() call fs%final() call mesh%final() call grid%final() #else #warning test test_nodes disabled #endif END_TEST ! ----------------------------------------------------------------------------- TEST( test_nodescolumns ) #if 1 type(atlas_StructuredGrid) :: grid type(atlas_MeshGenerator) :: meshgenerator type(atlas_Mesh) :: mesh type(atlas_functionspace_NodeColumns) :: fs type(atlas_Field) :: field, template integer :: halo_size, levels halo_size = 1 levels = 10 grid = atlas_StructuredGrid("N24") meshgenerator = atlas_MeshGenerator() mesh = meshgenerator%generate(grid) call meshgenerator%final() fs = atlas_functionspace_NodeColumns(mesh,halo_size) !levels = fs%nb_levels() write(msg,*) "nb_levels = ",levels; call atlas_log%info(msg) field = fs%create_field(atlas_real(c_float),levels=levels) FCTEST_CHECK_EQUAL( field%rank() , 2 ) FCTEST_CHECK_EQUAL( field%name() , "" ) FCTEST_CHECK_EQUAL( field%kind() , atlas_real(c_float) ) call field%final() field = fs%create_field(name="field",kind=atlas_real(c_float),levels=levels) FCTEST_CHECK_EQUAL( field%rank() , 2 ) FCTEST_CHECK_EQUAL( field%name() , "field" ) FCTEST_CHECK_EQUAL( field%kind() , atlas_real(c_float) ) call field%final() field = fs%create_field(atlas_real(c_float),levels=levels,variables=2) FCTEST_CHECK_EQUAL( field%rank() , 3 ) FCTEST_CHECK_EQUAL( field%name() , "" ) call field%final() field = fs%create_field(name="field",kind=atlas_integer(c_int),levels=levels,variables=2*2) FCTEST_CHECK_EQUAL( field%rank() , 3 ) FCTEST_CHECK_EQUAL( field%name() , "field" ) template = field field = fs%create_field(template) FCTEST_CHECK_EQUAL( field%rank() , 3 ) FCTEST_CHECK_EQUAL( field%name() , template%name() ) call field%final() field = fs%create_field(template,name="field") FCTEST_CHECK_EQUAL( field%rank() , 3 ) FCTEST_CHECK_EQUAL( field%name() , "field" ) call field%final() call template%final() field = fs%create_field(atlas_real(c_float),levels=levels,global=.True.) FCTEST_CHECK_EQUAL( field%rank() , 2 ) FCTEST_CHECK_EQUAL( field%name() , "" ) FCTEST_CHECK_EQUAL( field%kind() , atlas_real(c_float) ) call field%final() field = fs%create_field(name="field",kind=atlas_real(c_float),levels=levels,global=.True.) FCTEST_CHECK_EQUAL( field%rank() , 2 ) FCTEST_CHECK_EQUAL( field%name() , "field" ) FCTEST_CHECK_EQUAL( field%kind() , atlas_real(c_float) ) call field%final() field = fs%create_field(atlas_real(c_float),levels=levels,variables=2,global=.True.) FCTEST_CHECK_EQUAL( field%rank() , 3 ) FCTEST_CHECK_EQUAL( field%name() , "" ) call field%final() field = fs%create_field(name="field",kind=atlas_integer(c_int),levels=levels,variables=2*2,global=.True.) FCTEST_CHECK_EQUAL( field%rank() , 3 ) FCTEST_CHECK_EQUAL( field%name() , "field" ) template = field field = fs%create_field(template,global=.True.) FCTEST_CHECK_EQUAL( field%rank() , 3 ) FCTEST_CHECK_EQUAL( field%name() , template%name() ) call field%final() field = fs%create_field(template,name="field",global=.True.) FCTEST_CHECK_EQUAL( field%rank() , 3 ) FCTEST_CHECK_EQUAL( field%name() , "field" ) call field%final() call template%final() fs = atlas_functionspace_NodeColumns(mesh,levels=5) field = fs%create_field(atlas_real(c_float)) FCTEST_CHECK_EQUAL( field%rank() , 2 ) FCTEST_CHECK_EQUAL( field%name() , "" ) FCTEST_CHECK_EQUAL( field%kind() , atlas_real(c_float) ) call field%final() FCTEST_CHECK_EQUAL( fs%owners(), 1 ) call fs%final() FCTEST_CHECK_EQUAL( mesh%owners(), 1 ) call mesh%final() FCTEST_CHECK_EQUAL( grid%owners(), 1 ) call grid%final() #else #warning test test_nodescolumns disabled #endif END_TEST ! ----------------------------------------------------------------------------- TEST( test_node_collectives ) #if 1 use fckit_mpi_module type(atlas_StructuredGrid) :: grid type(atlas_MeshGenerator) :: meshgenerator type(atlas_Mesh) :: mesh type(atlas_functionspace_NodeColumns) :: fs2d type(atlas_Field) :: field, global, scal type(atlas_Metadata) :: metadata type(fckit_mpi_comm) :: mpi real(c_float), pointer :: scalvalues(:) real(c_float), pointer :: values(:,:) real(c_float), pointer :: values3d(:,:,:) real(c_float) :: minimum, maximum, sum, oisum, mean, stddev real(c_float), allocatable :: minimumv(:), maximumv(:), meanv(:), stddevv(:) integer :: halo_size, levels integer(ATLAS_KIND_GIDX) :: glb_idx integer(ATLAS_KIND_GIDX), allocatable :: glb_idxv (:) integer(c_int) :: test_broadcast mpi = fckit_mpi_comm() halo_size = 1 levels = 10 grid = atlas_StructuredGrid("N24") meshgenerator = atlas_MeshGenerator() mesh = meshgenerator%generate(grid) call meshgenerator%final() fs2d = atlas_functionspace_NodeColumns(mesh,halo_size) field = fs2d%create_field(kind=atlas_real(c_float),variables=2) global = fs2d%create_field(field,global=.True.) scal = fs2d%create_field(kind=atlas_real(c_float)) write(msg,*) "field: rank",field%rank(), " shape [",field%shape(), "] size ", field%size(); call atlas_log%info(msg) write(msg,*) "global: rank",global%rank()," shape [",global%shape(),"] size ", global%size(); call atlas_log%info(msg) call fs2d%gather(field,global) call fs2d%halo_exchange(field) metadata = global%metadata() if( mpi%rank() == 0 ) then call metadata%set("test_broadcast",123) endif call fs2d%scatter(global,field) metadata = field%metadata() call metadata%get("test_broadcast",test_broadcast) FCTEST_CHECK_EQUAL( test_broadcast, 123 ) call field%data(values) call scal%data(scalvalues) values = 2. scalvalues = 2. call atlas_log%info(fs2d%checksum(field)) values = mpi%rank() scalvalues = mpi%rank() call fs2d%minimum(scal,minimum) call fs2d%maximum(scal,maximum) write(msg,*) "min = ",minimum, " max = ",maximum; call atlas_log%info(msg) call fs2d%minimum(field,minimumv) call fs2d%maximum(field,maximumv) write(msg,*) "minv = ",minimumv, " maxv = ",maximumv; call atlas_log%info(msg) call fs2d%minimum_and_location(scal,minimum,glb_idx) write(msg,*) "min = ",minimum, " gidx = ",glb_idx; call atlas_log%info(msg) call fs2d%maximum_and_location(scal,maximum,glb_idx) write(msg,*) "max = ",maximum, " gidx = ",glb_idx; call atlas_log%info(msg) call fs2d%minimum_and_location(field,minimumv,glb_idxv) write(msg,*) "minv = ",minimumv, " gidxv = ",glb_idxv; call atlas_log%info(msg) call fs2d%maximum_and_location(field,maximumv,glb_idxv) write(msg,*) "minv = ",maximum, " gidxv = ",glb_idxv; call atlas_log%info(msg) call fs2d%sum(scal,sum) call fs2d%order_independent_sum(scal,oisum) write(msg,*) "sum = ",sum, " oisum = ",oisum; call atlas_log%info(msg) call fs2d%mean(scal,mean) call fs2d%mean(field,meanv) write(msg,*) "mean = ",mean, "meanv = ", meanv; call atlas_log%info(msg) call fs2d%mean_and_standard_deviation(scal,mean,stddev) call fs2d%mean_and_standard_deviation(field,meanv,stddevv) write(msg,*) "mean = ",mean, "meanv = ", meanv; call atlas_log%info(msg) write(msg,*) "stddev = ",stddev, "stddevv = ", stddevv; call atlas_log%info(msg) call scal%final() call field%final() call global%final() field = fs2d%create_field(kind=atlas_real(c_float),levels=levels,variables=2*3) global = fs2d%create_field(field,global=.True.,owner=mpi%size()-1) write(msg,*) "field: rank",field%rank(), " shape [",field%shape(), "] size ", field%size(); call atlas_log%info(msg) write(msg,*) "global: rank",global%rank()," shape [",global%shape(),"] size ", global%size(); call atlas_log%info(msg) call fs2d%gather(field,global) call fs2d%halo_exchange(field) call fs2d%scatter(global,field) call field%data(values3d) values3d = 2. call atlas_log%info(fs2d%checksum(field)) call field%final() call global%final() call fs2d%final() call mesh%final() call grid%final() #endif END_TEST ! ----------------------------------------------------------------------------- TEST( test_cell_collectives ) #if 1 use fckit_mpi_module type(atlas_StructuredGrid) :: grid type(atlas_MeshGenerator) :: meshgenerator type(atlas_Mesh) :: mesh type(atlas_functionspace_CellColumns) :: fs2d type(atlas_Field) :: field, global, scal type(atlas_Metadata) :: metadata type(fckit_mpi_comm) :: mpi real(c_float), pointer :: scalvalues(:) real(c_float), pointer :: values(:,:) real(c_float), pointer :: values3d(:,:,:) !real(c_float) :: minimum, maximum, sum, oisum, mean, stddev !real(c_float), allocatable :: minimumv(:), maximumv(:), meanv(:), stddevv(:) integer :: halo_size, levels integer(ATLAS_KIND_GIDX) :: glb_idx integer(ATLAS_KIND_GIDX), allocatable :: glb_idxv (:) integer(c_int) :: test_broadcast mpi = fckit_mpi_comm() halo_size = 1 levels = 10 grid = atlas_StructuredGrid("N24") meshgenerator = atlas_MeshGenerator() mesh = meshgenerator%generate(grid) call meshgenerator%final() fs2d = atlas_functionspace_CellColumns(mesh,halo_size) field = fs2d%create_field(kind=atlas_real(c_float),variables=2) global = fs2d%create_field(field,global=.True.) scal = fs2d%create_field(kind=atlas_real(c_float)) write(msg,*) "field: rank",field%rank(), " shape [",field%shape(), "] size ", field%size(); call atlas_log%info(msg) write(msg,*) "global: rank",global%rank()," shape [",global%shape(),"] size ", global%size(); call atlas_log%info(msg) call fs2d%gather(field,global) call fs2d%halo_exchange(field) metadata = global%metadata() if( mpi%rank() == 0 ) then call metadata%set("test_broadcast",123) endif call fs2d%scatter(global,field) metadata = field%metadata() call metadata%get("test_broadcast",test_broadcast) FCTEST_CHECK_EQUAL( test_broadcast, 123 ) call field%data(values) call scal%data(scalvalues) values = 2. scalvalues = 2. call atlas_log%info(fs2d%checksum(field)) values = mpi%rank() scalvalues = mpi%rank() call scal%final() call field%final() call global%final() field = fs2d%create_field(kind=atlas_real(c_float),levels=levels,variables=2*3) global = fs2d%create_field(field,global=.True.,owner=mpi%size()-1) write(msg,*) "field: rank",field%rank(), " shape [",field%shape(), "] size ", field%size(); call atlas_log%info(msg) write(msg,*) "global: rank",global%rank()," shape [",global%shape(),"] size ", global%size(); call atlas_log%info(msg) call fs2d%gather(field,global) call fs2d%halo_exchange(field) call fs2d%scatter(global,field) call field%data(values3d) values3d = 2. call atlas_log%info(fs2d%checksum(field)) call field%final() call global%final() call fs2d%final() call mesh%final() call grid%final() #endif END_TEST TEST( test_cells ) #if 1 type(atlas_StructuredGrid) :: grid type(atlas_MeshGenerator) :: meshgenerator type(atlas_Mesh) :: mesh type(atlas_functionspace_CellColumns) :: fs type(atlas_Field) :: field, template type(atlas_mesh_Nodes) :: cells integer :: halo_size, nb_cells halo_size = 1 grid = atlas_StructuredGrid("N24") meshgenerator = atlas_MeshGenerator() mesh = meshgenerator%generate(grid) call meshgenerator%final() fs = atlas_functionspace_CellColumns(mesh,halo_size) cells = fs%cells() nb_cells = fs%nb_cells() write(msg,*) "nb_cells = ",nb_cells; call atlas_log%info(msg) field = fs%create_field(atlas_real(c_float)) FCTEST_CHECK_EQUAL( field%rank() , 1 ) FCTEST_CHECK_EQUAL( field%name() , "" ) FCTEST_CHECK_EQUAL( field%kind() , atlas_real(c_float) ) call field%final() field = fs%create_field(name="field",kind=atlas_real(c_float)) FCTEST_CHECK_EQUAL( field%rank() , 1 ) FCTEST_CHECK_EQUAL( field%name() , "field" ) FCTEST_CHECK_EQUAL( field%kind() , atlas_real(c_float) ) call field%final() field = fs%create_field(atlas_real(c_float),variables=2) FCTEST_CHECK_EQUAL( field%rank() , 2 ) FCTEST_CHECK_EQUAL( field%name() , "" ) call field%final() field = fs%create_field(name="field",kind=atlas_integer(c_int),variables=2*2) FCTEST_CHECK_EQUAL( field%rank() , 2 ) FCTEST_CHECK_EQUAL( field%name() , "field" ) template = field field = fs%create_field(template) FCTEST_CHECK_EQUAL( field%rank() , 2 ) FCTEST_CHECK_EQUAL( field%name() , template%name() ) call field%final() field = fs%create_field(template,name="field") FCTEST_CHECK_EQUAL( field%rank() , 2 ) FCTEST_CHECK_EQUAL( field%name() , "field" ) call field%final() call template%final() field = fs%create_field(atlas_real(c_float),global=.True.) FCTEST_CHECK_EQUAL( field%rank() , 1 ) FCTEST_CHECK_EQUAL( field%name() , "" ) FCTEST_CHECK_EQUAL( field%kind() , atlas_real(c_float) ) call field%final() field = fs%create_field(name="field",kind=atlas_real(c_float),global=.True.) FCTEST_CHECK_EQUAL( field%rank() , 1 ) FCTEST_CHECK_EQUAL( field%name() , "field" ) FCTEST_CHECK_EQUAL( field%kind() , atlas_real(c_float) ) call field%final() field = fs%create_field(atlas_real(c_float),variables=2,global=.True.) FCTEST_CHECK_EQUAL( field%rank() , 2 ) FCTEST_CHECK_EQUAL( field%name() , "" ) call field%final() field = fs%create_field(name="field",kind=atlas_integer(c_int),variables=2*2,global=.True.) FCTEST_CHECK_EQUAL( field%rank() , 2 ) FCTEST_CHECK_EQUAL( field%name() , "field" ) template = field field = fs%create_field(template,global=.True.) FCTEST_CHECK_EQUAL( field%rank() , 2 ) FCTEST_CHECK_EQUAL( field%name() , template%name() ) call field%final() field = fs%create_field(template,name="field",global=.True.) FCTEST_CHECK_EQUAL( field%rank() , 2 ) FCTEST_CHECK_EQUAL( field%name() , "field" ) call field%final() call template%final() call fs%final() call mesh%final() call grid%final() #else #warning test test_cells disabled #endif END_TEST ! ----------------------------------------------------------------------------- TEST( test_cellscolumns ) #if 1 type(atlas_StructuredGrid) :: grid type(atlas_MeshGenerator) :: meshgenerator type(atlas_Mesh) :: mesh type(atlas_functionspace_CellColumns) :: fs type(atlas_Field) :: field, template integer :: halo_size, levels halo_size = 1 levels = 10 grid = atlas_StructuredGrid("N24") meshgenerator = atlas_MeshGenerator() mesh = meshgenerator%generate(grid) call meshgenerator%final() fs = atlas_functionspace_CellColumns(mesh,halo_size) !levels = fs%nb_levels() write(msg,*) "nb_levels = ",levels; call atlas_log%info(msg) field = fs%create_field(atlas_real(c_float),levels=levels) FCTEST_CHECK_EQUAL( field%rank() , 2 ) FCTEST_CHECK_EQUAL( field%name() , "" ) FCTEST_CHECK_EQUAL( field%kind() , atlas_real(c_float) ) call field%final() field = fs%create_field(name="field",kind=atlas_real(c_float),levels=levels) FCTEST_CHECK_EQUAL( field%rank() , 2 ) FCTEST_CHECK_EQUAL( field%name() , "field" ) FCTEST_CHECK_EQUAL( field%kind() , atlas_real(c_float) ) call field%final() field = fs%create_field(atlas_real(c_float),levels=levels,variables=2) FCTEST_CHECK_EQUAL( field%rank() , 3 ) FCTEST_CHECK_EQUAL( field%name() , "" ) call field%final() field = fs%create_field(name="field",kind=atlas_integer(c_int),levels=levels,variables=2*2) FCTEST_CHECK_EQUAL( field%rank() , 3 ) FCTEST_CHECK_EQUAL( field%name() , "field" ) template = field field = fs%create_field(template) FCTEST_CHECK_EQUAL( field%rank() , 3 ) FCTEST_CHECK_EQUAL( field%name() , template%name() ) call field%final() field = fs%create_field(template,name="field") FCTEST_CHECK_EQUAL( field%rank() , 3 ) FCTEST_CHECK_EQUAL( field%name() , "field" ) call field%final() call template%final() field = fs%create_field(atlas_real(c_float),levels=levels,global=.True.) FCTEST_CHECK_EQUAL( field%rank() , 2 ) FCTEST_CHECK_EQUAL( field%name() , "" ) FCTEST_CHECK_EQUAL( field%kind() , atlas_real(c_float) ) call field%final() field = fs%create_field(name="field",kind=atlas_real(c_float),levels=levels,global=.True.) FCTEST_CHECK_EQUAL( field%rank() , 2 ) FCTEST_CHECK_EQUAL( field%name() , "field" ) FCTEST_CHECK_EQUAL( field%kind() , atlas_real(c_float) ) call field%final() field = fs%create_field(atlas_real(c_float),levels=levels,variables=2,global=.True.) FCTEST_CHECK_EQUAL( field%rank() , 3 ) FCTEST_CHECK_EQUAL( field%name() , "" ) call field%final() field = fs%create_field(name="field",kind=atlas_integer(c_int),levels=levels,variables=2*2,global=.True.) FCTEST_CHECK_EQUAL( field%rank() , 3 ) FCTEST_CHECK_EQUAL( field%name() , "field" ) template = field field = fs%create_field(template,global=.True.) FCTEST_CHECK_EQUAL( field%rank() , 3 ) FCTEST_CHECK_EQUAL( field%name() , template%name() ) call field%final() field = fs%create_field(template,name="field",global=.True.) FCTEST_CHECK_EQUAL( field%rank() , 3 ) FCTEST_CHECK_EQUAL( field%name() , "field" ) call field%final() call template%final() fs = atlas_functionspace_CellColumns(mesh,levels=5) field = fs%create_field(atlas_real(c_float)) FCTEST_CHECK_EQUAL( field%rank() , 2 ) FCTEST_CHECK_EQUAL( field%name() , "" ) FCTEST_CHECK_EQUAL( field%kind() , atlas_real(c_float) ) call field%final() FCTEST_CHECK_EQUAL( fs%owners(), 1 ) call fs%final() FCTEST_CHECK_EQUAL( mesh%owners(), 1 ) call mesh%final() FCTEST_CHECK_EQUAL( grid%owners(), 1 ) call grid%final() #else #warning test test_cellscolumns disabled #endif END_TEST ! ----------------------------------------------------------------------------- ! ----------------------------------------------------------------------------- TEST( test_edges ) #if 1 type(atlas_StructuredGrid) :: grid type(atlas_MeshGenerator) :: meshgenerator type(atlas_Mesh) :: mesh type(atlas_functionspace_EdgeColumns) :: fs type(atlas_Field) :: field, template type(atlas_mesh_Edges) :: edges integer :: halo_size, nb_edges type( atlas_trace ) :: trace type( atlas_trace ) :: trace_a type( atlas_trace ) :: trace_b type( atlas_Output ) :: gmsh trace = atlas_Trace("fctest_functionspace.F90",__LINE__,"test_edges") halo_size = 0 grid = atlas_StructuredGrid("N24") meshgenerator = atlas_MeshGenerator() mesh = meshgenerator%generate(grid) gmsh = atlas_Output_gmsh("test_edges.msh") call gmsh%write(mesh) call gmsh%final() FCTEST_CHECK_EQUAL( mesh%owners(), 1 ) edges = mesh%edges() FCTEST_CHECK_EQUAL( edges%owners(), 3 ) ! Mesh holds 2 references (facets == edges) trace_a = atlas_Trace("fctest_functionspace.F90",__LINE__,"EdgeColumns, no-levels") fs = atlas_functionspace_EdgeColumns(mesh) call trace_a%final() FCTEST_CHECK_EQUAL( mesh%owners(), 2 ) FCTEST_CHECK_EQUAL( edges%owners(), 3 ) edges = fs%edges() FCTEST_CHECK_EQUAL( edges%owners(), 3 ) nb_edges = fs%nb_edges() write(msg,*) "nb_edges = ",nb_edges; call atlas_log%info(msg) field = fs%create_field(atlas_real(c_float)) FCTEST_CHECK_EQUAL( field%rank() , 1 ) FCTEST_CHECK_EQUAL( field%name() , "" ) FCTEST_CHECK_EQUAL( field%kind() , atlas_real(c_float) ) call field%final() field = fs%create_field(name="field",kind=atlas_real(c_float)) FCTEST_CHECK_EQUAL( field%rank() , 1 ) FCTEST_CHECK_EQUAL( field%name() , "field" ) FCTEST_CHECK_EQUAL( field%kind() , atlas_real(c_float) ) call field%final() field = fs%create_field(atlas_real(c_float),variables=2) FCTEST_CHECK_EQUAL( field%rank() , 2 ) FCTEST_CHECK_EQUAL( field%name() , "" ) call field%final() field = fs%create_field(name="field",kind=atlas_integer(c_int),variables=2*2) FCTEST_CHECK_EQUAL( field%rank() , 2 ) FCTEST_CHECK_EQUAL( field%name() , "field" ) template = field field = fs%create_field(name="field",template=template) FCTEST_CHECK_EQUAL( field%rank() , 2 ) FCTEST_CHECK_EQUAL( field%name() , "field" ) call field%final() call template%final() field = fs%create_field(atlas_real(c_float),levels=10,variables=2) FCTEST_CHECK_EQUAL( field%rank() , 3 ) FCTEST_CHECK_EQUAL( field%name() , "" ) call field%final() field = fs%create_field(name="field",kind=atlas_integer(c_int),variables=2*2,levels=10) FCTEST_CHECK_EQUAL( field%rank() , 3 ) FCTEST_CHECK_EQUAL( field%name() , "field" ) template = field field = fs%create_field(name="field",template=template) FCTEST_CHECK_EQUAL( field%rank() , 3 ) FCTEST_CHECK_EQUAL( field%name() , "field" ) call field%final() call template%final() field = fs%create_field(kind=atlas_real(c_float),global=.true.) FCTEST_CHECK_EQUAL( field%rank() , 1 ) FCTEST_CHECK_EQUAL( field%name() , "" ) FCTEST_CHECK_EQUAL( field%kind() , atlas_real(c_float) ) call field%final() field = fs%create_field(name="field", kind=atlas_real(c_float),global=.true.) FCTEST_CHECK_EQUAL( field%rank() , 1 ) FCTEST_CHECK_EQUAL( field%name() , "field" ) FCTEST_CHECK_EQUAL( field%kind() , atlas_real(c_float) ) call field%final() field = fs%create_field(kind=atlas_real(c_float),variables=2,global=.true.) FCTEST_CHECK_EQUAL( field%rank() , 2 ) FCTEST_CHECK_EQUAL( field%name() , "" ) call field%final() field = fs%create_field(name="field",kind=atlas_integer(c_int),variables=2*2,global=.true.) FCTEST_CHECK_EQUAL( field%rank() , 2 ) FCTEST_CHECK_EQUAL( field%name() , "field" ) template = field field = fs%create_field(template,global=.true.) FCTEST_CHECK_EQUAL( field%rank() , 2 ) FCTEST_CHECK_EQUAL( field%name() , "" ) call field%final() field = fs%create_field(template,name="field",global=.true.) FCTEST_CHECK_EQUAL( field%rank() , 2 ) FCTEST_CHECK_EQUAL( field%name() , "field" ) call field%final() call template%final() trace_b = atlas_Trace("fctest_functionspace.F90",__LINE__,"EdgeColumns, 5 levels") fs = atlas_functionspace_EdgeColumns(mesh,levels=5) call trace_b%final() field = fs%create_field(atlas_real(c_float)) FCTEST_CHECK_EQUAL( field%rank() , 2 ) FCTEST_CHECK_EQUAL( field%name() , "" ) FCTEST_CHECK_EQUAL( field%kind() , atlas_real(c_float) ) call field%final() call fs%final() call edges%final() call mesh%final() call grid%final() call trace%final() #else #warning test test_edges disabled #endif END_TEST TEST( test_structuredcolumns ) #if 1 type(atlas_StructuredGrid) :: grid type(atlas_functionspace_StructuredColumns) :: fs type(atlas_functionspace) :: fs_base integer(ATLAS_KIND_IDX) :: i, j character(len=10) str type(atlas_Field) :: field type(atlas_Field) :: field_xy type(atlas_Field) :: field_global_index type(atlas_Field) :: field_index_j real(8), pointer :: xy(:,:), x(:) integer(ATLAS_KIND_GIDX), pointer :: global_index(:) integer(ATLAS_KIND_IDX), pointer :: index_j(:) integer, parameter :: XX=1 grid = atlas_StructuredGrid("O8") fs = atlas_functionspace_StructuredColumns(grid,halo=2) write(0,*) __LINE__ field = fs%create_field(name="field",kind=atlas_real(8)) FCTEST_CHECK_EQUAL( field%owners(), 1 ) field_xy = fs%xy() FCTEST_CHECK_EQUAL( field_xy%owners(), 2 ) call field%data(x) call field_xy%data(xy) field_global_index = fs%global_index() call field_global_index%data(global_index) field_index_j = fs%index_j() call field_index_j%data(index_j) do j=fs%j_begin_halo(),fs%j_end_halo() write(str,'(I4,A)') j, ' : ' call atlas_log%info(str,newl=.false.) do i=fs%i_begin_halo(j),fs%i_end_halo(j) write(str,'(I4)') global_index( fs%index(i,j) ) call atlas_log%info(str,newl=.false.) x(fs%index(i,j)) = xy(XX,fs%index(i,j)) enddo call atlas_log%info("",newl=.true.) enddo call fs%halo_exchange(field) FCTEST_CHECK_EQUAL( field_xy%owners(), 2 ) fs = atlas_functionspace_StructuredColumns(grid,levels=5) field = fs%create_field(atlas_real(c_float)) FCTEST_CHECK_EQUAL( field%rank() , 2 ) FCTEST_CHECK_EQUAL( field%name() , "" ) FCTEST_CHECK_EQUAL( field%kind() , atlas_real(c_float) ) write(0,*) "before: name = ", fs%name() write(0,*) "before: owners = ", fs%owners() fs_base = field%functionspace() write(0,*) "after: name = " , fs_base%name() write(0,*) "after: owners = " , fs_base%owners() FCTEST_CHECK_EQUAL( field%owners(), 1 ) call field%final() FCTEST_CHECK_EQUAL( field_xy%owners(), 1 ) call field_xy%final() call field_global_index%final() call fs%final() call fs_base%final() call grid%final() #else #warning test test_structuredcolumns disabled #endif END_TEST TEST( test_pointcloud ) #if 1 implicit none type(atlas_StructuredGrid) :: grid type(atlas_functionspace_PointCloud) :: fs type(atlas_functionspace) :: fs_base type(atlas_trace) :: trace character(len=10) str type(atlas_Field) :: field, field2 type(atlas_Field) :: field_lonlat real(8), pointer :: lonlat(:,:), x(:) trace = atlas_Trace("fctest_functionspace.F90",__LINE__,"test_pointcloud") grid = atlas_Grid("O8") fs = atlas_functionspace_PointCloud(grid) field = fs%create_field(name="field",kind=atlas_real(8)) FCTEST_CHECK_EQUAL( field%owners(), 1 ) FCTEST_CHECK_EQUAL( field%levels(), 0 ) field_lonlat = fs%lonlat() FCTEST_CHECK_EQUAL( field_lonlat%owners(), 2 ) call field%data(x) call field_lonlat%data(lonlat) FCTEST_CHECK_EQUAL( field_lonlat%owners(), 2 ) fs = atlas_functionspace_PointCloud(grid) field = fs%create_field(atlas_real(c_float), levels=5) FCTEST_CHECK_EQUAL( field%rank() , 2 ) FCTEST_CHECK_EQUAL( field%name() , "" ) FCTEST_CHECK_EQUAL( field%kind() , atlas_real(c_float) ) write(0,*) "before: name = ", fs%name() write(0,*) "before: owners = ", fs%owners() fs_base = field%functionspace() write(0,*) "after: name = " , fs%name() write(0,*) "after: owners = " , fs%owners() field2 = fs%create_field(name="field2",kind=atlas_real(8),levels=3,variables=2) FCTEST_CHECK_EQUAL( field%shape(), ([5,int(fs%size())]) ) FCTEST_CHECK_EQUAL( field2%shape(), ([2,3,int(fs%size())]) ) #ifndef _CRAYFTN FCTEST_CHECK_EQUAL( field%owners(), 1 ) #endif call field%final() #ifndef _CRAYFTN FCTEST_CHECK_EQUAL( field_lonlat%owners(), 1 ) #endif call field_lonlat%final() call fs%final() call fs_base%final() call grid%final() #else #warning test test_pointcloud disabled #endif END_TEST TEST( test_pointcloud_partition_remote ) #if 1 use fckit_module implicit none type(atlas_functionspace_PointCloud) :: fs type(atlas_Field) :: fld_points type(atlas_Field) :: fld_ghost type(atlas_Field) :: fld_partition type(atlas_Field) :: fld_remote_index type(atlas_Field) :: fld_values type(atlas_Field) :: fld_values_save type(atlas_FieldSet) :: fset type(atlas_functionspace) :: fs_base type(atlas_trace) :: trace integer(c_int) :: i, k integer(c_int) :: rank real(c_double) :: space real(c_double), allocatable :: point_values(:,:) integer(c_int), dimension(4) :: ghost_values integer(c_int), dimension(4) :: partition_index integer(ATLAS_KIND_IDX), dimension(4) :: remote_index real(c_double), pointer :: field_values(:,:) real(c_double), pointer :: field_values_save(:,:) real(c_double), pointer :: field_values_new(:,:) real(c_double), parameter :: tol = 1.e-12_dp trace = atlas_Trace("fctest_functionspace.F90",__LINE__,"test_pointcloud_partition_remote") rank = fckit_mpi%rank() ! the idea here is that we have a latitudinal circle around the equator ! that is partitioned equally ! ! test here is for 4 PEs (but could be more!) ! PE 0 PE 1 PE 2 PE 3 ! ! Owned values ! 0 45 90 135 180 225 270 315 ! ! Each PE has owned data followed by ghost data ! Ghost data is 1 before followed by 1 point after owned data ie. ! ! Correct halo_exchange for PE 0 ! 0 45 | 315 90 space = 360.0_c_double /(2.0_c_double * fckit_mpi%size()) fset = atlas_FieldSet() allocate(point_values(2, 4)) point_values = reshape((/space * (2 * rank), 0.0_c_double, & space * (2 * rank + 1), 0.0_c_double, & space * (2 * rank - 1), 0.0_c_double, & space * (2 * rank + 2), 0.0_c_double/), shape(point_values)) if (point_values(1, 3) < 0.0) point_values(1, 3) = 360.0 + point_values(1, 3) if (point_values(1, 4) > 360.0 - tol) point_values(1, 4) = 0.0d0 fld_points = atlas_Field("lonlat", point_values(:, :)) call fset%add(fld_points) ghost_values = (/ 0, 0, 1, 1 /) fld_ghost = atlas_Field("ghost", ghost_values(:)) call fset%add(fld_ghost) partition_index = (/ rank, rank, rank - 1, rank + 1 /) if (partition_index(3) < 0) partition_index(3) = fckit_mpi%size() - 1 if (partition_index(4) == fckit_mpi%size()) partition_index(4) = 0 fld_partition = atlas_Field("partition", partition_index(:)) call fset%add(fld_partition) remote_index = (/ 1, 2, 2, 1 /) fld_remote_index = atlas_Field("remote_index", remote_index(:)) call fset%add(fld_remote_index) fs = atlas_functionspace_PointCloud(fset) fld_values = fs%create_field(name="values", kind=atlas_real(c_double), levels=1) call fld_values%data(field_values) fld_values_save = fs%create_field(name="values_save", kind=atlas_real(c_double), levels=1) call fld_values_save%data(field_values_save) do i = 1, 4 do k = 1, 1 field_values(k, i) = point_values(1, i) field_values_save(k, i) = point_values(1, i) end do end do call fs%halo_exchange(fld_values) call fld_values%data(field_values_new) do i = 1, 4 do k = 1, 1 FCTEST_CHECK_CLOSE(field_values_new(k, i), field_values_save(k, i), tol) end do end do call fs%adjoint_halo_exchange(fld_values) do i = 1, 4 do k = 1, 1 if (i < 3) then FCTEST_CHECK_CLOSE(field_values_new(k, i), 2.0 * field_values_save(k, i), tol) else FCTEST_CHECK_CLOSE(field_values_new(k, i), 0.0_dp, tol) end if end do end do #else #warning test_pointcloud_partition_remote disabled #endif END_TEST ! ----------------------------------------------------------------------------- END_TESTSUITE atlas-0.46.0/src/tests/functionspace/test_polygons.cc0000664000175000017500000001744515160212070023110 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "atlas/array/ArrayView.h" #include "atlas/array/MakeView.h" #include "atlas/field/Field.h" #include "atlas/functionspace/NodeColumns.h" #include "atlas/functionspace/StructuredColumns.h" #include "atlas/grid/Partitioner.h" #include "atlas/grid/StructuredGrid.h" #include "atlas/meshgenerator.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/util/PolygonLocator.h" #include "atlas/util/PolygonXY.h" #include "tests/AtlasTestEnvironment.h" using namespace atlas::functionspace; using namespace atlas::util; namespace atlas { namespace test { namespace { std::string grid_name() { static std::string _gridname = eckit::Resource("--grid", "O32"); return _gridname; } std::string functionspace_name() { static std::string _name = eckit::Resource("--functionspace", "StructuredColumns"); return _name; } std::string configuration() { std::stringstream out; out << "mpi::size()=" << mpi::size() << ",grid_name()=" << grid_name() << ",functionspace_name()=" << functionspace_name(); return out.str(); }; FunctionSpace structured_columns() { static functionspace::StructuredColumns _fs = []() { Grid grid(grid_name()); return functionspace::StructuredColumns{grid}; }(); return _fs; } FunctionSpace node_columns() { static functionspace::NodeColumns _fs = []() { Grid grid(grid_name()); Mesh mesh = StructuredMeshGenerator().generate(grid); return functionspace::NodeColumns{mesh}; }(); return _fs; } FunctionSpace functionspace() { static FunctionSpace _fs = functionspace_name() == "NodeColumns" ? node_columns() : structured_columns(); return _fs; } std::vector& points() { static std::vector _points{ {45., 75.}, {90., 75.}, {135., 75.}, {180., 75.}, {225., 75.}, {270., 75.}, {315., 75.}, {45., 15.}, {90., 15.}, {135., 15.}, {180., 15.}, {225., 15.}, {270., 15.}, {315., 15.}, {45., -75.}, {90., -75.}, {135., -75.}, {180., -75.}, {225., -75.}, {270., -75.}, {315., -75.}, }; return _points; } void check_part(const std::vector& vec) { Log::debug() << "part = " << vec << std::endl; std::vector expected; if (mpi::size() == 1 && grid_name() == "O32") { expected = std::vector{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}; } if (mpi::size() == 4 && grid_name() == "O32" && functionspace_name() == "StructuredColumns") { expected = std::vector{0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3}; } if (mpi::size() == 4 && grid_name() == "O32" && functionspace_name() == "NodeColumns") { expected = std::vector{0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3}; } if (expected.size()) { EXPECT(vec == expected); } else { Log::warning() << "Check for part not implemented for configuration " << configuration() << std::endl; } } void check_sizes(const std::vector& vec) { Log::debug() << "sizes = " << vec << std::endl; std::vector expected; if (mpi::size() == 1 && grid_name() == "O32" && functionspace_name() == "StructuredColumns") { expected = std::vector{5}; } if (mpi::size() == 4 && grid_name() == "O32" && functionspace_name() == "StructuredColumns") { expected = std::vector{7, 43, 43, 7}; } if (mpi::size() == 1 && grid_name() == "O32" && functionspace_name() == "NodeColumns") { expected = std::vector{167}; } if (mpi::size() == 4 && grid_name() == "O32" && functionspace_name() == "NodeColumns") { expected = std::vector{169, 147, 149, 165}; } if (expected.size()) { EXPECT(vec == expected); } else { Log::warning() << "Check for sizes not implemented for configuration " << configuration() << std::endl; } } void check_simplified_sizes(const std::vector& vec) { Log::debug() << "simplified_sizes = " << vec << std::endl; std::vector expected; if (mpi::size() == 1 && grid_name() == "O32" && functionspace_name() == "StructuredColumns") { expected = std::vector{5}; } if (mpi::size() == 4 && grid_name() == "O32" && functionspace_name() == "StructuredColumns") { expected = std::vector{7, 43, 43, 7}; } if (mpi::size() == 1 && grid_name() == "O32" && functionspace_name() == "NodeColumns") { expected = std::vector{5}; } if (mpi::size() == 4 && grid_name() == "O32" && functionspace_name() == "NodeColumns") { expected = std::vector{8, 5, 8, 7}; } if (expected.size()) { EXPECT(vec == expected); } else { Log::warning() << "Check for simplified_sizes not implemented for configuration " << configuration() << std::endl; } } } // namespace //----------------------------------------------------------------------------- CASE("info") { functionspace().polygon().outputPythonScript("polygon.py"); } CASE("test_polygons") { auto fs = functionspace(); ATLAS_TRACE("computations after setup"); auto polygons = ListPolygonXY(fs.polygons()); std::vector sizes(mpi::size()); std::vector simplified_sizes(mpi::size()); for (idx_t i = 0; i < mpi::size(); ++i) { sizes[i] = fs.polygons()[i].size(); simplified_sizes[i] = polygons[i].size(); } // Test iterator: for (auto& polygon : fs.polygons()) { Log::info() << "size of polygon = " << polygon.size() << std::endl; } for (auto& polygon : polygons) { Log::info() << "size of PolygonXY = " << polygon.size() << std::endl; } std::vector part(points().size()); for (size_t n = 0; n < points().size(); ++n) { Log::debug() << n << " " << points()[n]; // A brute force approach. // Use PolygonLocator class for optimized result for (idx_t p = 0; p < polygons.size(); ++p) { if (polygons[p].contains(points()[n])) { Log::debug() << " : " << p; part[n] = p; } } Log::debug() << std::endl; } check_part(part); check_sizes(sizes); check_simplified_sizes(simplified_sizes); PolygonLocator find_partition(polygons); for (size_t n = 0; n < points().size(); ++n) { EXPECT_EQ(find_partition(points()[n]), part[n]); } Log::info() << std::endl; } CASE("test_polygon_locator_from_const_ref_polygons") { auto polygons = ListPolygonXY{functionspace().polygons()}; PolygonLocator find_partition(polygons); EXPECT_EQ(find_partition(PointLonLat{0., 90.}), 0); EXPECT_EQ(find_partition(PointLonLat{0., -90.}), mpi::size() - 1); } CASE("test_polygon_locator_from_move") { PolygonLocator find_partition(ListPolygonXY{functionspace().polygons()}); EXPECT_EQ(find_partition(PointLonLat{0., 90.}), 0); EXPECT_EQ(find_partition(PointLonLat{0., -90.}), mpi::size() - 1); } CASE("test_polygon_locator_from_shared") { auto polygons = std::make_shared(functionspace().polygons()); PolygonLocator find_partition(polygons); EXPECT_EQ(find_partition(PointLonLat{0., 90.}), 0); EXPECT_EQ(find_partition(PointLonLat{0., -90.}), mpi::size() - 1); } //----------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/functionspace/test_structuredcolumns_haloexchange.cc0000664000175000017500000000763615160212070027552 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include #include #include #include #include "atlas/array.h" #include "atlas/field.h" #include "atlas/functionspace.h" #include "atlas/grid.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/util/Config.h" #include #include #include #include #include "tests/AtlasTestEnvironment.h" using Grid = atlas::Grid; using Config = atlas::util::Config; namespace atlas { namespace test { atlas::FieldSet getIJ(const atlas::functionspace::StructuredColumns& fs) { atlas::FieldSet ij; auto vi0 = atlas::array::make_view(fs.index_i()); auto vj0 = atlas::array::make_view(fs.index_j()); auto fi = fs.createField(); auto fj = fs.createField(); auto vi1 = atlas::array::make_view(fi); auto vj1 = atlas::array::make_view(fj); for (int i = 0; i < fs.size(); i++) { vi1(i) = vi0(i); vj1(i) = vj0(i); } ij.add(fi); ij.add(fj); return ij; } //----------------------------------------------------------------------------- CASE("Two haloexchanges for StructuredColumns differing only by 'periodic_points'") { Grid grid("L400x200"); grid::Distribution dist(grid, grid::Partitioner("checkerboard")); functionspace::StructuredColumns fs1(grid, dist, Config("halo", 1) | Config("periodic_points", true)); functionspace::StructuredColumns fs2(grid, dist, Config("halo", 1)); if (mpi::size() == 1) { EXPECT_EQ(fs1.sizeOwned(), 80000); EXPECT_EQ(fs2.sizeOwned(), 80000); EXPECT_EQ(fs1.sizeHalo(), 81406); EXPECT_EQ(fs2.sizeHalo(), 81204); } if (mpi::size() == 4) { EXPECT_EQ(fs1.sizeOwned(), 20000); EXPECT_EQ(fs2.sizeOwned(), 20000); EXPECT_EQ(fs1.sizeHalo(), (std::vector{20604, 20604, 20604, 20806}[mpi::rank()])); EXPECT_EQ(fs2.sizeHalo(), (std::vector{20604, 20604, 20604, 20604}[mpi::rank()])); } auto ij1 = getIJ(fs1); auto ij2 = getIJ(fs2); EXPECT_NO_THROW(fs1.haloExchange(ij1)); EXPECT_NO_THROW(fs2.haloExchange(ij2)); } //----------------------------------------------------------------------------- CASE("Two haloexchanges for StructuredColumns differing only by 'distribution'") { Grid grid("L400x201"); grid::Distribution dist1(grid, grid::Partitioner("checkerboard")); grid::Distribution dist2(grid, grid::Partitioner("regular_bands")); functionspace::StructuredColumns fs1(grid, dist1, Config("halo", 1)); functionspace::StructuredColumns fs2(grid, dist2, Config("halo", 1)); if (mpi::size() == 1) { EXPECT_EQ(fs1.sizeOwned(), 80400); EXPECT_EQ(fs2.sizeOwned(), 80400); EXPECT_EQ(fs1.sizeHalo(), 81606); EXPECT_EQ(fs2.sizeHalo(), 81606); } if (mpi::size() == 4) { EXPECT_EQ(fs1.sizeOwned(), 20100); EXPECT_EQ(fs2.sizeOwned(), (std::vector{20400, 20000, 20000, 20000}[mpi::rank()])); EXPECT_EQ(fs1.sizeHalo(), (std::vector{20706, 20706, 20706, 20706}[mpi::rank()])); EXPECT_EQ(fs2.sizeHalo(), (std::vector{21306, 20904, 20904, 20904}[mpi::rank()])); } auto ij1 = getIJ(fs1); auto ij2 = getIJ(fs2); EXPECT_NO_THROW(fs1.haloExchange(ij1)); EXPECT_NO_THROW(fs2.haloExchange(ij2)); } //----------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/functionspace/test_structuredcolumns_biperiodic.cc0000664000175000017500000000656115160212070027231 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include #include #include #include #include "atlas/array.h" #include "atlas/field.h" #include "atlas/functionspace.h" #include "atlas/grid.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/util/Config.h" #include #include #include #include #include "tests/AtlasTestEnvironment.h" using Grid = atlas::Grid; using Config = atlas::util::Config; namespace atlas { namespace test { //----------------------------------------------------------------------------- CASE("biperiodic_latlon") { auto& comm = atlas::mpi::comm(); const int myproc = comm.rank(); using namespace atlas::util; using namespace atlas; using namespace atlas::grid; const int Nx = 80, Ny = 80; const double xmin = +20, xmax = +60, ymin = +20, ymax = +60; std::vector spacings(Ny); for (int i = 0; i < Ny; i++) { spacings[i] = Spacing(Config("type", "linear") | Config("N", Nx) | Config("start", xmin) | Config("end", xmax)); } StructuredGrid::XSpace xspace(spacings); StructuredGrid::YSpace yspace(Config("type", "linear") | Config("N", Ny) | Config("start", ymin) | Config("end", ymax)); Projection proj(Config("type", "lonlat")); atlas::StructuredGrid grid(xspace, yspace, proj, Domain()); atlas::grid::Distribution dist(grid, atlas::util::Config("type", "checkerboard")); atlas::functionspace::StructuredColumns fs(grid, dist, atlas::util::Config("halo", 3) | atlas::util::Config("periodic_x", true) | atlas::util::Config("periodic_y", true)); auto f = atlas::Field("f", atlas::array::DataType::kind(), atlas::array::make_shape(fs.size())); auto v = atlas::array::make_view(f); for (int i = 0; i < f.size(); i++) { v(i) = static_cast(myproc); } fs.haloExchange(f); auto clamp = [](int i, int n) { while (i < 0) { i += n; } while (i >= n) { i -= n; } return i; }; auto gv = atlas::array::make_view(fs.global_index()); auto pv = atlas::array::make_view(fs.partition()); for (int j = fs.j_begin_halo(); j < fs.j_end_halo(); j++) { for (int i = fs.i_begin_halo(j); i < fs.i_end_halo(j); i++) { int k = fs.index(i, j); int jj = clamp(j, grid.ny()); int ii = clamp(i, grid.nx(jj)); int g = grid.index(ii, jj); int p = dist.partition(g); EXPECT_EQ(v(k), p); EXPECT_EQ(p, pv(k)); EXPECT_EQ(gv(k) - 1, g); } } } //----------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/functionspace/test_functionspace.cc0000664000175000017500000007061515160212070024075 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "eckit/types/Types.h" #include "atlas/array/ArrayView.h" #include "atlas/array/MakeView.h" #include "atlas/field/Field.h" #include "atlas/field/FieldSet.h" #include "atlas/functionspace/BlockStructuredColumns.h" #include "atlas/functionspace/CellColumns.h" #include "atlas/functionspace/EdgeColumns.h" #include "atlas/functionspace/NodeColumns.h" #include "atlas/functionspace/PointCloud.h" #include "atlas/functionspace/Spectral.h" #include "atlas/functionspace/StructuredColumns.h" #include "atlas/grid/Grid.h" #include "atlas/grid/StructuredGrid.h" #include "atlas/grid/UnstructuredGrid.h" #include "atlas/mesh/Mesh.h" #include "atlas/mesh/MeshBuilder.h" #include "atlas/mesh/Nodes.h" #include "atlas/meshgenerator.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/trans/Trans.h" #include "tests/AtlasTestEnvironment.h" using namespace eckit; using namespace atlas::functionspace; using namespace atlas::util; namespace atlas { namespace test { //----------------------------------------------------------------------------- CASE("test_functionspace_NodeColumns_no_halo") { Grid grid("O8"); Mesh mesh = StructuredMeshGenerator().generate(grid); functionspace::NodeColumns nodes_fs(mesh); Field field(nodes_fs.createField()); array::ArrayView value = array::make_view(field); array::ArrayView ghost = array::make_view(mesh.nodes().ghost()); const size_t nb_nodes = mesh.nodes().size(); for (size_t j = 0; j < nb_nodes; ++j) { if (ghost(j)) { value(j) = -1; } else { value(j) = 1; } } nodes_fs.haloExchange(field); for (size_t j = 0; j < nb_nodes; ++j) { EXPECT(value(j) == 1); } } CASE("test_functionspace_NodeColumns") { ReducedGaussianGrid grid({4, 8, 8, 4}); StructuredMeshGenerator generator; // generator.options.set("3d",true); Mesh mesh = generator.generate(grid); // grid.reset(); idx_t nb_levels = 10; functionspace::NodeColumns nodes_fs(mesh, option::halo(1) | option::levels(nb_levels)); // NodesColumnFunctionSpace columns_fs("columns",mesh,nb_levels,Halo(1)); // EXPECT( nodes_fs.nb_nodes() == columns_fs.nb_nodes() ); // EXPECT( columns_fs.nb_levels() == 10 ); Field surface_scalar_field = nodes_fs.createField(option::name("scalar") | option::levels(false)); Field surface_vector_field = nodes_fs.createField(option::name("vector") | option::levels(false) | option::variables(2)); Field surface_tensor_field = nodes_fs.createField(option::name("tensor") | option::levels(false) | option::variables(2 * 2)); EXPECT(surface_scalar_field.name() == std::string("scalar")); EXPECT(surface_vector_field.name() == std::string("vector")); EXPECT(surface_tensor_field.name() == std::string("tensor")); EXPECT(surface_scalar_field.size() == nodes_fs.nb_nodes()); EXPECT(surface_vector_field.size() == nodes_fs.nb_nodes() * 2); EXPECT(surface_tensor_field.size() == nodes_fs.nb_nodes() * 2 * 2); EXPECT(surface_scalar_field.rank() == 1); EXPECT(surface_vector_field.rank() == 2); EXPECT(surface_tensor_field.rank() == 2); EXPECT(surface_scalar_field.levels() == 0); EXPECT(surface_vector_field.levels() == 0); EXPECT(surface_tensor_field.levels() == 0); array::ArrayView surface_scalar = array::make_view(surface_scalar_field); array::ArrayView surface_vector = array::make_view(surface_vector_field); array::ArrayView surface_tensor = array::make_view(surface_tensor_field); EXPECT(surface_scalar.shape(0) == nodes_fs.nb_nodes()); EXPECT(surface_vector.shape(0) == nodes_fs.nb_nodes()); EXPECT(surface_tensor.shape(0) == nodes_fs.nb_nodes()); EXPECT(surface_vector.shape(1) == 2); EXPECT(surface_tensor.shape(1) == 2 * 2); Field columns_scalar_field = nodes_fs.createField(option::name("scalar")); Field columns_vector_field = nodes_fs.createField(option::name("vector") | option::variables(2)); Field columns_tensor_field = nodes_fs.createField(option::name("tensor") | option::variables(2 * 2)); EXPECT(columns_scalar_field.name() == std::string("scalar")); EXPECT(columns_vector_field.name() == std::string("vector")); EXPECT(columns_tensor_field.name() == std::string("tensor")); EXPECT(columns_scalar_field.size() == nodes_fs.nb_nodes() * nb_levels); EXPECT(columns_vector_field.size() == nodes_fs.nb_nodes() * nb_levels * 2); EXPECT(columns_tensor_field.size() == nodes_fs.nb_nodes() * nb_levels * 2 * 2); EXPECT(columns_scalar_field.rank() == 2); EXPECT(columns_vector_field.rank() == 3); EXPECT(columns_tensor_field.rank() == 3); EXPECT(columns_scalar_field.levels() == nb_levels); EXPECT(columns_vector_field.levels() == nb_levels); EXPECT(columns_tensor_field.levels() == nb_levels); array::ArrayView columns_scalar = array::make_view(columns_scalar_field); array::ArrayView columns_vector = array::make_view(columns_vector_field); array::ArrayView columns_tensor = array::make_view(columns_tensor_field); EXPECT(columns_scalar.shape(0) == nodes_fs.nb_nodes()); EXPECT(columns_vector.shape(0) == nodes_fs.nb_nodes()); EXPECT(columns_tensor.shape(0) == nodes_fs.nb_nodes()); EXPECT(columns_scalar.shape(1) == nb_levels); EXPECT(columns_vector.shape(1) == nb_levels); EXPECT(columns_tensor.shape(1) == nb_levels); EXPECT(columns_vector.shape(2) == 2); EXPECT(columns_tensor.shape(2) == 2 * 2); Field field = nodes_fs.createField(option::name("partition")); array::ArrayView arr = array::make_view(field); arr.assign(int(mpi::comm().rank())); // field->dump( Log::info() ); nodes_fs.haloExchange(field); // field->dump( Log::info() ); Field field2 = nodes_fs.createField(option::name("partition2") | option::variables(2)); Log::info() << "field2.rank() = " << field2.rank() << std::endl; array::ArrayView arr2 = array::make_view(field2); arr2.assign(int(mpi::comm().rank())); // field2->dump( Log::info() ); nodes_fs.haloExchange(field2); // field2->dump( Log::info() ); Log::info() << nodes_fs.checksum(field) << std::endl; size_t root = mpi::comm().size() - 1; Field glb_field = nodes_fs.createField(option::name("partition") | option::datatype(field.datatype()) | option::levels(field.levels()) | option::variables(field.variables()) | option::global(root)); nodes_fs.gather(field, glb_field); EXPECT(glb_field.rank() == field.rank()); EXPECT(glb_field.levels() == nb_levels); EXPECT(field.levels() == nb_levels); Log::info() << "field = " << field << std::endl; Log::info() << "global_field = " << glb_field << std::endl; Log::info() << "local points = " << nodes_fs.nb_nodes() << std::endl; Log::info() << "grid points = " << grid.size() << std::endl; Log::info() << "glb_field.shape(0) = " << glb_field.shape(0) << std::endl; EXPECT(glb_field.metadata().get("global") == true); EXPECT(glb_field.metadata().get("owner") == int(root)); // glb_field->dump( Log::info() ); if (mpi::comm().rank() == root) { glb_field.metadata().set("test_broadcast", 123); } arr.assign(-1); nodes_fs.scatter(glb_field, field); EXPECT(field.metadata().get("test_broadcast") == 123); nodes_fs.haloExchange(field); // field->dump( Log::info() ); Log::info() << nodes_fs.checksum(field) << std::endl; FieldSet fields; fields.add(field); fields.add(field2); Log::info() << nodes_fs.checksum(fields) << std::endl; Log::info() << "Testing collectives for nodes scalar field" << std::endl; { Field field = surface_scalar_field; const functionspace::NodeColumns fs = nodes_fs; double max; double min; double sum; double mean; double stddev; idx_t N; gidx_t gidx_max; gidx_t gidx_min; auto sfc_arr = array::make_view(field); sfc_arr.assign(mpi::comm().rank() + 1); fs.maximum(surface_scalar_field, max); EXPECT(max == double(mpi::comm().size())); fs.minimum(surface_scalar_field, min); EXPECT(min == 1); fs.maximumAndLocation(field, max, gidx_max); EXPECT(max == double(mpi::comm().size())); Log::info() << "global index for maximum: " << gidx_max << std::endl; fs.minimumAndLocation(field, min, gidx_min); EXPECT(min == 1); Log::info() << "global index for minimum: " << gidx_min << std::endl; fs.orderIndependentSum(field, sum, N); Log::info() << "oisum: " << sum << std::endl; Log::info() << "oiN: " << N << std::endl; fs.sum(field, sum, N); Log::info() << "sum: " << sum << std::endl; Log::info() << "N: " << N << std::endl; fs.mean(field, mean, N); Log::info() << "mean: " << mean << std::endl; Log::info() << "N: " << N << std::endl; fs.meanAndStandardDeviation(field, mean, stddev, N); Log::info() << "mean: " << mean << std::endl; Log::info() << "standard deviation: " << stddev << std::endl; Log::info() << "N: " << N << std::endl; int sumint; fs.orderIndependentSum(field, sumint, N); Log::info() << "sumint: " << sumint << std::endl; fs.sum(field, sumint, N); Log::info() << "sumint: " << sumint << std::endl; } Log::info() << "Testing collectives for nodes vector field" << std::endl; { Field& field = surface_vector_field; const functionspace::NodeColumns fs = nodes_fs; std::vector max; std::vector min; std::vector sum; std::vector mean; std::vector stddev; idx_t N; std::vector gidx_max; std::vector gidx_min; auto vec_arr = array::make_view(field); vec_arr.assign(mpi::comm().rank() + 1); fs.maximum(field, max); std::vector check_max(field.variables(), mpi::comm().size()); EXPECT(max == check_max); fs.minimum(field, min); std::vector check_min(field.variables(), 1); EXPECT(min == check_min); fs.maximumAndLocation(field, max, gidx_max); EXPECT(max == check_max); Log::info() << "global index for maximum: " << gidx_max << std::endl; fs.minimumAndLocation(field, min, gidx_min); EXPECT(min == check_min); Log::info() << "global index for minimum: " << gidx_min << std::endl; fs.orderIndependentSum(field, sum, N); Log::info() << "oisum: " << sum << std::endl; Log::info() << "oiN: " << N << std::endl; fs.mean(field, mean, N); Log::info() << "mean: " << mean << std::endl; Log::info() << "N: " << N << std::endl; fs.meanAndStandardDeviation(field, mean, stddev, N); Log::info() << "mean: " << mean << std::endl; Log::info() << "standard deviation: " << stddev << std::endl; Log::info() << "N: " << N << std::endl; std::vector sumint; fs.orderIndependentSum(field, sumint, N); Log::info() << "sumint: " << sumint << std::endl; fs.sum(field, sumint, N); Log::info() << "sumint: " << sumint << std::endl; } Log::info() << "Testing collectives for columns scalar field" << std::endl; if (1) { Field& field = columns_scalar_field; const functionspace::NodeColumns fs = nodes_fs; double max; double min; double sum; double mean; double stddev; idx_t N; gidx_t gidx_max; gidx_t gidx_min; idx_t level; EXPECT(field.levels() == nb_levels); auto arr = array::make_view(field); arr.assign(mpi::comm().rank() + 1); fs.maximum(field, max); EXPECT(max == double(mpi::comm().size())); fs.minimum(field, min); EXPECT(min == 1); fs.maximumAndLocation(field, max, gidx_max, level); EXPECT(max == double(mpi::comm().size())); Log::info() << "global index for maximum: " << gidx_max << std::endl; Log::info() << "level for maximum: " << level << std::endl; fs.minimumAndLocation(field, min, gidx_min, level); EXPECT(min == 1); Log::info() << "global index for minimum: " << gidx_min << std::endl; Log::info() << "level for minimum: " << level << std::endl; fs.orderIndependentSum(field, sum, N); Log::info() << "order independent sum: " << sum << std::endl; Log::info() << "N: " << N << std::endl; fs.sum(field, sum, N); Log::info() << "sum: " << sum << std::endl; Log::info() << "N: " << N << std::endl; fs.mean(field, mean, N); Log::info() << "mean: " << mean << std::endl; Log::info() << "N: " << N << std::endl; fs.meanAndStandardDeviation(field, mean, stddev, N); Log::info() << "mean: " << mean << std::endl; Log::info() << "standard deviation: " << stddev << std::endl; Log::info() << "N: " << N << std::endl; int sumint; fs.orderIndependentSum(field, sumint, N); Log::info() << "order independent sum in int: " << sumint << std::endl; fs.sum(field, sumint, N); Log::info() << "sum in int: " << sumint << std::endl; Field max_per_level("max", array::make_datatype(), array::make_shape(nb_levels)); Field min_per_level("min", array::make_datatype(), array::make_shape(nb_levels)); Field sum_per_level("sum", array::make_datatype(), array::make_shape(nb_levels)); Field mean_per_level("mean", array::make_datatype(), array::make_shape(nb_levels)); Field stddev_per_level("stddev", array::make_datatype(), array::make_shape(nb_levels)); Field gidx_per_level("gidx", array::make_datatype(), array::make_shape(nb_levels)); fs.maximumPerLevel(field, max_per_level); // max_per_level.dump(Log::info()); fs.minimumPerLevel(field, min_per_level); // min_per_level.dump(Log::info()); fs.sumPerLevel(field, sum_per_level, N); // sum_per_level.dump(Log::info()); fs.meanPerLevel(field, mean_per_level, N); // mean_per_level.dump(Log::info()); fs.meanAndStandardDeviationPerLevel(field, mean_per_level, stddev_per_level, N); // mean_per_level.dump(Log::info()); // stddev_per_level.dump(Log::info()); fs.orderIndependentSumPerLevel(field, sum_per_level, N); // sum_per_level.dump(Log::info()); } Log::info() << "Testing collectives for columns vector field" << std::endl; if (1) { Field& field = columns_vector_field; const functionspace::NodeColumns fs = nodes_fs; idx_t nvar = field.variables(); std::vector max; std::vector min; std::vector sum; std::vector mean; std::vector stddev; idx_t N; std::vector gidx_max; std::vector gidx_min; std::vector levels; auto vec_arr = array::make_view(field); vec_arr.assign(mpi::comm().rank() + 1); fs.maximum(field, max); std::vector check_max(nvar, mpi::comm().size()); EXPECT(max == check_max); fs.minimum(field, min); std::vector check_min(nvar, 1); EXPECT(min == check_min); fs.maximumAndLocation(field, max, gidx_max, levels); EXPECT(max == check_max); Log::info() << "global index for maximum: " << gidx_max << std::endl; fs.minimumAndLocation(field, min, gidx_min, levels); EXPECT(min == check_min); Log::info() << "global index for minimum: " << gidx_min << std::endl; fs.orderIndependentSum(field, sum, N); Log::info() << "oisum: " << sum << std::endl; Log::info() << "N: " << N << std::endl; fs.mean(field, mean, N); Log::info() << "mean: " << mean << std::endl; Log::info() << "N: " << N << std::endl; fs.meanAndStandardDeviation(field, mean, stddev, N); Log::info() << "mean: " << mean << std::endl; Log::info() << "standard deviation: " << stddev << std::endl; Log::info() << "N: " << N << std::endl; std::vector sumint; fs.orderIndependentSum(field, sumint, N); Log::info() << "sumint: " << sumint << std::endl; fs.sum(field, sumint, N); Log::info() << "sumint: " << sumint << std::endl; Field max_per_level("max", array::make_datatype(), array::make_shape(nb_levels, nvar)); Field min_per_level("min", array::make_datatype(), array::make_shape(nb_levels, nvar)); Field sum_per_level("sum", array::make_datatype(), array::make_shape(nb_levels, nvar)); Field mean_per_level("mean", array::make_datatype(), array::make_shape(nb_levels, nvar)); Field stddev_per_level("stddev", array::make_datatype(), array::make_shape(nb_levels, nvar)); Field gidx_per_level("gidx", array::make_datatype(), array::make_shape(nb_levels, nvar)); fs.maximumPerLevel(field, max_per_level); // max_per_level.dump(Log::info()); fs.minimumPerLevel(field, min_per_level); // min_per_level.dump(Log::info()); fs.sumPerLevel(field, sum_per_level, N); // sum_per_level.dump(Log::info()); fs.meanPerLevel(field, mean_per_level, N); // mean_per_level.dump(Log::info()); fs.meanAndStandardDeviationPerLevel(field, mean_per_level, stddev_per_level, N); // mean_per_level.dump(Log::info()); // stddev_per_level.dump(Log::info()); fs.orderIndependentSumPerLevel(field, sum_per_level, N); // sum_per_level.dump(Log::info()); } Field tmp = nodes_fs.createField(option::datatypeT() | option::global(0) | option::levels(10) | option::name("tmp")); } CASE("test_SpectralFunctionSpace") { idx_t truncation = 159; idx_t nb_levels = 10; idx_t nspec2g = (truncation + 1) * (truncation + 2); Spectral spectral_fs(truncation); Field surface_scalar_field = spectral_fs.createField(option::name("scalar")); EXPECT(surface_scalar_field.name() == std::string("scalar")); EXPECT(surface_scalar_field.size() == nspec2g); EXPECT(surface_scalar_field.rank() == 1); auto surface_scalar = array::make_view(surface_scalar_field); EXPECT(surface_scalar.shape(0) == nspec2g); Field columns_scalar_field = spectral_fs.createField(option::name("scalar") | option::levels(nb_levels)); EXPECT(columns_scalar_field.name() == std::string("scalar")); EXPECT(columns_scalar_field.size() == nspec2g * nb_levels); EXPECT(columns_scalar_field.rank() == 2); auto columns_scalar = array::make_view(columns_scalar_field); EXPECT(columns_scalar.shape(0) == nspec2g); EXPECT(columns_scalar.shape(1) == nb_levels); } #if ATLAS_HAVE_TRANS CASE("test_SpectralFunctionSpace_trans_dist") { trans::Trans trans(Grid("F80"), 159); idx_t nb_levels(10); Spectral spectral_fs(trans); idx_t nspec2 = spectral_fs.nb_spectral_coefficients(); Field surface_scalar_field = spectral_fs.createField(option::name("scalar")); EXPECT(surface_scalar_field.name() == std::string("scalar")); EXPECT(surface_scalar_field.size() == nspec2); EXPECT(surface_scalar_field.rank() == 1); auto surface_scalar = array::make_view(surface_scalar_field); EXPECT(surface_scalar.shape(0) == nspec2); // size_t surface_scalar_shape[] = { nspec2 }; // EXPECT( eckit::testing::make_view( // surface_scalar.shape(),surface_scalar.shape()+1) == // eckit::testing::make_view(surface_scalar_shape,surface_scalar_shape+1) ); Field columns_scalar_field = spectral_fs.createField(option::name("scalar") | option::levels(nb_levels)); EXPECT(columns_scalar_field.name() == std::string("scalar")); EXPECT(columns_scalar_field.size() == nspec2 * nb_levels); EXPECT(columns_scalar_field.rank() == 2); auto columns_scalar = array::make_view(columns_scalar_field); EXPECT(columns_scalar.shape(0) == nspec2); EXPECT(columns_scalar.shape(1) == nb_levels); // size_t columns_scalar_shape[] = { nspec2, nb_levels }; // EXPECT(eckit::testing::make_view(columns_scalar.shape(),columns_scalar.shape()+2) // == eckit::testing::make_view(columns_scalar_shape,columns_scalar_shape+2)); } CASE("test_SpectralFunctionSpace_trans_global") { idx_t nb_levels(10); idx_t truncation = 159; Spectral spectral_fs(truncation, option::levels(nb_levels)); idx_t nspec2g = spectral_fs.nb_spectral_coefficients_global(); Field surface_scalar_field = spectral_fs.createField(option::name("scalar") | option::levels(false) | option::global()); EXPECT(surface_scalar_field.name() == std::string("scalar")); if (eckit::mpi::comm().rank() == 0) { EXPECT(surface_scalar_field.size() == nspec2g); } EXPECT(surface_scalar_field.rank() == 1); EXPECT(surface_scalar_field.metadata().get("global")); EXPECT(surface_scalar_field.metadata().get("owner") == 0); auto surface_scalar = array::make_view(surface_scalar_field); if (eckit::mpi::comm().rank() == 0) { EXPECT(surface_scalar.shape(0) == nspec2g); } Field columns_scalar_field = spectral_fs.createField(option::name("scalar") | option::global()); EXPECT(columns_scalar_field.name() == std::string("scalar")); if (eckit::mpi::comm().rank() == 0) { EXPECT(columns_scalar_field.size() == nspec2g * nb_levels); } else { EXPECT(columns_scalar_field.size() == 0); } EXPECT(columns_scalar_field.rank() == 2); auto columns_scalar = array::make_view(columns_scalar_field); if (eckit::mpi::comm().rank() == 0) { EXPECT(columns_scalar.shape(0) == nspec2g); EXPECT(columns_scalar.shape(1) == nb_levels); } } CASE("test_SpectralFunctionSpace_norm") { trans::Trans trans(Grid("F80"), 159); size_t nb_levels(10); Spectral spectral_fs(trans); Field twoD_field = spectral_fs.createField(option::name("2d")); Field threeD_field = spectral_fs.createField(option::name("3d") | option::levels(nb_levels)); // Set first wave number { auto twoD = array::make_view(twoD_field); twoD.assign(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) { threeD((size_t)0, jlev) = jlev; } } } double twoD_norm(0.); std::vector threeD_norms(threeD_field.levels(), 0.); spectral_fs.norm(twoD_field, twoD_norm); if (not threeD_field.contiguous()) { EXPECT_THROWS_AS(spectral_fs.norm(threeD_field, threeD_norms), eckit::Exception); return; } else { EXPECT_NO_THROW(spectral_fs.norm(threeD_field, threeD_norms)); } if (eckit::mpi::comm().rank() == 0) { EXPECT(eckit::types::is_approximately_equal(twoD_norm, 1.0)); // before is_approximately_equal tolerance was 1.e-10 for (size_t jlev = 0; jlev < nb_levels; ++jlev) { EXPECT(eckit::types::is_approximately_equal( threeD_norms[jlev], double(jlev))); // before is_approximately_equal tolerance was 1.e-10 } } } template mdspan> make_mdspan(std::vector& v) { return mdspan>{v.data(), v.size()}; } template mdspan> make_mdspan(std::vector>& v) { return mdspan>{reinterpret_cast(v.data()), v.size()}; } CASE("test_functionspace_grid") { // Create list of points and construct Grid from them std::vector points = { {0.0, 5.0}, {0.0, 0.0}, {10.0, 0.0}, {15.0, 0.0}, {5.0, 5.0}, {15.0, 5.0} }; UnstructuredGrid grid(points); // Create Mesh from Grid Mesh mesh_from_grid(grid); // Create Mesh from list of points using MeshBuilder std::vector lons; std::vector lats; for (const auto& point : points) { lons.push_back(point.x()); lats.push_back(point.y()); } constexpr gidx_t global_index_base = 1; std::vector ghosts(6, 0); std::vector global_indices(6); std::iota(global_indices.begin(), global_indices.end(), 1); const idx_t remote_index_base = 0; std::vector remote_indices(6); std::iota(remote_indices.begin(), remote_indices.end(), remote_index_base); std::vector partitions(6, 0); std::vector> tri_boundary_nodes = {{{3, 6, 5}}, {{3, 4, 6}}}; std::vector tri_global_indices = {1, 2}; std::vector> quad_boundary_nodes = {{{1, 2, 3, 5}}}; std::vector quad_global_indices = {3}; const mesh::MeshBuilder mesh_builder{}; const Mesh mesh_from_meshbuilder = mesh_builder( make_mdspan(global_indices), make_mdspan(lons), make_mdspan(lats), make_mdspan(lons), make_mdspan(lats), make_mdspan(ghosts), make_mdspan(partitions), make_mdspan(remote_indices), remote_index_base, make_mdspan(tri_global_indices), make_mdspan(tri_boundary_nodes), make_mdspan(quad_global_indices), make_mdspan(quad_boundary_nodes), global_index_base); // Create Cell/Edge/NodeColumns and PointCloud FunctionSpaces that will save the Grid on construction functionspace::CellColumns cells_from_grid(mesh_from_grid); functionspace::EdgeColumns edges_from_grid(mesh_from_grid); functionspace::NodeColumns nodes_from_grid(mesh_from_grid); functionspace::PointCloud pointcloud_from_grid(grid); // Create Cell/Edge/NodeColumns and PointCloud FunctionSpaces that will generate the Grid when requested functionspace::CellColumns cells_from_meshbuilder(mesh_from_meshbuilder); functionspace::EdgeColumns edges_from_meshbuilder(mesh_from_meshbuilder); functionspace::NodeColumns nodes_from_meshbuilder(mesh_from_meshbuilder); functionspace::PointCloud pointcloud_from_points(points); // All Grids should match original EXPECT(cells_from_grid.grid().uid() == grid.uid()); EXPECT(edges_from_grid.grid().uid() == grid.uid()); EXPECT(nodes_from_grid.grid().uid() == grid.uid()); EXPECT(pointcloud_from_grid.grid().uid() == grid.uid()); EXPECT(cells_from_meshbuilder.grid().uid() == grid.uid()); EXPECT(edges_from_meshbuilder.grid().uid() == grid.uid()); EXPECT(nodes_from_meshbuilder.grid().uid() == grid.uid()); EXPECT(pointcloud_from_points.grid().uid() == grid.uid()); // Repeat with a StructuredGrid to check StructuredColumns and BlockStructuredColumns Grid structured_grid("O8"); Mesh structured_mesh = StructuredMeshGenerator().generate(structured_grid); functionspace::StructuredColumns sc(structured_grid); functionspace::BlockStructuredColumns bsc(structured_grid); EXPECT(sc.grid() == structured_grid); EXPECT(bsc.grid() == structured_grid); // Check that Spectral throws exception functionspace::Spectral spectral(159); EXPECT_THROWS(spectral.grid()); } #endif //----------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/functionspace/test_pointcloud_haloexchange_3PE.cc0000664000175000017500000000726215160212070026567 0ustar alastairalastair/* * (C) Copyright 2023 ECMWF * (C) Crown Copyright 2023 Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * */ #include #include "atlas/array.h" #include "atlas/field.h" #include "atlas/functionspace/PointCloud.h" #include "atlas/option.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 { CASE("test_halo_exchange_01") { // // ++ point order ++ // // PE 0 PE 1 // _________________ // | 0 1 || 0 1 | // | 3 2 || 3 2 | // ------------------ // ------------------ // | 0 1 2 3 | // | 7 6 5 4 | // ------------------ // PE 2 // // 'own points': clockwise starting from top left // // number of points (own + ghost) int no_points{0}; if (atlas::mpi::rank() == 0) { no_points = 8; } else if (atlas::mpi::rank() == 1) { no_points = 8; } else if (atlas::mpi::rank() == 2) { no_points = 12; } Field lonlat("lonlat", array::make_datatype(), array::make_shape(no_points, 2)); // ghost points flags: 0={is not a ghost point}, 1={is a ghost point} Field gpoints("ghost", array::make_datatype(), array::make_shape(no_points)); auto lonlatv = array::make_view(lonlat); auto gpointsv = array::make_view(gpoints); if (atlas::mpi::rank() == 0) { // own points: clockwise starting from top left // halo points: clockwise starting from top left lonlatv.assign({0.0, 1.0, 1.0, 1.0, // center, first row [own] 1.0, -1.0, 0.0, -1.0, // center, second row [own] 2.0, 1.0, 2.0, -1.0, // right [ghost] 1.0, -2.0, 0.0, -2.0}); // down [ghost] gpointsv.assign({0, 0, 0, 0, 1, 1, 1, 1}); } else if (atlas::mpi::rank() == 1) { lonlatv.assign({2.0, 1.0, 3.0, 1.0, // center, first row [own] 3.0, -1.0, 2.0, -1.0, // center, second row [own] 3.0, -2.0, 2.0, -2.0, // down [ghost] 1.0, -1.0, 1.0, 1.0}); // left [ghost] gpointsv.assign({0, 0, 0, 0, 1, 1, 1, 1}); } else if (atlas::mpi::rank() == 2) { lonlatv.assign({0.0, -2.0, 1.0, -2.0, 2.0, -2.0, 3.0, -2.0, // center, first row [own] 3.0, -3.0, 2.0, -3.0, 1.0, -3.0, 0.0, -3.0, // center, second row [own] 0.0, -1.0, 1.0, -1.0, 2.0, -1.0, 3.0, -1.0}); // top [ghost] gpointsv.assign({0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1}); } // function space auto pcfs = functionspace::PointCloud(lonlat, gpoints); // remote indexes (reference) std::vector remote_idxs_ref; if (atlas::mpi::rank() == 0) { remote_idxs_ref = {0, 1, 2, 3, 0, 3, 1, 0}; } else if (atlas::mpi::rank() == 1) { remote_idxs_ref = {0, 1, 2, 3, 3, 2, 2, 1}; } else if (atlas::mpi::rank() == 2) { remote_idxs_ref = {0, 1, 2, 3, 4, 5, 6, 7, 3, 2, 3, 2}; } // remote indexes auto remote_index = pcfs.remote_index(); EXPECT(remote_index.size() == (lonlat.size()/2));; auto remote_indexv = array::make_indexview(remote_index); for (idx_t i = 0; i < remote_indexv.shape(0); ++i) { EXPECT(remote_indexv(i) == remote_idxs_ref.at(i));; } } } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/functionspace/test_pointcloud.cc0000664000175000017500000001366615160212070023417 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF * (C) Crown Copyright 2023 Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * */ #include #include "atlas/array.h" #include "atlas/field.h" #include "atlas/functionspace/PointCloud.h" #include "atlas/option.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 { //----------------------------------------------------------------------------- std::vector ref_lonlat() { static std::vector v = {{00., 0.}, {10., 0.}, {20., 0.}, {30., 0.}, {40., 0.}, {50., 0.}, {60., 0.}, {70., 0.}, {80., 0.}, {90., 0.}}; return v; } std::vector ref_xy() { static std::vector v = {{00., 0.}, {10., 0.}, {20., 0.}, {30., 0.}, {40., 0.}, {50., 0.}, {60., 0.}, {70., 0.}, {80., 0.}, {90., 0.}}; return v; } std::vector ref_xyz() { static std::vector v = {{00., 0., 1.}, {10., 0., 2.}, {20., 0., 3.}, {30., 0., 4.}, {40., 0., 5.}, {50., 0., 6.}, {60., 0., 7.}, {70., 0., 8.}, {80., 0., 9.}, {90., 0., 10.}}; return v; } template bool equal(const C1& c1, const C2& c2) { return c1.size() == c2.size() && std::equal(std::begin(c1), std::end(c1), std::begin(c2)); } CASE("test_functionspace_PointCloud create from field") { Field points("points", array::make_datatype(), array::make_shape(10, 2)); auto xy = array::make_view(points); xy.assign({00., 0., 10., 0., 20., 0., 30., 0., 40., 0., 50., 0., 60., 0., 70., 0., 80., 0., 90., 0.}); functionspace::PointCloud pointcloud(points); EXPECT(pointcloud.size() == 10); points.dump(Log::info()); Log::info() << std::endl; EXPECT_THROWS(pointcloud.iterate().xyz()); EXPECT(equal(pointcloud.iterate().xy(), ref_xy())); EXPECT(equal(pointcloud.iterate().lonlat(), ref_lonlat())); } CASE("test_functionspace_PointCloud create from 2D vector") { std::vector points{{00., 0.}, {10., 0.}, {20., 0.}, {30., 0.}, {40., 0.}, {50., 0.}, {60., 0.}, {70., 0.}, {80., 0.}, {90., 0.}}; functionspace::PointCloud pointcloud(points); EXPECT(pointcloud.size() == 10); pointcloud.lonlat().dump(Log::info()); Log::info() << std::endl; EXPECT_THROWS(pointcloud.iterate().xyz()); EXPECT(equal(pointcloud.iterate().xy(), ref_xy())); EXPECT(equal(pointcloud.iterate().lonlat(), ref_lonlat())); } CASE("test_functionspace_PointCloud create from 3D vector") { std::vector points{{00., 0., 1.}, {10., 0., 2.}, {20., 0., 3.}, {30., 0., 4.}, {40., 0., 5.}, {50., 0., 6.}, {60., 0., 7.}, {70., 0., 8.}, {80., 0., 9.}, {90., 0., 10.}}; functionspace::PointCloud pointcloud(points); EXPECT(pointcloud.size() == 10); pointcloud.lonlat().dump(Log::info()); Log::info() << std::endl; EXPECT(equal(pointcloud.iterate().xyz(), ref_xyz())); EXPECT(equal(pointcloud.iterate().xy(), ref_xy())); EXPECT(equal(pointcloud.iterate().lonlat(), ref_lonlat())); } CASE("test_functionspace_PointCloud create from 2D initializer list") { functionspace::PointCloud pointcloud = {{00., 0.}, {10., 0.}, {20., 0.}, {30., 0.}, {40., 0.}, {50., 0.}, {60., 0.}, {70., 0.}, {80., 0.}, {90., 0.}}; EXPECT(pointcloud.size() == 10); pointcloud.lonlat().dump(Log::info()); Log::info() << std::endl; EXPECT_THROWS(pointcloud.iterate().xyz()); EXPECT(equal(pointcloud.iterate().xy(), ref_xy())); EXPECT(equal(pointcloud.iterate().lonlat(), ref_lonlat())); } CASE("test_functionspace_PointCloud create from 3D initializer list") { functionspace::PointCloud pointcloud = {{00., 0., 1.}, {10., 0., 2.}, {20., 0., 3.}, {30., 0., 4.}, {40., 0., 5.}, {50., 0., 6.}, {60., 0., 7.}, {70., 0., 8.}, {80., 0., 9.}, {90., 0., 10.}}; EXPECT(pointcloud.size() == 10); pointcloud.lonlat().dump(Log::info()); Log::info() << std::endl; pointcloud.vertical().dump(Log::info()); Log::info() << std::endl; EXPECT(equal(pointcloud.iterate().xyz(), ref_xyz())); EXPECT(equal(pointcloud.iterate().xy(), ref_xy())); EXPECT(equal(pointcloud.iterate().lonlat(), ref_lonlat())); } //----------------------------------------------------------------------------- CASE("test_createField") { FunctionSpace p1; { Field points("points", array::make_datatype(), array::make_shape(10, 2)); auto xy = array::make_view(points); xy.assign({00., 0., 10., 0., 20., 0., 30., 0., 40., 0., 50., 0., 60., 0., 70., 0., 80., 0., 90., 0.}); p1 = functionspace::PointCloud(points); } Field f1 = p1.createField(option::name("f1") | option::levels(3)); EXPECT_EQ(f1.levels(), 3); EXPECT_EQ(f1.shape(0), 10); EXPECT_EQ(f1.shape(1), 3); FunctionSpace p2; { Field points("points", array::make_datatype(), array::make_shape(4, 2)); auto xy = array::make_view(points); xy.assign({20., 0., 40., 0., 70., 0., 90., 0.}); p2 = functionspace::PointCloud(points); } Field f2 = p2.createField(f1, util::NoConfig()); EXPECT_EQ(f2.levels(), 3); EXPECT_EQ(f2.shape(0), 4); EXPECT_EQ(f2.shape(1), 3); Field f3 = p2.createField(f1, option::variables(5)); EXPECT_EQ(f3.levels(), 3); EXPECT_EQ(f3.shape(0), 4); EXPECT_EQ(f3.shape(1), 3); EXPECT_EQ(f3.shape(2), 5); } //----------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/functionspace/test_cellcolumns.cc0000664000175000017500000001126015160212070023543 0ustar alastairalastair/* * (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 "atlas/array/ArrayView.h" #include "atlas/array/MakeView.h" #include "atlas/field/Field.h" #include "atlas/functionspace/CellColumns.h" #include "atlas/grid.h" #include "atlas/mesh.h" #include "atlas/meshgenerator.h" #include "atlas/output/Gmsh.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{grid.meshgenerator()}; return meshgenerator.generate(grid); } void set_partition_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); } } field.set_dirty(); } void check_partition_field_values(const Mesh& mesh, Field& field) { auto value = array::make_view(field); auto partition = array::make_view(mesh.cells().partition()); const size_t nb_cells = mesh.cells().size(); for (size_t j = 0; j < nb_cells; ++j) { EXPECT(value(j) == partition(j)); } } void set_global_index_field_values(const Mesh& mesh, Field& field) { auto value = array::make_view(field); auto gidx = array::make_view(mesh.cells().global_index()); 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) = gidx(j); } } field.set_dirty(); } void check_global_index_field_values(const Mesh& mesh, Field& field) { auto value = array::make_view(field); auto gidx = array::make_view(mesh.cells().global_index()); 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) { if (halo(j)) { EXPECT(value(j) != -1); } else { EXPECT(value(j) == gidx(j)); } } } //----------------------------------------------------------------------------- CASE("test_functionspace_CellColumns_no_halo") { Mesh mesh = generate_mesh(); CellColumns fs(mesh); Field field(fs.createField()); set_partition_field_values(mesh, field); fs.haloExchange(field); check_partition_field_values(mesh, field); set_global_index_field_values(mesh, field); fs.haloExchange(field); check_global_index_field_values(mesh, field); output::Gmsh output("cellcolumns_halo0.msh"); output.write(mesh, util::Config("ghost", true)); output.write(field); } CASE("test_functionspace_CellColumns_halo_1") { Mesh mesh = generate_mesh(); CellColumns fs(mesh, option::halo(1)); Field field(fs.createField()); set_partition_field_values(mesh, field); fs.haloExchange(field); check_partition_field_values(mesh, field); set_global_index_field_values(mesh, field); fs.haloExchange(field); check_global_index_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); } atlas-0.46.0/src/tests/functionspace/test_stencil_parallel.cc0000664000175000017500000000650515160212070024546 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "atlas/array.h" #include "atlas/functionspace/StructuredColumns.h" #include "atlas/grid/Partitioner.h" #include "atlas/grid/Stencil.h" #include "atlas/grid/StencilComputer.h" #include "atlas/grid/StructuredGrid.h" #include "atlas/parallel/mpi/mpi.h" #include "tests/AtlasTestEnvironment.h" using namespace atlas::functionspace; using namespace atlas::util; using namespace atlas::grid; namespace atlas { namespace test { //----------------------------------------------------------------------------- CASE("test horizontal stencil") { std::string gridname = eckit::Resource("--grid", "O16"); StructuredGrid grid(gridname); int halo = eckit::Resource("--halo", 2); Config config; config.set("halo", halo); config.set("periodic_points", true); StructuredColumns fs(grid, Partitioner("equal_regions"), config); auto gidx = array::make_view(fs.global_index()); HorizontalStencil<4> stencil; ComputeHorizontalStencil compute_stencil(grid, stencil.width()); auto test = [&](std::vector& points) { for (auto p : points) { std::cout << p << std::endl; compute_stencil(p.x(), p.y(), stencil); for (idx_t j = 0; j < stencil.width(); ++j) { std::cout << gidx(fs.index(stencil.i(0, j), stencil.j(j))) << std::endl; for (idx_t i = 0; i < stencil.width(); ++i) { std::cout << stencil.i(i, j) << ", " << stencil.j(j); std::cout << " -- " << "x,y = " << fs.compute_xy(stencil.i(i, j), stencil.j(j)) << std::endl; EXPECT(stencil.j(j) >= fs.j_begin_halo()); EXPECT(stencil.j(j) < fs.j_end_halo()); EXPECT(stencil.i(i, j) >= fs.i_begin_halo(stencil.j(j))); EXPECT(stencil.i(i, j) < fs.i_end_halo(stencil.j(j))); } } std::cout << std::endl; } }; std::vector departure_points; if (mpi::comm().size() == 4) { SECTION("mpi-size = 4, mpi-rank = 2 ") { if (mpi::comm().rank() == 2) { departure_points = { PointXY(236.25, -30.9375), }; test(departure_points); } } } if (mpi::comm().size() == 16) { SECTION("ATLAS-186 workaround") { if (mpi::comm().rank() == 12) { // ATLAS-186: point that is found using matching-mesh domain decomposition, which does not really match the halo of StructuredColumns departure_points = { PointXY(205.3125, -67.5), }; test(departure_points); } } } } //----------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/functionspace/test_polygons_projection.cc0000664000175000017500000001516215160212070025336 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "atlas/array/ArrayView.h" #include "atlas/array/MakeView.h" #include "atlas/field/Field.h" #include "atlas/functionspace/NodeColumns.h" #include "atlas/functionspace/StructuredColumns.h" #include "atlas/grid/Partitioner.h" #include "atlas/grid/StructuredGrid.h" #include "atlas/meshgenerator.h" #include "atlas/output/Gmsh.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/util/PolygonLocator.h" #include "atlas/util/PolygonXY.h" #include "tests/AtlasTestEnvironment.h" using namespace atlas::functionspace; using namespace atlas::util; namespace atlas { namespace test { namespace { std::string grid_name() { static std::string _gridname = eckit::Resource("--grid", "L90"); return _gridname; } std::string functionspace_name() { static std::string _name = eckit::Resource("--functionspace", "StructuredColumns"); return _name; } std::string configuration() { std::stringstream out; out << "mpi::size()=" << mpi::size() << ",grid_name()=" << grid_name() << ",functionspace_name()=" << functionspace_name(); return out.str(); }; Grid grid() { static Grid _grid{ option::name(grid_name()) | util::Config("projection", util::Config("type", "rotated_lonlat")( "south_pole", std::vector{45., -60.})), RectangularDomain{{0., 90}, {-45, 45.}}}; return _grid; } Mesh mesh() { static Mesh _mesh{StructuredMeshGenerator{util::Config("partitioner", "checkerboard")}.generate(grid())}; return _mesh; } FunctionSpace structured_columns() { static functionspace::StructuredColumns _fs{grid(), grid::Partitioner("checkerboard")}; return _fs; } FunctionSpace node_columns() { static functionspace::NodeColumns _fs{mesh()}; return _fs; } FunctionSpace functionspace() { static FunctionSpace _fs = functionspace_name() == "NodeColumns" ? node_columns() : structured_columns(); return _fs; } std::vector& points() { static std::vector _points{{90., 45.}, {135., 45.}, {60., 0.}, {90., 0.}}; return _points; } void check_part(const std::vector& vec) { Log::debug() << "part = " << vec << std::endl; std::vector expected; if (mpi::size() == 1 && grid_name() == "L90") { expected = std::vector{0, 0, 0, 0}; } if (mpi::size() == 4 && grid_name() == "L90" && functionspace_name() == "StructuredColumns") { expected = std::vector{0, 1, 2, 3}; } if (mpi::size() == 4 && grid_name() == "L90" && functionspace_name() == "NodeColumns") { expected = std::vector{0, 1, 2, 3}; } if (expected.size()) { EXPECT(vec == expected); } else { Log::warning() << "Check for part not implemented for configuration " << configuration() << std::endl; } } void check_sizes(const std::vector& vec) { Log::debug() << "sizes = " << vec << std::endl; std::vector expected; if (mpi::size() == 1 && grid_name() == "L90" && functionspace_name() == "StructuredColumns") { expected = std::vector{5}; } if (mpi::size() == 4 && grid_name() == "L90" && functionspace_name() == "StructuredColumns") { expected = std::vector{7, 9, 5, 5}; } if (mpi::size() == 1 && grid_name() == "L90" && functionspace_name() == "NodeColumns") { expected = std::vector{361}; } if (mpi::size() == 4 && grid_name() == "L90" && functionspace_name() == "NodeColumns") { expected = std::vector{185, 183, 181, 179}; } if (expected.size()) { EXPECT(vec == expected); } else { Log::warning() << "Check for sizes not implemented for configuration " << configuration() << std::endl; } } void check_simplified_sizes(const std::vector& vec) { Log::debug() << "simplified_sizes = " << vec << std::endl; std::vector expected; if (mpi::size() == 1 && grid_name() == "L90" && functionspace_name() == "StructuredColumns") { expected = std::vector{5}; } if (mpi::size() == 4 && grid_name() == "L90" && functionspace_name() == "StructuredColumns") { expected = std::vector{7, 9, 5, 5}; } if (mpi::size() == 1 && grid_name() == "L90" && functionspace_name() == "NodeColumns") { expected = std::vector{5}; } if (mpi::size() == 4 && grid_name() == "L90" && functionspace_name() == "NodeColumns") { expected = std::vector{7, 9, 5, 5}; } if (expected.size()) { EXPECT(vec == expected); } else { Log::warning() << "Check for simplified_sizes not implemented for configuration " << configuration() << std::endl; } } } // namespace CASE("info") { Log::info() << grid().spec() << std::endl; functionspace().polygon().outputPythonScript("rotated_polygon.py"); output::Gmsh{"mesh.msh", util::Config("coordinates", "lonlat")}.write(mesh()); } //----------------------------------------------------------------------------- CASE("test_polygon_sizes") { auto fs = functionspace(); auto polygons = ListPolygonXY(fs.polygons()); std::vector sizes(mpi::size()); std::vector simplified_sizes(mpi::size()); for (idx_t i = 0; i < mpi::size(); ++i) { sizes[i] = fs.polygons()[i].size(); simplified_sizes[i] = polygons[i].size(); } check_sizes(sizes); check_simplified_sizes(simplified_sizes); } CASE("test_polygon_locator (per point)") { std::vector part; auto fs = functionspace(); auto find_partition = PolygonLocator{ListPolygonXY{fs.polygons()}, fs.projection()}; part.reserve(points().size()); for (auto& point : points()) { part.emplace_back(find_partition(point)); } check_part(part); } CASE("test_polygon_locator (for array)") { std::vector part(points().size()); auto fs = functionspace(); auto find_partition = PolygonLocator{ListPolygonXY{fs.polygons()}, fs.projection()}; find_partition(points(), part); check_part(part); } //----------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/functionspace/test_pointcloud_haloexchange_2PE.cc0000664000175000017500000002372515160212070026570 0ustar alastairalastair/* * (C) Copyright 2023 ECMWF * (C) Crown Copyright 2023 Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * */ #include "atlas/array.h" #include "atlas/field.h" #include "atlas/functionspace/PointCloud.h" #include "atlas/option.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 { double innerproductwithhalo(const Field& f1, const Field& f2) { long sum(0); auto view1 = array::make_view(f1); auto view2 = array::make_view(f2); for (idx_t jn = 0; jn < f1.shape(0); ++jn) { for (idx_t jl = 0; jl < f1.levels(); ++jl) { sum += view1(jn, jl) * view2(jn, jl); } } auto& comm = atlas::mpi::comm(f1.functionspace().mpi_comm()); comm.allReduceInPlace(sum, eckit::mpi::sum()); return sum; } CASE("test_halo_exchange_01") { // Here is some ascii art to describe the test. // Remote index is the same for both PEs in this case // // _1____0__ // 1| 0 1 | 0 // 3| 2 3 | 2 // --------- // 3 2 // // Point order (locally is also the same) // // _4____5__ // 11| 0 1 | 6 // 10| 2 3 | 7 // --------- // 9 8 // // Partition index // PE 0: PE 1: // // _1____1__ _0____0__ // 1| 0 0 | 1 0| 1 1 | 0 // 1| 0 0 | 1 0| 1 1 | 0 // --------- --------- // 1 1 0 0 // // Initial Values // PE 0: PE 1: // // _0____0__ _0____0__ // 0|10 11 | 0 0|20 21 | 0 // 0|12 13 | 0 0|22 23 | 0 // --------- --------- // 0 0 0 0 // // Values after halo exchange // PE 0: PE 1: // // 21___20__ 11___10__ // 21|10 11 | 20 11|20 21 | 10 // 23|12 13 | 22 13|22 23 | 12 // --------- --------- // 23 22 13 12 // // Values after halo exchange and adjoint halo exchange // PE 0: PE 1: // // _0____0__ _0___0__ // 0|30 33 | 0 0|60 63 | 0 // 0|36 39 | 0 0|66 69 | 0 // --------- --------- // 0 0 0 0 double tolerance(1e-16); Field lonlat("lonlat", array::make_datatype(), array::make_shape(12, 2)); Field ghost("ghost", array::make_datatype(), array::make_shape(12)); Field remote_index("remote_index", array::make_datatype(), array::make_shape(12)); Field partition("partition", array::make_datatype(), array::make_shape(12)); auto lonlatv = array::make_view(lonlat); auto ghostv = array::make_view(ghost); auto remote_indexv = array::make_indexview(remote_index); auto partitionv = array::make_view(partition); ghostv.assign({ 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1}); remote_indexv.assign({0, 1, 2, 3, 1, 0, 0, 2, 2, 3, 3, 1}); int color = 0; if( mpi::rank() >= mpi::size() - 2) { color = 1; } auto& comm = mpi::comm().split(color,"split"); if( color == 0 ) { eckit::mpi::deleteComm("split"); return; } util::Config mpi_comm("mpi_comm","split"); int mpi_rank = comm.rank(); if (mpi_rank == 0) { // center followed by clockwise halo starting from top left lonlatv.assign({-45.0, 45.0, 45.0, 45.0, // center, first row -45.0, -45.0, 45.0, -45.0, // center, second row 225.0, 45.0, 135.0, 45.0, // up 135.0, 45.0, 135.0, -45.0, // right 135.0, -45.0, 225.0, -45.0, // down 225.0, -45.0, 225.0, 45.0}); // left partitionv.assign({0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1}); } else if (mpi_rank == 1) { // center followed by clockwise halo starting from top left lonlatv.assign({135.0, 45.0, 225.0, 45.0, 135.0, -45.0, 225.0, -45.0, 45.0, 45.0, -45.0, 45.0, -45.0, 45.0, -45.0, -45.0, -45.0, -45.0, 45.0, -45.0, 45.0, -45.0, 45.0, 45.0}); partitionv.assign({1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0}); } FieldSet fset; fset.add(lonlat); fset.add(ghost); fset.add(remote_index); fset.add(partition); auto fs2 = functionspace::PointCloud(fset, mpi_comm); Field f1 = fs2.createField(option::name("f1") | option::levels(2)); Field f2 = fs2.createField(option::name("f2") | option::levels(2)); auto f1v = array::make_view(f1); auto f2v = array::make_view(f2); f1v.assign(0.0); f2v.assign(0.0); for (idx_t i = 0; i < f2v.shape(0); ++i) { for (idx_t l = 0; l < f2v.shape(1); ++l) { auto ghostv2 = array::make_view(fs2.ghost()); if (ghostv2(i) == 0) { f1v(i, l) = (mpi_rank +1) * 10.0 + i; f2v(i, l) = f1v(i, l); } } } f2.haloExchange(); // adjoint test double sum1 = innerproductwithhalo(f2, f2); f2.adjointHaloExchange(); double sum2 = innerproductwithhalo(f1, f2); comm.allReduceInPlace(sum1, eckit::mpi::sum()); comm.allReduceInPlace(sum2, eckit::mpi::sum()); EXPECT(std::abs(sum1 - sum2)/ std::abs(sum1) < tolerance); atlas::Log::info() << "adjoint test passed :: " << "sum1 " << sum1 << " sum2 " << sum2 << " normalised difference " << std::abs(sum1 - sum2)/ std::abs(sum1) << std::endl; // In this case the effect of the halo exchange followed by the adjoint halo exchange // multiples the values by a factor of 3 (see pictures above) for (idx_t i = 0; i < f2v.shape(0); ++i) { for (idx_t l = 0; l < f2v.shape(1); ++l) { EXPECT( std::abs(f2v(i, l) - 3.0 * f1v(i, l)) < tolerance); } } atlas::Log::info() << "values from halo followed by halo adjoint are as expected " << std::endl; eckit::mpi::deleteComm("split"); } CASE("test_halo_exchange_02") { double tolerance(1e-16); Field lonlat("lonlat", array::make_datatype(), array::make_shape(12, 2)); Field ghost("ghost", array::make_datatype(), array::make_shape(12)); auto lonlatv = array::make_view(lonlat); auto ghostv = array::make_view(ghost); int color = 0; if( mpi::rank() >= mpi::size() - 2) { color = 1; } auto& comm = mpi::comm().split(color,"split"); if( color == 0 ) { eckit::mpi::deleteComm("split"); return; } util::Config mpi_comm("mpi_comm","split"); int mpi_rank = comm.rank(); if (mpi_rank == 0) { // center followed by clockwise halo starting from top left lonlatv.assign({-45.0, 45.0, 45.0, 45.0, // center, first row -45.0, -45.0, 45.0, -45.0, // center, second row 225.0, 45.0, 135.0, 45.0, // up 135.0, 45.0, 135.0, -45.0, // right 135.0, -45.0, 225.0, -45.0, // down 225.0, -45.0, 225.0, 45.0}); // left ghostv.assign({ 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1}); } else if (mpi_rank == 1) { // center followed by clockwise halo starting from top left lonlatv.assign({135.0, 45.0, 225.0, 45.0, 135.0, -45.0, 225.0, -45.0, 45.0, 45.0, -45.0, 45.0, -45.0, 45.0, -45.0, -45.0, -45.0, -45.0, 45.0, -45.0, 45.0, -45.0, 45.0, 45.0}); ghostv.assign({ 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1}); } auto pcfs = functionspace::PointCloud(lonlat, ghost, mpi_comm); Field f1 = pcfs.createField(option::name("f1") | option::levels(2)); Field f2 = pcfs.createField(option::name("f2") | option::levels(2)); auto f1v = array::make_view(f1); auto f2v = array::make_view(f2); f1v.assign(0.0); f2v.assign(0.0); for (idx_t i = 0; i < f2v.shape(0); ++i) { for (idx_t l = 0; l < f2v.shape(1); ++l) { auto ghostv2 = array::make_view(pcfs.ghost()); if (ghostv2(i) == 0) { f1v(i, l) = (mpi_rank +1) * 10.0 + i; f2v(i, l) = f1v(i, l); } } } f2.haloExchange(); // adjoint test double sum1 = innerproductwithhalo(f2, f2); f2.adjointHaloExchange(); double sum2 = innerproductwithhalo(f1, f2); comm.allReduceInPlace(sum1, eckit::mpi::sum()); comm.allReduceInPlace(sum2, eckit::mpi::sum()); EXPECT(std::abs(sum1 - sum2)/ std::abs(sum1) < tolerance); atlas::Log::info() << "adjoint test passed :: " << "sum1 " << sum1 << " sum2 " << sum2 << " normalised difference " << std::abs(sum1 - sum2)/ std::abs(sum1) << std::endl; // In this case the effect of the halo exchange followed by the adjoint halo exchange // multiples the values by a factor of 3 (see pictures above) for (idx_t i = 0; i < f2v.shape(0); ++i) { for (idx_t l = 0; l < f2v.shape(1); ++l) { EXPECT( std::abs(f2v(i, l) - 3.0 * f1v(i, l)) < tolerance); } } atlas::Log::info() << "values from halo followed by halo adjoint are as expected " << std::endl; eckit::mpi::deleteComm("split"); } } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/functionspace/test_functionspace_splitcomm.cc0000664000175000017500000001371715160212070026164 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "atlas/array.h" #include "atlas/grid.h" #include "atlas/mesh/Mesh.h" #include "atlas/output/Gmsh.h" #include "tests/AtlasTestEnvironment.h" #include "atlas/functionspace/NodeColumns.h" #include "atlas/functionspace/StructuredColumns.h" #include "atlas/functionspace/BlockStructuredColumns.h" #include "atlas/functionspace/PointCloud.h" #include "atlas/grid/Partitioner.h" #include "atlas/field/for_each.h" namespace atlas { namespace test { //----------------------------------------------------------------------------- namespace option { struct mpi_split_comm : public util::Config { mpi_split_comm() { set("mpi_comm","split"); } }; } int color() { static int c = mpi::comm("world").rank()%2; return c; } Grid grid() { static Grid g (color() == 0 ? "O32" : "N32" ); return g; } std::string expected_checksum() { if (grid().name()=="O32") { return "75e913d400755a0d2782fc65e2035e97"; } else if (grid().name()=="N32") { return "bcb344196d20becbb66f098d91f83abb"; } else { return "unknown"; } } struct Fixture { Fixture() { mpi::comm().split(color(),"split"); } ~Fixture() { if (eckit::mpi::hasComm("split")) { eckit::mpi::deleteComm("split"); } } }; void field_init(Field& field) { auto fs = field.functionspace(); auto f = array::make_view(field); auto g = array::make_view(fs.global_index()); for( idx_t j=0; j(); field_init(field); // HaloExchange field.haloExchange(); // TODO CHECK // Gather auto fieldg = fs.createField(atlas::option::global()); fs.gather(field,fieldg); if (fieldg.size()) { idx_t g{0}; field::for_each_value(fieldg,[&](double x) { EXPECT_EQ(++g,x); }); } // Checksum auto checksum = fs.checksum(field); EXPECT_EQ(checksum, expected_checksum()); // Output output::Gmsh gmsh(grid().name()+".msh"); gmsh.write(mesh); gmsh.write(field); } CASE("test FunctionSpace StructuredColumns") { Fixture fixture; auto fs = functionspace::StructuredColumns(grid(),atlas::option::halo(1)|option::mpi_split_comm()); EXPECT_EQUAL(fs.part(),mpi::comm("split").rank()); EXPECT_EQUAL(fs.nb_parts(),mpi::comm("split").size()); auto field = fs.createField(); field_init(field); // HaloExchange field.haloExchange(); // TODO CHECK // Gather auto fieldg = fs.createField(atlas::option::global()); fs.gather(field,fieldg); if (fieldg.size()) { idx_t g{0}; field::for_each_value(fieldg,[&](double x) { EXPECT_EQ(++g,x); }); } // Checksum auto checksum = fs.checksum(field); EXPECT_EQ(checksum, expected_checksum()); } CASE("test FunctionSpace BlockStructuredColumns") { Fixture fixture; auto fs = functionspace::BlockStructuredColumns(grid(),atlas::option::halo(1)|option::mpi_split_comm()); EXPECT_EQUAL(fs.part(),mpi::comm("split").rank()); EXPECT_EQUAL(fs.nb_parts(),mpi::comm("split").size()); auto field = fs.createField(); // field_init(field); // HaloExchange // field.haloExchange(); // TODO CHECK // Gather auto fieldg = fs.createField(atlas::option::global()); // fs.gather(field,fieldg); // if (fieldg.size()) { // idx_t g{0}; // field::for_each_value(fieldg,[&](double x) { // EXPECT_EQ(++g,x); // }); // } // // Checksum // auto checksum = fs.checksum(field); // EXPECT_EQ(checksum, expected_checksum()); } //----------------------------------------------------------------------------- CASE("test FunctionSpace PointCloud") { Fixture fixture; auto fs = functionspace::PointCloud(grid(),util::Config("halo_radius",400*1000)|option::mpi_split_comm()); EXPECT_EQUAL(fs.part(),mpi::comm("split").rank()); EXPECT_EQUAL(fs.nb_parts(),mpi::comm("split").size()); auto field = fs.createField(); field_init(field); // HaloExchange field.haloExchange(); // TODO CHECK // Gather auto fieldg = fs.createField(atlas::option::global()); fs.gather(field,fieldg); if (fieldg.size()) { idx_t g{0}; field::for_each_value(fieldg,[&](double x) { EXPECT_EQ(++g,x); }); } // Checksum // auto checksum = fs.checksum(field); // EXPECT_EQ(checksum, expected_checksum()); } //----------------------------------------------------------------------------- CASE("test FunctionSpace StructuredColumns with MatchingPartitioner") { Fixture fixture; auto fs_A = functionspace::StructuredColumns(grid(), option::mpi_split_comm()); auto fs_B = functionspace::StructuredColumns(grid(), grid::MatchingPartitioner(fs_A), option::mpi_split_comm()); fs_A.polygon().outputPythonScript("fs_A_polygons.py"); fs_B.polygon().outputPythonScript("fs_B_polygons.py"); } //----------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/functionspace/test_structuredcolumns_regional.cc0000664000175000017500000001353715160212070026721 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include #include #include #include #include "atlas/array.h" #include "atlas/field.h" #include "atlas/functionspace.h" #include "atlas/grid.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/util/Config.h" #include #include #include #include #include "tests/AtlasTestEnvironment.h" using Config = atlas::util::Config; namespace atlas { namespace test { //----------------------------------------------------------------------------- CASE("regional lonlat") { const int Nx = 8, Ny = 8; const double xmin = +20, xmax = +60, ymin = +20, ymax = +60; StructuredGrid::XSpace xspace(Config("type", "linear")("N", Nx)("start", xmin)("end", xmax)); StructuredGrid::YSpace yspace(Config("type", "linear")("N", Ny)("start", ymin)("end", ymax)); StructuredGrid grid(xspace, yspace); functionspace::StructuredColumns fs(grid, grid::Partitioner("checkerboard"), Config("halo", 1)); ATLAS_TRACE_SCOPE("Output python") { auto xy = array::make_view(fs.xy()); auto g = array::make_view(fs.global_index()); auto p = array::make_view(fs.partition()); eckit::PathName filepath("test_structuredcolumns_regional_p" + std::to_string(mpi::comm().rank()) + ".py"); std::ofstream f(filepath.asString().c_str(), std::ios::trunc); f << "\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" "" "\n" "fig = plt.figure(figsize=(20,10))" "\n" "ax = fig.add_subplot(111,aspect='equal')" "\n" ""; double xmin = std::numeric_limits::max(); double xmax = -std::numeric_limits::max(); double ymin = std::numeric_limits::max(); double ymax = -std::numeric_limits::max(); f << "\n" "x = ["; for (idx_t j = fs.j_begin_halo(); j < fs.j_end_halo(); ++j) { for (idx_t i = fs.i_begin_halo(j); i < fs.i_end_halo(j); ++i) { idx_t n = fs.index(i, j); f << xy(n, XX) << ", "; xmin = std::min(xmin, xy(n, XX)); xmax = std::max(xmax, xy(n, XX)); } } f << "]"; f << "\n" "y = ["; for (idx_t j = fs.j_begin_halo(); j < fs.j_end_halo(); ++j) { for (idx_t i = fs.i_begin_halo(j); i < fs.i_end_halo(j); ++i) { idx_t n = fs.index(i, j); f << xy(n, YY) << ", "; ymin = std::min(ymin, xy(n, YY)); ymax = std::max(ymax, xy(n, YY)); } } f << "]"; f << "\n" "g = ["; for (idx_t j = fs.j_begin_halo(); j < fs.j_end_halo(); ++j) { for (idx_t i = fs.i_begin_halo(j); i < fs.i_end_halo(j); ++i) { idx_t n = fs.index(i, j); f << g(n) << ", "; } } f << "]"; f << "\n" "p = ["; for (idx_t j = fs.j_begin_halo(); j < fs.j_end_halo(); ++j) { for (idx_t i = fs.i_begin_halo(j); i < fs.i_end_halo(j); ++i) { idx_t n = fs.index(i, j); f << p(n) << ", "; } } f << "]"; // f << "\n" // "r = ["; // for (idx_t j = fs.j_begin_halo(); j < fs.j_end_halo(); ++j) { // for (idx_t i = fs.i_begin_halo(j); i < fs.i_end_halo(j); ++i) { // idx_t n = fs.index(i, j); // f << r(n) << ", "; // } // } // f << "]"; f << "\n" "" "\n" "c = [ cm.Paired( float(pp%13)/12. ) for pp in p ]" "\n" "ax.scatter(x, y, color=c, marker='o')" "\n" "for i in range(" << fs.size() << "):" "\n" " ax.annotate(g[i], (x[i],y[i]), fontsize=8)" "\n" ""; f << "\n" "ax.set_xlim( " << std::min(0., xmin) << "-5, " << std::max(360., xmax) << "+5)" "\n" "ax.set_ylim( " << std::min(-90., ymin) << "-5, " << std::max(90., ymax) << "+5)" "\n" "ax.set_xticks([0,45,90,135,180,225,270,315,360])" "\n" "ax.set_yticks([-90,-45,0,45,90])" "\n" "plt.grid()" "\n" "plt.show()" "\n"; } auto lonlat = array::make_view(fs.lonlat()); for (idx_t n = 0; n < lonlat.shape(0); ++n) { // Log::info() << n << "\t" << PointLonLat(lonlat(n, LON), lonlat(n, LAT)) << std::endl; EXPECT(lonlat(n, LON) >= xmin); EXPECT(lonlat(n, LON) <= xmax); EXPECT(lonlat(n, LAT) >= ymin); EXPECT(lonlat(n, LAT) <= ymax); } } //----------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/functionspace/test_functionspace_locate.cc0000664000175000017500000000674115160212070025423 0ustar alastairalastair/* * (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/array.h" #include "atlas/grid.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/functionspace.h" #include "atlas/functionspace/Locate.h" #include "atlas/parallel/Collect.h" #include "tests/AtlasTestEnvironment.h" //----------------------------------------------------------------------------- namespace atlas::test { CASE("real test") { const idx_t remote_index_base = ATLAS_HAVE_FORTRAN; const gidx_t global_index_base = 1; // convention const int partition_base = 0; Log::info() << "remote_index_base = " << remote_index_base << std::endl; int mpi_rank = mpi::comm().rank(); int mpi_size = mpi::comm().size(); std::vector receive_gidx; std::vector expected_receive_ridx; std::vector expected_receive_part; if (mpi_size == 3) { if (mpi_rank == 0) { receive_gidx = {4, 9, 20, 32, 109, 205, 27, 11, 893}; expected_receive_part = {0, 0, 0, 0, 0, 0, 0, 0, 0}; expected_receive_ridx = {3, 8, 19, 31, 108, 204, 26, 10, 892}; } if (mpi_rank == 1) { receive_gidx = {7, 52, 305, 1004, 5008}; expected_receive_part = {0, 0, 0, 0, 2}; expected_receive_ridx = {6, 51, 304, 1003, 1508}; } if (mpi_rank == 2) { receive_gidx = {989, 2781, 306, 3819, 29}; expected_receive_part = {0, 1, 0, 2, 0}; expected_receive_ridx = {988, 1030, 305, 319, 28}; } for( auto& ridx: expected_receive_ridx ) { ridx += remote_index_base; } } else { Log::warning() << "WARNING: This test needs to be called with mpi_size = 3" << std::endl; } atlas::Grid grid("O32"); functionspace::StructuredColumns fs(grid); std::vector receive_ridx(receive_gidx.size()); std::vector receive_part(receive_gidx.size()); functionspace::Locator locator(fs); locator.locate( receive_gidx, global_index_base, receive_part, partition_base, receive_ridx, remote_index_base); EXPECT_EQ(receive_part,expected_receive_part); EXPECT_EQ(receive_ridx,expected_receive_ridx); // From here, test if we can use this in Collect as a second validation parallel::Collect collect; collect.setup(receive_part.size(), receive_part.data(),receive_ridx.data(),remote_index_base); array::ArrayT receive(receive_gidx.size()); collect.execute(fs.global_index(), receive); for( int rank=0; rank < mpi_size; ++rank ) { mpi::comm().barrier(); if (rank == mpi_rank) { std::cerr << "[" << mpi_rank << "] receive = "; receive.dump(std::cerr); std::cerr << std::endl; } } { auto receive_view = array::make_view(receive); for (int i=0; i #include "atlas/array.h" #include "atlas/field.h" #include "atlas/functionspace/PointCloud.h" #include "atlas/option.h" #include "atlas/parallel/mpi/mpi.h" #include "tests/AtlasTestEnvironment.h" #include "atlas/grid.h" #include "atlas/util/KDTree.h" using namespace eckit; using namespace atlas::functionspace; using namespace atlas::util; namespace atlas { namespace test { constexpr double tol = 1.e-12; //----------------------------------------------------------------------------- CASE("Distributed creation from unstructured grid with halo") { std::vector points = { {180,0}, {90,0}, {-90,0}, {0,90}, {0,-90}, {0,0}, {18,0}, {36,0}, {54,0}, {72,0}, {108,0}, {126,0}, {144,0}, {162,0}, {-162,0}, {-144,0}, {-126,0}, {-108,0}, {-72,0}, {-54,0}, {-36,0}, {-18,0}, {0,18}, {0,36}, {0,54}, {0,72}, {180,72}, {180,54}, {180,36}, {180,18}, {180,-18}, {180,-36}, {180,-54}, {180,-72}, {0,-72}, {0,-54}, {0,-36}, {0,-18}, {90,18}, {90,36}, {90,54}, {90,72}, {-90,72}, {-90,54}, {-90,36}, {-90,18}, {-90,-18}, {-90,-36}, {-90,-54}, {-90,-72}, {90,-72}, {90,-54}, {90,-36}, {90,-18}, {123.974,-58.6741}, {154.087,-16.9547}, {154.212,-58.8675}, {114.377,-41.9617}, {125.567,-23.5133}, {137.627,-40.8524}, {106.162,-24.5874}, {158.508,-38.55}, {137.826,-72.8109}, {142.103,-26.799}, {138.256,-13.8871}, {168.39,-24.3266}, {168.954,-12.0094}, {117.333,-12.35}, {102.254,-11.1537}, {120.307,59.7167}, {107.196,26.0167}, {144.768,28.3721}, {150.891,60.0343}, {164.566,25.5053}, {116.851,14.0295}, {124.84,28.3978}, {157.901,42.042}, {111.41,43.1056}, {134.333,44.6677}, {103.277,11.707}, {135.358,73.2119}, {135.349,14.2311}, {153.48,13.386}, {168.071,11.5344}, {-162.99,26.3775}, {-147.519,56.1313}, {-122.579,27.4824}, {-117.909,59.2376}, {-104.052,27.3616}, {-153.107,14.9717}, {-110.833,41.7436}, {-144.847,32.8534}, {-161.546,42.1031}, {-129.866,44.5201}, {-133.883,72.4163}, {-166.729,11.8907}, {-135.755,15.2529}, {-106.063,14.4869}, {-119.452,11.7037}, {-146.026,-58.6741}, {-115.913,-16.9547}, {-115.788,-58.8675}, {-155.623,-41.9617}, {-144.433,-23.5133}, {-132.373,-40.8524}, {-163.838,-24.5874}, {-111.492,-38.55}, {-132.174,-72.8109}, {-127.897,-26.799}, {-131.744,-13.8871}, {-101.61,-24.3266}, {-101.046,-12.0094}, {-152.667,-12.35}, {-167.746,-11.1537}, {-14.0127,-27.2963}, {-59.193,-57.0815}, {-56.465,-19.5751}, {-27.056,-59.3077}, {-57.124,-35.9752}, {-33.4636,-28.3914}, {-74.8037,-46.8602}, {-40.089,-45.1376}, {-74.8149,-28.3136}, {-21.3072,-42.2177}, {-44.0778,-72.6353}, {-19.6969,-12.8527}, {-40.1318,-12.1601}, {-72.691,-11.4129}, {-56.0261,58.6741}, {-25.9127,16.9547}, {-25.7876,58.8675}, {-65.6229,41.9617}, {-54.4335,23.5133}, {-42.373,40.8524}, {-73.838,24.5874}, {-21.4917,38.55}, {-42.1744,72.8109}, {-37.8974,26.799}, {-41.7437,13.8871}, {-11.6095,24.3266}, {-11.0459,12.0094}, {-62.667,12.35}, {-77.7456,11.1537}, {30.3071,59.7167}, {17.1956,26.0167}, {54.7676,28.3721}, {60.8915,60.0343}, {74.5657,25.5053}, {26.8506,14.0295}, {34.8398,28.3978}, {67.9014,42.042}, {21.41,43.1056}, {44.3335,44.6677}, {13.2772,11.707}, {45.3579,73.2119}, {45.3492,14.2311}, {63.4799,13.386}, {78.0714,11.5344}, {17.01,-26.3775}, {32.4806,-56.1313}, {57.4213,-27.4824}, {62.0912,-59.2376}, {75.9483,-27.3616}, {26.893,-14.9717}, {69.1672,-41.7436}, {35.1527,-32.8534}, {18.4543,-42.1031}, {50.1344,-44.5201}, {46.1172,-72.4163}, {13.2711,-11.8907}, {44.2448,-15.2529}, {73.9368,-14.4869}, {60.5478,-11.7037} }; auto grid = UnstructuredGrid(points); auto pointcloud = functionspace::PointCloud(grid,util::Config("halo_radius",2000000)); auto field = pointcloud.createField(option::name("field")); auto lonlat = array::make_view(pointcloud.lonlat()); auto ghost = array::make_view(pointcloud.ghost()); auto view = array::make_view(field); auto fieldg_init = pointcloud.createField(option::name("fieldg_init")|option::global()); if (mpi::rank() == 0) { auto viewg = array::make_view(fieldg_init); gidx_t g=0; for (auto& p: grid.lonlat()) { double lat = p.lat() * M_PI/180.; viewg(g) = std::cos(4.*lat); g++; } } pointcloud.scatter(fieldg_init,field); size_t count_ghost{0}; for (idx_t i=0; i(option::name("field")|option::global()); if( mpi::rank() == 0 ) { EXPECT_EQ(fieldg.size(), grid.size()); } else { EXPECT_EQ(fieldg.size(), 0); } pointcloud.gather(field, fieldg); if (mpi::rank() == 0) { auto viewg = array::make_view(fieldg); gidx_t g=0; for (auto& p: grid.lonlat()) { double lat = p.lat() * M_PI/180.; EXPECT_APPROX_EQ( viewg(g), std::cos(4.*lat), tol ); g++; } } } } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/src/tests/functionspace/CMakeLists.txt0000664000175000017500000001364115160212070022422 0ustar alastairalastair# (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. if( HAVE_FCTEST ) if( NOT atlas_HAVE_ECTRANS ) set( transi_HAVE_MPI 1 ) set( ectrans_HAVE_MPI 1 ) endif() add_fctest( TARGET atlas_fctest_functionspace MPI 4 CONDITION eckit_HAVE_MPI AND (transi_HAVE_MPI OR ectrans_HAVE_MPI) AND MPI_SLOTS GREATER_EQUAL 4 LINKER_LANGUAGE Fortran SOURCES fctest_functionspace.F90 LIBS atlas_f ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) add_fctest( TARGET atlas_fctest_blockstructuredcolumns MPI 4 CONDITION eckit_HAVE_MPI AND MPI_SLOTS GREATER_EQUAL 4 LINKER_LANGUAGE Fortran SOURCES fctest_blockstructuredcolumns.F90 LIBS atlas_f ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) endif() ecbuild_add_test( TARGET atlas_test_functionspace SOURCES test_functionspace.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) ecbuild_add_test( TARGET atlas_test_functionspace_splitcomm MPI 4 CONDITION eckit_HAVE_MPI AND MPI_SLOTS GREATER_EQUAL 4 SOURCES test_functionspace_splitcomm.cc LIBS atlas 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_cubedsphere_functionspace MPI 8 CONDITION eckit_HAVE_MPI AND MPI_SLOTS GREATER_EQUAL 8 SOURCES test_cubedsphere_functionspace.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) ecbuild_add_test( TARGET atlas_test_structuredcolumns_biperiodic SOURCES test_structuredcolumns_biperiodic.cc LIBS atlas MPI 5 ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} CONDITION eckit_HAVE_MPI AND MPI_SLOTS GREATER_EQUAL 5 ) ecbuild_add_test( TARGET test_structuredcolumns_regional SOURCES test_structuredcolumns_regional.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) ecbuild_add_test( TARGET atlas_test_structuredcolumns SOURCES test_structuredcolumns.cc LIBS atlas MPI 5 ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} CONDITION eckit_HAVE_MPI AND MPI_SLOTS GREATER_EQUAL 5 ) ecbuild_add_test( TARGET atlas_test_blockstructuredcolumns MPI 3 SOURCES test_blockstructuredcolumns.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} CONDITION eckit_HAVE_MPI AND MPI_SLOTS GREATER_EQUAL 3 ) ecbuild_add_test( TARGET atlas_test_pointcloud SOURCES test_pointcloud.cc LIBS atlas MPI 2 ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} CONDITION eckit_HAVE_MPI AND NOT HAVE_GRIDTOOLS_STORAGE ) ecbuild_add_test( TARGET atlas_test_pointcloud_halo_creation SOURCES test_pointcloud_halo_creation.cc LIBS atlas MPI 4 ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} CONDITION eckit_HAVE_MPI AND NOT HAVE_GRIDTOOLS_STORAGE AND MPI_SLOTS GREATER_EQUAL 4 ) ecbuild_add_test( TARGET atlas_test_pointcloud_he_2PE SOURCES test_pointcloud_haloexchange_2PE.cc LIBS atlas MPI 2 ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} CONDITION eckit_HAVE_MPI AND NOT HAVE_GRIDTOOLS_STORAGE ) ecbuild_add_test( TARGET atlas_test_pointcloud_he_3PE SOURCES test_pointcloud_haloexchange_3PE.cc LIBS atlas MPI 3 ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} CONDITION eckit_HAVE_MPI AND NOT HAVE_GRIDTOOLS_STORAGE ) ecbuild_add_test( TARGET atlas_test_reduced_halo SOURCES test_reduced_halo.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) ecbuild_add_test( TARGET atlas_test_stencil SOURCES test_stencil.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) ecbuild_add_executable( TARGET atlas_test_stencil_parallel SOURCES test_stencil_parallel.cc LIBS atlas NOINSTALL ) ecbuild_add_test( TARGET atlas_test_stencil_parallel_mpi4 COMMAND atlas_test_stencil_parallel MPI 4 ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} CONDITION eckit_HAVE_MPI AND MPI_SLOTS GREATER_EQUAL 4 ) ecbuild_add_test( TARGET atlas_test_stencil_parallel_mpi16 COMMAND atlas_test_stencil_parallel MPI 16 ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} CONDITION eckit_HAVE_MPI AND MPI_SLOTS GREATER_EQUAL 16 ) set( _WITH_MPI ) if( eckit_HAVE_MPI ) set( _WITH_MPI MPI 4 CONDITION MPI_SLOTS GREATER_EQUAL 4) endif() ecbuild_add_executable( TARGET atlas_test_polygons SOURCES test_polygons.cc LIBS atlas NOINSTALL ) ecbuild_add_test( TARGET atlas_test_polygons_structuredcolumns COMMAND atlas_test_polygons ARGS --functionspace StructuredColumns ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ${_WITH_MPI} ) ecbuild_add_test( TARGET atlas_test_polygons_nodecolumns COMMAND atlas_test_polygons ARGS --functionspace NodeColumns ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ${_WITH_MPI} ) ecbuild_add_executable( TARGET atlas_test_polygons_projection SOURCES test_polygons_projection.cc LIBS atlas NOINSTALL ) ecbuild_add_test( TARGET atlas_test_polygons_projections_structuredcolumns COMMAND atlas_test_polygons_projection ARGS --functionspace StructuredColumns ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ${_WITH_MPI} ) ecbuild_add_test( TARGET atlas_test_polygons_projections_nodecolumns COMMAND atlas_test_polygons_projection ARGS --functionspace NodeColumns ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ${_WITH_MPI} ) # Tests ATLAS-286 ecbuild_add_test( TARGET atlas_test_structuredcolumns_haloexchange ${_WITH_MPI} SOURCES test_structuredcolumns_haloexchange.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) ecbuild_add_test( TARGET atlas_test_functionspace_locate MPI 3 CONDITION eckit_HAVE_MPI AND MPI_SLOTS GREATER_EQUAL 3 SOURCES test_functionspace_locate.cc LIBS atlas ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) atlas-0.46.0/src/atlas_acc_support/0000775000175000017500000000000015160212070017360 5ustar alastairalastairatlas-0.46.0/src/atlas_acc_support/atlas_acc.h0000664000175000017500000000211015160212070021435 0ustar alastairalastair/* * (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 #ifdef __cplusplus extern "C" { #endif typedef enum { atlas_acc_device_host = 0, atlas_acc_device_not_host = 1 } atlas_acc_device_t; void atlas_acc_map_data(void* cpu_ptr, void* gpu_ptr, size_t bytes); void atlas_acc_unmap_data(void* cpu_ptr); int atlas_acc_is_present(void* cpu_ptr, size_t bytes); void* atlas_acc_deviceptr(void* cpu_ptr); atlas_acc_device_t atlas_acc_get_device_type(); int atlas_acc_get_num_devices(); typedef enum { atlas_acc_compiler_id_unknown = 0, atlas_acc_compiler_id_nvidia = 1, atlas_acc_compiler_id_cray = 2 } atlas_acc_compiler_id_t; atlas_acc_compiler_id_t atlas_acc_compiler_id(); #ifdef __cplusplus } #endif atlas-0.46.0/src/atlas_acc_support/atlas_acc.cc0000664000175000017500000001107215160212070021602 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ // This file needs to be compiled with an OpenACC capable C compiler that is // compatible with the Fortran compiler #ifndef _OPENACC #error atlas_acc_map_data must be compiled with OpenACC capable compiler #endif #include #include #include "atlas_acc.h" extern "C" { void atlas_acc_map_data(void* cpu_ptr, void* gpu_ptr, unsigned long bytes) { acc_map_data(cpu_ptr, gpu_ptr, bytes); } void atlas_acc_unmap_data(void* cpu_ptr) { acc_unmap_data(cpu_ptr); } int atlas_acc_is_present(void* cpu_ptr, unsigned long bytes) { return acc_is_present(cpu_ptr, bytes); } void* atlas_acc_deviceptr(void* cpu_ptr) { return acc_deviceptr(cpu_ptr); } atlas_acc_device_t atlas_acc_get_device_type() { acc_device_t device_type = acc_get_device_type(); if (device_type == acc_device_host || device_type == acc_device_none) { return atlas_acc_device_host; } return atlas_acc_device_not_host; } #define BUFFER_PRINTF(buffer, ...) snprintf(buffer + strlen(buffer), sizeof(buffer) - strlen(buffer), __VA_ARGS__); const char* atlas_acc_version_str() { static char buffer[16]; if( strlen(buffer) != 0 ) { return buffer; } BUFFER_PRINTF(buffer, "%i", _OPENACC); switch( _OPENACC ) { case 201111: BUFFER_PRINTF(buffer, " (1.0)"); break; case 201306: BUFFER_PRINTF(buffer, " (2.0)"); break; case 201308: BUFFER_PRINTF(buffer, " (2.0)"); break; case 201510: BUFFER_PRINTF(buffer, " (2.5)"); break; case 201711: BUFFER_PRINTF(buffer, " (2.6)"); break; default: break; } return buffer; } const char* atlas_acc_info_str() { static char buffer[1024]; if( strlen(buffer) != 0 ) { return buffer; } // Platform information acc_device_t devicetype = acc_get_device_type(); int num_devices = acc_get_num_devices(devicetype); BUFFER_PRINTF(buffer, " OpenACC version: %s\n", atlas_acc_version_str()); if (devicetype == acc_device_host ) { BUFFER_PRINTF(buffer, " No OpenACC GPU devices available\n"); return buffer; } BUFFER_PRINTF(buffer, " Number of OpenACC devices: %i\n", num_devices); int device_num = acc_get_device_num(devicetype); BUFFER_PRINTF(buffer, " OpenACC Device number: %i\n", device_num); BUFFER_PRINTF(buffer, " OpenACC acc_device_t (enum value): %d", devicetype); switch( devicetype ) { case acc_device_host: BUFFER_PRINTF(buffer, " (acc_device_host)"); break; case acc_device_none: BUFFER_PRINTF(buffer, " (acc_device_none)"); break; case acc_device_not_host: BUFFER_PRINTF(buffer, " (acc_device_not_host)"); break; case acc_device_nvidia: BUFFER_PRINTF(buffer, " (acc_device_nvidia)"); break; default: break; } BUFFER_PRINTF(buffer, "\n"); // acc_get_property, acc_get_property_string introduced with OpenACC 2.6 #if _OPENACC >= 201711 long int mem = acc_get_property( device_num, devicetype, acc_property_memory); long int free_mem = acc_get_property( device_num, devicetype, acc_property_free_memory); const char *property_name = acc_get_property_string(device_num, devicetype, acc_property_name); const char *property_vendor = acc_get_property_string(device_num, devicetype, acc_property_vendor ); const char *property_driver = acc_get_property_string(device_num, devicetype, acc_property_driver ); // if (mem > 0) { BUFFER_PRINTF(buffer, " Memory on OpenACC device: %li\n", mem); } // if (free_mem > 0) { BUFFER_PRINTF(buffer, " Free Memory on OpenACC device: %li\n", free_mem); } // if (property_name != NULL) { BUFFER_PRINTF(buffer, " OpenACC device name: %s\n", property_name); } // if (property_vendor != NULL) { BUFFER_PRINTF(buffer, " OpenACC device vendor: %s\n", property_vendor); } // if (property_driver != NULL) { BUFFER_PRINTF(buffer, " OpenACC device driver: %s\n", property_driver); } #endif return buffer; } int atlas_acc_get_num_devices() { return acc_get_num_devices(acc_get_device_type()); } atlas_acc_compiler_id_t atlas_acc_compiler_id() { #if defined(__NVCOMPILER) return atlas_acc_compiler_id_nvidia; #else return atlas_acc_compiler_id_unknown; #endif } } atlas-0.46.0/src/atlas_acc_support/atlas_acc.F900000664000175000017500000000504315160212070021554 0ustar alastairalastairmodule atlas_acc use openacc implicit none private public :: atlas_acc_get_num_devices public :: atlas_acc_map_data public :: atlas_acc_unmap_data public :: atlas_acc_is_present public :: atlas_acc_get_device_type public :: atlas_acc_deviceptr contains function atlas_acc_compiler_id() bind(C,name="atlas_acc_compiler_id") result(compiler_id) use, intrinsic :: iso_c_binding, only : c_int integer(c_int) :: compiler_id ! compiler_id must match number in atlas_acc.h enum type #ifdef _CRAYFTN compiler_id = 2 ! cray #else compiler_id = 0 ! unknown #endif end function function atlas_acc_get_num_devices() bind(C,name="atlas_acc_get_num_devices") result(num_devices) use, intrinsic :: iso_c_binding, only : c_int integer(c_int) :: num_devices integer(acc_device_kind) :: devicetype devicetype = acc_get_device_type() num_devices = acc_get_num_devices(devicetype) end function subroutine atlas_acc_map_data(data_arg, data_dev, bytes) bind(C,name="atlas_acc_map_data") use, intrinsic :: iso_c_binding, only : c_ptr, c_size_t type(*), dimension(*) :: data_arg type(c_ptr), value :: data_dev integer(c_size_t), value :: bytes call acc_map_data(data_arg, data_dev, bytes) end subroutine subroutine atlas_acc_unmap_data(data_arg) bind(C,name="atlas_acc_unmap_data") use, intrinsic :: iso_c_binding, only : c_ptr type(*), dimension(*) :: data_arg call acc_unmap_data(data_arg) end subroutine function atlas_acc_is_present(data_arg, bytes) bind(C,name="atlas_acc_is_present") result(is_present) use, intrinsic :: iso_c_binding, only : c_size_t, c_ptr, c_char, c_int integer(c_int) :: is_present logical :: lpresent type(c_ptr), value :: data_arg integer(c_size_t), value :: bytes character(kind=c_char), pointer :: data_f(:) call c_f_pointer(data_arg, data_f,[bytes]) lpresent = acc_is_present(data_f) is_present = 0 if (lpresent) is_present = 1 end function function atlas_acc_deviceptr(data_arg) bind(C,name="atlas_acc_deviceptr") result(deviceptr) use, intrinsic :: iso_c_binding, only : c_ptr type(*), dimension(*) :: data_arg type(c_ptr):: deviceptr deviceptr = acc_deviceptr(data_arg) end function function atlas_acc_get_device_type() bind(C,name="atlas_acc_get_device_type") result(devicetype) use, intrinsic :: iso_c_binding, only : c_int integer(c_int) :: devicetype integer(acc_device_kind) :: acc_devicetype acc_devicetype = acc_get_device_type() if (acc_devicetype == acc_device_host .or. acc_devicetype == acc_device_none) then devicetype = 0 else devicetype = 1 endif end function end module atlas-0.46.0/src/atlas_acc_support/CMakeLists.txt0000664000175000017500000000160215160212070022117 0ustar alastairalastair# (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. if( atlas_HAVE_ACC ) if( CMAKE_CXX_COMPILER_ID MATCHES NVHPC ) if( NOT TARGET OpenACC::OpenACC_CXX ) ecbuild_error("ERROR: OpenACC::OpenACC_CXX TARGET not found") endif() ecbuild_add_library( TARGET atlas_acc_support SOURCES atlas_acc.cc ) target_link_libraries( atlas_acc_support PRIVATE OpenACC::OpenACC_CXX ) else() ecbuild_add_library( TARGET atlas_acc_support SOURCES atlas_acc.F90 ) target_link_libraries( atlas_acc_support PRIVATE OpenACC::OpenACC_Fortran ) endif() endif() atlas-0.46.0/src/atlas_f/0000775000175000017500000000000015160212070015263 5ustar alastairalastairatlas-0.46.0/src/atlas_f/redistribution/0000775000175000017500000000000015160212070020331 5ustar alastairalastairatlas-0.46.0/src/atlas_f/redistribution/atlas_Redistribution_module.F900000664000175000017500000001045515160212070026355 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. #include "atlas/atlas_f.h" module atlas_Redistribution_module use, intrinsic :: iso_c_binding, only : c_ptr use atlas_config_module, only : atlas_Config use atlas_functionspace_module, only : atlas_FunctionSpace use fckit_owned_object_module, only: fckit_owned_object implicit none public :: atlas_Redistribution private !------------------------------------------------------------------------------ TYPE, extends(fckit_owned_object) :: atlas_Redistribution ! Purpose : ! ------- ! *atlas_Redistribution* : Object passed from atlas to inspect redistribution ! Methods : ! ------- ! Author : ! ------ ! October-2023 Slavko Brdar *ECMWF* ! August-2015 Willem Deconinck *ECMWF* !------------------------------------------------------------------------------ contains procedure, public :: execute => atlas_Redistribution__execute procedure, public :: source => atlas_Redistribution__source procedure, public :: target => atlas_Redistribution__target #if FCKIT_FINAL_NOT_INHERITING final :: atlas_Redistribution__final_auto #endif END TYPE atlas_Redistribution !------------------------------------------------------------------------------ interface atlas_Redistribution module procedure ctor_cptr module procedure ctor_create end interface private :: c_ptr private :: fckit_owned_object !======================================================== contains !======================================================== ! ----------------------------------------------------------------------------- ! Redistribution routines function empty_config() result(config) type(atlas_Config) :: config config = atlas_Config() call config%return() end function function ctor_cptr( cptr ) result(this) use atlas_redistribution_c_binding type(atlas_Redistribution) :: this type(c_ptr), intent(in) :: cptr call this%reset_c_ptr( cptr ) call this%return() end function function ctor_create(fspace1, fspace2, redist_name) result(this) use atlas_redistribution_c_binding class(atlas_FunctionSpace), intent(in) :: fspace1, fspace2 character(len=*), intent(in), optional :: redist_name type(atlas_Redistribution) :: this type(atlas_Config) :: config config = empty_config() if (present(redist_name)) call config%set("type", redist_name) call this%reset_c_ptr( atlas__Redistribution__new__config(fspace1%CPTR_PGIBUG_A, fspace2%CPTR_PGIBUG_A, config%CPTR_PGIBUG_B) ) call config%final() call this%return() end function subroutine atlas_Redistribution__execute(this, field_1, field_2) use atlas_redistribution_c_binding use atlas_Field_module class(atlas_Redistribution), intent(in) :: this class(atlas_Field), intent(in) :: field_1 class(atlas_Field), intent(inout) :: field_2 call atlas__Redistribution__execute(this%CPTR_PGIBUG_A, field_1%CPTR_PGIBUG_A, field_2%CPTR_PGIBUG_A) end subroutine function atlas_Redistribution__source(this) result(fspace) use atlas_redistribution_c_binding class(atlas_Redistribution), intent(in) :: this type(atlas_FunctionSpace) :: fspace call fspace%reset_c_ptr(atlas__Redistribution__source(this%CPTR_PGIBUG_A)) call fspace%return() end function function atlas_Redistribution__target(this) result(fspace) use atlas_redistribution_c_binding class(atlas_Redistribution), intent(in) :: this type(atlas_FunctionSpace) :: fspace call fspace%reset_c_ptr(atlas__Redistribution__target(this%CPTR_PGIBUG_A)) call fspace%return() end function ! ---------------------------------------------------------------------------------------- #if FCKIT_FINAL_NOT_INHERITING ATLAS_FINAL subroutine atlas_Redistribution__final_auto(this) type(atlas_Redistribution), intent(inout) :: this #if FCKIT_FINAL_DEBUGGING write(0,*) "atlas_Redistribution__final_auto" #endif #if FCKIT_FINAL_NOT_PROPAGATING call this%final() #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine #endif ! ---------------------------------------------------------------------------------------- end module atlas_Redistribution_module atlas-0.46.0/src/atlas_f/numerics/0000775000175000017500000000000015160212070017110 5ustar alastairalastairatlas-0.46.0/src/atlas_f/numerics/atlas_Nabla_module.F900000664000175000017500000001122015160212070023132 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. #include "atlas/atlas_f.h" module atlas_Nabla_module use fckit_owned_object_module, only : fckit_owned_object implicit none private :: fckit_owned_object public :: atlas_Nabla private !------------------------------------------------------------------------------ TYPE, extends(fckit_owned_object) :: atlas_Nabla ! Purpose : ! ------- ! *Nabla* : ! Methods : ! ------- ! Author : ! ------ ! October-2015 Willem Deconinck *ECMWF* !------------------------------------------------------------------------------ contains procedure, public :: gradient => atlas_Nabla__gradient procedure, public :: divergence => atlas_Nabla__divergence procedure, public :: curl => atlas_Nabla__curl procedure, public :: laplacian => atlas_Nabla__laplacian procedure, public :: functionspace => atlas_Nabla__functionspace #if FCKIT_FINAL_NOT_INHERITING final :: atlas_Nabla__final_auto #endif END TYPE atlas_Nabla interface atlas_Nabla module procedure atlas_Nabla__cptr module procedure atlas_Nabla__method_config end interface !======================================================== contains !======================================================== function atlas_Nabla__cptr(cptr) result(this) use, intrinsic :: iso_c_binding, only: c_ptr type(atlas_Nabla) :: this type(c_ptr), intent(in) :: cptr call this%reset_c_ptr( cptr ) call this%return() end function function atlas_Nabla__method_config(method,config) result(this) use atlas_Nabla_c_binding use atlas_Method_module, only : atlas_Method use atlas_Config_module, only : atlas_Config type(atlas_Nabla) :: this class(atlas_Method), intent(in) :: method type(atlas_Config), intent(in), optional :: config type(atlas_Config) :: opt_config if( present(config) ) then call this%reset_c_ptr( atlas__Nabla__create(method%CPTR_PGIBUG_A,config%CPTR_PGIBUG_B) ) else opt_config = atlas_Config() call this%reset_c_ptr( atlas__Nabla__create(method%CPTR_PGIBUG_A,opt_config%CPTR_PGIBUG_B) ) call opt_config%final() endif call this%return() end function subroutine atlas_Nabla__gradient(this,scalar,grad) use atlas_Nabla_c_binding use atlas_Field_module, only : atlas_Field class(atlas_Nabla), intent(in) :: this class(atlas_Field), intent(in) :: scalar class(atlas_Field), intent(inout) :: grad call atlas__Nabla__gradient(this%CPTR_PGIBUG_A,scalar%CPTR_PGIBUG_A,grad%CPTR_PGIBUG_A) end subroutine subroutine atlas_Nabla__divergence(this,vector,div) use atlas_Nabla_c_binding use atlas_Field_module, only : atlas_Field class(atlas_Nabla), intent(in) :: this class(atlas_Field), intent(in) :: vector class(atlas_Field), intent(inout) :: div call atlas__Nabla__divergence(this%CPTR_PGIBUG_A,vector%CPTR_PGIBUG_A,div%CPTR_PGIBUG_A) end subroutine subroutine atlas_Nabla__curl(this,vector,curl) use atlas_Nabla_c_binding use atlas_Field_module, only : atlas_Field class(atlas_Nabla), intent(in) :: this class(atlas_Field), intent(in) :: vector class(atlas_Field), intent(inout) :: curl call atlas__Nabla__curl(this%CPTR_PGIBUG_A,vector%CPTR_PGIBUG_A,curl%CPTR_PGIBUG_A) end subroutine subroutine atlas_Nabla__laplacian(this,scalar,lapl) use atlas_Nabla_c_binding use atlas_Field_module, only : atlas_Field class(atlas_Nabla), intent(in) :: this class(atlas_Field), intent(in) :: scalar class(atlas_Field), intent(inout) :: lapl call atlas__Nabla__laplacian(this%CPTR_PGIBUG_A,scalar%CPTR_PGIBUG_A,lapl%CPTR_PGIBUG_A) end subroutine function atlas_Nabla__functionspace(this) result(functionspace) use atlas_Nabla_c_binding use atlas_Functionspace_module, only : atlas_FunctionSpace type(atlas_FunctionSpace) :: functionspace class(atlas_Nabla), intent(in) :: this call functionspace%reset_c_ptr( atlas__Nabla__functionspace(this%CPTR_PGIBUG_A) ) call functionspace%return() end function !------------------------------------------------------------------------------- #if FCKIT_FINAL_NOT_INHERITING ATLAS_FINAL subroutine atlas_Nabla__final_auto(this) type(atlas_Nabla), intent(inout) :: this #if FCKIT_FINAL_DEBUGGING write(0,*) "atlas_Nabla__final_auto" #endif #if FCKIT_FINAL_NOT_PROPAGATING call this%final() #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine #endif ! ----------------------------------------------------------------------------- end module atlas_Nabla_module atlas-0.46.0/src/atlas_f/numerics/atlas_Method_module.F900000664000175000017500000000465415160212070023352 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. #include "atlas/atlas_f.h" module atlas_Method_module use fckit_owned_object_module, only : fckit_owned_object implicit none private :: fckit_owned_object public :: atlas_Method private !------------------------------------------------------------------------------ TYPE, extends(fckit_owned_object) :: atlas_Method ! Purpose : ! ------- ! *Method* : ! Describes how nodes are ordered ! Describes how parallelisation for fields is done ! Methods : ! ------- ! name : The name or tag this function space was created with ! Author : ! ------ ! 20-Nov-2013 Willem Deconinck *ECMWF* !------------------------------------------------------------------------------ contains procedure, public :: name => atlas_Method__name #if FCKIT_FINAL_NOT_INHERITING final :: atlas_Method__final_auto #endif END TYPE atlas_Method interface atlas_Method module procedure atlas_Method__cptr end interface !======================================================== contains !======================================================== function atlas_Method__cptr(cptr) result(this) use, intrinsic :: iso_c_binding, only : c_ptr type(atlas_Method) :: this type(c_ptr), intent(in) :: cptr call this%reset_c_ptr( cptr ) call this%return() end function function atlas_Method__name(this) result(name) use atlas_Method_c_binding use fckit_c_interop_module, only : c_ptr_to_string use, intrinsic :: iso_c_binding, only : c_ptr class(atlas_Method), intent(in) :: this character(len=:), allocatable :: name type(c_ptr) :: name_c_str name_c_str = atlas__Method__name(this%CPTR_PGIBUG_A) name = c_ptr_to_string(name_c_str) end function !------------------------------------------------------------------------------- #if FCKIT_FINAL_NOT_INHERITING ATLAS_FINAL subroutine atlas_Method__final_auto(this) type(atlas_Method), intent(inout) :: this #if FCKIT_FINAL_DEBUGGING write(0,*) "atlas_Method__final_auto" #endif #if FCKIT_FINAL_NOT_PROPAGATING call this%final() #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine #endif end module atlas_Method_module atlas-0.46.0/src/atlas_f/numerics/atlas_fvm_module.F900000664000175000017500000000727615160212070022725 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. #include "atlas/atlas_f.h" module atlas_fvm_module use fckit_owned_object_module, only : fckit_owned_object use atlas_Method_module, only : atlas_Method implicit none private :: fckit_owned_object private :: atlas_Method public :: atlas_fvm_Method private !------------------------------------------------------------------------------ TYPE, extends(atlas_Method) :: atlas_fvm_Method ! Purpose : ! ------- ! *Method* : ! Describes how nodes are ordered ! Describes how parallelisation for fields is done ! Methods : ! ------- ! name : The name or tag this function space was created with ! Author : ! ------ ! 20-Nov-2013 Willem Deconinck *ECMWF* !------------------------------------------------------------------------------ contains procedure, public :: node_columns procedure, public :: edge_columns #if FCKIT_FINAL_NOT_INHERITING final :: atlas_fvm_Method__final_auto #endif END TYPE atlas_fvm_Method interface atlas_fvm_Method module procedure atlas_fvm_Method__cptr module procedure atlas_fvm_Method__mesh_config end interface !======================================================== contains !======================================================== function atlas_fvm_Method__cptr(cptr) result(this) use, intrinsic :: iso_c_binding, only : c_ptr type(atlas_fvm_Method) :: this type(c_ptr), intent(in) :: cptr call this%reset_c_ptr( cptr ) end function function atlas_fvm_Method__mesh_config(mesh,config) result(this) use atlas_fvm_method_c_binding use atlas_Config_module, only : atlas_Config use atlas_Mesh_module, only : atlas_Mesh type(atlas_fvm_Method) :: this type(atlas_Mesh), intent(inout) :: mesh type(atlas_Config), intent(in), optional :: config type(atlas_Config) :: opt_config if( present(config) ) then call this%reset_c_ptr( atlas__numerics__fvm__Method__new(mesh%CPTR_PGIBUG_A, & config%CPTR_PGIBUG_B) ) else opt_config = atlas_Config() call this%reset_c_ptr( atlas__numerics__fvm__Method__new(mesh%CPTR_PGIBUG_A, & opt_config%CPTR_PGIBUG_B) ) call opt_config%final() endif call this%return() end function function node_columns(this) use atlas_fvm_method_c_binding use atlas_functionspace_NodeColumns_module, only : atlas_functionspace_NodeColumns type(atlas_functionspace_NodeColumns) :: node_columns class(atlas_fvm_Method) :: this node_columns = atlas_functionspace_NodeColumns( & & atlas__numerics__fvm__Method__functionspace_nodes(this%CPTR_PGIBUG_A) ) call node_columns%return() end function function edge_columns(this) use atlas_fvm_method_c_binding use atlas_functionspace_EdgeColumns_module, only : atlas_functionspace_EdgeColumns type(atlas_functionspace_EdgeColumns) :: edge_columns class(atlas_fvm_Method) :: this edge_columns = atlas_functionspace_EdgeColumns( & & atlas__numerics__fvm__Method__functionspace_edges(this%CPTR_PGIBUG_A) ) call edge_columns%return() end function !------------------------------------------------------------------------------- #if FCKIT_FINAL_NOT_INHERITING ATLAS_FINAL subroutine atlas_fvm_Method__final_auto(this) type(atlas_fvm_Method), intent(inout) :: this #if FCKIT_FINAL_DEBUGGING write(0,*) "atlas_fvm_Method__final_auto" #endif #if FCKIT_FINAL_NOT_PROPAGATING call this%final() #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine #endif end module atlas_fvm_module atlas-0.46.0/src/atlas_f/projection/0000775000175000017500000000000015160212070017437 5ustar alastairalastairatlas-0.46.0/src/atlas_f/projection/atlas_Projection_module.F900000664000175000017500000001644715160212070024600 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. #include "atlas/atlas_f.h" module atlas_Projection_module use fckit_owned_object_module, only : fckit_owned_object use atlas_config_module, only : atlas_Config implicit none private :: fckit_owned_object private :: atlas_Config public :: atlas_Projection public :: atlas_RotatedLonLatProjection public :: atlas_LambertConformalConicProjection public :: atlas_RotatedSchmidtProjection private !------------------------------------------------------------------------------ TYPE, extends(fckit_owned_object) :: atlas_Projection ! Purpose : ! ------- ! *atlas_Projection* : ! Methods : ! ------- ! type: The name or tag this function space was created with ! Author : ! ------ ! May-2020 Willem Deconinck *ECMWF* !------------------------------------------------------------------------------ contains procedure, public :: type => atlas_Projection__type procedure, public :: hash procedure, public :: spec procedure, public :: xy2lonlat procedure, public :: lonlat2xy #if FCKIT_FINAL_NOT_INHERITING final :: atlas_Projection__final_auto #endif END TYPE atlas_Projection interface atlas_Projection module procedure atlas_Projection__ctor_cptr module procedure atlas_Projection__ctor_config end interface interface atlas_RotatedSchmidtProjection module procedure atlas_RotatedSchmidtProjection_real64 end interface interface atlas_LambertConformalConicProjection module procedure atlas_LambertConformalConicProjection_real64 end interface interface atlas_RotatedLonLatProjection module procedure atlas_RotatedLonLatProjection_NP_real64 end interface !======================================================== contains !======================================================== function atlas_Projection__ctor_cptr(cptr) result(this) use, intrinsic :: iso_c_binding, only : c_ptr type(atlas_Projection) :: this type(c_ptr), intent(in) :: cptr call this%reset_c_ptr( cptr ) call this%return() end function function atlas_Projection__ctor_config(config) result(this) use atlas_projection_c_binding use, intrinsic :: iso_c_binding, only : c_ptr type(atlas_Projection) :: this type(atlas_Config) :: config call this%reset_c_ptr( atlas__Projection__ctor_config(config%CPTR_PGIBUG_B) ) call this%return() end function function atlas_Projection__type(this) result(type_) use atlas_projection_c_binding use fckit_c_interop_module, only : c_ptr_to_string, c_ptr_free use, intrinsic :: iso_c_binding, only : c_ptr class(atlas_Projection), intent(in) :: this character(len=:), allocatable :: type_ type(c_ptr) :: type_c_str integer :: size call atlas__Projection__type(this%CPTR_PGIBUG_A, type_c_str, size ) type_ = c_ptr_to_string(type_c_str) call c_ptr_free(type_c_str) end function function hash(this) use atlas_projection_c_binding use fckit_c_interop_module, only : c_ptr_to_string, c_ptr_free use, intrinsic :: iso_c_binding, only : c_ptr class(atlas_Projection), intent(in) :: this character(len=:), allocatable :: hash type(c_ptr) :: hash_c_str integer :: size call atlas__Projection__hash(this%CPTR_PGIBUG_A, hash_c_str, size ) hash = c_ptr_to_string(hash_c_str) call c_ptr_free(hash_c_str) end function function spec(this) use atlas_projection_c_binding class(atlas_Projection), intent(in) :: this type(atlas_Config) :: spec spec = atlas_Config( atlas__Projection__spec(this%CPTR_PGIBUG_A) ) call spec%return() end function subroutine xy2lonlat(this, x, y, lon, lat) use atlas_projection_c_binding use, intrinsic :: iso_c_binding, only : c_double class(atlas_Projection), intent(in) :: this real(c_double), intent(in) :: x real(c_double), intent(in) :: y real(c_double), intent(out) :: lon real(c_double), intent(out) :: lat call atlas__Projection__xy2lonlat(this%CPTR_PGIBUG_A, x, y, lon, lat ) end subroutine subroutine lonlat2xy(this, lon, lat, x, y) use atlas_projection_c_binding use, intrinsic :: iso_c_binding, only : c_double class(atlas_Projection), intent(in) :: this real(c_double), intent(in) :: lon real(c_double), intent(in) :: lat real(c_double), intent(out) :: x real(c_double), intent(out) :: y call atlas__Projection__lonlat2xy(this%CPTR_PGIBUG_A, lon, lat, x, y ) end subroutine !------------------------------------------------------------------------------ #if FCKIT_FINAL_NOT_INHERITING ATLAS_FINAL subroutine atlas_Projection__final_auto(this) type(atlas_Projection), intent(inout) :: this #if FCKIT_FINAL_DEBUGGING write(0,*) "atlas_Projection__final_auto" #endif #if FCKIT_FINAL_NOT_PROPAGATING call this%final() #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine #endif !------------------------------------------------------------------------------ function atlas_RotatedSchmidtProjection_real64(stretching_factor,north_pole,rotation_angle) result(this) use, intrinsic :: iso_c_binding, only : c_double type(atlas_Projection) :: this real(c_double), intent(in) :: stretching_factor real(c_double), intent(in), optional :: north_pole(2) real(c_double), intent(in), optional :: rotation_angle type(atlas_Config) :: config config = atlas_Config() call config%set("type","rotated_schmidt") call config%set("stretching_factor",stretching_factor) if( present(rotation_angle) ) then call config%set("rotation_angle", rotation_angle) endif if( present(north_pole) ) then call config%set("north_pole",north_pole) endif this = atlas_Projection(config) call this%return() end function ! ----------------------------------------------------------------------------- function atlas_LambertConformalConicProjection_real64(longitude0,latitude0,latitude1,latitude2) result(this) use, intrinsic :: iso_c_binding, only : c_double type(atlas_Projection) :: this real(c_double), intent(in) :: longitude0 real(c_double), intent(in) :: latitude0 real(c_double), intent(in), optional :: latitude1 real(c_double), intent(in), optional :: latitude2 type(atlas_Config) :: config config = atlas_Config() call config%set("type","lambert_conformal_conic") call config%set("longitude0",longitude0) call config%set("latitude0",latitude0) if( present(latitude1) ) then call config%set("latitude1", latitude1) endif if( present(latitude2) ) then call config%set("latitude2",latitude2) endif this = atlas_Projection(config) call this%return() end function ! ----------------------------------------------------------------------------- function atlas_RotatedLonLatProjection_NP_real64(north_pole,rotation_angle) result(this) use, intrinsic :: iso_c_binding, only : c_double type(atlas_Projection) :: this real(c_double), intent(in) :: north_pole(2) real(c_double), intent(in), optional :: rotation_angle type(atlas_Config) :: config config = atlas_Config() call config%set("type","rotated_lonlat") call config%set("north_pole",north_pole) if( present(rotation_angle) ) then call config%set("rotation_angle", rotation_angle) endif this = atlas_Projection(config) call this%return() end function ! ----------------------------------------------------------------------------- end module atlas_Projection_module atlas-0.46.0/src/atlas_f/util/0000775000175000017500000000000015160212070016240 5ustar alastairalastairatlas-0.46.0/src/atlas_f/util/atlas_Geometry_module.F900000664000175000017500000002027215160212070023047 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. #include "atlas/atlas_f.h" module atlas_Geometry_module use, intrinsic :: iso_c_binding use fckit_exception_module, only : fckit_exception use fckit_owned_object_module, only : fckit_owned_object implicit none private :: fckit_owned_object public :: atlas_Geometry private !------------------------------------------------------------------------------ TYPE, extends(fckit_owned_object) :: atlas_Geometry ! Purpose : ! ------- ! *Geometry* : Container of Geometry ! Methods : ! ------- ! Author : ! ------ ! April-2020 Benjamin Menetrier *IRIT-JCSDA* !------------------------------------------------------------------------------ contains procedure, public :: delete => atlas_Geometry__delete procedure :: xyz2lonlat_separate_coords => Geometry__xyz2lonlat_separate_coords procedure :: xyz2lonlat_vectorized_coords => Geometry__xyz2lonlat_vectorized_coords generic :: xyz2lonlat => xyz2lonlat_separate_coords, xyz2lonlat_vectorized_coords procedure :: lonlat2xyz_separate_coords => Geometry__lonlat2xyz_separate_coords procedure :: lonlat2xyz_vectorized_coords => Geometry__lonlat2xyz_vectorized_coords generic :: lonlat2xyz => lonlat2xyz_separate_coords, lonlat2xyz_vectorized_coords procedure :: distance_lonlat_separate_coords => Geometry__distance_lonlat_separate_coords procedure :: distance_xyz_separate_coords => Geometry__distance_xyz_separate_coords procedure :: distance_vectorized_coords => Geometry__distance_vectorized_coords generic :: distance => distance_lonlat_separate_coords, distance_xyz_separate_coords, distance_vectorized_coords procedure :: radius => Geometry__radius procedure :: area => Geometry__area #if FCKIT_FINAL_NOT_INHERITING final :: atlas_Geometry__final_auto #endif END TYPE atlas_Geometry !------------------------------------------------------------------------------ interface atlas_Geometry module procedure atlas_Geometry__ctor_name module procedure atlas_Geometry__ctor_radius end interface !------------------------------------------------------------------------------ !======================================================== contains !======================================================== ! ----------------------------------------------------------------------------- ! Geometry routines function atlas_Geometry__ctor_name(name) result(this) use atlas_Geometry_c_binding use fckit_c_interop_module character(kind=c_char,len=*), intent(in) :: name type(atlas_Geometry) :: this call this%reset_c_ptr( atlas__Geometry__new_name( c_str(name) ) ) call this%return() end function atlas_Geometry__ctor_name function atlas_Geometry__ctor_radius(radius) result(this) use atlas_Geometry_c_binding use fckit_c_interop_module real(c_double), intent(in) :: radius type(atlas_Geometry) :: this call this%reset_c_ptr( atlas__Geometry__new_radius( radius ) ) call this%return() end function atlas_Geometry__ctor_radius subroutine atlas_Geometry__delete(this) use atlas_Geometry_c_binding class(atlas_Geometry), intent(inout) :: this if ( .not. this%is_null() ) then call atlas__Geometry__delete(this%CPTR_PGIBUG_A) end if call this%reset_c_ptr() end subroutine atlas_Geometry__delete subroutine Geometry__xyz2lonlat_separate_coords(this, x, y, z, lon, lat) use atlas_Geometry_c_binding use fckit_c_interop_module class(atlas_Geometry), intent(in) :: this real(c_double), intent(in) :: x real(c_double), intent(in) :: y real(c_double), intent(in) :: z real(c_double), intent(out) :: lon real(c_double), intent(out) :: lat call atlas__Geometry__xyz2lonlat(this%CPTR_PGIBUG_A, x, y, z, lon, lat) end subroutine Geometry__xyz2lonlat_separate_coords subroutine Geometry__xyz2lonlat_vectorized_coords(this, xyz, lonlat) use atlas_Geometry_c_binding use fckit_c_interop_module class(atlas_Geometry), intent(in) :: this real(c_double), intent(in) :: xyz(3) real(c_double), intent(out) :: lonlat(2) call atlas__Geometry__xyz2lonlat(this%CPTR_PGIBUG_A, xyz(1), xyz(2), xyz(3), lonlat(1), lonlat(2)) end subroutine Geometry__xyz2lonlat_vectorized_coords subroutine Geometry__lonlat2xyz_separate_coords(this, lon, lat, x, y, z) use atlas_Geometry_c_binding use fckit_c_interop_module class(atlas_Geometry), intent(in) :: this real(c_double), intent(in) :: lon real(c_double), intent(in) :: lat real(c_double), intent(out) :: x real(c_double), intent(out) :: y real(c_double), intent(out) :: z call atlas__Geometry__lonlat2xyz(this%CPTR_PGIBUG_A, lon, lat, x, y, z) end subroutine Geometry__lonlat2xyz_separate_coords subroutine Geometry__lonlat2xyz_vectorized_coords(this, lonlat, xyz) use atlas_Geometry_c_binding use fckit_c_interop_module class(atlas_Geometry), intent(in) :: this real(c_double), intent(in) :: lonlat(2) real(c_double), intent(out) :: xyz(3) call atlas__Geometry__lonlat2xyz(this%CPTR_PGIBUG_A, lonlat(1), lonlat(2), xyz(1), xyz(2), xyz(3)) end subroutine Geometry__lonlat2xyz_vectorized_coords function Geometry__distance_lonlat_separate_coords(this, lon1, lat1, lon2, lat2) result(distance_lonlat) use atlas_Geometry_c_binding use fckit_c_interop_module class(atlas_Geometry), intent(in) :: this real(c_double), intent(in) :: lon1 real(c_double), intent(in) :: lat1 real(c_double), intent(in) :: lon2 real(c_double), intent(in) :: lat2 real(c_double) :: distance_lonlat distance_lonlat = atlas__Geometry__distance_lonlat(this%CPTR_PGIBUG_A, lon1, lat1, lon2, lat2) end function Geometry__distance_lonlat_separate_coords function Geometry__distance_xyz_separate_coords(this, x1, y1, z1, x2, y2, z2) result(distance_xyz) use atlas_Geometry_c_binding use fckit_c_interop_module class(atlas_Geometry), intent(in) :: this real(c_double), intent(in) :: x1 real(c_double), intent(in) :: y1 real(c_double), intent(in) :: z1 real(c_double), intent(in) :: x2 real(c_double), intent(in) :: y2 real(c_double), intent(in) :: z2 real(c_double) :: distance_xyz distance_xyz = atlas__Geometry__distance_xyz(this%CPTR_PGIBUG_A, x1, y1, z1, x2, y2, z2) end function Geometry__distance_xyz_separate_coords function Geometry__distance_vectorized_coords(this, point1, point2) result(distance_point) use atlas_Geometry_c_binding use fckit_c_interop_module class(atlas_Geometry), intent(in) :: this real(c_double), intent(in) :: point1(:) real(c_double), intent(in) :: point2(:) real(c_double) :: distance_point if ((size(point1)==2).and.(size(point2)==2)) then ! Lon/lat distance distance_point = atlas__Geometry__distance_lonlat(this%CPTR_PGIBUG_A, point1(1), point1(2), point2(1), point2(2)) elseif ((size(point1)==3).and.(size(point2)==3)) then ! XYZ distance distance_point = atlas__Geometry__distance_xyz(this%CPTR_PGIBUG_A, point1(1), point1(2), point1(3), point2(1), point2(2), & & point2(3)) else ! Abort call fckit_exception%abort('wrong inputs for geometry distance with vectorized coordinates', 'altas_Geometry_module', 190) end if end function Geometry__distance_vectorized_coords function Geometry__radius(this) result(radius) use atlas_Geometry_c_binding use fckit_c_interop_module class(atlas_Geometry), intent(in) :: this real(c_double) :: radius radius = atlas__Geometry__radius(this%CPTR_PGIBUG_A) end function Geometry__radius function Geometry__area(this) result(area) use atlas_Geometry_c_binding use fckit_c_interop_module class(atlas_Geometry), intent(in) :: this real(c_double) :: area area = atlas__Geometry__area(this%CPTR_PGIBUG_A) end function Geometry__area !------------------------------------------------------------------------------- ATLAS_FINAL subroutine atlas_Geometry__final_auto(this) type(atlas_Geometry), intent(inout) :: this #if FCKIT_FINAL_DEBUGGING write(0,*) "atlas_Geometry__final_auto" #endif #if FCKIT_FINAL_NOT_PROPAGATING call this%final() #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine end module atlas_Geometry_module atlas-0.46.0/src/atlas_f/util/atlas_kinds_module.F900000664000175000017500000000332215160212070022361 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. #include "atlas/atlas_f.h" module atlas_kinds_module use, intrinsic :: iso_c_binding, only: & & c_int, c_long, c_double, c_float implicit none private private :: c_long, c_int, c_double, c_float public :: ATLAS_KIND_GIDX public :: ATLAS_KIND_IDX public :: ATLAS_KIND_REAL64 public :: ATLAS_KIND_REAL32 public :: ATLAS_KIND_INT64 public :: ATLAS_KIND_INT32 #if ATLAS_BITS_GLOBAL == 32 integer, parameter :: ATLAS_KIND_GIDX = c_int #elif ATLAS_BITS_GLOBAL == 64 integer, parameter :: ATLAS_KIND_GIDX = c_long #else #error ATLAS_BITS_GLOBAL must be either 32 or 64 #endif #if ATLAS_BITS_LOCAL == 32 integer, parameter :: ATLAS_KIND_IDX = c_int #elif ATLAS_BITS_LOCAL == 64 integer, parameter :: ATLAS_KIND_IDX = c_long #else #error ATLAS_BITS_LOCAL must be either 32 or 64 #endif integer, parameter :: ATLAS_KIND_REAL64 = c_double integer, parameter :: ATLAS_KIND_REAL32 = c_float integer, parameter :: ATLAS_KIND_INT64 = c_long integer, parameter :: ATLAS_KIND_INT32 = c_int ENUM, bind(c) enumerator :: openmode enumerator :: app = 1 enumerator :: out = 16 end ENUM ! ============================================================================= CONTAINS ! ============================================================================= ! ----------------------------------------------------------------------------- end module atlas_kinds_module atlas-0.46.0/src/atlas_f/util/atlas_Config_module.F900000664000175000017500000005121515160212070022462 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. #include "atlas/atlas_f.h" module atlas_config_module use fckit_shared_object_module, only : fckit_shared_object, fckit_c_deleter, fckit_c_nodeleter implicit none public :: atlas_Config private TYPE, extends(fckit_shared_object) :: atlas_Config ! Purpose : ! ------- ! *Config* : Container of Config, parameters or attributes ! The Config are seted as key, value pairs ! Methods : ! ------- ! set : set a new property with given key and value ! set : Modify a property with given key and value ! get : Return a property value for given key ! Author : ! ------ ! June-2015 Willem Deconinck *ECMWF* !------------------------------------------------------------------------------ contains procedure, private :: set_config => atlas_Config__set_config procedure, private :: set_config_list => atlas_Config__set_config_list procedure, private :: set_logical => atlas_Config__set_logical procedure, private :: set_int32 => atlas_Config__set_int32 procedure, private :: set_int64 => atlas_Config__set_int64 procedure, private :: set_real32 => atlas_Config__set_real32 procedure, private :: set_real64 => atlas_Config__set_real64 procedure, private :: set_string => atlas_Config__set_string procedure, private :: set_array_int32 => atlas_Config__set_array_int32 procedure, private :: set_array_int64 => atlas_Config__set_array_int64 procedure, private :: set_array_real32 => atlas_Config__set_array_real32 procedure, private :: set_array_real64 => atlas_Config__set_array_real64 procedure :: has => atlas_Config__has generic :: set => set_config, set_config_list, set_logical, set_int32, set_int64, set_real32, set_real64, & set_string, set_array_int32, set_array_int64, set_array_real32, set_array_real64 procedure, private :: get_config => atlas_Config__get_config procedure, private :: get_config_list => atlas_Config__get_config_list procedure, private :: get_int32 => atlas_Config__get_int32 procedure, private :: get_logical => atlas_Config__get_logical procedure, private :: get_real32 => atlas_Config__get_real32 procedure, private :: get_real64 => atlas_Config__get_real64 procedure, private :: get_string => atlas_Config__get_string procedure, private :: get_array_int32 => atlas_Config__get_array_int32 procedure, private :: get_array_int64 => atlas_Config__get_array_int64 procedure, private :: get_array_real32 => atlas_Config__get_array_real32 procedure, private :: get_array_real64 => atlas_Config__get_array_real64 generic :: get => get_config, get_config_list, get_int32, get_logical, get_real32, get_real64, & get_string, get_array_int32, get_array_int64, get_array_real32, get_array_real64 procedure :: json => atlas_Config__json #if FCKIT_FINAL_NOT_INHERITING final :: atlas_Config__final_auto #endif END TYPE atlas_Config !------------------------------------------------------------------------------ interface atlas_Config module procedure atlas_Config__ctor module procedure atlas_Config__ctor_from_cptr module procedure atlas_Config__ctor_from_file module procedure atlas_Config__ctor_from_json end interface !------------------------------------------------------------------------------ private :: fckit_shared_object private :: fckit_c_deleter private :: fckit_c_nodeleter !======================================================== contains !======================================================== ! ----------------------------------------------------------------------------- ! Config routines #if FCKIT_FINAL_NOT_INHERITING ATLAS_FINAL subroutine atlas_Config__final_auto(this) type(atlas_Config), intent(inout) :: this #if FCKIT_FINAL_DEBUGGING write(0,*) "atlas_Config__final_auto" #endif #if FCKIT_FINAL_NOT_PROPAGATING call this%final() #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine #endif function atlas_Config__ctor() result(this) use atlas_Config_c_binding type(atlas_Config) :: this call this%reset_c_ptr( atlas__Config__new(), & & fckit_c_deleter(atlas__Config__delete) ) call this%return() end function atlas_Config__ctor function atlas_Config__ctor_from_cptr(cptr,delete) result(this) use, intrinsic :: iso_c_binding, only : c_ptr use atlas_Config_c_binding type(c_ptr), value :: cptr type(atlas_Config) :: this logical, optional :: delete logical :: opt_delete opt_delete = .true. if( present(delete) ) opt_delete = delete if( opt_delete ) then call this%reset_c_ptr( cptr, fckit_c_deleter(atlas__Config__delete) ) else call this%reset_c_ptr( cptr, fckit_c_nodeleter() ) endif call this%return() end function function atlas_Config__ctor_from_json(json) result(this) use fckit_c_interop_module, only : c_str use atlas_Config_c_binding use atlas_JSON_module type(atlas_Config) :: this class(atlas_JSON) :: json call this%reset_c_ptr( atlas__Config__new_from_json(c_str(json%str())), & & fckit_c_deleter(atlas__Config__delete) ) call this%return() end function atlas_Config__ctor_from_json function atlas_Config__ctor_from_file(path) result(this) use fckit_c_interop_module, only : c_str use atlas_Config_c_binding use atlas_JSON_module type(atlas_Config) :: this class(atlas_PathName), intent(in) :: path call this%reset_c_ptr( atlas__Config__new_from_file(c_str(path%str())), & & fckit_c_deleter(atlas__Config__delete) ) call this%return() end function atlas_Config__ctor_from_file function atlas_Config__has(this, name) result(value) use fckit_c_interop_module, only : c_str use atlas_Config_c_binding class(atlas_Config), intent(inout) :: this character(len=*), intent(in) :: name logical :: value integer :: value_int value_int = atlas__Config__has(this%CPTR_PGIBUG_B, c_str(name) ) if( value_int == 1 ) then value = .True. else value = .False. end if end function atlas_Config__has subroutine atlas_Config__set_config(this, name, value) use fckit_c_interop_module, only : c_str use atlas_Config_c_binding class(atlas_Config), intent(inout) :: this character(len=*), intent(in) :: name class(atlas_Config), intent(in) :: value call atlas__Config__set_config(this%CPTR_PGIBUG_B, c_str(name), value%CPTR_PGIBUG_B ) end subroutine atlas_Config__set_config subroutine atlas_Config__set_config_list(this, name, value) use, intrinsic :: iso_c_binding, only : c_ptr, c_loc use fckit_c_interop_module, only : c_str use atlas_Config_c_binding class(atlas_Config), intent(inout) :: this character(len=*), intent(in) :: name type(atlas_Config), intent(in) :: value(:) !PGI (17.7) compiler bug when "type" replaced with "class" type(c_ptr), target :: value_cptrs(size(value)) integer :: j if( size(value) > 0 ) then do j=1,size(value) value_cptrs(j) = value(j)%CPTR_PGIBUG_B enddo call atlas__Config__set_config_list(this%CPTR_PGIBUG_B, c_str(name), c_loc(value_cptrs(1)), size(value_cptrs) ) endif end subroutine atlas_Config__set_config_list subroutine atlas_Config__set_logical(this, name, value) use fckit_c_interop_module, only : c_str use atlas_Config_c_binding class(atlas_Config), intent(inout) :: this character(len=*), intent(in) :: name logical, intent(in) :: value integer :: value_int if( value ) then value_int = 1 else value_int = 0 end if call atlas__Config__set_int(this%CPTR_PGIBUG_B, c_str(name), value_int ) end subroutine atlas_Config__set_logical subroutine atlas_Config__set_int32(this, name, value) use, intrinsic :: iso_c_binding, only : c_int use fckit_c_interop_module, only : c_str use atlas_Config_c_binding class(atlas_Config), intent(inout) :: this character(len=*), intent(in) :: name integer(c_int), intent(in) :: value call atlas__Config__set_int(this%CPTR_PGIBUG_B, c_str(name), value) end subroutine atlas_Config__set_int32 subroutine atlas_Config__set_int64(this, name, value) use, intrinsic :: iso_c_binding, only : c_long use fckit_c_interop_module, only : c_str use atlas_Config_c_binding class(atlas_Config), intent(inout) :: this character(len=*), intent(in) :: name integer(c_long), intent(in) :: value call atlas__Config__set_long(this%CPTR_PGIBUG_B, c_str(name), value) end subroutine atlas_Config__set_int64 subroutine atlas_Config__set_real32(this, name, value) use, intrinsic :: iso_c_binding, only : c_float use fckit_c_interop_module, only : c_str use atlas_Config_c_binding class(atlas_Config), intent(inout) :: this character(len=*), intent(in) :: name real(c_float), intent(in) :: value call atlas__Config__set_float(this%CPTR_PGIBUG_B, c_str(name) ,value) end subroutine atlas_Config__set_real32 subroutine atlas_Config__set_real64(this, name, value) use, intrinsic :: iso_c_binding, only : c_double use fckit_c_interop_module, only : c_str use atlas_Config_c_binding class(atlas_Config), intent(inout) :: this character(len=*), intent(in) :: name real(c_double), intent(in) :: value call atlas__Config__set_double(this%CPTR_PGIBUG_B, c_str(name) ,value) end subroutine atlas_Config__set_real64 subroutine atlas_Config__set_string(this, name, value) use fckit_c_interop_module, only : c_str use atlas_Config_c_binding class(atlas_Config), intent(inout) :: this character(len=*), intent(in) :: name character(len=*), intent(in) :: value call atlas__Config__set_string(this%CPTR_PGIBUG_B, c_str(name) , c_str(value) ) end subroutine atlas_Config__set_string function atlas_Config__get_config(this, name, value) result(found) use fckit_c_interop_module, only : c_str use atlas_Config_c_binding logical :: found class(atlas_Config), intent(in) :: this character(len=*), intent(in) :: name class(atlas_Config), intent(inout) :: value integer :: found_int value = atlas_Config() found_int = atlas__Config__get_config( this%CPTR_PGIBUG_B, c_str(name), value%CPTR_PGIBUG_B ) found = .False. if (found_int == 1) found = .True. end function atlas_Config__get_config function atlas_Config__get_config_list(this, name, value) result(found) use, intrinsic :: iso_c_binding, only : c_ptr, c_f_pointer, c_null_ptr use fckit_c_interop_module, only : c_str, c_ptr_free use atlas_Config_c_binding logical :: found class(atlas_Config), intent(in) :: this character(len=*), intent(in) :: name type(atlas_Config), allocatable, intent(inout) :: value(:) type(c_ptr) :: value_list_cptr type(c_ptr), pointer :: value_cptrs(:) integer :: value_list_allocated integer :: value_list_size integer :: found_int integer :: j value_list_cptr = c_null_ptr found_int = atlas__Config__get_config_list(this%CPTR_PGIBUG_B, c_str(name), & & value_list_cptr, value_list_size, value_list_allocated ) if( found_int == 1 ) then call c_f_pointer(value_list_cptr,value_cptrs,(/value_list_size/)) if( allocated(value) ) deallocate(value) allocate(value(value_list_size)) do j=1,value_list_size value(j) = atlas_Config( value_cptrs(j) ) enddo if( value_list_allocated == 1 ) call c_ptr_free(value_list_cptr) endif found = .False. if (found_int == 1) found = .True. end function atlas_Config__get_config_list function atlas_Config__get_logical(this, name, value) result(found) use fckit_c_interop_module, only : c_str use atlas_Config_c_binding logical :: found class(atlas_Config), intent(in) :: this character(len=*), intent(in) :: name logical, intent(inout) :: value integer :: value_int integer :: found_int found_int = atlas__Config__get_int(this%CPTR_PGIBUG_B,c_str(name), value_int ) found = .False. if (found_int == 1) found = .True. if (found) then if (value_int > 0) then value = .True. else value = .False. end if endif end function atlas_Config__get_logical function atlas_Config__get_int32(this, name, value) result(found) use fckit_c_interop_module, only : c_str use atlas_Config_c_binding logical :: found class(atlas_Config), intent(in) :: this character(len=*), intent(in) :: name integer, intent(inout) :: value integer :: found_int found_int = atlas__Config__get_int(this%CPTR_PGIBUG_B, c_str(name), value ) found = .False. if (found_int == 1) found = .True. end function atlas_Config__get_int32 function atlas_Config__get_real32(this, name, value) result(found) use, intrinsic :: iso_c_binding, only : c_float use fckit_c_interop_module, only : c_str use atlas_Config_c_binding logical :: found class(atlas_Config), intent(in) :: this character(len=*), intent(in) :: name real(c_float), intent(inout) :: value integer :: found_int found_int = atlas__Config__get_float(this%CPTR_PGIBUG_B, c_str(name), value ) found = .False. if (found_int == 1) found = .True. end function atlas_Config__get_real32 function atlas_Config__get_real64(this, name, value) result(found) use, intrinsic :: iso_c_binding, only : c_double use fckit_c_interop_module, only : c_str use atlas_Config_c_binding logical :: found class(atlas_Config), intent(in) :: this character(len=*), intent(in) :: name real(c_double), intent(inout) :: value integer :: found_int found_int = atlas__Config__get_double(this%CPTR_PGIBUG_B, c_str(name), value ) found = .False. if (found_int == 1) found = .True. end function atlas_Config__get_real64 function atlas_Config__get_string(this, name, value) result(found) use, intrinsic :: iso_c_binding, only : c_ptr, c_int use fckit_c_interop_module, only : c_str, c_ptr_to_string, c_ptr_free use atlas_Config_c_binding logical :: found class(atlas_Config), intent(in) :: this character(len=*), intent(in) :: name character(len=:), allocatable, intent(inout) :: value type(c_ptr) :: value_cptr integer :: found_int integer(c_int) :: value_size integer(c_int) :: value_allocated found_int = atlas__Config__get_string(this%CPTR_PGIBUG_B,c_str(name),value_cptr,value_size,value_allocated) if( found_int == 1 ) then if( allocated(value) ) deallocate(value) allocate(character(len=value_size) :: value ) value = c_ptr_to_string(value_cptr) if( value_allocated == 1 ) call c_ptr_free(value_cptr) endif found = .False. if (found_int == 1) found = .True. end function atlas_Config__get_string subroutine atlas_Config__set_array_int32(this, name, value) use, intrinsic :: iso_c_binding, only : c_int use fckit_c_interop_module, only : c_str use atlas_Config_c_binding class(atlas_Config), intent(in) :: this character(len=*), intent(in) :: name integer(c_int), intent(in) :: value(:) call atlas__Config__set_array_int(this%CPTR_PGIBUG_B, c_str(name), & & value, size(value) ) end subroutine atlas_Config__set_array_int32 subroutine atlas_Config__set_array_int64(this, name, value) use, intrinsic :: iso_c_binding, only : c_long use fckit_c_interop_module, only : c_str use atlas_Config_c_binding class(atlas_Config), intent(in) :: this character(len=*), intent(in) :: name integer(c_long), intent(in) :: value(:) call atlas__Config__set_array_long(this%CPTR_PGIBUG_B, c_str(name), & & value, size(value) ) end subroutine atlas_Config__set_array_int64 subroutine atlas_Config__set_array_real32(this, name, value) use, intrinsic :: iso_c_binding, only : c_float use fckit_c_interop_module, only : c_str use atlas_Config_c_binding class(atlas_Config), intent(in) :: this character(len=*), intent(in) :: name real(c_float), intent(in) :: value(:) call atlas__Config__set_array_float(this%CPTR_PGIBUG_B, c_str(name), & & value, size(value) ) end subroutine atlas_Config__set_array_real32 subroutine atlas_Config__set_array_real64(this, name, value) use, intrinsic :: iso_c_binding, only : c_double use fckit_c_interop_module, only : c_str use atlas_Config_c_binding class(atlas_Config), intent(in) :: this character(len=*), intent(in) :: name real(c_double), intent(in) :: value(:) call atlas__Config__set_array_double(this%CPTR_PGIBUG_B, c_str(name), & & value, size(value) ) end subroutine atlas_Config__set_array_real64 function atlas_Config__get_array_int32(this, name, value) result(found) use, intrinsic :: iso_c_binding, only : c_ptr, c_int, c_f_pointer use fckit_c_interop_module, only : c_str, c_ptr_free use atlas_Config_c_binding logical :: found class(atlas_Config), intent(in) :: this character(len=*), intent(in) :: name integer(c_int), allocatable, intent(inout) :: value(:) type(c_ptr) :: value_cptr integer(c_int), pointer :: value_fptr(:) integer :: value_size integer :: value_allocated integer :: found_int found_int = atlas__Config__get_array_int(this%CPTR_PGIBUG_B, c_str(name), & & value_cptr, value_size, value_allocated ) if (found_int ==1 ) then call c_f_pointer(value_cptr,value_fptr,(/value_size/)) if( allocated(value) ) deallocate(value) allocate(value(value_size)) value(:) = value_fptr(:) if( value_allocated == 1 ) call c_ptr_free(value_cptr) endif found = .False. if (found_int == 1) found = .True. end function atlas_Config__get_array_int32 function atlas_Config__get_array_int64(this, name, value) result(found) use, intrinsic :: iso_c_binding, only : c_long, c_ptr, c_f_pointer use fckit_c_interop_module, only : c_str, c_ptr_free use atlas_Config_c_binding logical :: found class(atlas_Config), intent(in) :: this character(len=*), intent(in) :: name integer(c_long), allocatable, intent(inout) :: value(:) type(c_ptr) :: value_cptr integer(c_long), pointer :: value_fptr(:) integer :: value_size integer :: value_allocated integer :: found_int found_int = atlas__Config__get_array_long(this%CPTR_PGIBUG_B, c_str(name), & & value_cptr, value_size, value_allocated ) if (found_int == 1) then call c_f_pointer(value_cptr,value_fptr,(/value_size/)) if( allocated(value) ) deallocate(value) allocate(value(value_size)) value(:) = value_fptr(:) if( value_allocated == 1 ) call c_ptr_free(value_cptr) endif found = .False. if (found_int == 1) found = .True. end function atlas_Config__get_array_int64 function atlas_Config__get_array_real32(this, name, value) result(found) use, intrinsic :: iso_c_binding, only : c_float, c_ptr, c_f_pointer use fckit_c_interop_module, only : c_str, c_ptr_free use atlas_Config_c_binding logical :: found class(atlas_Config), intent(in) :: this character(len=*), intent(in) :: name real(c_float), allocatable, intent(inout) :: value(:) type(c_ptr) :: value_cptr real(c_float), pointer :: value_fptr(:) integer :: value_size integer :: value_allocated integer :: found_int found_int = atlas__Config__get_array_float(this%CPTR_PGIBUG_B, c_str(name), & & value_cptr, value_size, value_allocated ) if (found_int == 1 ) then call c_f_pointer(value_cptr,value_fptr,(/value_size/)) if( allocated(value) ) deallocate(value) allocate(value(value_size)) value(:) = value_fptr(:) if( value_allocated == 1 ) call c_ptr_free(value_cptr) endif found = .False. if (found_int == 1) found = .True. end function atlas_Config__get_array_real32 function atlas_Config__get_array_real64(this, name, value) result(found) use, intrinsic :: iso_c_binding, only : c_double, c_ptr, c_f_pointer use fckit_c_interop_module, only : c_str, c_ptr_free use atlas_Config_c_binding logical :: found class(atlas_Config), intent(in) :: this character(len=*), intent(in) :: name real(c_double), allocatable, intent(inout) :: value(:) type(c_ptr) :: value_cptr real(c_double), pointer :: value_fptr(:) integer :: value_size integer :: value_allocated integer :: found_int found_int = atlas__Config__get_array_double(this%CPTR_PGIBUG_B, c_str(name), & & value_cptr, value_size, value_allocated ) if (found_int == 1) then call c_f_pointer(value_cptr,value_fptr,(/value_size/)) if( allocated(value) ) deallocate(value) allocate(value(value_size)) value(:) = value_fptr(:) if( value_allocated == 1 ) call c_ptr_free(value_cptr) endif found = .False. if (found_int == 1) found = .True. end function atlas_Config__get_array_real64 function atlas_Config__json(this) result(json) use, intrinsic :: iso_c_binding, only : c_ptr, c_int use fckit_c_interop_module, only : c_str, c_ptr_to_string, c_ptr_free use atlas_Config_c_binding character(len=:), allocatable :: json class(atlas_Config), intent(in) :: this type(c_ptr) :: json_cptr integer(c_int) :: json_size integer(c_int) :: json_allocated call atlas__Config__json(this%CPTR_PGIBUG_B,json_cptr,json_size,json_allocated) allocate(character(len=json_size) :: json ) json = c_ptr_to_string(json_cptr) if( json_allocated == 1 ) call c_ptr_free(json_cptr) end function atlas_Config__json end module atlas_config_module atlas-0.46.0/src/atlas_f/util/atlas_Metadata_module.F900000664000175000017500000003417515160212070023003 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. #include "atlas/atlas_f.h" module atlas_metadata_module use fckit_object_module, only : fckit_object implicit none private :: fckit_object public :: atlas_Metadata private integer, parameter :: MAX_STR_LEN=255 !------------------------------------------------------------------------------ TYPE, extends(fckit_object) :: atlas_Metadata ! Purpose : ! ------- ! *Metadata* : Container of Metadata, parameters or attributes ! The Metadata are seted as key, value pairs ! Methods : ! ------- ! set : set a new property with given key and value ! set : Modify a property with given key and value ! get : Return a property value for given key ! Author : ! ------ ! 20-Nov-2013 Willem Deconinck *ECMWF* !------------------------------------------------------------------------------ contains procedure, private :: set_logical => Metadata__set_logical procedure, private :: set_int32 => Metadata__set_int32 procedure, private :: set_real32 => Metadata__set_real32 procedure, private :: set_real64 => Metadata__set_real64 procedure, private :: set_string => Metadata__set_string procedure, private :: set_array_int32 => Metadata__set_array_int32 procedure, private :: set_array_int64 => Metadata__set_array_int64 procedure, private :: set_array_real32 => Metadata__set_array_real32 procedure, private :: set_array_real64 => Metadata__set_array_real64 procedure :: has => Metadata__has generic :: set => set_logical, set_int32, set_real32, set_real64, set_string, set_array_int32, & set_array_int64, set_array_real32, set_array_real64 procedure :: get_int32 => Metadata__get_int32 procedure :: get_logical => Metadata__get_logical procedure :: get_real32 => Metadata__get_real32 procedure :: get_real64 => Metadata__get_real64 procedure :: get_string => Metadata__get_string procedure :: get_array_int32 => Metadata__get_array_int32 procedure :: get_array_int64 => Metadata__get_array_int64 procedure :: get_array_real32 => Metadata__get_array_real32 procedure :: get_array_real64 => Metadata__get_array_real64 generic :: get => get_int32, get_logical, get_real32, get_real64, get_string, get_array_int32, & get_array_int64, get_array_real32, get_array_real64 procedure :: print => Metadata__print procedure :: json => Metadata__json procedure, public :: delete => atlas_Metadata__delete #if FCKIT_FINAL_NOT_INHERITING final :: atlas_Metadata__final_auto #endif END TYPE atlas_Metadata !------------------------------------------------------------------------------ interface atlas_Metadata module procedure atlas_Metadata__ctor end interface !------------------------------------------------------------------------------ !======================================================== contains !======================================================== ! ----------------------------------------------------------------------------- ! Metadata routines function atlas_Metadata__ctor() result(this) use atlas_metadata_c_binding use fckit_c_interop_module type(atlas_Metadata) :: this call this%reset_c_ptr( atlas__Metadata__new() ) end function atlas_Metadata__ctor subroutine atlas_Metadata__delete(this) use atlas_metadata_c_binding class(atlas_Metadata), intent(inout) :: this if ( .not. this%is_null() ) then call atlas__Metadata__delete(this%CPTR_PGIBUG_A) end if call this%reset_c_ptr() end subroutine atlas_Metadata__delete function Metadata__has(this, name) result(value) use atlas_metadata_c_binding use fckit_c_interop_module, only : c_str class(atlas_Metadata), intent(inout) :: this character(len=*), intent(in) :: name logical :: value integer :: value_int value_int = atlas__Metadata__has(this%CPTR_PGIBUG_A, c_str(name) ) if( value_int == 1 ) then value = .True. else value = .False. end if end function Metadata__has subroutine Metadata__set_logical(this, name, value) use atlas_metadata_c_binding use fckit_c_interop_module, only : c_str class(atlas_Metadata), intent(inout) :: this character(len=*), intent(in) :: name logical, intent(in) :: value integer :: value_int if( value ) then value_int = 1 else value_int = 0 end if call atlas__Metadata__set_int(this%CPTR_PGIBUG_A, c_str(name), value_int ) end subroutine Metadata__set_logical subroutine Metadata__set_int32(this, name, value) use atlas_metadata_c_binding use fckit_c_interop_module, only : c_str class(atlas_Metadata), intent(inout) :: this character(len=*), intent(in) :: name integer, intent(in) :: value call atlas__Metadata__set_int(this%CPTR_PGIBUG_A, c_str(name), value) end subroutine Metadata__set_int32 subroutine Metadata__set_real32(this, name, value) use atlas_metadata_c_binding use fckit_c_interop_module, only : c_str use, intrinsic :: iso_c_binding class(atlas_Metadata), intent(inout) :: this character(len=*), intent(in) :: name real(c_float), intent(in) :: value call atlas__Metadata__set_float(this%CPTR_PGIBUG_A, c_str(name) ,value) end subroutine Metadata__set_real32 subroutine Metadata__set_real64(this, name, value) use atlas_metadata_c_binding use, intrinsic :: iso_c_binding use fckit_c_interop_module, only : c_str class(atlas_Metadata), intent(inout) :: this character(len=*), intent(in) :: name real(c_double), intent(in) :: value call atlas__Metadata__set_double(this%CPTR_PGIBUG_A, c_str(name) ,value) end subroutine Metadata__set_real64 subroutine Metadata__set_string(this, name, value) use atlas_metadata_c_binding use fckit_c_interop_module, only : c_str class(atlas_Metadata), intent(inout) :: this character(len=*), intent(in) :: name character(len=*), intent(in) :: value call atlas__Metadata__set_string(this%CPTR_PGIBUG_A, c_str(name) , c_str(value) ) end subroutine Metadata__set_string subroutine Metadata__get_logical(this, name, value) use atlas_metadata_c_binding use fckit_c_interop_module, only : c_str class(atlas_Metadata), intent(in) :: this character(len=*), intent(in) :: name logical, intent(out) :: value integer :: value_int value_int = atlas__Metadata__get_int(this%CPTR_PGIBUG_A,c_str(name) ) if (value_int > 0) then value = .True. else value = .False. end if end subroutine Metadata__get_logical subroutine Metadata__get_int32(this, name, value) use atlas_metadata_c_binding use fckit_c_interop_module, only : c_str class(atlas_Metadata), intent(in) :: this character(len=*), intent(in) :: name integer, intent(out) :: value value = atlas__Metadata__get_int(this%CPTR_PGIBUG_A, c_str(name) ) end subroutine Metadata__get_int32 subroutine Metadata__get_real32(this, name, value) use atlas_metadata_c_binding use, intrinsic :: iso_c_binding use fckit_c_interop_module, only : c_str class(atlas_Metadata), intent(in) :: this character(len=*), intent(in) :: name real(c_float), intent(out) :: value value = atlas__Metadata__get_float(this%CPTR_PGIBUG_A, c_str(name) ) end subroutine Metadata__get_real32 subroutine Metadata__get_real64(this, name, value) use atlas_metadata_c_binding use, intrinsic :: iso_c_binding use fckit_c_interop_module, only : c_str class(atlas_Metadata), intent(in) :: this character(len=*), intent(in) :: name real(c_double), intent(out) :: value value = atlas__Metadata__get_double(this%CPTR_PGIBUG_A, c_str(name) ) end subroutine Metadata__get_real64 subroutine Metadata__get_string(this, name, value) use atlas_metadata_c_binding use fckit_c_interop_module, only : c_str, c_str_to_string class(atlas_Metadata), intent(in) :: this character(len=*), intent(in) :: name character(len=:), allocatable, intent(out) :: value character(len=MAX_STR_LEN) :: value_cstr call atlas__Metadata__get_string(this%CPTR_PGIBUG_A, c_str(name), value_cstr, MAX_STR_LEN ) value = c_str_to_string(value_cstr) end subroutine Metadata__get_string subroutine Metadata__set_array_int32(this, name, value) use atlas_metadata_c_binding use fckit_c_interop_module, only : c_str use, intrinsic :: iso_c_binding class(atlas_Metadata), intent(in) :: this character(len=*), intent(in) :: name integer(c_int), intent(in) :: value(:) call atlas__Metadata__set_array_int(this%CPTR_PGIBUG_A, c_str(name), & & value, size(value) ) end subroutine Metadata__set_array_int32 subroutine Metadata__set_array_int64(this, name, value) use atlas_metadata_c_binding use fckit_c_interop_module, only : c_str use, intrinsic :: iso_c_binding class(atlas_Metadata), intent(in) :: this character(len=*), intent(in) :: name integer(c_long), intent(in) :: value(:) call atlas__Metadata__set_array_long(this%CPTR_PGIBUG_A, c_str(name), & & value, size(value) ) end subroutine Metadata__set_array_int64 subroutine Metadata__set_array_real32(this, name, value) use atlas_metadata_c_binding use fckit_c_interop_module, only : c_str use, intrinsic :: iso_c_binding class(atlas_Metadata), intent(in) :: this character(len=*), intent(in) :: name real(c_float), intent(in) :: value(:) call atlas__Metadata__set_array_float(this%CPTR_PGIBUG_A, c_str(name), & & value, size(value) ) end subroutine Metadata__set_array_real32 subroutine Metadata__set_array_real64(this, name, value) use atlas_metadata_c_binding use fckit_c_interop_module, only : c_str use, intrinsic :: iso_c_binding class(atlas_Metadata), intent(in) :: this character(len=*), intent(in) :: name real(c_double), intent(in) :: value(:) call atlas__Metadata__set_array_double(this%CPTR_PGIBUG_A, c_str(name), & & value, size(value) ) end subroutine Metadata__set_array_real64 subroutine Metadata__get_array_int32(this, name, value) use atlas_metadata_c_binding use fckit_c_interop_module, only : c_str, c_ptr_free use, intrinsic :: iso_c_binding class(atlas_Metadata), intent(in) :: this character(len=*), intent(in) :: name integer(c_int), allocatable, intent(out) :: value(:) type(c_ptr) :: value_cptr integer(c_int), pointer :: value_fptr(:) integer :: value_size integer :: value_allocated call atlas__Metadata__get_array_int(this%CPTR_PGIBUG_A, c_str(name), & & value_cptr, value_size, value_allocated ) call c_f_pointer(value_cptr,value_fptr,[value_size]) allocate(value(value_size)) value(:) = value_fptr(:) if( value_allocated == 1 ) call c_ptr_free(value_cptr) end subroutine Metadata__get_array_int32 subroutine Metadata__get_array_int64(this, name, value) use atlas_metadata_c_binding use fckit_c_interop_module, only : c_str, c_ptr_free use, intrinsic :: iso_c_binding class(atlas_Metadata), intent(in) :: this character(len=*), intent(in) :: name integer(c_long), allocatable, intent(out) :: value(:) type(c_ptr) :: value_cptr integer(c_long), pointer :: value_fptr(:) integer :: value_size integer :: value_allocated call atlas__Metadata__get_array_long(this%CPTR_PGIBUG_A, c_str(name), & & value_cptr, value_size, value_allocated ) call c_f_pointer(value_cptr,value_fptr,(/value_size/)) allocate(value(value_size)) value(:) = value_fptr(:) if( value_allocated == 1 ) call c_ptr_free(value_cptr) end subroutine Metadata__get_array_int64 subroutine Metadata__get_array_real32(this, name, value) use atlas_metadata_c_binding use, intrinsic :: iso_c_binding use fckit_c_interop_module, only : c_str, c_ptr_free class(atlas_Metadata), intent(in) :: this character(len=*), intent(in) :: name real(c_float), allocatable, intent(out) :: value(:) type(c_ptr) :: value_cptr real(c_float), pointer :: value_fptr(:) integer :: value_size integer :: value_allocated call atlas__Metadata__get_array_float(this%CPTR_PGIBUG_A, c_str(name), & & value_cptr, value_size, value_allocated ) call c_f_pointer(value_cptr,value_fptr,(/value_size/)) allocate(value(value_size)) value(:) = value_fptr(:) if( value_allocated == 1 ) call c_ptr_free(value_cptr) end subroutine Metadata__get_array_real32 subroutine Metadata__get_array_real64(this, name, value) use atlas_metadata_c_binding use, intrinsic :: iso_c_binding use fckit_c_interop_module, only : c_str, c_ptr_free class(atlas_Metadata), intent(in) :: this character(len=*), intent(in) :: name real(c_double), allocatable, intent(out) :: value(:) type(c_ptr) :: value_cptr real(c_double), pointer :: value_fptr(:) integer :: value_size integer :: value_allocated call atlas__Metadata__get_array_double(this%CPTR_PGIBUG_A, c_str(name), & & value_cptr, value_size, value_allocated ) call c_f_pointer(value_cptr,value_fptr,(/value_size/)) allocate(value(value_size)) value(:) = value_fptr(:) if( value_allocated == 1 ) call c_ptr_free(value_cptr) end subroutine Metadata__get_array_real64 subroutine MetaData__print(this,channel) use atlas_metadata_c_binding use fckit_log_module, only : fckit_logchannel class(atlas_Metadata), intent(in) :: this type(fckit_logchannel), intent(in) :: channel call atlas__Metadata__print(this%CPTR_PGIBUG_A,channel%CPTR_PGIBUG_A) end subroutine Metadata__print function Metadata__json(this) result(json) use atlas_metadata_c_binding use, intrinsic :: iso_c_binding use fckit_c_interop_module, only : c_ptr_to_string, c_ptr_free character(len=:), allocatable :: json class(atlas_Metadata), intent(in) :: this type(c_ptr) :: json_cptr integer(c_int) :: json_size integer(c_int) :: json_allocated call atlas__Metadata__json(this%CPTR_PGIBUG_A,json_cptr,json_size,json_allocated) allocate(character(len=json_size) :: json ) json = c_ptr_to_string(json_cptr) if( json_allocated == 1 ) call c_ptr_free(json_cptr) end function Metadata__json !------------------------------------------------------------------------------- #if FCKIT_FINAL_NOT_INHERITING ATLAS_FINAL subroutine atlas_Metadata__final_auto(this) type(atlas_Metadata), intent(inout) :: this #if FCKIT_FINAL_DEBUGGING write(0,*) "atlas_Metadata__final_auto" #endif #if FCKIT_FINAL_NOT_PROPAGATING call this%final() #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine #endif end module atlas_metadata_module atlas-0.46.0/src/atlas_f/util/atlas_allocate_module.F900000664000175000017500000002233015160212070023035 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. #include "atlas/atlas_f.h" module atlas_allocate_module implicit none private public :: atlas_allocate_managedmem public :: atlas_deallocate_managedmem interface atlas_allocate_managedmem module procedure atlas_allocate_managedmem_real64_r1 module procedure atlas_allocate_managedmem_real32_r1 module procedure atlas_allocate_managedmem_int32_r1 module procedure atlas_allocate_managedmem_int64_r1_int32 module procedure atlas_allocate_managedmem_int64_r1_int64 module procedure atlas_allocate_managedmem_real64_r2 module procedure atlas_allocate_managedmem_real32_r2 module procedure atlas_allocate_managedmem_int32_r2 module procedure atlas_allocate_managedmem_int64_r2_int32 module procedure atlas_allocate_managedmem_int64_r2_int64 end interface interface atlas_deallocate_managedmem module procedure atlas_deallocate_managedmem_real64_r1 module procedure atlas_deallocate_managedmem_real32_r1 module procedure atlas_deallocate_managedmem_int32_r1 module procedure atlas_deallocate_managedmem_int64_r1 module procedure atlas_deallocate_managedmem_real64_r2 module procedure atlas_deallocate_managedmem_real32_r2 module procedure atlas_deallocate_managedmem_int32_r2 module procedure atlas_deallocate_managedmem_int64_r2 end interface !------------------------------------------------------------------------------ contains !------------------------------------------------------------------------------ subroutine atlas_allocate_managedmem_real64_r1( A, dims ) use, intrinsic :: iso_c_binding use atlas_allocate_c_binding real(c_double), pointer :: a(:) integer(c_int) :: dims(:) type(c_ptr) :: value_cptr integer(c_size_t) :: size size = dims(1) if( size > 0 ) then call atlas__allocate_managedmem_double( value_cptr, size ) call c_f_pointer(value_cptr,a,dims) endif end subroutine subroutine atlas_allocate_managedmem_real32_r1( A, dims ) use, intrinsic :: iso_c_binding use atlas_allocate_c_binding real(c_float), pointer :: a(:) integer(c_int) :: dims(:) type(c_ptr) :: value_cptr integer(c_size_t) :: size size = dims(1) if( size > 0 ) then call atlas__allocate_managedmem_float( value_cptr, size ) call c_f_pointer(value_cptr,a,dims) endif end subroutine subroutine atlas_allocate_managedmem_int32_r1( A, dims ) use, intrinsic :: iso_c_binding use atlas_allocate_c_binding integer(c_int), pointer :: a(:) integer(c_int) :: dims(:) type(c_ptr) :: value_cptr integer(c_size_t) :: size size = dims(1) if( size > 0 ) then call atlas__allocate_managedmem_int( value_cptr, size ) call c_f_pointer(value_cptr,a,dims) endif end subroutine subroutine atlas_allocate_managedmem_int64_r1_int32( A, dims ) use, intrinsic :: iso_c_binding use atlas_allocate_c_binding integer(c_long), pointer :: a(:) integer(c_int32_t) :: dims(:) type(c_ptr) :: value_cptr integer(c_size_t) :: size size = dims(1) if( size > 0 ) then call atlas__allocate_managedmem_long( value_cptr, size ) call c_f_pointer(value_cptr,a,dims) endif end subroutine subroutine atlas_allocate_managedmem_int64_r1_int64( A, dims ) use, intrinsic :: iso_c_binding use atlas_allocate_c_binding integer(c_long), pointer :: a(:) integer(c_int64_t) :: dims(:) type(c_ptr) :: value_cptr integer(c_size_t) :: size size = dims(1) if( size > 0 ) then call atlas__allocate_managedmem_long( value_cptr, size ) call c_f_pointer(value_cptr,a,dims) endif end subroutine subroutine atlas_allocate_managedmem_int32_r2( A, dims ) use, intrinsic :: iso_c_binding use atlas_allocate_c_binding integer(c_int), pointer :: a(:,:) integer(c_int) :: dims(:) type(c_ptr) :: value_cptr integer(c_size_t) :: size size = dims(1)*dims(2) if( size > 0 ) then call atlas__allocate_managedmem_int( value_cptr, size ) call c_f_pointer(value_cptr,a,dims) endif end subroutine subroutine atlas_allocate_managedmem_int64_r2_int32( A, dims ) use, intrinsic :: iso_c_binding use atlas_allocate_c_binding integer(c_long), pointer :: a(:,:) integer(c_int32_t) :: dims(:) type(c_ptr) :: value_cptr integer(c_size_t) :: size size = dims(1)*dims(2) if( size > 0 ) then call atlas__allocate_managedmem_long( value_cptr, size ) call c_f_pointer(value_cptr,a,dims) endif end subroutine subroutine atlas_allocate_managedmem_int64_r2_int64( A, dims ) use, intrinsic :: iso_c_binding use atlas_allocate_c_binding integer(c_long), pointer :: a(:,:) integer(c_int64_t) :: dims(:) type(c_ptr) :: value_cptr integer(c_size_t) :: size size = dims(1)*dims(2) if( size > 0 ) then call atlas__allocate_managedmem_long( value_cptr, size ) call c_f_pointer(value_cptr,a,dims) endif end subroutine subroutine atlas_allocate_managedmem_real64_r2( A, dims ) use, intrinsic :: iso_c_binding use atlas_allocate_c_binding real(c_double), pointer :: a(:,:) integer(c_int) :: dims(:) type(c_ptr) :: value_cptr integer(c_size_t) :: size size = dims(1)*dims(2) if( size > 0 ) then call atlas__allocate_managedmem_double( value_cptr, size ) call c_f_pointer(value_cptr,a,dims) endif end subroutine subroutine atlas_allocate_managedmem_real32_r2( A, dims ) use, intrinsic :: iso_c_binding use atlas_allocate_c_binding real(c_float), pointer :: a(:,:) integer(c_int) :: dims(:) type(c_ptr) :: value_cptr integer(c_size_t) :: size size = dims(1)*dims(2) if( size > 0 ) then call atlas__allocate_managedmem_float( value_cptr, size ) call c_f_pointer(value_cptr,a,dims) endif end subroutine !------------------------------------------------------------------------------ ! These functions are private in fckit_array_module function c_loc_int32(x) use, intrinsic :: iso_c_binding integer(c_int32_t), target :: x type(c_ptr) :: c_loc_int32 c_loc_int32 = c_loc(x) end function ! ============================================================================= function c_loc_int64(x) use, intrinsic :: iso_c_binding integer(c_int64_t), target :: x type(c_ptr) :: c_loc_int64 c_loc_int64 = c_loc(x) end function ! ============================================================================= function c_loc_real32(x) use, intrinsic :: iso_c_binding real(c_float), target :: x type(c_ptr) :: c_loc_real32 c_loc_real32 = c_loc(x) end function ! ============================================================================= function c_loc_real64(x) use, intrinsic :: iso_c_binding real(c_double), target :: x type(c_ptr) :: c_loc_real64 c_loc_real64 = c_loc(x) end function ! ============================================================================= !------------------------------------------------------------------------------ subroutine atlas_deallocate_managedmem_real64_r1( A ) use, intrinsic :: iso_c_binding use atlas_allocate_c_binding real(c_double), pointer :: a(:) call atlas__deallocate_managedmem_double( c_loc_real64(A(1)), size(A,KIND=c_size_t) ) nullify( a ) end subroutine subroutine atlas_deallocate_managedmem_real32_r1( A ) use, intrinsic :: iso_c_binding use atlas_allocate_c_binding real(c_float), pointer :: a(:) call atlas__deallocate_managedmem_float( c_loc_real32(A(1)), size(A,KIND=c_size_t) ) nullify( a ) end subroutine subroutine atlas_deallocate_managedmem_int32_r1( A ) use, intrinsic :: iso_c_binding use atlas_allocate_c_binding integer(c_int), pointer :: a(:) call atlas__deallocate_managedmem_int( c_loc_int32(A(1)), size(A,KIND=c_size_t) ) nullify( a ) end subroutine subroutine atlas_deallocate_managedmem_int64_r1( A ) use, intrinsic :: iso_c_binding use atlas_allocate_c_binding integer(c_long), pointer :: a(:) call atlas__deallocate_managedmem_long( c_loc_int64(A(1)), size(A,KIND=c_size_t) ) nullify( a ) end subroutine subroutine atlas_deallocate_managedmem_real64_r2( A ) use, intrinsic :: iso_c_binding use atlas_allocate_c_binding real(c_double), pointer :: a(:,:) call atlas__deallocate_managedmem_double( c_loc_real64(A(1,1)), size(A,KIND=c_size_t) ) nullify( a ) end subroutine subroutine atlas_deallocate_managedmem_real32_r2( A ) use, intrinsic :: iso_c_binding use atlas_allocate_c_binding real(c_float), pointer :: a(:,:) call atlas__deallocate_managedmem_float( c_loc_real32(A(1,1)), size(A,KIND=c_size_t) ) nullify( a ) end subroutine subroutine atlas_deallocate_managedmem_int32_r2( A ) use, intrinsic :: iso_c_binding use atlas_allocate_c_binding integer(c_int), pointer :: a(:,:) call atlas__deallocate_managedmem_int( c_loc_int32(A(1,1)), size(A,KIND=c_size_t) ) nullify( a ) end subroutine subroutine atlas_deallocate_managedmem_int64_r2( A ) use, intrinsic :: iso_c_binding use atlas_allocate_c_binding integer(c_long), pointer :: a(:,:) call atlas__deallocate_managedmem_long( c_loc_int64(A(1,1)), size(A,KIND=c_size_t) ) nullify( a ) end subroutine !------------------------------------------------------------------------------ end module atlas_allocate_module atlas-0.46.0/src/atlas_f/util/atlas_functions_module.F900000664000175000017500000000522315160212070023263 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. #include "atlas/atlas_f.h" module atlas_functions_module use, intrinsic :: iso_c_binding implicit none interface pure function atlas__functions__MDPI_sinusoid( lon, lat ) bind(C,name="atlas__functi& &ons__MDPI_sinusoid") use iso_c_binding, only: c_double real(c_double) :: atlas__functions__MDPI_sinusoid real(c_double), intent(in) :: lon, lat end function pure function atlas__functions__MDPI_harmonic( lon, lat ) bind(C,name="atlas__functi& &ons__MDPI_harmonic") use iso_c_binding, only: c_double real(c_double) :: atlas__functions__MDPI_harmonic real(c_double), intent(in) :: lon, lat end function pure function atlas__functions__MDPI_vortex( lon, lat ) bind(C,name="atlas__function& &s__MDPI_vortex") use iso_c_binding, only: c_double real(c_double) :: atlas__functions__MDPI_vortex real(c_double), intent(in) :: lon, lat end function pure function atlas__functions__MDPI_gulfstream( lon, lat ) bind(C,name="atlas__func& &tions__MDPI_gulfstream") use iso_c_binding, only: c_double real(c_double) :: atlas__functions__MDPI_gulfstream real(c_double), intent(in) :: lon, lat end function end interface contains elemental function MDPI_sinusoid(lon, lat) result(val) real(c_double), intent(in) :: lon, lat real(c_double) :: val val = atlas__functions__MDPI_sinusoid(lon, lat) end function MDPI_sinusoid ! ----------------------------------------------------------------------------- elemental function MDPI_harmonic(lon, lat) result(val) real(c_double), intent(in) :: lon, lat real(c_double) :: val val = atlas__functions__MDPI_harmonic(lon, lat) end function MDPI_harmonic ! ----------------------------------------------------------------------------- elemental function MDPI_vortex(lon, lat) result(val) real(c_double), intent(in) :: lon, lat real(c_double) :: val val = atlas__functions__MDPI_vortex(lon, lat) end function MDPI_vortex ! ----------------------------------------------------------------------------- elemental function MDPI_gulfstream(lon, lat) result(val) real(c_double), intent(in) :: lon, lat real(c_double) :: val val = atlas__functions__MDPI_gulfstream(lon, lat) end function MDPI_gulfstream ! ----------------------------------------------------------------------------- end module atlas_functions_module atlas-0.46.0/src/atlas_f/util/atlas_JSON_module.F900000664000175000017500000000612215160212070022023 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. #include "atlas/atlas_f.h" module atlas_JSON_module implicit none public :: atlas_PathName public :: atlas_JSON private TYPE :: atlas_PathName character(len=1), allocatable, private :: string(:) contains procedure :: str => atlas_PathName__str END TYPE atlas_PathName interface atlas_PathName module procedure atlas_PathName__ctor_str end interface TYPE :: atlas_JSON character(len=1), allocatable, private :: string(:) contains procedure :: str => atlas_JSON__str procedure, public :: delete => atlas_JSON__delete END TYPE atlas_JSON interface atlas_JSON module procedure atlas_JSON__ctor_str module procedure atlas_JSON__ctor_path end interface !------------------------------------------------------------------------------ !======================================================== contains !======================================================== function atlas_PathName__ctor_str(str) result(PathName) type(atlas_PathName) :: PathName character(len=*), intent(in) :: str integer i, nchars nchars = len(str) allocate( PathName%string(nchars) ) do i=1,nchars PathName%string(i) = str(i:i) enddo end function function atlas_PathName__str(this) result(str) character(len=:), allocatable :: str class(atlas_PathName) :: this integer i, nchars nchars = size(this%string) allocate(character(len=nchars) :: str) do i=1,nchars str(i:i) = this%string(i) enddo end function function atlas_JSON__ctor_str(str) result(JSON) type(atlas_JSON) :: JSON character(len=*), intent(in) :: str integer i, nchars nchars = len(str) allocate( JSON%string(nchars) ) do i=1,nchars JSON%string(i) = str(i:i) enddo end function function atlas_JSON__ctor_path(path) result(JSON) use, intrinsic :: iso_c_binding, only : c_char, c_int, c_ptr use atlas_atlas_read_file_c_binding use fckit_c_interop_module, only : c_str, c_ptr_to_string, c_ptr_free type(atlas_JSON) :: JSON type(atlas_PathName), intent(in) :: path character(len=:), allocatable :: str integer (c_int) :: iret type(c_ptr) :: str_cptr integer(c_int) :: str_size iret = atlas__read_file(c_str(path%str()), str_cptr, str_size) allocate(character(len=str_size) :: str ) str = c_ptr_to_string(str_cptr) call c_ptr_free(str_cptr) JSON = atlas_JSON__ctor_str(str) end function function atlas_JSON__str(this) result(str) character(len=:), allocatable :: str class(atlas_JSON) :: this integer i, nchars nchars = size(this%string) allocate(character(len=nchars) :: str) do i=1,nchars str(i:i) = this%string(i) enddo end function subroutine atlas_JSON__delete(this) class(atlas_JSON), intent(inout) :: this if( allocated(this%string) ) deallocate(this%string) end subroutine end module atlas_JSON_module atlas-0.46.0/src/atlas_f/util/atlas_KDTree_module.F900000664000175000017500000004344715160212070022403 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. #include "atlas/atlas_f.h" module atlas_KDTree_module use, intrinsic :: iso_c_binding use fckit_owned_object_module, only : fckit_owned_object use atlas_geometry_module implicit none private :: fckit_owned_object public :: atlas_IndexKDTree private !------------------------------------------------------------------------------ TYPE, extends(fckit_owned_object) :: atlas_IndexKDTree ! Purpose : ! ------- ! *IndexKDTree* : Container of IndexKDTree ! Methods : ! ------- ! Author : ! ------ ! April-2020 Benjamin Menetrier *IRIT-JCSDA* !------------------------------------------------------------------------------ contains procedure, public :: delete => atlas_IndexKDTree__delete procedure, public :: empty => atlas_IndexKDTree__empty procedure, public :: size => atlas_IndexKDTree__size procedure :: reserve => IndexKDTree__reserve procedure :: insert_separate_coords => IndexKDTree__insert_separate_coords procedure :: insert_vectorized_coords => IndexKDTree__insert_vectorized_coords generic :: insert => insert_separate_coords, insert_vectorized_coords procedure :: build_only => IndexKDTree__build_only procedure :: build_list_separate_coords => IndexKDTree__build_list_separate_coords procedure :: build_list_vectorized_coords => IndexKDTree__build_list_vectorized_coords generic :: build => build_only, build_list_separate_coords, build_list_vectorized_coords procedure :: closestPoints_separate_coords => IndexKDTree__closestPoints_separate_coords procedure :: closestPoints_vectorized_coords => IndexKDTree__closestPoints_vectorized_coords generic :: closestPoints => closestPoints_separate_coords, closestPoints_vectorized_coords procedure :: closestPoint_separate_coords => IndexKDTree__closestPoint_separate_coords procedure :: closestPoint_vectorized_coords => IndexKDTree__closestPoint_vectorized_coords generic :: closestPoint => closestPoint_separate_coords, closestPoint_vectorized_coords procedure :: closestPointsWithinRadius_separate_coords => IndexKDTree__closestPointsWithinRadius_separate_coords procedure :: closestPointsWithinRadius_vectorized_coords => IndexKDTree__closestPointsWithinRadius_vectorized_coords generic :: closestPointsWithinRadius => closestPointsWithinRadius_separate_coords, closestPointsWithinRadius_vectorized_coords procedure :: geometry => IndexKDTree__geometry #if FCKIT_FINAL_NOT_INHERITING final :: atlas_IndexKDTree__final_auto #endif END TYPE atlas_IndexKDTree !------------------------------------------------------------------------------ interface atlas_IndexKDTree module procedure atlas_IndexKDTree__ctor module procedure atlas_IndexKDTree__ctor_geometry end interface !------------------------------------------------------------------------------ !======================================================== contains !======================================================== ! ----------------------------------------------------------------------------- ! IndexKDTree routines function atlas_IndexKDTree__ctor() result(this) use atlas_KDTree_c_binding use fckit_c_interop_module type(atlas_IndexKDTree) :: this call this%reset_c_ptr( atlas__IndexKDTree__new() ) call this%return() end function atlas_IndexKDTree__ctor function atlas_IndexKDTree__ctor_geometry(geometry) result(this) use atlas_KDTree_c_binding use fckit_c_interop_module type(atlas_Geometry), intent(in) :: geometry type(atlas_IndexKDTree) :: this call this%reset_c_ptr( atlas__IndexKDTree__new_geometry( geometry%CPTR_PGIBUG_A ) ) call this%return() end function atlas_IndexKDTree__ctor_geometry subroutine atlas_IndexKDTree__delete(this) use atlas_KDTree_c_binding class(atlas_IndexKDTree), intent(inout) :: this if ( .not. this%is_null() ) then call atlas__IndexKDTree__delete(this%CPTR_PGIBUG_A) end if call this%reset_c_ptr() end subroutine atlas_IndexKDTree__delete function atlas_IndexKDTree__empty(this) result(empty) use atlas_KDTree_c_binding logical :: empty class(atlas_IndexKDTree), intent(in) :: this if( atlas__IndexKDTree__empty(this%CPTR_PGIBUG_A) == 0 ) then empty = .False. else empty = .True. endif endfunction function atlas_IndexKDTree__size(this) result(size) use atlas_KDTree_c_binding use atlas_kinds_module integer(ATLAS_KIND_IDX) :: size class(atlas_IndexKDTree), intent(in) :: this size = atlas__IndexKDTree__size(this%CPTR_PGIBUG_A) endfunction subroutine IndexKDTree__reserve(this, size) use atlas_KDTree_c_binding use fckit_c_interop_module use atlas_kinds_module class(atlas_IndexKDTree), intent(in) :: this integer(ATLAS_KIND_IDX), intent(in) :: size call atlas__IndexKDTree__reserve(this%CPTR_PGIBUG_A, size) end subroutine IndexKDTree__reserve subroutine IndexKDTree__insert_separate_coords(this, lon, lat, index) use atlas_KDTree_c_binding use fckit_c_interop_module use atlas_kinds_module class(atlas_IndexKDTree), intent(in) :: this real(c_double), intent(in) :: lon real(c_double), intent(in) :: lat integer(ATLAS_KIND_IDX), intent(in) :: index call atlas__IndexKDTree__insert(this%CPTR_PGIBUG_A, lon, lat, index) end subroutine IndexKDTree__insert_separate_coords subroutine IndexKDTree__insert_vectorized_coords(this, lonlat, index) use atlas_KDTree_c_binding use fckit_c_interop_module use atlas_kinds_module class(atlas_IndexKDTree), intent(in) :: this real(c_double), intent(in) :: lonlat(2) integer(ATLAS_KIND_IDX), intent(in) :: index call atlas__IndexKDTree__insert(this%CPTR_PGIBUG_A, lonlat(1), lonlat(2), index) end subroutine IndexKDTree__insert_vectorized_coords subroutine IndexKDTree__build_only(this) use atlas_KDTree_c_binding use fckit_c_interop_module class(atlas_IndexKDTree), intent(in) :: this call atlas__IndexKDTree__build(this%CPTR_PGIBUG_A) end subroutine IndexKDTree__build_only subroutine IndexKDTree__build_list_separate_coords(this, n, lons, lats, indices) use atlas_KDTree_c_binding use fckit_c_interop_module use atlas_kinds_module class(atlas_IndexKDTree), intent(in) :: this integer(ATLAS_KIND_IDX), intent(in) :: n real(c_double), intent(in) :: lons(n) real(c_double), intent(in) :: lats(n) integer(ATLAS_KIND_IDX), intent(in), optional :: indices(n) integer(ATLAS_KIND_IDX) :: i integer(ATLAS_KIND_IDX) :: index do i = 1, n if (present(indices)) then index = indices(i) else index = i end if call this%insert(lons(i), lats(i), index) end do call this%build() end subroutine IndexKDTree__build_list_separate_coords subroutine IndexKDTree__build_list_vectorized_coords(this, n, lonlats, indices) use atlas_KDTree_c_binding use fckit_c_interop_module use atlas_kinds_module class(atlas_IndexKDTree), intent(in) :: this integer(ATLAS_KIND_IDX), intent(in) :: n real(c_double), intent(in) :: lonlats(n,2) integer(ATLAS_KIND_IDX), intent(in), optional :: indices(n) integer(ATLAS_KIND_IDX) :: i, index do i = 1, n if (present(indices)) then index = indices(i) else index = i end if call this%insert(lonlats(i,:), index) end do call this%build() end subroutine IndexKDTree__build_list_vectorized_coords subroutine IndexKDTree__closestPoints_separate_coords(this, plon, plat, k, indices, distances, lons, lats) use atlas_KDTree_c_binding use, intrinsic :: iso_c_binding, only : c_f_pointer use fckit_c_interop_module use atlas_kinds_module class(atlas_IndexKDTree), intent(in) :: this real(c_double), intent(in) :: plon real(c_double), intent(in) :: plat integer(c_int), intent(in) :: k integer(ATLAS_KIND_IDX), intent(out) :: indices(k) real(c_double), intent(out), optional :: distances(k) real(c_double), intent(out), optional :: lons(k) real(c_double), intent(out), optional :: lats(k) type(c_ptr) :: indices_cptr type(c_ptr) :: distances_cptr type(c_ptr) :: lons_cptr type(c_ptr) :: lats_cptr integer(ATLAS_KIND_IDX), pointer :: indices_fptr(:) real(c_double), pointer :: distances_fptr(:) real(c_double), pointer :: lons_fptr(:) real(c_double), pointer :: lats_fptr(:) if ( k > 0 ) then call atlas__IndexKDTree__closestPoints(this%CPTR_PGIBUG_A, plon, plat, int(k, c_size_t), lons_cptr, lats_cptr, indices_cptr, & & distances_cptr) call c_f_pointer(indices_cptr, indices_fptr, (/k/)) indices(:) = indices_fptr(:) if (present(distances)) then call c_f_pointer(distances_cptr, distances_fptr, (/k/)) distances(:) = distances_fptr(:) end if if (present(lons)) then call c_f_pointer(lons_cptr, lons_fptr, (/k/)) lons(:) = lons_fptr(:) end if if (present(lats)) then call c_f_pointer(lats_cptr, lats_fptr, (/k/)) lats(:) = lats_fptr(:) end if call c_ptr_free(lons_cptr) call c_ptr_free(lats_cptr) call c_ptr_free(indices_cptr) call c_ptr_free(distances_cptr) end if end subroutine IndexKDTree__closestPoints_separate_coords subroutine IndexKDTree__closestPoints_vectorized_coords(this, point, k, indices, distances, lonlats) use atlas_KDTree_c_binding use, intrinsic :: iso_c_binding, only : c_f_pointer use fckit_c_interop_module use atlas_kinds_module class(atlas_IndexKDTree), intent(in) :: this real(c_double), intent(in) :: point(2) integer(c_int), intent(in) :: k integer(ATLAS_KIND_IDX), intent(out) :: indices(k) real(c_double), intent(out), optional :: distances(k) real(c_double), intent(out), optional :: lonlats(k,2) type(c_ptr) :: indices_cptr type(c_ptr) :: distances_cptr type(c_ptr) :: lons_cptr type(c_ptr) :: lats_cptr integer(ATLAS_KIND_IDX), pointer :: indices_fptr(:) real(c_double), pointer :: distances_fptr(:) real(c_double), pointer :: lons_fptr(:) real(c_double), pointer :: lats_fptr(:) if ( k > 0 ) then call atlas__IndexKDTree__closestPoints(this%CPTR_PGIBUG_A, point(1), point(2), int(k, c_size_t), lons_cptr, lats_cptr, & & indices_cptr, distances_cptr) call c_f_pointer(indices_cptr, indices_fptr, (/k/)) indices(:) = indices_fptr(:) if (present(distances)) then call c_f_pointer(distances_cptr, distances_fptr, (/k/)) distances(:) = distances_fptr(:) end if if (present(lonlats)) then call c_f_pointer(lons_cptr, lons_fptr, (/k/)) call c_f_pointer(lats_cptr, lats_fptr, (/k/)) lonlats(:,1) = lons_fptr(:) lonlats(:,2) = lats_fptr(:) end if call c_ptr_free(lons_cptr) call c_ptr_free(lats_cptr) call c_ptr_free(indices_cptr) call c_ptr_free(distances_cptr) end if end subroutine IndexKDTree__closestPoints_vectorized_coords subroutine IndexKDTree__closestPoint_separate_coords(this, plon, plat, index, distance, lon, lat) use atlas_KDTree_c_binding use fckit_c_interop_module use atlas_kinds_module class(atlas_IndexKDTree), intent(in) :: this real(c_double), intent(in) :: plon real(c_double), intent(in) :: plat integer(ATLAS_KIND_IDX), intent(out) :: index real(c_double), intent(out), optional :: distance real(c_double), intent(out), optional :: lon real(c_double), intent(out), optional :: lat real(c_double) :: distance_tmp, lon_tmp, lat_tmp call atlas__IndexKDTree__closestPoint(this%CPTR_PGIBUG_A, plon, plat, lon_tmp, lat_tmp, index, distance_tmp) if (present(distance)) distance = distance_tmp if (present(lon)) lon = lon_tmp if (present(lat)) lat = lat_tmp end subroutine IndexKDTree__closestPoint_separate_coords subroutine IndexKDTree__closestPoint_vectorized_coords(this, point, index, distance, lonlat) use atlas_KDTree_c_binding use fckit_c_interop_module use atlas_kinds_module class(atlas_IndexKDTree), intent(in) :: this real(c_double), intent(in) :: point(2) integer(ATLAS_KIND_IDX), intent(out) :: index real(c_double), intent(out), optional :: distance real(c_double), intent(out), optional :: lonlat(2) real(c_double) :: distance_tmp, lon_tmp, lat_tmp call atlas__IndexKDTree__closestPoint(this%CPTR_PGIBUG_A, point(1), point(2), lon_tmp, lat_tmp, index, distance_tmp) if (present(distance)) distance = distance_tmp if (present(lonlat)) then lonlat(1) = lon_tmp lonlat(2) = lat_tmp end if end subroutine IndexKDTree__closestPoint_vectorized_coords subroutine IndexKDTree__closestPointsWithinRadius_separate_coords(this, plon, plat, radius, k, indices, distances, lons, lats) use atlas_KDTree_c_binding use, intrinsic :: iso_c_binding, only : c_f_pointer use fckit_c_interop_module use atlas_kinds_module class(atlas_IndexKDTree), intent(in) :: this real(c_double), intent(in) :: plon real(c_double), intent(in) :: plat real(c_double), intent(in) :: radius integer(c_int), intent(out) :: k integer(ATLAS_KIND_IDX), allocatable, intent(inout), optional :: indices(:) real(c_double), allocatable, intent(inout), optional :: distances(:) real(c_double), allocatable, intent(inout), optional :: lons(:) real(c_double), allocatable, intent(inout), optional :: lats(:) integer(c_size_t) :: k_tmp type(c_ptr) :: lons_cptr type(c_ptr) :: lats_cptr type(c_ptr) :: indices_cptr type(c_ptr) :: distances_cptr real(c_double), pointer :: lons_fptr(:) real(c_double), pointer :: lats_fptr(:) integer(ATLAS_KIND_IDX), pointer :: indices_fptr(:) real(c_double), pointer :: distances_fptr(:) call atlas__IndexKDTree__closestPointsWithinRadius(this%CPTR_PGIBUG_A, plon, plat, radius, & & k_tmp, lons_cptr, lats_cptr, indices_cptr, distances_cptr) k = int(k_tmp, c_int) if (present(indices)) then if (allocated(indices)) deallocate(indices) allocate(indices(k)) end if if (present(distances)) then if (allocated(distances)) deallocate(distances) allocate(distances(k)) end if if (present(lons)) then if (allocated(lons)) deallocate(lons) allocate(lons(k)) end if if (present(lats)) then if (allocated(lats)) deallocate(lats) allocate(lats(k)) end if if ( k > 0 ) then if (present(indices)) then call c_f_pointer(indices_cptr, indices_fptr, (/k/)) indices(:) = indices_fptr(:) end if if (present(distances)) then call c_f_pointer(distances_cptr, distances_fptr, (/k/)) distances(:) = distances_fptr(:) end if if (present(lons)) then call c_f_pointer(lons_cptr, lons_fptr, (/k/)) lons(:) = lons_fptr(:) end if if (present(lats)) then call c_f_pointer(lats_cptr, lats_fptr, (/k/)) lats(:) = lats_fptr(:) end if end if call c_ptr_free(lons_cptr) call c_ptr_free(lats_cptr) call c_ptr_free(indices_cptr) call c_ptr_free(distances_cptr) end subroutine IndexKDTree__closestPointsWithinRadius_separate_coords subroutine IndexKDTree__closestPointsWithinRadius_vectorized_coords(this, point, radius, k, indices, distances, lonlats) use atlas_KDTree_c_binding use, intrinsic :: iso_c_binding, only : c_f_pointer use fckit_c_interop_module use atlas_kinds_module class(atlas_IndexKDTree), intent(in) :: this real(c_double), intent(in) :: point(2) real(c_double), intent(in) :: radius integer(c_int), intent(out) :: k integer(ATLAS_KIND_IDX), allocatable, intent(inout), optional :: indices(:) real(c_double), allocatable, intent(inout), optional :: distances(:) real(c_double), allocatable, intent(inout), optional :: lonlats(:,:) integer(c_size_t) :: k_tmp type(c_ptr) :: lons_cptr type(c_ptr) :: lats_cptr type(c_ptr) :: indices_cptr type(c_ptr) :: distances_cptr real(c_double), pointer :: lons_fptr(:) real(c_double), pointer :: lats_fptr(:) integer(ATLAS_KIND_IDX), pointer :: indices_fptr(:) real(c_double), pointer :: distances_fptr(:) call atlas__IndexKDTree__closestPointsWithinRadius(this%CPTR_PGIBUG_A, point(1), point(2), radius, & & k_tmp, lons_cptr, lats_cptr, indices_cptr, distances_cptr) k = int(k_tmp, c_int) if (present(indices)) then if (allocated(indices)) deallocate(indices) allocate(indices(k)) end if if (present(distances)) then if (allocated(distances)) deallocate(distances) allocate(distances(k)) end if if (present(lonlats)) then if (allocated(lonlats)) deallocate(lonlats) allocate(lonlats(k,2)) end if if ( k > 0 ) then if (present(indices)) then call c_f_pointer(indices_cptr, indices_fptr, (/k/)) indices(:) = indices_fptr(:) end if if (present(distances)) then call c_f_pointer(distances_cptr, distances_fptr, (/k/)) distances(:) = distances_fptr(:) end if if (present(lonlats)) then call c_f_pointer(lons_cptr, lons_fptr, (/k/)) call c_f_pointer(lats_cptr, lats_fptr, (/k/)) lonlats(:,1) = lons_fptr(:) lonlats(:,2) = lats_fptr(:) end if end if call c_ptr_free(lons_cptr) call c_ptr_free(lats_cptr) call c_ptr_free(indices_cptr) call c_ptr_free(distances_cptr) end subroutine IndexKDTree__closestPointsWithinRadius_vectorized_coords function IndexKDTree__geometry(this) result(geometry) use atlas_KDTree_c_binding use fckit_c_interop_module class(atlas_IndexKDTree), intent(in) :: this type(atlas_Geometry) :: geometry call geometry%reset_c_ptr( atlas__IndexKDTree__geometry( this%CPTR_PGIBUG_A ) ) call geometry%return() end function IndexKDTree__geometry !------------------------------------------------------------------------------- #if FCKIT_FINAL_NOT_INHERITING ATLAS_FINAL subroutine atlas_IndexKDTree__final_auto(this) type(atlas_IndexKDTree), intent(inout) :: this #if FCKIT_FINAL_DEBUGGING write(0,*) "atlas_IndexKDTree__final_auto" #endif #if FCKIT_FINAL_NOT_PROPAGATING call this%final() #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine #endif end module atlas_KDTree_module atlas-0.46.0/src/atlas_f/runtime/0000775000175000017500000000000015160212070016746 5ustar alastairalastairatlas-0.46.0/src/atlas_f/runtime/atlas_Trace_module.F900000664000175000017500000001604215160212070023020 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. #include "atlas/atlas_f.h" module atlas_Trace_module use fckit_shared_object_module, only : fckit_shared_object implicit none private :: fckit_shared_object public :: atlas_Trace private !----------------------------- ! atlas_Trace ! !----------------------------- type, extends(fckit_shared_object) :: atlas_Trace contains ! Public methods procedure, public :: running procedure, public :: start procedure, public :: stop procedure, public :: pause procedure, public :: resume procedure, public :: elapsed #if FCKIT_FINAL_NOT_INHERITING final :: atlas_Trace__final_auto #endif end type interface atlas_Trace module procedure atlas_Trace__loc module procedure atlas_Trace__labels_1 module procedure atlas_Trace__labels_2 module procedure atlas_Trace__labels_3 module procedure atlas_Trace__labels_4 module procedure atlas_Trace__labels_5 end interface !======================================================== contains !======================================================== function atlas_Trace__loc(file,line,title) result(this) use, intrinsic :: iso_c_binding, only : c_ptr use atlas_Trace_c_binding use fckit_c_interop_module type(atlas_Trace) :: this character(len=*) , intent(in) :: file integer , intent(in) :: line character(len=*) , intent(in) :: title call this%reset_c_ptr( new_atlas_Trace( c_str(file), line, c_str(title) ), fckit_c_deleter(delete_atlas_Trace) ) call this%return() end function function atlas_Trace__labels_1(file,line,title,label) result(this) use, intrinsic :: iso_c_binding, only : c_ptr use atlas_Trace_c_binding use fckit_c_interop_module type(atlas_Trace) :: this character(len=*) , intent(in) :: file integer , intent(in) :: line character(len=*) , intent(in) :: title character(len=*) , intent(in) :: label call this%reset_c_ptr( new_atlas_Trace_labels_1( c_str(file), line, c_str(title), c_str(label) ), & & fckit_c_deleter(delete_atlas_Trace) ) call this%return() end function function atlas_Trace__labels_2(file,line,title,label1,label2) result(this) use, intrinsic :: iso_c_binding, only : c_ptr use atlas_Trace_c_binding use fckit_c_interop_module type(atlas_Trace) :: this character(len=*) , intent(in) :: file integer , intent(in) :: line character(len=*) , intent(in) :: title character(len=*) , intent(in) :: label1 character(len=*) , intent(in) :: label2 call this%reset_c_ptr( new_atlas_Trace_labels_2( c_str(file), line, c_str(title), c_str(label1), c_str(label2) ), & & fckit_c_deleter(delete_atlas_Trace) ) call this%return() end function function atlas_Trace__labels_3(file,line,title,label1,label2,label3) result(this) use, intrinsic :: iso_c_binding, only : c_ptr use atlas_Trace_c_binding use fckit_c_interop_module type(atlas_Trace) :: this character(len=*) , intent(in) :: file integer , intent(in) :: line character(len=*) , intent(in) :: title character(len=*) , intent(in) :: label1 character(len=*) , intent(in) :: label2 character(len=*) , intent(in) :: label3 call this%reset_c_ptr( new_atlas_Trace_labels_3( c_str(file), line, c_str(title), c_str(label1), c_str(label2), & & c_str(label3) ), fckit_c_deleter(delete_atlas_Trace) ) call this%return() end function function atlas_Trace__labels_4(file,line,title,label1,label2,label3,label4) result(this) use, intrinsic :: iso_c_binding, only : c_ptr use atlas_Trace_c_binding use fckit_c_interop_module type(atlas_Trace) :: this character(len=*) , intent(in) :: file integer , intent(in) :: line character(len=*) , intent(in) :: title character(len=*) , intent(in) :: label1 character(len=*) , intent(in) :: label2 character(len=*) , intent(in) :: label3 character(len=*) , intent(in) :: label4 call this%reset_c_ptr( new_atlas_Trace_labels_4( c_str(file), line, c_str(title), c_str(label1), c_str(label2), & & c_str(label3), c_str(label4) ), fckit_c_deleter(delete_atlas_Trace) ) call this%return() end function function atlas_Trace__labels_5(file,line,title,label1,label2,label3,label4,label5) result(this) use, intrinsic :: iso_c_binding, only : c_ptr use atlas_Trace_c_binding use fckit_c_interop_module type(atlas_Trace) :: this character(len=*) , intent(in) :: file integer , intent(in) :: line character(len=*) , intent(in) :: title character(len=*) , intent(in) :: label1 character(len=*) , intent(in) :: label2 character(len=*) , intent(in) :: label3 character(len=*) , intent(in) :: label4 character(len=*) , intent(in) :: label5 call this%reset_c_ptr( new_atlas_Trace_labels_5( c_str(file), line, c_str(title), c_str(label1), c_str(label2), & & c_str(label3), c_str(label4), c_str(label5) ), fckit_c_deleter(delete_atlas_Trace) ) call this%return() end function !------------------------------------------------------------------------------- function running( this ) use atlas_Trace_c_binding logical :: running class(atlas_Trace) :: this if( atlas_Trace__running( this%CPTR_PGIBUG_B ) == 0 ) then running = .False. else running = .True. endif end function !------------------------------------------------------------------------------- function elapsed( this ) use, intrinsic :: iso_c_binding, only : c_double use atlas_Trace_c_binding real(c_double) :: elapsed class(atlas_Trace) :: this elapsed = atlas_Trace__elapsed( this%CPTR_PGIBUG_B ) end function !------------------------------------------------------------------------------- subroutine start( this ) use atlas_Trace_c_binding class(atlas_Trace) :: this call atlas_Trace__start( this%CPTR_PGIBUG_B ) end subroutine !------------------------------------------------------------------------------- subroutine stop( this ) use atlas_Trace_c_binding class(atlas_Trace) :: this call atlas_Trace__stop( this%CPTR_PGIBUG_B ) end subroutine !------------------------------------------------------------------------------- subroutine pause( this ) use atlas_Trace_c_binding class(atlas_Trace) :: this call atlas_Trace__pause( this%CPTR_PGIBUG_B ) end subroutine !------------------------------------------------------------------------------- subroutine resume( this ) use atlas_Trace_c_binding class(atlas_Trace) :: this call atlas_Trace__resume( this%CPTR_PGIBUG_B ) end subroutine !------------------------------------------------------------------------------- #if FCKIT_FINAL_NOT_INHERITING ATLAS_FINAL subroutine atlas_Trace__final_auto(this) type(atlas_Trace), intent(inout) :: this #if FCKIT_FINAL_DEBUGGING write(0,*) "atlas_Trace__final_auto" #endif #if FCKIT_FINAL_NOT_PROPAGATING call this%final() #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine #endif end module atlas_Trace_module atlas-0.46.0/src/atlas_f/runtime/atlas_trace.cc0000664000175000017500000000720515160212070021543 0ustar alastairalastair/* * (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_trace.h" #include #include #include "atlas/runtime/Exception.h" #include "atlas/runtime/Log.h" #include "atlas/runtime/Trace.h" #include "atlas/runtime/trace/CodeLocation.h" namespace atlas { //static std::vector Labels( int size, const char* labels[] ) { // std::vector _labels; // _labels.reserve( size ); // for ( int i = 0; i < size; ++i ) { // _labels.emplace_back( labels[i] ); // } // return _labels; //} extern "C" { Trace* new_atlas_Trace(const char* file, int line, const char* title) { return new Trace(CodeLocation(file, line, nullptr, true), std::string(title)); } Trace* new_atlas_Trace_labels_1(const char* file, int line, const char* title, const char* label1) { std::vector labels{label1}; return new Trace(CodeLocation(file, line, nullptr, true), std::string(title), labels); } Trace* new_atlas_Trace_labels_2(const char* file, int line, const char* title, const char* label1, const char* label2) { std::vector labels{label1, label2}; return new Trace(CodeLocation(file, line, nullptr, true), std::string(title), labels); } Trace* new_atlas_Trace_labels_3(const char* file, int line, const char* title, const char* label1, const char* label2, const char* label3) { std::vector labels{label1, label2, label3}; return new Trace(CodeLocation(file, line, nullptr, true), std::string(title), labels); } Trace* new_atlas_Trace_labels_4(const char* file, int line, const char* title, const char* label1, const char* label2, const char* label3, const char* label4) { std::vector labels{label1, label2, label3, label4}; return new Trace(CodeLocation(file, line, nullptr, true), std::string(title), labels); } Trace* new_atlas_Trace_labels_5(const char* file, int line, const char* title, const char* label1, const char* label2, const char* label3, const char* label4, const char* label5) { std::vector labels{label1, label2, label3, label4, label5}; return new Trace(CodeLocation(file, line, nullptr, true), std::string(title), labels); } void delete_atlas_Trace(Trace* This) { ATLAS_ASSERT(This != nullptr, "Cannot delete uninitialised atlas_Trace"); delete This; This = nullptr; } void atlas_Trace__start(Trace* This) { ATLAS_ASSERT(This != nullptr, "Cannot start uninitialised atlas_Trace"); This->start(); } void atlas_Trace__stop(Trace* This) { ATLAS_ASSERT(This != nullptr, "Cannot stop uninitialised atlas_Trace"); This->stop(); } void atlas_Trace__pause(Trace* This) { ATLAS_ASSERT(This != nullptr, "Cannot pause uninitialised atlas_Trace"); This->pause(); } void atlas_Trace__resume(Trace* This) { ATLAS_ASSERT(This != nullptr, "Cannot resume uninitialised atlas_Trace"); This->resume(); } int atlas_Trace__running(Trace* This) { ATLAS_ASSERT(This != nullptr, "Cannot check 'running' status of uninitialised atlas_Trace"); return This->running(); } double atlas_Trace__elapsed(Trace* This) { ATLAS_ASSERT(This != nullptr, "Cannot check elapsed time of uninitialised atlas_Trace"); return This->elapsed(); } } // extern C } // namespace atlas atlas-0.46.0/src/atlas_f/runtime/atlas_trace.h0000664000175000017500000000333715160212070021407 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once namespace atlas { class Trace; } extern "C" { atlas::Trace* new_atlas_Trace(const char* file, int line, const char* title); atlas::Trace* new_atlas_Trace_labels_1(const char* file, int line, const char* title, const char* label1); atlas::Trace* new_atlas_Trace_labels_2(const char* file, int line, const char* title, const char* label1, const char* label2); atlas::Trace* new_atlas_Trace_labels_3(const char* file, int line, const char* title, const char* label1, const char* label2, const char* label3); atlas::Trace* new_atlas_Trace_labels_4(const char* file, int line, const char* title, const char* label1, const char* label2, const char* label3, const char* label4); atlas::Trace* new_atlas_Trace_labels_5(const char* file, int line, const char* title, const char* label1, const char* label2, const char* label3, const char* label4, const char* label5); void delete_atlas_Trace(atlas::Trace* This); void atlas_Trace__start(atlas::Trace* This); void atlas_Trace__stop(atlas::Trace* This); void atlas_Trace__pause(atlas::Trace* This); void atlas_Trace__resume(atlas::Trace* This); int atlas_Trace__running(atlas::Trace* This); double atlas_Trace__elapsed(atlas::Trace* This); } // extern C atlas-0.46.0/src/atlas_f/interpolation/0000775000175000017500000000000015160212070020152 5ustar alastairalastairatlas-0.46.0/src/atlas_f/interpolation/atlas_Interpolation_module.F900000664000175000017500000001361215160212070026015 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. #include "atlas/atlas_f.h" module atlas_Interpolation_module use fckit_owned_object_module, only : fckit_owned_object implicit none private :: fckit_owned_object public :: atlas_Interpolation private !------------------------------------------------------------------------------ TYPE, extends(fckit_owned_object) :: atlas_Interpolation ! Purpose : ! ------- ! *Interpolation* : ! Methods : ! ------- ! Author : ! ------ ! October-2015 Willem Deconinck *ECMWF* !------------------------------------------------------------------------------ contains procedure, private :: execute_field procedure, private :: execute_fieldset procedure, private :: execute_adjoint_field procedure, private :: execute_adjoint_fieldset generic, public :: execute => execute_field, execute_fieldset generic, public :: execute_adjoint => execute_adjoint_field, execute_adjoint_fieldset #if FCKIT_FINAL_NOT_INHERITING final :: atlas_Interpolation__final_auto #endif END TYPE atlas_Interpolation interface atlas_Interpolation module procedure atlas_Interpolation__cptr module procedure atlas_Interpolation__config_funcspace module procedure atlas_Interpolation__config_funcspace_field module procedure atlas_Interpolation__config_funcspace_fieldset end interface !======================================================== contains !======================================================== function atlas_Interpolation__cptr(cptr) result(this) use, intrinsic :: iso_c_binding, only: c_ptr type(atlas_Interpolation) :: this type(c_ptr), intent(in) :: cptr call this%reset_c_ptr( cptr ) call this%return() end function function atlas_Interpolation__config_funcspace(config,source,target) result(this) use atlas_Interpolation_c_binding use atlas_Config_module, only : atlas_Config use atlas_FunctionSpace_module, only : atlas_FunctionSpace type(atlas_Interpolation) :: this type(atlas_Config), intent(in) :: config class(atlas_FunctionSpace), intent(in) :: source class(atlas_FunctionSpace), intent(in) :: target this = atlas_Interpolation__cptr(atlas__interpolation__new(config%CPTR_PGIBUG_B, & source%CPTR_PGIBUG_A,target%CPTR_PGIBUG_A)) call this%return() end function function atlas_Interpolation__config_funcspace_field(config,source,target) result(this) use atlas_Interpolation_c_binding use atlas_Config_module, only : atlas_Config use atlas_FunctionSpace_module, only : atlas_FunctionSpace use atlas_Field_module, only : atlas_Field type(atlas_Interpolation) :: this type(atlas_Config), intent(in) :: config class(atlas_FunctionSpace), intent(in) :: source class(atlas_Field), intent(in) :: target this = atlas_Interpolation__cptr(atlas__interpolation__new_tgt_field(config%CPTR_PGIBUG_B, & source%CPTR_PGIBUG_A,target%CPTR_PGIBUG_A)) call this%return() end function function atlas_Interpolation__config_funcspace_fieldset(config,source,target) result(this) use atlas_Interpolation_c_binding use atlas_Config_module, only : atlas_Config use atlas_FunctionSpace_module, only : atlas_FunctionSpace use atlas_FieldSet_module, only : atlas_FieldSet type(atlas_Interpolation) :: this type(atlas_Config), intent(in) :: config class(atlas_FunctionSpace), intent(in) :: source class(atlas_FieldSet), intent(in) :: target this = atlas_Interpolation__cptr(atlas__interpolation__new_tgt_fieldset(config%CPTR_PGIBUG_B, & source%CPTR_PGIBUG_A,target%CPTR_PGIBUG_A)) call this%return() end function subroutine execute_field(this,source,target) use atlas_Interpolation_c_binding use atlas_Field_module, only : atlas_Field class(atlas_Interpolation), intent(in) :: this class(atlas_Field), intent(in) :: source class(atlas_Field), intent(inout) :: target call atlas__Interpolation__execute_field(this%CPTR_PGIBUG_A,source%CPTR_PGIBUG_A,target%CPTR_PGIBUG_A) end subroutine subroutine execute_fieldset(this,source,target) use atlas_Interpolation_c_binding use atlas_FieldSet_module, only : atlas_FieldSet class(atlas_Interpolation), intent(in) :: this class(atlas_FieldSet), intent(in) :: source class(atlas_FieldSet), intent(inout) :: target call atlas__Interpolation__execute_fieldset(this%CPTR_PGIBUG_A,source%CPTR_PGIBUG_A,target%CPTR_PGIBUG_A) end subroutine subroutine execute_adjoint_field(this,source,target) use atlas_Interpolation_c_binding use atlas_Field_module, only : atlas_Field class(atlas_Interpolation), intent(in) :: this class(atlas_Field), intent(inout) :: source class(atlas_Field), intent(in) :: target call atlas__Interpolation__execute_adjoint_field(this%CPTR_PGIBUG_A,source%CPTR_PGIBUG_A,target%CPTR_PGIBUG_A) end subroutine subroutine execute_adjoint_fieldset(this,source,target) use atlas_Interpolation_c_binding use atlas_FieldSet_module, only : atlas_FieldSet class(atlas_Interpolation), intent(in) :: this class(atlas_FieldSet), intent(inout) :: source class(atlas_FieldSet), intent(in) :: target call atlas__Interpolation__execute_adjoint_fieldset(this%CPTR_PGIBUG_A,source%CPTR_PGIBUG_A,target%CPTR_PGIBUG_A) end subroutine !------------------------------------------------------------------------------- #if FCKIT_FINAL_NOT_INHERITING ATLAS_FINAL subroutine atlas_Interpolation__final_auto(this) type(atlas_Interpolation), intent(inout) :: this #if FCKIT_FINAL_DEBUGGING write(0,*) "atlas_Interpolation__final_auto" #endif #if FCKIT_FINAL_NOT_PROPAGATING call this%final() #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine #endif ! ----------------------------------------------------------------------------- end module atlas_Interpolation_module atlas-0.46.0/src/atlas_f/trans/0000775000175000017500000000000015160212070016412 5ustar alastairalastairatlas-0.46.0/src/atlas_f/trans/atlas_Trans_module.F900000664000175000017500000002706615160212070022525 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. #include "atlas/atlas_f.h" module atlas_Trans_module use fckit_object_module, only: fckit_object use fckit_owned_object_module, only: fckit_owned_object use atlas_config_module, only : atlas_Config use atlas_field_module, only : atlas_Field use atlas_fieldset_module, only : atlas_FieldSet use atlas_grid_module, only : atlas_Grid use atlas_functionspace_module, only : atlas_functionspace implicit none public :: atlas_Trans private !----------------------------- ! atlas_Trans ! !----------------------------- !------------------------------------------------------------------------------ TYPE, extends(fckit_owned_object) :: atlas_Trans ! Purpose : ! ------- ! *Trans* : To do spectral transforms ! Methods : ! ------- ! Author : ! ------ ! 12-Mar-2015 Willem Deconinck *ECMWF* !------------------------------------------------------------------------------ contains procedure, nopass :: has_backend procedure, nopass :: set_backend procedure, nopass :: backend procedure :: handle procedure :: grid procedure :: truncation procedure :: spectral #if 0 procedure :: nb_spectral_coefficients procedure :: nb_spectral_coefficients_global procedure :: nb_gridpoints procedure :: nb_gridpoints_global #endif procedure, private :: dirtrans_field procedure, private :: dirtrans_fieldset procedure, public :: dirtrans_wind2vordiv => dirtrans_wind2vordiv_field generic, public :: dirtrans => & & dirtrans_field, & & dirtrans_fieldset procedure, private :: invtrans_field procedure, private :: invtrans_fieldset procedure, public :: invtrans_vordiv2wind => invtrans_vordiv2wind_field generic, public :: invtrans => & & invtrans_field, & & invtrans_fieldset procedure, private :: invtrans_grad_field generic, public :: invtrans_grad => & & invtrans_grad_field ! removed #if 0 procedure, private :: gathspec_r1 procedure, private :: gathspec_r2 generic, public :: gathspec => gathspec_r1, gathspec_r2 procedure, private :: specnorm_r1_scalar procedure, private :: specnorm_r2 generic, public :: specnorm => specnorm_r1_scalar, specnorm_r2 #endif #if FCKIT_FINAL_NOT_INHERITING final :: atlas_Trans__final_auto #endif END TYPE atlas_Trans !------------------------------------------------------------------------------ interface atlas_Trans module procedure atlas_Trans__ctor end interface !------------------------------------------------------------------------------ private :: fckit_owned_object private :: fckit_object private :: atlas_Config private :: atlas_Field private :: atlas_FieldSet private :: atlas_Grid !======================================================== contains !======================================================== ! ----------------------------------------------------------------------------- ! Trans routines function backend() use, intrinsic :: iso_c_binding, only: c_size_t, c_ptr use atlas_trans_c_binding use fckit_c_interop_module, only : c_str, c_ptr_to_string, c_ptr_free character(len=:), allocatable :: backend type(c_ptr) :: value_cptr integer(c_size_t) :: value_size call atlas__Trans__backend(value_cptr,value_size) allocate( character(len=value_size) :: backend ) backend = c_ptr_to_string(value_cptr) call c_ptr_free(value_cptr) end function function has_backend( backend ) use, intrinsic :: iso_c_binding, only: c_int use fckit_c_interop_module, only : c_str use atlas_trans_c_binding logical :: has_backend character(len=*) :: backend integer(c_int) :: has_backend_int has_backend_int = atlas__Trans__has_backend( c_str(backend) ) if( has_backend_int == 1 ) then has_backend = .True. else has_backend = .False. end if end function subroutine set_backend( backend ) use atlas_trans_c_binding use fckit_c_interop_module, only : c_str character(len=*), intent(in) :: backend call atlas__Trans__set_backend( c_str(backend) ) end subroutine function atlas_Trans__ctor( grid, nsmax, config ) result(this) use, intrinsic :: iso_c_binding, only: c_null_ptr use atlas_trans_c_binding type(atlas_Trans) :: this class(atlas_Grid), intent(in) :: grid integer, intent(in), optional :: nsmax type(atlas_Config), intent(in), optional :: config if( present( config ) ) then if( present(nsmax) ) then call this%reset_c_ptr( atlas__Trans__new_config( grid%CPTR_PGIBUG_A, nsmax, config%CPTR_PGIBUG_B ) ) else call this%reset_c_ptr( atlas__Trans__new_config( grid%CPTR_PGIBUG_A, 0, config%CPTR_PGIBUG_B ) ) endif else if( present(nsmax) ) then call this%reset_c_ptr( atlas__Trans__new( grid%CPTR_PGIBUG_A, nsmax ) ) else call this%reset_c_ptr( atlas__Trans__new( grid%CPTR_PGIBUG_A, 0 ) ) endif endif call this%return() end function atlas_Trans__ctor function handle( this ) use atlas_trans_c_binding integer :: handle class(atlas_Trans) :: this handle = atlas__Trans__handle (this%CPTR_PGIBUG_A) end function function truncation( this ) use atlas_trans_c_binding integer :: truncation class(atlas_Trans) :: this truncation = atlas__Trans__truncation (this%CPTR_PGIBUG_A) end function function spectral( this ) use atlas_trans_c_binding type(atlas_FunctionSpace) :: spectral class(atlas_Trans) :: this spectral = atlas_FunctionSpace( atlas__Trans__spectral (this%CPTR_PGIBUG_A) ) call spectral%return() end function function grid( this ) use, intrinsic :: iso_c_binding, only: c_null_ptr use atlas_trans_c_binding class(atlas_Trans) :: this type(atlas_Grid) :: grid grid = atlas_Grid( atlas__Trans__grid(this%CPTR_PGIBUG_A) ) call grid%return() end function subroutine dirtrans_fieldset(this, gpfields, spfields, config) use atlas_trans_c_binding class(atlas_Trans), intent(in) :: this class(atlas_FieldSet), intent(in) :: gpfields class(atlas_FieldSet), intent(inout) :: spfields class(atlas_Config), intent(in), optional :: config type(atlas_Config) :: p if( present(config) ) then call p%reset_c_ptr( config%CPTR_PGIBUG_B ) else p = atlas_Config() endif call atlas__Trans__dirtrans_fieldset( this%CPTR_PGIBUG_A, & & gpfields%CPTR_PGIBUG_A, & & spfields%CPTR_PGIBUG_A, & & p%CPTR_PGIBUG_B ) if( .not. present(config) ) then call p%final() endif end subroutine dirtrans_fieldset subroutine invtrans_fieldset(this, spfields, gpfields, config) use atlas_trans_c_binding class(atlas_Trans), intent(in) :: this class(atlas_FieldSet), intent(in) :: spfields class(atlas_FieldSet), intent(inout) :: gpfields class(atlas_Config), intent(in), optional :: config type(atlas_Config) :: p if( present(config) ) then call p%reset_c_ptr( config%CPTR_PGIBUG_B ) else p = atlas_Config() endif call atlas__Trans__invtrans_fieldset( this%CPTR_PGIBUG_A, & & spfields%CPTR_PGIBUG_A, & & gpfields%CPTR_PGIBUG_A, & & p%CPTR_PGIBUG_B ) if( .not. present(config) ) then call p%final() endif end subroutine invtrans_fieldset subroutine dirtrans_field(this, gpfield, spfield, config) use atlas_trans_c_binding class(atlas_Trans), intent(in) :: this class(atlas_Field), intent(in) :: gpfield class(atlas_Field), intent(inout) :: spfield class(atlas_Config), intent(in), optional :: config type(atlas_Config) :: p if( present(config) ) then call p%reset_c_ptr( config%CPTR_PGIBUG_B ) else p = atlas_Config() endif call atlas__Trans__dirtrans_field( this%CPTR_PGIBUG_A, & & gpfield%CPTR_PGIBUG_A, & & spfield%CPTR_PGIBUG_A, & & p%CPTR_PGIBUG_B ) if( .not. present(config) ) then call p%final() endif end subroutine dirtrans_field subroutine dirtrans_wind2vordiv_field(this, gpwind, spvor, spdiv, config) use atlas_trans_c_binding class(atlas_Trans), intent(in) :: this type(atlas_Field), intent(in) :: gpwind type(atlas_Field), intent(inout) :: spvor type(atlas_Field), intent(inout) :: spdiv type(atlas_Config), intent(in), optional :: config type(atlas_Config) :: p if( present(config) ) then call p%reset_c_ptr( config%CPTR_PGIBUG_B ) else p = atlas_Config() endif call atlas__Trans__dirtrans_wind2vordiv_field( this%CPTR_PGIBUG_A, & & gpwind%CPTR_PGIBUG_A, & & spvor%CPTR_PGIBUG_A, & & spdiv%CPTR_PGIBUG_A, & & p%CPTR_PGIBUG_B ) if( .not. present(config) ) then call p%final() endif end subroutine dirtrans_wind2vordiv_field subroutine invtrans_field(this, spfield, gpfield, config) use atlas_trans_c_binding class(atlas_Trans), intent(in) :: this class(atlas_Field), intent(in) :: spfield class(atlas_Field), intent(inout) :: gpfield class(atlas_Config), intent(in), optional :: config type(atlas_Config) :: p if( present(config) ) then call p%reset_c_ptr( config%CPTR_PGIBUG_B ) else p = atlas_Config() endif call atlas__Trans__invtrans_field( this%CPTR_PGIBUG_A, & & spfield%CPTR_PGIBUG_A, & & gpfield%CPTR_PGIBUG_A, & & p%CPTR_PGIBUG_B ) if( .not. present(config) ) then call p%final() endif end subroutine invtrans_field subroutine invtrans_vordiv2wind_field(this, spvor, spdiv, gpwind, config) use atlas_trans_c_binding class(atlas_Trans), intent(in) :: this class(atlas_Field), intent(in) :: spvor class(atlas_Field), intent(in) :: spdiv class(atlas_Field), intent(inout) :: gpwind class(atlas_Config), intent(in), optional :: config type(atlas_Config) :: p if( present(config) ) then call p%reset_c_ptr( config%CPTR_PGIBUG_B ) else p = atlas_Config() endif call atlas__Trans__invtrans_vordiv2wind_field( this%CPTR_PGIBUG_A, & & spvor%CPTR_PGIBUG_A, & & spdiv%CPTR_PGIBUG_A, & & gpwind%CPTR_PGIBUG_A, & & p%CPTR_PGIBUG_B ) if( .not. present(config) ) then call p%final() endif end subroutine invtrans_vordiv2wind_field subroutine invtrans_grad_field(this, spfield, gpfield) use atlas_trans_c_binding class(atlas_Trans), intent(in) :: this class(atlas_Field), intent(in) :: spfield class(atlas_Field), intent(inout) :: gpfield type(atlas_Config) :: config config = atlas_Config() call atlas__Trans__invtrans_grad_field( this%CPTR_PGIBUG_A, & & spfield%CPTR_PGIBUG_A, & & gpfield%CPTR_PGIBUG_A, & & config%CPTR_PGIBUG_B) call config%final() end subroutine invtrans_grad_field !------------------------------------------------------------------------------- #if FCKIT_FINAL_NOT_INHERITING ATLAS_FINAL subroutine atlas_Trans__final_auto(this) type(atlas_Trans), intent(inout) :: this #if FCKIT_FINAL_DEBUGGING write(0,*) "atlas_Trans__final_auto" #endif #if FCKIT_FINAL_NOT_PROPAGATING call this%final() #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine #endif ! ---------------------------------------------------------------------------------------- end module atlas_Trans_module atlas-0.46.0/src/atlas_f/output/0000775000175000017500000000000015160212070016623 5ustar alastairalastairatlas-0.46.0/src/atlas_f/output/atlas_output_module.F900000664000175000017500000001570115160212070023200 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. #include "atlas/atlas_f.h" module atlas_output_module use fckit_owned_object_module, only : fckit_owned_object use atlas_Config_module, only : atlas_Config use atlas_FunctionSpace_module, only: atlas_FunctionSpace use atlas_FieldSet_module, only: atlas_FieldSet use atlas_Field_module, only: atlas_Field use atlas_Mesh_module, only: atlas_Mesh use, intrinsic :: iso_c_binding, only : c_ptr implicit none public :: atlas_Output public :: atlas_output_Gmsh private !------------------------------------------------------------------------------ TYPE, extends(fckit_owned_object) :: atlas_Output ! Purpose : ! ------- ! Methods : ! ------- ! Author : ! ------ ! October-2015 Willem Deconinck *ECMWF* !------------------------------------------------------------------------------ contains procedure, private :: write_mesh procedure, private :: write_field_fs procedure, private :: write_field procedure, private :: write_fieldset_fs procedure, private :: write_fieldset generic, public :: write => & & write_mesh, & & write_field_fs, & & write_fieldset_fs, & & write_field, & & write_fieldset #if FCKIT_FINAL_NOT_INHERITING final :: atlas_Output__final_auto #endif END TYPE atlas_Output interface atlas_Output module procedure atlas_Output__cptr end interface interface atlas_output_Gmsh module procedure atlas_output_Gmsh__pathname_mode end interface !------------------------------------------------------------------------------ private :: fckit_owned_object private :: atlas_Config private :: atlas_FunctionSpace private :: atlas_FieldSet private :: atlas_Field private :: atlas_Mesh private :: c_ptr ! ============================================================================= CONTAINS ! ============================================================================= function atlas_Output__cptr(cptr) result(this) type(atlas_Output) :: this type(c_ptr), intent(in) :: cptr call this%reset_c_ptr( cptr ) call this%return() end function function atlas_output_Gmsh__pathname_mode(file,mode,coordinates,levels,gather,ghost) result(this) use fckit_c_interop_module, only : c_str use atlas_output_gmsh_c_binding type(atlas_Output) :: this character(len=*), intent(in) :: file character(len=1), intent(in), optional :: mode character(len=*), intent(in), optional :: coordinates integer, intent(in), optional :: levels(:) logical, intent(in), optional :: gather logical, intent(in), optional :: ghost character(len=1) :: opt_mode type(atlas_Config) :: opt_config opt_config = atlas_Config() opt_mode = "w" if( present(mode) ) opt_mode = mode if( present(coordinates) ) call opt_config%set("coordinates",coordinates) if( present(levels) ) call opt_config%set("levels",levels) if( present(gather) ) call opt_config%set("gather",gather) if( present(ghost) ) call opt_config%set("ghost",ghost) call this%reset_c_ptr( atlas__output__Gmsh__create_pathname_mode_config(c_str(file),c_str(opt_mode),& opt_config%CPTR_PGIBUG_B) ) call this%return() call opt_config%final() end function subroutine write_mesh(this,mesh,config) use atlas_output_c_binding class(atlas_Output), intent(in) :: this type(atlas_Mesh), intent(in) :: mesh type(atlas_Config), intent(in), optional :: config type(atlas_Config) :: opt_config if( present(config) ) then call atlas__output__write_mesh(this%CPTR_PGIBUG_A,mesh%CPTR_PGIBUG_A,config%CPTR_PGIBUG_B) else opt_config = atlas_Config() call atlas__output__write_mesh(this%CPTR_PGIBUG_A,mesh%CPTR_PGIBUG_A,opt_config%CPTR_PGIBUG_B) call opt_config%final() endif end subroutine subroutine write_field_fs(this,field,functionspace,config) use atlas_output_c_binding class(atlas_Output), intent(in) :: this type(atlas_Field), intent(in) :: field class(atlas_FunctionSpace), intent(in) :: functionspace type(atlas_Config), intent(in), optional :: config type(atlas_Config) :: opt_config if( present(config) ) then call atlas__output__write_field_fs(this%CPTR_PGIBUG_A,field%CPTR_PGIBUG_A,functionspace%CPTR_PGIBUG_A, & config%CPTR_PGIBUG_B) else opt_config = atlas_Config() call atlas__output__write_field_fs(this%CPTR_PGIBUG_A,field%CPTR_PGIBUG_A,functionspace%CPTR_PGIBUG_A, & opt_config%CPTR_PGIBUG_B) call opt_config%final() endif end subroutine subroutine write_fieldset_fs(this,fieldset,functionspace,config) use atlas_output_c_binding class(atlas_Output), intent(in) :: this type(atlas_FieldSet), intent(in) :: fieldset class(atlas_FunctionSpace), intent(in) :: functionspace type(atlas_Config), intent(in), optional :: config type(atlas_Config) :: opt_config if( present(config) ) then call atlas__output__write_fieldset_fs(this%CPTR_PGIBUG_A,fieldset%CPTR_PGIBUG_A,functionspace%CPTR_PGIBUG_A, & config%CPTR_PGIBUG_B) else opt_config = atlas_Config() call atlas__output__write_fieldset_fs(this%CPTR_PGIBUG_A,fieldset%CPTR_PGIBUG_A,functionspace%CPTR_PGIBUG_A, & opt_config%CPTR_PGIBUG_B) call opt_config%final() endif end subroutine subroutine write_field(this,field,config) use atlas_output_c_binding class(atlas_Output), intent(in) :: this type(atlas_Field), intent(in) :: field type(atlas_Config), intent(in), optional :: config type(atlas_Config) :: opt_config if( present(config) ) then call atlas__output__write_field(this%CPTR_PGIBUG_A,field%CPTR_PGIBUG_A, & config%CPTR_PGIBUG_B) else opt_config = atlas_Config() call atlas__output__write_field(this%CPTR_PGIBUG_A,field%CPTR_PGIBUG_A, & opt_config%CPTR_PGIBUG_B) call opt_config%final() endif end subroutine subroutine write_fieldset(this,fieldset,config) use atlas_output_c_binding class(atlas_Output), intent(in) :: this type(atlas_FieldSet), intent(in) :: fieldset type(atlas_Config), intent(in), optional :: config type(atlas_Config) :: opt_config if( present(config) ) then call atlas__output__write_fieldset(this%CPTR_PGIBUG_A,fieldset%CPTR_PGIBUG_A, & config%CPTR_PGIBUG_B) else opt_config = atlas_Config() call atlas__output__write_fieldset(this%CPTR_PGIBUG_A,fieldset%CPTR_PGIBUG_A, & opt_config%CPTR_PGIBUG_B) call opt_config%final() endif end subroutine !------------------------------------------------------------------------------- #if FCKIT_FINAL_NOT_INHERITING ATLAS_FINAL subroutine atlas_Output__final_auto(this) type(atlas_Output), intent(inout) :: this #if FCKIT_FINAL_DEBUGGING write(0,*) "atlas_Output__final_auto" #endif #if FCKIT_FINAL_NOT_PROPAGATING call this%final() #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine #endif end module atlas_output_module atlas-0.46.0/src/atlas_f/parallel/0000775000175000017500000000000015160212070017057 5ustar alastairalastairatlas-0.46.0/src/atlas_f/parallel/atlas_Checksum_module.fypp0000664000175000017500000001445015160212070024256 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. #include "atlas/atlas_f.h" #:include "internals/atlas_generics.fypp" #:set ranks = [1,2,3] module atlas_checksum_module use, intrinsic :: iso_c_binding, only : c_ptr, c_int, c_long, c_float, c_double, c_char use fckit_array_module, only : array_stride, array_view1d use fckit_c_interop_module, only : c_str_to_string use fckit_object_module, only : fckit_object use atlas_kinds_module, only : ATLAS_KIND_IDX implicit none private :: c_ptr, c_int, c_long, c_float, c_double, c_char private :: array_stride, array_view1d, c_str_to_string private :: fckit_object public :: atlas_Checksum private !------------------------------------------------------------------------------ TYPE, extends(fckit_object) :: atlas_Checksum ! Purpose : ! ------- ! *Checksum* : ! Methods : ! ------- ! setup : Setup using arrays detailing proc, glb_idx, remote_idx, max_glb_idx ! execute : Do the Checksum ! Author : ! ------ ! 27-Jun-2014 Willem Deconinck *ECMWF* !------------------------------------------------------------------------------ contains procedure, private :: Checksum__setup32 procedure, private :: Checksum__setup64 generic :: setup => & & Checksum__setup32, & & Checksum__setup64 @:generic_public_interface( execute ) procedure, public :: delete => atlas_Checksum__delete #if FCKIT_FINAL_NOT_INHERITING final :: atlas_Checksum__final_auto #endif END TYPE atlas_Checksum !------------------------------------------------------------------------------ interface atlas_Checksum module procedure atlas_Checksum__ctor end interface !------------------------------------------------------------------------------ !======================================================== contains !======================================================== ! ------------------------------------------------------------------------------ ! Checksum routines function atlas_Checksum__ctor() result(Checksum) use atlas_checksum_c_binding type(atlas_Checksum) :: Checksum call Checksum%reset_c_ptr( atlas__Checksum__new() ) end function atlas_checksum__ctor subroutine atlas_Checksum__delete(this) use atlas_checksum_c_binding class(atlas_Checksum), intent(inout) :: this if ( .not. this%is_null() ) then call atlas__Checksum__delete(this%CPTR_PGIBUG_A) end if call this%reset_c_ptr() end subroutine atlas_Checksum__delete subroutine Checksum__setup32(this, part, remote_idx, glb_idx ) use atlas_checksum_c_binding class(atlas_Checksum), intent(in) :: this integer(c_int), intent(in) :: part(:) integer(ATLAS_KIND_IDX), intent(in) :: remote_idx(:) integer(c_int), intent(in) :: glb_idx(:) call atlas__Checksum__setup32( this%CPTR_PGIBUG_A, part, remote_idx, 1, & & glb_idx, size(part) ) end subroutine Checksum__setup32 subroutine Checksum__setup64(this, part, remote_idx, glb_idx) use atlas_checksum_c_binding class(atlas_Checksum), intent(in) :: this integer(c_int), intent(in) :: part(:) integer(ATLAS_KIND_IDX), intent(in) :: remote_idx(:) integer(c_long), intent(in) :: glb_idx(:) call atlas__Checksum__setup64( this%CPTR_PGIBUG_A, part, remote_idx, 1, & & glb_idx, size(part) ) end subroutine Checksum__setup64 #:for dtype,ftype,ctype in types[:4] function execute_${dtype}$_r1(this, loc_field_data) result(checksum) use atlas_checksum_c_binding class(atlas_Checksum), intent(in) :: this ${ftype}$, intent(in) :: loc_field_data(:) character(len=:), allocatable :: checksum character(kind=c_char) :: checksum_c_str(132) integer :: lstrides(1), lextents(1), lrank=1 ${ftype}$, pointer :: lview(:) lstrides = (/ array_stride(loc_field_data,1) /) lextents = (/ 1 /) lview => array_view1d(loc_field_data) call atlas__Checksum__execute_strided_${ctype}$( this%CPTR_PGIBUG_A, & & lview, lstrides, lextents, lrank, checksum_c_str ) checksum = c_str_to_string(checksum_c_str) end function function execute_${dtype}$_r2(this, loc_field_data) result(checksum) use atlas_checksum_c_binding class(atlas_Checksum), intent(in) :: this ${ftype}$, intent(in) :: loc_field_data(:,:) character(len=:), allocatable :: checksum character(kind=c_char) :: checksum_c_str(132) ${ftype}$, pointer :: lview(:) integer :: lstrides(2), lextents(2), lrank=2 lstrides = (/ array_stride(loc_field_data,2), array_stride(loc_field_data,1) /) lextents = (/ 1, size (loc_field_data,1) /) lview => array_view1d(loc_field_data) call atlas__Checksum__execute_strided_${ctype}$( this%CPTR_PGIBUG_A, & & lview, lstrides, lextents, lrank, checksum_c_str ) checksum = c_str_to_string(checksum_c_str) end function function execute_${dtype}$_r3(this, loc_field_data) result(checksum) use atlas_checksum_c_binding class(atlas_Checksum), intent(in) :: this ${ftype}$, intent(in) :: loc_field_data(:,:,:) character(len=:), allocatable :: checksum character(kind=c_char) :: checksum_c_str(132) ${ftype}$, pointer :: lview(:) integer :: lstrides(3), lextents(3), lrank=3 lstrides = (/ array_stride(loc_field_data,3), array_stride(loc_field_data,2) , array_stride(loc_field_data,1) /) lextents = (/ 1, size (loc_field_data,2) , size (loc_field_data,1) /) lview => array_view1d(loc_field_data) call atlas__Checksum__execute_strided_${ctype}$( this%CPTR_PGIBUG_A, & & lview, lstrides, lextents, lrank, checksum_c_str ) checksum = c_str_to_string(checksum_c_str) end function #:endfor !------------------------------------------------------------------------------- #if FCKIT_FINAL_NOT_INHERITING ATLAS_FINAL subroutine atlas_Checksum__final_auto(this) type(atlas_Checksum), intent(inout) :: this #if FCKIT_FINAL_DEBUGGING write(0,*) "atlas_Checksum__final_auto" #endif #if FCKIT_FINAL_NOT_PROPAGATING call this%final() #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine #endif ! ----------------------------------------------------------------------------- end module atlas_checksum_module atlas-0.46.0/src/atlas_f/parallel/atlas_HaloExchange_module.fypp0000664000175000017500000001326115160212070025041 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. #include "atlas/atlas_f.h" #:include "internals/atlas_generics.fypp" #:set ranks = [1,2,3,4] module atlas_haloexchange_module use, intrinsic :: iso_c_binding, only : c_ptr, c_int, c_long, c_float, c_double use fckit_array_module, only : array_stride, array_view1d use fckit_object_module, only : fckit_object use atlas_kinds_module, only : ATLAS_KIND_IDX implicit none private :: c_ptr, c_int, c_long, c_float, c_double private :: array_stride, array_view1d private :: fckit_object public :: atlas_HaloExchange private !------------------------------------------------------------------------------ TYPE, extends(fckit_object) :: atlas_HaloExchange ! Purpose : ! ------- ! *HaloExchange* : ! Methods : ! ------- ! setup : Setup using arrays detailing proc and glb_idx, bounds and parbound ! execute : Do the halo exchange ! Author : ! ------ ! 17-Dec-2013 Willem Deconinck *ECMWF* !------------------------------------------------------------------------------ contains procedure :: setup => HaloExchange__setup @:generic_public_interface( execute, HaloExchange__execute ) procedure, public :: delete => atlas_HaloExchange__delete #if FCKIT_FINAL_NOT_INHERITING final :: atlas_HaloExchange__final_auto #endif END TYPE atlas_HaloExchange !------------------------------------------------------------------------------ interface atlas_HaloExchange module procedure atlas_HaloExchange__ctor end interface !======================================================== contains !======================================================== ! ------------------------------------------------------------------------------ ! HaloExchange routines function atlas_HaloExchange__ctor() result(halo_exchange) use atlas_haloexchange_c_binding type(atlas_HaloExchange) :: halo_exchange call halo_exchange%reset_c_ptr( atlas__HaloExchange__new() ) end function atlas_HaloExchange__ctor subroutine atlas_HaloExchange__delete(this) use atlas_haloexchange_c_binding class(atlas_HaloExchange), intent(inout) :: this if ( .not. this%is_null() ) then call atlas__HaloExchange__delete(this%CPTR_PGIBUG_A) end if call this%reset_c_ptr() end subroutine atlas_HaloExchange__delete subroutine HaloExchange__setup(this, part, remote_idx) use atlas_haloexchange_c_binding class(atlas_HaloExchange), intent(in) :: this integer(c_int), intent(in) :: part(:) integer(ATLAS_KIND_IDX), intent(in) :: remote_idx(:) call atlas__HaloExchange__setup( this%CPTR_PGIBUG_A, part, remote_idx, 1, size(part) ) end subroutine HaloExchange__setup #:for dtype,ftype,ctype in types[:4] subroutine HaloExchange__execute_${dtype}$_r1(this, field_data) use atlas_haloexchange_c_binding class(atlas_HaloExchange), intent(in) :: this ${ftype}$, intent(inout) :: field_data(:) integer :: strides(1), extents(1) strides = (/ array_stride(field_data,1) /) extents = (/ 1 /) call atlas__HaloExchange__execute_strided_${ctype}$( this%CPTR_PGIBUG_A, field_data, & & strides, extents, 1 ) end subroutine subroutine HaloExchange__execute_${dtype}$_r2(this, field_data) use atlas_haloexchange_c_binding class(atlas_HaloExchange), intent(in) :: this ${ftype}$, intent(inout) :: field_data(:,:) ${ftype}$, pointer :: view(:) integer :: strides(2), extents(2) view => array_view1d(field_data) strides = (/ array_stride(field_data,2) , array_stride(field_data,1) /) extents = (/ 1 , ubound(field_data,1) /) call atlas__HaloExchange__execute_strided_${ctype}$( this%CPTR_PGIBUG_A, view, & & strides, extents, 2 ) end subroutine subroutine HaloExchange__execute_${dtype}$_r3(this, field_data) use atlas_haloexchange_c_binding class(atlas_HaloExchange), intent(in) :: this ${ftype}$, intent(inout) :: field_data(:,:,:) ${ftype}$, pointer :: view(:) integer :: strides(3), extents(3) view => array_view1d(field_data) strides = (/ array_stride(field_data,3), array_stride(field_data,2) , array_stride(field_data,1) /) extents = (/ 1, ubound(field_data,2) , ubound(field_data,1) /) call atlas__HaloExchange__execute_strided_${ctype}$( this%CPTR_PGIBUG_A, view, & & strides, extents, 3 ) end subroutine subroutine HaloExchange__execute_${dtype}$_r4(this, field_data) use atlas_haloexchange_c_binding class(atlas_HaloExchange), intent(in) :: this ${ftype}$, intent(inout) :: field_data(:,:,:,:) ${ftype}$, pointer :: view(:) integer :: strides(4), extents(4) view => array_view1d(field_data) strides = (/ array_stride(field_data,4), array_stride(field_data,3), array_stride(field_data,2) , & array_stride(field_data,1) /) extents = (/ 1, ubound(field_data,3) , ubound(field_data,2) , ubound(field_data,1) /) call atlas__HaloExchange__execute_strided_${ctype}$( this%CPTR_PGIBUG_A, view, & & strides, extents, 4 ) end subroutine #:endfor !------------------------------------------------------------------------------- #if FCKIT_FINAL_NOT_INHERITING ATLAS_FINAL subroutine atlas_HaloExchange__final_auto(this) type(atlas_HaloExchange), intent(inout) :: this #if FCKIT_FINAL_DEBUGGING write(0,*) "atlas_HaloExchange__final_auto" #endif #if FCKIT_FINAL_NOT_PROPAGATING call this%final() #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine #endif ! ----------------------------------------------------------------------------- end module atlas_haloexchange_module atlas-0.46.0/src/atlas_f/parallel/atlas_GatherScatter_module.fypp0000664000175000017500000002477615160212070025270 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. #include "atlas/atlas_f.h" #:include "internals/atlas_generics.fypp" #:set ranks = [1,2,3] module atlas_gatherscatter_module use, intrinsic :: iso_c_binding, only : c_ptr, c_int, c_long, c_float, c_double use fckit_array_module, only : array_stride, array_view1d use fckit_object_module, only : fckit_object use atlas_kinds_module, only : ATLAS_KIND_IDX implicit none private :: c_ptr, c_int, c_long, c_float, c_double private :: array_stride, array_view1d private :: fckit_object public :: atlas_GatherScatter private !------------------------------------------------------------------------------ TYPE, extends(fckit_object) :: atlas_GatherScatter ! Purpose : ! ------- ! *Gather* : ! Methods : ! ------- ! setup : Setup using arrays detailing proc, glb_idx, remote_idx, max_glb_idx ! execute : Do the gather ! Author : ! ------ ! 17-Dec-2013 Willem Deconinck *ECMWF* !------------------------------------------------------------------------------ contains procedure :: glb_dof => GatherScatter__glb_dof procedure, private :: GatherScatter__setup32 procedure, private :: GatherScatter__setup64 generic :: setup => & & GatherScatter__setup32, & & GatherScatter__setup64 @:generic_public_interface( gather ) @:generic_public_interface( scatter ) procedure, public :: delete => atlas_GatherScatter__delete #if FCKIT_FINAL_NOT_INHERITING final :: atlas_GatherScatter__final_auto #endif END TYPE atlas_GatherScatter !------------------------------------------------------------------------------ interface atlas_GatherScatter module procedure atlas_GatherScatter__ctor end interface !------------------------------------------------------------------------------ !======================================================== contains !======================================================== ! ------------------------------------------------------------------------------ ! Gather routines function atlas_GatherScatter__ctor() result(gather) use atlas_gatherscatter_c_binding type(atlas_GatherScatter) :: gather call gather%reset_c_ptr( atlas__GatherScatter__new() ) end function atlas_GatherScatter__ctor subroutine atlas_GatherScatter__delete(this) use atlas_gatherscatter_c_binding class(atlas_GatherScatter), intent(inout) :: this if ( .not. this%is_null() ) then call atlas__GatherScatter__delete(this%CPTR_PGIBUG_A) end if call this%reset_c_ptr() end subroutine atlas_GatherScatter__delete subroutine GatherScatter__setup32(this, part, remote_idx, glb_idx) use atlas_gatherscatter_c_binding class(atlas_GatherScatter), intent(in) :: this integer(c_int), intent(in) :: part(:) integer(ATLAS_KIND_IDX), intent(in) :: remote_idx(:) integer(c_int), intent(in) :: glb_idx(:) call atlas__GatherScatter__setup32( this%CPTR_PGIBUG_A, part, remote_idx, 1, & & glb_idx, size(part) ) end subroutine GatherScatter__setup32 subroutine GatherScatter__setup64(this, part, remote_idx, glb_idx ) use atlas_gatherscatter_c_binding class(atlas_GatherScatter), intent(in) :: this integer(c_int), intent(in) :: part(:) integer(ATLAS_KIND_IDX), intent(in) :: remote_idx(:) integer(c_long), intent(in) :: glb_idx(:) call atlas__GatherScatter__setup64( this%CPTR_PGIBUG_A, part, remote_idx, 1, & & glb_idx, size(part) ) end subroutine GatherScatter__setup64 function GatherScatter__glb_dof(this) result(glb_dof) use atlas_gatherscatter_c_binding class(atlas_GatherScatter), intent(in) :: this integer :: glb_dof glb_dof = atlas__GatherScatter__glb_dof(this%CPTR_PGIBUG_A) end function GatherScatter__glb_dof ! ----------------------------------------------------------------------------- #:for dtype,ftype,ctype in types[:4] subroutine gather_${dtype}$_r1(this, loc_field_data, glb_field_data) use atlas_gatherscatter_c_binding class(atlas_GatherScatter), intent(in) :: this ${ftype}$, intent(in) :: loc_field_data(:) ${ftype}$, intent(out) :: glb_field_data(:) integer :: lstrides(1), lextents(1), lrank=1 integer :: gstrides(1), gextents(1), grank=1 ${ftype}$, pointer :: lview(:), gview(:) lstrides = (/ array_stride(loc_field_data,2) /) lextents = (/ 1 /) lview => array_view1d(loc_field_data) gstrides = (/ array_stride(glb_field_data,2) /) gextents = (/ 1 /) gview => array_view1d(glb_field_data) if( size(gview) == 0 ) then allocate(gview(0)) endif call atlas__GatherScatter__gather_${ctype}$( this%CPTR_PGIBUG_A, & & lview, lstrides, lextents, lrank, & & gview, gstrides, gextents, grank ) end subroutine subroutine gather_${dtype}$_r2(this, loc_field_data, glb_field_data) use atlas_gatherscatter_c_binding class(atlas_GatherScatter), intent(in) :: this ${ftype}$, intent(in) :: loc_field_data(:,:) ${ftype}$, intent(out) :: glb_field_data(:,:) ${ftype}$, pointer :: lview(:), gview(:) integer :: lstrides(2), lextents(2), lrank=2 integer :: gstrides(2), gextents(2), grank=2 lstrides = (/ array_stride(loc_field_data,2), array_stride(loc_field_data,1) /) lextents = (/ 1, size (loc_field_data,1) /) lview => array_view1d(loc_field_data) gstrides = (/ array_stride(glb_field_data,2), array_stride(glb_field_data,1) /) gextents = (/ 1, size (glb_field_data,1) /) gview => array_view1d(glb_field_data) if( size(gview) == 0 ) then allocate(gview(0)) endif call atlas__GatherScatter__gather_${ctype}$( this%CPTR_PGIBUG_A, & & lview, lstrides, lextents, lrank, & & gview, gstrides, gextents, grank ) end subroutine subroutine gather_${dtype}$_r3(this, loc_field_data, glb_field_data) use atlas_gatherscatter_c_binding class(atlas_GatherScatter), intent(in) :: this ${ftype}$, intent(in) :: loc_field_data(:,:,:) ${ftype}$, intent(out) :: glb_field_data(:,:,:) ${ftype}$, pointer :: lview(:), gview(:) integer :: lstrides(3), lextents(3), lrank=3 integer :: gstrides(3), gextents(3), grank=3 lstrides = (/ array_stride(loc_field_data,3), array_stride(loc_field_data,2) , array_stride(loc_field_data,1) /) lextents = (/ 1, size (loc_field_data,2) , size (loc_field_data,1) /) lview => array_view1d(loc_field_data) gstrides = (/ array_stride(glb_field_data,3), array_stride(glb_field_data,2) , array_stride(glb_field_data,1) /) gextents = (/ 1, size (glb_field_data,2) , size (glb_field_data,1) /) gview => array_view1d(glb_field_data) if( size(gview) == 0 ) then allocate(gview(0)) endif call atlas__GatherScatter__gather_${ctype}$( this%CPTR_PGIBUG_A, & & lview, lstrides, lextents, lrank, & & gview, gstrides, gextents, grank ) end subroutine ! ----------------------------------------------------------------------------- subroutine scatter_${dtype}$_r1(this, glb_field_data, loc_field_data) use atlas_gatherscatter_c_binding class(atlas_GatherScatter), intent(in) :: this ${ftype}$, intent(in) :: glb_field_data(:) ${ftype}$, intent(out) :: loc_field_data(:) ${ftype}$, pointer :: lview(:), gview(:) integer :: lstrides(1), lextents(1), lrank=1 integer :: gstrides(1), gextents(1), grank=1 lstrides = (/ array_stride(loc_field_data,1) /) lextents = (/ 1 /) lview => array_view1d(loc_field_data) gstrides = (/ array_stride(glb_field_data,1) /) gextents = (/ 1 /) gview => array_view1d(glb_field_data) call atlas__GatherScatter__scatter_${ctype}$( this%CPTR_PGIBUG_A, & & gview, gstrides, gextents, grank, & & lview, lstrides, lextents, lrank ) end subroutine subroutine scatter_${dtype}$_r2(this, glb_field_data, loc_field_data) use atlas_gatherscatter_c_binding class(atlas_GatherScatter), intent(in) :: this ${ftype}$, intent(in) :: glb_field_data(:,:) ${ftype}$, intent(out) :: loc_field_data(:,:) ${ftype}$, pointer :: lview(:), gview(:) integer :: lstrides(2), lextents(2), lrank=2 integer :: gstrides(2), gextents(2), grank=2 lstrides = (/ array_stride(loc_field_data,2), array_stride(loc_field_data,1) /) lextents = (/ 1, size (loc_field_data,1) /) lview => array_view1d(loc_field_data) gstrides = (/ array_stride(glb_field_data,2), array_stride(glb_field_data,1) /) gextents = (/ 1, size (glb_field_data,1) /) gview => array_view1d(glb_field_data) if( size(gview) == 0 ) then allocate(gview(0)) endif call atlas__GatherScatter__scatter_${ctype}$( this%CPTR_PGIBUG_A, & & gview, gstrides, gextents, grank, & & lview, lstrides, lextents, lrank ) end subroutine subroutine scatter_${dtype}$_r3(this, glb_field_data, loc_field_data) use atlas_gatherscatter_c_binding class(atlas_GatherScatter), intent(in) :: this ${ftype}$, intent(in) :: glb_field_data(:,:,:) ${ftype}$, intent(out) :: loc_field_data(:,:,:) ${ftype}$, pointer :: lview(:), gview(:) integer :: lstrides(3), lextents(3), lrank=3 integer :: gstrides(3), gextents(3), grank=3 lstrides = (/ array_stride(loc_field_data,3), array_stride(loc_field_data,2), array_stride(loc_field_data,1) /) lextents = (/ 1, size (loc_field_data,2) , size (loc_field_data,1) /) lview => array_view1d(loc_field_data) gstrides = (/ array_stride(glb_field_data,3), array_stride(glb_field_data,2), array_stride(glb_field_data,1) /) gextents = (/ 1, size (glb_field_data,2) , size (glb_field_data,1) /) gview => array_view1d(glb_field_data) if( size(gview) == 0 ) then allocate(gview(0)) endif call atlas__GatherScatter__scatter_${ctype}$( this%CPTR_PGIBUG_A, & & gview, gstrides, gextents, grank, & & lview, lstrides, lextents, lrank ) end subroutine #:endfor !------------------------------------------------------------------------------- #if FCKIT_FINAL_NOT_INHERITING ATLAS_FINAL subroutine atlas_GatherScatter__final_auto(this) type(atlas_GatherScatter), intent(inout) :: this #if FCKIT_FINAL_DEBUGGING write(0,*) "atlas_GatherScatter__final_auto" #endif #if FCKIT_FINAL_NOT_PROPAGATING call this%final() #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine #endif ! ----------------------------------------------------------------------------- end module atlas_gatherscatter_module atlas-0.46.0/src/atlas_f/mesh/0000775000175000017500000000000015160212070016217 5ustar alastairalastairatlas-0.46.0/src/atlas_f/mesh/atlas_ElementType_module.F900000664000175000017500000001035115160212070023463 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. #include "atlas/atlas_f.h" module atlas_ElementType_module use fckit_owned_object_module, only : fckit_owned_object use atlas_kinds_module, only : ATLAS_KIND_IDX implicit none private :: fckit_owned_object public :: atlas_ElementType public :: atlas_Triangle public :: atlas_Quadrilateral public :: atlas_Line private !----------------------------- ! atlas_ElementType ! !----------------------------- type, extends(fckit_owned_object) :: atlas_ElementType contains ! Public methods procedure, public :: nb_nodes procedure, public :: nb_edges procedure, public :: name procedure, public :: parametric #if FCKIT_FINAL_NOT_INHERITING final :: atlas_ElementType__final_auto #endif end type interface atlas_ElementType module procedure atlas_ElementType__cptr end interface interface atlas_Triangle module procedure atlas_Triangle__constructor end interface interface atlas_Quadrilateral module procedure atlas_Quadrilateral__constructor end interface interface atlas_Line module procedure atlas_Line__constructor end interface !======================================================== contains !======================================================== function atlas_ElementType__cptr(cptr) result(this) use, intrinsic :: iso_c_binding, only : c_ptr use atlas_elementtype_c_binding type(atlas_ElementType) :: this type(c_ptr) :: cptr call this%reset_c_ptr( cptr ) call this%return() end function function atlas_Quadrilateral__constructor() result(this) use atlas_elementtype_c_binding use fckit_c_interop_module, only : c_str type(atlas_ElementType) :: this call this%reset_c_ptr( atlas__mesh__ElementType__create(c_str("Quadrilateral")) ) call this%return() end function function atlas_Triangle__constructor() result(this) use atlas_elementtype_c_binding use fckit_c_interop_module, only : c_str type(atlas_ElementType) :: this call this%reset_c_ptr( atlas__mesh__ElementType__create(c_str("Triangle")) ) call this%return() end function function atlas_Line__constructor() result(this) use atlas_elementtype_c_binding use fckit_c_interop_module, only : c_str type(atlas_ElementType) :: this call this%reset_c_ptr( atlas__mesh__ElementType__create(c_str("Line")) ) call this%return() end function function nb_nodes(this) use atlas_elementtype_c_binding integer(ATLAS_KIND_IDX) :: nb_nodes class(atlas_ElementType), intent(in) :: this nb_nodes = atlas__mesh__ElementType__nb_nodes(this%CPTR_PGIBUG_A) end function function nb_edges(this) use atlas_elementtype_c_binding integer(ATLAS_KIND_IDX) :: nb_edges class(atlas_ElementType), intent(in) :: this nb_edges = atlas__mesh__ElementType__nb_edges(this%CPTR_PGIBUG_A) end function function name(this) use, intrinsic :: iso_c_binding, only : c_ptr use fckit_c_interop_module, only : c_ptr_to_string use atlas_elementtype_c_binding character(len=:), allocatable :: name class(atlas_ElementType) :: this type(c_ptr) :: name_c_str name_c_str = atlas__mesh__ElementType__name(this%CPTR_PGIBUG_A) name = c_ptr_to_string(name_c_str) end function function parametric(this) use, intrinsic :: iso_c_binding, only : c_int use atlas_elementtype_c_binding logical :: parametric class(atlas_ElementType), intent(in) :: this integer(c_int) :: parametric_int parametric_int = atlas__mesh__ElementType__parametric(this%CPTR_PGIBUG_A) if( parametric_int == 0 ) then parametric = .False. else parametric = .True. endif end function !------------------------------------------------------------------------------- #if FCKIT_FINAL_NOT_INHERITING ATLAS_FINAL subroutine atlas_ElementType__final_auto(this) type(atlas_ElementType), intent(inout) :: this #if FCKIT_FINAL_DEBUGGING write(0,*) "atlas_ElementType__final_auto" #endif #if FCKIT_FINAL_NOT_PROPAGATING call this%final() #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine #endif end module atlas_ElementType_module atlas-0.46.0/src/atlas_f/mesh/atlas_mesh_actions_module.F900000664000175000017500000001146615160212070023714 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. #include "atlas/atlas_f.h" module atlas_mesh_actions_module implicit none public :: atlas_build_parallel_fields public :: atlas_build_nodes_parallel_fields public :: atlas_build_edges_parallel_fields public :: atlas_build_periodic_boundaries public :: atlas_build_halo public :: atlas_build_edges public :: atlas_build_pole_edges public :: atlas_build_node_to_cell_connectivity public :: atlas_build_node_to_edge_connectivity public :: atlas_build_median_dual_mesh public :: atlas_write_load_balance_report ! ============================================================================= CONTAINS ! ============================================================================= subroutine atlas_build_parallel_fields(mesh) use atlas_BuildParallelFields_c_binding use atlas_Mesh_module, only: atlas_Mesh type(atlas_Mesh), intent(inout) :: mesh call atlas__build_parallel_fields(mesh%CPTR_PGIBUG_A) end subroutine atlas_build_parallel_fields subroutine atlas_build_nodes_parallel_fields(nodes) use atlas_BuildParallelFields_c_binding use atlas_mesh_Nodes_module, only: atlas_mesh_Nodes type(atlas_mesh_Nodes), intent(inout) :: nodes call atlas__build_nodes_parallel_fields(nodes%CPTR_PGIBUG_A) end subroutine atlas_build_nodes_parallel_fields subroutine atlas_renumber_nodes_glb_idx(nodes) use atlas_BuildParallelFields_c_binding use atlas_mesh_Nodes_module, only: atlas_mesh_Nodes type(atlas_mesh_Nodes), intent(inout) :: nodes call atlas__renumber_nodes_glb_idx(nodes%CPTR_PGIBUG_A) end subroutine atlas_renumber_nodes_glb_idx subroutine atlas_build_edges_parallel_fields(mesh) use atlas_BuildParallelFields_c_binding use atlas_Mesh_module, only: atlas_Mesh type(atlas_Mesh), intent(inout) :: mesh call atlas__build_edges_parallel_fields(mesh%CPTR_PGIBUG_A) end subroutine atlas_build_edges_parallel_fields subroutine atlas_build_periodic_boundaries(mesh) use atlas_BuildPeriodicBoundaries_c_binding use atlas_Mesh_module, only: atlas_Mesh type(atlas_Mesh), intent(inout) :: mesh call atlas__build_periodic_boundaries(mesh%CPTR_PGIBUG_A) end subroutine atlas_build_periodic_boundaries subroutine atlas_build_halo(mesh,nelems) use atlas_BuildHalo_c_binding use atlas_Mesh_module, only: atlas_Mesh type(atlas_Mesh), intent(inout) :: mesh integer, intent(in) :: nelems call atlas__build_halo(mesh%CPTR_PGIBUG_A,nelems) end subroutine atlas_build_halo subroutine atlas_build_edges(mesh) use atlas_BuildEdges_c_binding use atlas_Mesh_module, only: atlas_Mesh type(atlas_Mesh), intent(inout) :: mesh call atlas__build_edges(mesh%CPTR_PGIBUG_A) end subroutine atlas_build_edges subroutine atlas_build_pole_edges(mesh) use atlas_BuildEdges_c_binding use atlas_Mesh_module, only: atlas_Mesh type(atlas_Mesh), intent(inout) :: mesh call atlas__build_pole_edges(mesh%CPTR_PGIBUG_A) end subroutine atlas_build_pole_edges subroutine atlas_build_node_to_cell_connectivity(mesh) use atlas_BuildNode2CellConnectivity_c_binding use atlas_Mesh_module, only: atlas_Mesh type(atlas_Mesh), intent(inout) :: mesh call atlas__build_node_to_cell_connectivity(mesh%CPTR_PGIBUG_A) end subroutine atlas_build_node_to_cell_connectivity subroutine atlas_build_node_to_edge_connectivity(mesh) use atlas_BuildEdges_c_binding use atlas_Mesh_module, only: atlas_Mesh type(atlas_Mesh), intent(inout) :: mesh call atlas__build_node_to_edge_connectivity(mesh%CPTR_PGIBUG_A) end subroutine atlas_build_node_to_edge_connectivity subroutine atlas_build_median_dual_mesh(mesh) use atlas_BuildDualMesh_c_binding use atlas_Mesh_module, only: atlas_Mesh type(atlas_Mesh), intent(inout) :: mesh call atlas__build_median_dual_mesh(mesh%CPTR_PGIBUG_A) end subroutine atlas_build_median_dual_mesh subroutine atlas_build_centroid_dual_mesh(mesh) use atlas_BuildDualMesh_c_binding use atlas_Mesh_module, only: atlas_Mesh type(atlas_Mesh), intent(inout) :: mesh call atlas__build_centroid_dual_mesh(mesh%CPTR_PGIBUG_A) end subroutine atlas_build_centroid_dual_mesh subroutine atlas_write_load_balance_report(mesh,filename) use fckit_c_interop_module, only : c_str use atlas_WriteLoadBalanceReport_c_binding use atlas_Mesh_module, only: atlas_Mesh type(atlas_Mesh), intent(in) :: mesh character(len=*), intent(in) :: filename call atlas__write_load_balance_report(mesh%CPTR_PGIBUG_A,c_str(filename)) end subroutine atlas_write_load_balance_report ! ----------------------------------------------------------------------------- end module atlas_mesh_actions_module atlas-0.46.0/src/atlas_f/mesh/atlas_HybridElements_module.F900000664000175000017500000003145315160212070024154 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. #include "atlas/atlas_f.h" module atlas_HybridElements_module use fckit_owned_object_module, only: fckit_owned_object use atlas_Connectivity_module, only: atlas_MultiBlockConnectivity use atlas_Field_module, only: atlas_Field use atlas_ElementType_module, only: atlas_ElementType use atlas_Elements_module, only: atlas_Elements use atlas_kinds_module, only: ATLAS_KIND_IDX implicit none private :: fckit_owned_object private :: atlas_MultiBlockConnectivity private :: atlas_Field private :: atlas_ElementType private :: atlas_Elements public :: atlas_HybridElements private !----------------------------- ! atlas_HybridElements ! !----------------------------- type, extends(fckit_owned_object) :: atlas_HybridElements contains ! Public methods procedure, public :: size => atlas_HybridElements__size procedure, public :: node_connectivity procedure, public :: edge_connectivity procedure, public :: cell_connectivity #if ATLAS_BITS_LOCAL != 32 generic, public :: add => add_elements_int, add_elements_long, add_elements_with_nodes_int, & & add_elements_with_nodes_long, add_field, & & add_elements_with_nodes_int_int32, add_elements_with_nodes_long_int32 #else generic, public :: add => add_elements_int, add_elements_long, add_elements_with_nodes_int, & & add_elements_with_nodes_long, add_field #endif generic, public :: field => field_by_idx_long, field_by_idx_int, field_by_name generic, public :: elements => elements_int, elements_long procedure, public :: nb_fields procedure, public :: has_field procedure, public :: global_index procedure, public :: remote_index procedure, public :: partition procedure, public :: halo procedure, public :: nb_types ! Private methods procedure, private :: add_elements_int procedure, private :: add_elements_long procedure, private :: add_elements_with_nodes_int procedure, private :: add_elements_with_nodes_long #if ATLAS_BITS_LOCAL != 32 procedure, private :: add_elements_with_nodes_int_int32 procedure, private :: add_elements_with_nodes_long_int32 #endif procedure, private :: add_field procedure, private :: field_by_idx_int procedure, private :: field_by_idx_long procedure, private :: field_by_name procedure, private :: elements_int procedure, private :: elements_long #if FCKIT_FINAL_NOT_INHERITING final :: atlas_HybridElements__final_auto #endif end type interface atlas_HybridElements module procedure atlas_HybridElements__cptr module procedure atlas_HybridElements__constructor end interface !======================================================== contains !======================================================== function atlas_HybridElements__cptr(cptr) result(this) use, intrinsic :: iso_c_binding use atlas_hybridelements_c_binding type(atlas_HybridElements) :: this type(c_ptr), intent(in) :: cptr call this%reset_c_ptr( cptr ) call this%return() end function function atlas_HybridElements__constructor() result(this) use atlas_hybridelements_c_binding type(atlas_HybridElements) :: this call this%reset_c_ptr( atlas__mesh__HybridElements__create() ) call this%return() end function function atlas_HybridElements__size(this) result(val) use, intrinsic :: iso_c_binding use atlas_hybridelements_c_binding integer(ATLAS_KIND_IDX) :: val class(atlas_HybridElements), intent(in) :: this val = atlas__mesh__HybridElements__size(this%CPTR_PGIBUG_A) end function function node_connectivity(this) result(connectivity) use atlas_hybridelements_c_binding class(atlas_HybridElements), intent(in) :: this type(atlas_MultiBlockConnectivity) :: connectivity connectivity = atlas_MultiBlockConnectivity( & atlas__mesh__HybridElements__node_connectivity(this%CPTR_PGIBUG_A) ) call connectivity%return() end function function edge_connectivity(this) result(connectivity) use atlas_hybridelements_c_binding class(atlas_HybridElements), intent(in) :: this type(atlas_MultiBlockConnectivity) :: connectivity connectivity = atlas_MultiBlockConnectivity( & atlas__mesh__HybridElements__edge_connectivity(this%CPTR_PGIBUG_A) ) call connectivity%return() end function function cell_connectivity(this) result(connectivity) use atlas_hybridelements_c_binding class(atlas_HybridElements), intent(in) :: this type(atlas_MultiBlockConnectivity) :: connectivity connectivity = atlas_MultiBlockConnectivity( & atlas__mesh__HybridElements__cell_connectivity(this%CPTR_PGIBUG_A) ) call connectivity%return() end function subroutine add_elements_int(this,elementtype,nb_elements) use, intrinsic :: iso_c_binding use atlas_hybridelements_c_binding class(atlas_HybridElements), intent(inout) :: this type(atlas_ElementType) :: elementtype integer(c_int) :: nb_elements call atlas__mesh__HybridElements__add_elements(this%CPTR_PGIBUG_A,elementtype%CPTR_PGIBUG_A,int(nb_elements,ATLAS_KIND_IDX)) end subroutine subroutine add_elements_long(this,elementtype,nb_elements) use, intrinsic :: iso_c_binding use atlas_hybridelements_c_binding class(atlas_HybridElements), intent(inout) :: this type(atlas_ElementType) :: elementtype integer(c_long) :: nb_elements call atlas__mesh__HybridElements__add_elements(this%CPTR_PGIBUG_A,elementtype%CPTR_PGIBUG_A,int(nb_elements,ATLAS_KIND_IDX)) end subroutine subroutine add_elements_with_nodes_int(this,elementtype,nb_elements,node_connectivity) use, intrinsic :: iso_c_binding use atlas_hybridelements_c_binding class(atlas_HybridElements), intent(inout) :: this type(atlas_ElementType), intent(in) :: elementtype integer(c_int), intent(in) :: nb_elements integer(ATLAS_KIND_IDX), intent(in) :: node_connectivity(:) call atlas__mesh__HybridElements__add_elements_with_nodes(this%CPTR_PGIBUG_A,& & elementtype%CPTR_PGIBUG_A,int(nb_elements,ATLAS_KIND_IDX),node_connectivity,1) end subroutine subroutine add_elements_with_nodes_long(this,elementtype,nb_elements,node_connectivity) use, intrinsic :: iso_c_binding use atlas_hybridelements_c_binding class(atlas_HybridElements), intent(inout) :: this type(atlas_ElementType), intent(in) :: elementtype integer(c_long), intent(in) :: nb_elements integer(ATLAS_KIND_IDX), intent(in) :: node_connectivity(:) call atlas__mesh__HybridElements__add_elements_with_nodes(this%CPTR_PGIBUG_A,& & elementtype%CPTR_PGIBUG_A,int(nb_elements,ATLAS_KIND_IDX),node_connectivity,1) end subroutine #if ATLAS_BITS_LOCAL != 32 subroutine add_elements_with_nodes_int_int32(this,elementtype,nb_elements,node_connectivity) use, intrinsic :: iso_c_binding use atlas_hybridelements_c_binding class(atlas_HybridElements), intent(inout) :: this type(atlas_ElementType), intent(in) :: elementtype integer(c_int), intent(in) :: nb_elements integer(c_int), intent(in) :: node_connectivity(:) integer(ATLAS_KIND_IDX), allocatable :: idx_node_connectivity(:) allocate( idx_node_connectivity( nb_elements * elementtype%nb_nodes() ) ) idx_node_connectivity(:) = node_connectivity(:) call atlas__mesh__HybridElements__add_elements_with_nodes(this%CPTR_PGIBUG_A,& & elementtype%CPTR_PGIBUG_A,int(nb_elements,ATLAS_KIND_IDX),idx_node_connectivity,1) deallocate( idx_node_connectivity ) end subroutine subroutine add_elements_with_nodes_long_int32(this,elementtype,nb_elements,node_connectivity) use, intrinsic :: iso_c_binding use atlas_hybridelements_c_binding class(atlas_HybridElements), intent(inout) :: this type(atlas_ElementType), intent(in) :: elementtype integer(c_long), intent(in) :: nb_elements integer(c_int), intent(in) :: node_connectivity(:) integer(ATLAS_KIND_IDX), allocatable :: idx_node_connectivity(:) allocate( idx_node_connectivity( nb_elements * elementtype%nb_nodes() ) ) idx_node_connectivity(:) = node_connectivity(:) call atlas__mesh__HybridElements__add_elements_with_nodes(this%CPTR_PGIBUG_A,& & elementtype%CPTR_PGIBUG_A,int(nb_elements,ATLAS_KIND_IDX),idx_node_connectivity,1) deallocate( idx_node_connectivity ) end subroutine #endif subroutine add_field(this,field) use atlas_hybridelements_c_binding class(atlas_HybridElements), intent(inout) :: this type(atlas_Field), intent(in) :: field call atlas__mesh__HybridElements__add_field(this%CPTR_PGIBUG_A, field%CPTR_PGIBUG_A) end subroutine function nb_types(this) result(val) use atlas_hybridelements_c_binding integer(ATLAS_KIND_IDX) :: val class(atlas_HybridElements), intent(in) :: this val = atlas__mesh__HybridElements__nb_types(this%CPTR_PGIBUG_A) end function function nb_fields(this) result(val) use atlas_hybridelements_c_binding integer(ATLAS_KIND_IDX) :: val class(atlas_HybridElements), intent(in) :: this val = atlas__mesh__HybridElements__nb_fields(this%CPTR_PGIBUG_A) end function function has_field(this,name) result(val) use fckit_c_interop_module, only: c_str use atlas_hybridelements_c_binding logical :: val class(atlas_HybridElements), intent(in) :: this character(len=*), intent(in) :: name if( atlas__mesh__HybridElements__has_field(this%CPTR_PGIBUG_A,c_str(name)) == 0 ) then val = .False. else val = .True. endif end function function field_by_name(this,name) result(field) use fckit_c_interop_module, only: c_str use atlas_hybridelements_c_binding type(atlas_Field) :: field class(atlas_HybridElements), intent(in) :: this character(len=*), intent(in) :: name field = atlas_Field( atlas__mesh__HybridElements__field_by_name(this%CPTR_PGIBUG_A,c_str(name)) ) call field%return() end function function field_by_idx_long(this,idx) result(field) use, intrinsic :: iso_c_binding use atlas_hybridelements_c_binding type(atlas_Field) :: field class(atlas_HybridElements), intent(in) :: this integer(c_long), intent(in) :: idx field = atlas_Field( atlas__mesh__HybridElements__field_by_idx(this%CPTR_PGIBUG_A,int(idx-1_c_long,ATLAS_KIND_IDX) ) ) call field%return() end function function field_by_idx_int(this,idx) result(field) use, intrinsic :: iso_c_binding use atlas_hybridelements_c_binding type(atlas_Field) :: field class(atlas_HybridElements), intent(in) :: this integer(c_int), intent(in) :: idx field = atlas_Field( atlas__mesh__HybridElements__field_by_idx(this%CPTR_PGIBUG_A,int(idx-1_c_int,ATLAS_KIND_IDX) ) ) call field%return() end function function global_index(this) result(field) use atlas_hybridelements_c_binding type(atlas_Field) :: field class(atlas_HybridElements), intent(in) :: this field = atlas_Field( atlas__mesh__HybridElements__global_index(this%CPTR_PGIBUG_A) ) call field%return() end function function remote_index(this) result(field) use atlas_hybridelements_c_binding type(atlas_Field) :: field class(atlas_HybridElements), intent(in) :: this field = atlas_Field( atlas__mesh__HybridElements__remote_index(this%CPTR_PGIBUG_A) ) call field%return() end function function partition(this) result(field) use atlas_hybridelements_c_binding type(atlas_Field) :: field class(atlas_HybridElements), intent(in) :: this field = atlas_Field( atlas__mesh__HybridElements__partition(this%CPTR_PGIBUG_A) ) call field%return() end function function halo(this) result(field) use atlas_hybridelements_c_binding type(atlas_Field) :: field class(atlas_HybridElements), intent(in) :: this field = atlas_Field( atlas__mesh__HybridElements__halo(this%CPTR_PGIBUG_A) ) call field%return() end function function elements_long(this,idx) result(elements) use, intrinsic :: iso_c_binding use atlas_hybridelements_c_binding type(atlas_Elements) :: elements class(atlas_HybridElements), intent(in) :: this integer(c_long), intent(in) :: idx elements = atlas_Elements( atlas__mesh__HybridElements__elements(this%CPTR_PGIBUG_A,int(idx-1_c_long,ATLAS_KIND_IDX) ) ) call elements%return() end function function elements_int(this,idx) result(elements) use, intrinsic :: iso_c_binding use atlas_hybridelements_c_binding type(atlas_Elements) :: elements class(atlas_HybridElements), intent(in) :: this integer(c_int), intent(in) :: idx elements = atlas_Elements( atlas__mesh__HybridElements__elements(this%CPTR_PGIBUG_A,int(idx-1_c_int,ATLAS_KIND_IDX) ) ) call elements%return() end function !------------------------------------------------------------------------------- #if FCKIT_FINAL_NOT_INHERITING ATLAS_FINAL subroutine atlas_HybridElements__final_auto(this) type(atlas_HybridElements), intent(inout) :: this #if FCKIT_FINAL_DEBUGGING write(0,*) "atlas_HybridElements__final_auto" #endif #if FCKIT_FINAL_NOT_PROPAGATING call this%final() #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine #endif end module atlas_HybridElements_module atlas-0.46.0/src/atlas_f/mesh/atlas_Elements_module.F900000664000175000017500000002053015160212070023004 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. #include "atlas/atlas_f.h" module atlas_Elements_module use fckit_owned_object_module, only: fckit_owned_object use atlas_kinds_module, only : ATLAS_KIND_IDX implicit none private :: fckit_owned_object public :: atlas_Elements private !----------------------------- ! atlas_Elements ! !----------------------------- type, extends(fckit_owned_object) :: atlas_Elements contains ! Public methods procedure, public :: size => atlas_Elements__size procedure, public :: begin => atlas_Elements__begin procedure, public :: end => atlas_Elements__end procedure, public :: node_connectivity procedure, public :: edge_connectivity procedure, public :: cell_connectivity generic, public :: add => add_elements_long, add_elements_int generic, public :: field => field_by_idx_long, field_by_idx_int, field_by_name procedure, public :: element_type procedure, public :: nb_fields procedure, public :: has_field procedure, public :: global_index procedure, public :: remote_index procedure, public :: partition procedure, public :: halo ! Private methods procedure, private :: field_by_idx_int procedure, private :: field_by_idx_long procedure, private :: field_by_name procedure, private :: add_elements_long procedure, private :: add_elements_int #if FCKIT_FINAL_NOT_INHERITING final :: atlas_Elements__final_auto #endif end type interface atlas_Elements module procedure atlas_Elements__cptr end interface !======================================================== contains !======================================================== function atlas_Elements__cptr(cptr) result(this) use, intrinsic :: iso_c_binding, only: c_ptr type(atlas_Elements) :: this type(c_ptr), intent(in) :: cptr call this%reset_c_ptr( cptr ) call this%return() end function function atlas_Elements__size(this) result(val) use atlas_elements_c_binding integer(ATLAS_KIND_IDX) :: val class(atlas_Elements), intent(in) :: this val = atlas__mesh__Elements__size(this%CPTR_PGIBUG_A) end function function node_connectivity(this) result(connectivity) use atlas_elements_c_binding use atlas_Connectivity_module, only: atlas_BlockConnectivity class(atlas_Elements), intent(in) :: this type(atlas_BlockConnectivity) :: connectivity connectivity = atlas_BlockConnectivity( & atlas__mesh__Elements__node_connectivity(this%CPTR_PGIBUG_A) ) !call connectivity%return() end function function edge_connectivity(this) result(connectivity) use atlas_elements_c_binding use atlas_Connectivity_module, only: atlas_BlockConnectivity class(atlas_Elements), intent(in) :: this type(atlas_BlockConnectivity) :: connectivity connectivity = atlas_BlockConnectivity( & atlas__mesh__Elements__edge_connectivity(this%CPTR_PGIBUG_A) ) !call connectivity%return() end function function cell_connectivity(this) result(connectivity) use atlas_elements_c_binding use atlas_Connectivity_module, only: atlas_BlockConnectivity class(atlas_Elements), intent(in) :: this type(atlas_BlockConnectivity) :: connectivity connectivity = atlas_BlockConnectivity( & atlas__mesh__Elements__cell_connectivity(this%CPTR_PGIBUG_A) ) !call connectivity%return() end function function element_type(this) result(etype) use atlas_elements_c_binding use atlas_ElementType_module, only: atlas_ElementType class(atlas_Elements), intent(in) :: this type(atlas_ElementType) :: etype etype = atlas_ElementType( & atlas__mesh__Elements__element_type(this%CPTR_PGIBUG_A) ) call etype%return() end function subroutine add_elements_long(this,nb_elements) use, intrinsic :: iso_c_binding, only: c_long, c_int use atlas_elements_c_binding class(atlas_Elements), intent(inout) :: this integer(c_long) :: nb_elements call atlas__mesh__Elements__add(this%CPTR_PGIBUG_A,int(nb_elements,ATLAS_KIND_IDX)) end subroutine subroutine add_elements_int(this,nb_elements) use, intrinsic :: iso_c_binding, only: c_int use atlas_elements_c_binding class(atlas_Elements), intent(inout) :: this integer(c_int) :: nb_elements call atlas__mesh__Elements__add(this%CPTR_PGIBUG_A,int(nb_elements,ATLAS_KIND_IDX)) end subroutine function nb_fields(this) result(val) use atlas_elements_c_binding integer(ATLAS_KIND_IDX) :: val class(atlas_Elements), intent(in) :: this val = atlas__mesh__Elements__nb_fields(this%CPTR_PGIBUG_A) end function function has_field(this,name) result(val) use fckit_c_interop_module, only: c_str use atlas_elements_c_binding logical :: val class(atlas_Elements), intent(in) :: this character(len=*), intent(in) :: name if( atlas__mesh__Elements__has_field(this%CPTR_PGIBUG_A,c_str(name)) == 0 ) then val = .False. else val = .True. endif end function function field_by_name(this,name) result(field) use fckit_c_interop_module, only: c_str use atlas_elements_c_binding use atlas_Field_module, only: atlas_Field type(atlas_Field) :: field class(atlas_Elements), intent(in) :: this character(len=*), intent(in) :: name field = atlas_Field( atlas__mesh__Elements__field_by_name(this%CPTR_PGIBUG_A,c_str(name)) ) call field%return() end function function field_by_idx_long(this,idx) result(field) use, intrinsic :: iso_c_binding, only: c_long use atlas_elements_c_binding use atlas_Field_module, only: atlas_Field type(atlas_Field) :: field class(atlas_Elements), intent(in) :: this integer(c_long), intent(in) :: idx field = atlas_Field( atlas__mesh__Elements__field_by_idx(this%CPTR_PGIBUG_A,int(idx-1_c_long,ATLAS_KIND_IDX) ) ) call field%return() end function function field_by_idx_int(this,idx) result(field) use, intrinsic :: iso_c_binding, only: c_int use atlas_elements_c_binding use atlas_Field_module, only: atlas_Field type(atlas_Field) :: field class(atlas_Elements), intent(in) :: this integer(c_int), intent(in) :: idx field = atlas_Field( atlas__mesh__Elements__field_by_idx(this%CPTR_PGIBUG_A,int(idx-1_c_int,ATLAS_KIND_IDX) ) ) call field%return() end function function global_index(this) result(field) use atlas_elements_c_binding use atlas_Field_module, only: atlas_Field type(atlas_Field) :: field class(atlas_Elements), intent(in) :: this field = atlas_Field( atlas__mesh__Elements__global_index(this%CPTR_PGIBUG_A) ) call field%return() end function function remote_index(this) result(field) use atlas_elements_c_binding use atlas_Field_module, only: atlas_Field type(atlas_Field) :: field class(atlas_Elements), intent(in) :: this field = atlas_Field( atlas__mesh__Elements__remote_index(this%CPTR_PGIBUG_A) ) call field%return() end function function partition(this) result(field) use atlas_elements_c_binding use atlas_Field_module, only: atlas_Field type(atlas_Field) :: field class(atlas_Elements), intent(in) :: this field = atlas_Field( atlas__mesh__Elements__partition(this%CPTR_PGIBUG_A) ) call field%return() end function function halo(this) result(field) use atlas_elements_c_binding use atlas_Field_module, only: atlas_Field type(atlas_Field) :: field class(atlas_Elements), intent(in) :: this field = atlas_Field( atlas__mesh__Elements__halo(this%CPTR_PGIBUG_A) ) call field%return() end function function atlas_Elements__begin(this) result(val) use atlas_elements_c_binding integer(ATLAS_KIND_IDX) :: val class(atlas_Elements), intent(in) :: this val = atlas__mesh__Elements__begin(this%CPTR_PGIBUG_A) + 1 end function function atlas_Elements__end(this) result(val) use atlas_elements_c_binding integer(ATLAS_KIND_IDX) :: val class(atlas_Elements), intent(in) :: this val = atlas__mesh__Elements__end(this%CPTR_PGIBUG_A) end function !------------------------------------------------------------------------------- #if FCKIT_FINAL_NOT_INHERITING ATLAS_FINAL subroutine atlas_Elements__final_auto(this) type(atlas_Elements), intent(inout) :: this #if FCKIT_FINAL_DEBUGGING write(0,*) "atlas_Elements__final_auto" #endif #if FCKIT_FINAL_NOT_PROPAGATING call this%final() #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine #endif end module atlas_Elements_module atlas-0.46.0/src/atlas_f/mesh/atlas_MeshGenerator_module.F900000664000175000017500000001154115160212070023775 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. #include "atlas/atlas_f.h" module atlas_MeshGenerator_module use fckit_owned_object_module, only: fckit_owned_object implicit none private :: fckit_owned_object public :: atlas_MeshGenerator private !-----------------------------! ! atlas_MeshGenerator ! !-----------------------------! !------------------------------------------------------------------------------ TYPE, extends(fckit_owned_object) :: atlas_MeshGenerator contains procedure, private :: atlas_MeshGenerator__generate procedure, private :: atlas_MeshGenerator__generate_partitioner generic :: generate => atlas_MeshGenerator__generate, atlas_MeshGenerator__generate_partitioner #if FCKIT_FINAL_NOT_INHERITING final :: atlas_MeshGenerator__final_auto #endif END TYPE atlas_MeshGenerator interface atlas_MeshGenerator module procedure atlas_MeshGenerator__cptr module procedure atlas_MeshGenerator__config module procedure atlas_MeshGenerator__type end interface !------------------------------------------------------------------------------ !======================================================== contains !======================================================== function atlas_MeshGenerator__cptr(cptr) result(this) use, intrinsic :: iso_c_binding, only: c_ptr type(atlas_MeshGenerator) :: this type(c_ptr), intent(in) :: cptr call this%reset_c_ptr( cptr ) call this%return() end function function atlas_MeshGenerator__type(type) result(this) use fckit_c_interop_module, only: c_str use atlas_MeshGenerator_c_binding type(atlas_MeshGenerator) :: this character(len=*), intent(in) :: type call this%reset_c_ptr( atlas__MeshGenerator__create_noconfig(c_str(type)) ) call this%return() end function function atlas_MeshGenerator__config(config) result(this) use fckit_c_interop_module, only: c_str use atlas_MeshGenerator_c_binding use atlas_Config_module, only: atlas_Config type(atlas_MeshGenerator) :: this type(atlas_Config), intent(in), optional :: config character(len=:), allocatable :: meshgenerator_type if( present(config) ) then if( .not. config%get("type",meshgenerator_type) ) then meshgenerator_type='structured' endif call this%reset_c_ptr( atlas__MeshGenerator__create( & c_str(meshgenerator_type),config%CPTR_PGIBUG_B) ) else call this%reset_c_ptr( atlas__MeshGenerator__create_noconfig(c_str('structured')) ) endif call this%return() end function function atlas_MeshGenerator__generate(this,grid,distribution) result(mesh) use atlas_MeshGenerator_c_binding use atlas_Grid_module, only: atlas_Grid use atlas_GridDistribution_module, only: atlas_GridDistribution use atlas_Mesh_module, only: atlas_Mesh type(atlas_Mesh) :: mesh class(atlas_MeshGenerator), intent(in) :: this class(atlas_Grid), intent(in) :: grid class(atlas_GridDistribution), intent(in), optional :: distribution call mesh%reset_c_ptr() ! Somehow needed with PGI/16.7 and build-type "bit" if( present(distribution) ) then mesh = atlas_Mesh( atlas__MeshGenerator__generate__grid_griddist( & this%CPTR_PGIBUG_A,grid%CPTR_PGIBUG_A,distribution%CPTR_PGIBUG_A) ) else mesh = atlas_Mesh( atlas__MeshGenerator__generate__grid( & this%CPTR_PGIBUG_A,grid%CPTR_PGIBUG_A) ) endif call mesh%return() end function function atlas_MeshGenerator__generate_partitioner(this,grid,partitioner) result(mesh) use atlas_MeshGenerator_c_binding use atlas_Grid_module, only: atlas_Grid use atlas_Partitioner_module, only: atlas_Partitioner use atlas_Mesh_module, only: atlas_Mesh type(atlas_Mesh) :: mesh class(atlas_MeshGenerator), intent(in) :: this class(atlas_Grid), intent(in) :: grid class(atlas_Partitioner), intent(in) :: partitioner call mesh%reset_c_ptr() ! Somehow needed with PGI/16.7 and build-type "bit" mesh = atlas_Mesh( atlas__MeshGenerator__generate__grid_partitioner( & this%CPTR_PGIBUG_A,grid%CPTR_PGIBUG_A,partitioner%CPTR_PGIBUG_A) ) call mesh%return() end function !------------------------------------------------------------------------------- #if FCKIT_FINAL_NOT_INHERITING ATLAS_FINAL subroutine atlas_MeshGenerator__final_auto(this) type(atlas_MeshGenerator), intent(inout) :: this #if FCKIT_FINAL_DEBUGGING write(0,*) "atlas_MeshGenerator__final_auto" #endif #if FCKIT_FINAL_NOT_PROPAGATING call this%final() #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine #endif ! ---------------------------------------------------------------------------------------- end module atlas_MeshGenerator_module atlas-0.46.0/src/atlas_f/mesh/atlas_mesh_Nodes_module.F900000664000175000017500000002325315160212070023321 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. #include "atlas/atlas_f.h" module atlas_mesh_Nodes_module use, intrinsic :: iso_c_binding, only: c_ptr, c_int, c_long use fckit_owned_object_module, only: fckit_owned_object use atlas_Metadata_module, only: atlas_Metadata use atlas_Field_module, only: atlas_Field use atlas_Connectivity_module, only: atlas_Connectivity use atlas_kinds_module, only : ATLAS_KIND_IDX implicit none private :: fckit_owned_object private :: c_ptr, c_int private :: atlas_Metadata private :: atlas_Field private :: atlas_Connectivity public :: atlas_mesh_Nodes private !----------------------------- ! atlas_mesh_Nodes ! !----------------------------- TYPE, extends(fckit_owned_object) :: atlas_mesh_Nodes contains procedure, public :: size => atlas_mesh_Nodes__size procedure, private :: resize_int procedure, private :: resize_long generic, public :: resize => resize_int, resize_long procedure, private :: add_field procedure, private :: add_connectivity generic, public :: add => & & add_field, & & add_connectivity procedure, public :: remove_field procedure, private :: field_by_idx_int procedure, private :: field_by_idx_long procedure, private :: field_by_name generic, public :: field => & & field_by_idx_long, field_by_idx_int, & & field_by_name procedure, public :: nb_fields procedure, public :: has_field procedure, public :: metadata procedure, public :: str procedure, public :: xy procedure, public :: lonlat procedure, public :: global_index procedure, public :: remote_index procedure, public :: partition procedure, public :: ghost procedure, public :: edge_connectivity procedure, public :: cell_connectivity procedure, public :: connectivity #if FCKIT_FINAL_NOT_INHERITING final :: atlas_mesh_Nodes__final_auto #endif end type interface atlas_mesh_Nodes module procedure atlas_mesh_Nodes__cptr module procedure atlas_mesh_Nodes__constructor end interface !======================================================== contains !======================================================== function atlas_mesh_Nodes__cptr(cptr) result(this) use atlas_nodes_c_binding type(atlas_mesh_Nodes) :: this type(c_ptr), intent(in) :: cptr call this%reset_c_ptr( cptr ) call this%return() end function function atlas_mesh_Nodes__constructor() result(this) use atlas_nodes_c_binding type(atlas_mesh_Nodes) :: this call this%reset_c_ptr( atlas__mesh__Nodes__create() ) call this%return() end function function atlas_mesh_Nodes__size(this) result(val) use atlas_nodes_c_binding integer(ATLAS_KIND_IDX) :: val class(atlas_mesh_Nodes), intent(in) :: this val = atlas__mesh__Nodes__size(this%CPTR_PGIBUG_A) end function function edge_connectivity(this) result(connectivity) use atlas_nodes_c_binding use atlas_Connectivity_module, only: atlas_Connectivity class(atlas_mesh_Nodes), intent(in) :: this type(atlas_Connectivity) :: connectivity connectivity = atlas_Connectivity( & atlas__mesh__Nodes__edge_connectivity(this%CPTR_PGIBUG_A) ) call connectivity%return() end function function cell_connectivity(this) result(connectivity) use atlas_nodes_c_binding class(atlas_mesh_Nodes), intent(in) :: this type(atlas_Connectivity) :: connectivity connectivity = atlas_Connectivity( & atlas__mesh__Nodes__cell_connectivity(this%CPTR_PGIBUG_A) ) call connectivity%return() end function function connectivity(this,name) use atlas_nodes_c_binding use fckit_c_interop_module, only: c_str type(atlas_Connectivity) :: connectivity class(atlas_mesh_Nodes), intent(in) :: this character(len=*), intent(in) :: name connectivity = atlas_Connectivity( & atlas__mesh__Nodes__connectivity(this%CPTR_PGIBUG_A,c_str(name)) ) call connectivity%return() end function subroutine add_connectivity(this,connectivity) use atlas_nodes_c_binding class(atlas_mesh_Nodes), intent(inout) :: this type(atlas_Connectivity), intent(in) :: connectivity call atlas__mesh__Nodes__add_connectivity(this%CPTR_PGIBUG_A, connectivity%CPTR_PGIBUG_A) end subroutine subroutine add_field(this,field) use atlas_nodes_c_binding class(atlas_mesh_Nodes), intent(inout) :: this type(atlas_Field), intent(in) :: field call atlas__mesh__Nodes__add_field(this%CPTR_PGIBUG_A, field%CPTR_PGIBUG_A) end subroutine subroutine remove_field(this,name) use atlas_nodes_c_binding use fckit_c_interop_module, only: c_str class(atlas_mesh_Nodes), intent(in) :: this character(len=*), intent(in) :: name call atlas__mesh__Nodes__remove_field(this%CPTR_PGIBUG_A,c_str(name)) end subroutine function nb_fields(this) result(val) use atlas_nodes_c_binding integer(ATLAS_KIND_IDX) :: val class(atlas_mesh_Nodes), intent(in) :: this val = atlas__mesh__Nodes__nb_fields(this%CPTR_PGIBUG_A) end function function has_field(this,name) result(val) use atlas_nodes_c_binding use fckit_c_interop_module, only: c_str logical :: val class(atlas_mesh_Nodes), intent(in) :: this character(len=*), intent(in) :: name if( atlas__mesh__Nodes__has_field(this%CPTR_PGIBUG_A,c_str(name)) == 0 ) then val = .False. else val = .True. endif end function function field_by_name(this,name) result(field) use atlas_nodes_c_binding use fckit_c_interop_module, only: c_str type(atlas_Field) :: field class(atlas_mesh_Nodes), intent(in) :: this character(len=*), intent(in) :: name field = atlas_Field( atlas__mesh__Nodes__field_by_name(this%CPTR_PGIBUG_A,c_str(name)) ) call field%return() end function function field_by_idx_long(this,idx) result(field) use atlas_nodes_c_binding use, intrinsic :: iso_c_binding, only: c_long type(atlas_Field) :: field class(atlas_mesh_Nodes), intent(in) :: this integer(c_long), intent(in) :: idx field = atlas_Field( atlas__mesh__Nodes__field_by_idx(this%CPTR_PGIBUG_A,int(idx-1_c_long,ATLAS_KIND_IDX) ) ) call field%return() end function function field_by_idx_int(this,idx) result(field) use atlas_nodes_c_binding use, intrinsic :: iso_c_binding, only: c_int type(atlas_Field) :: field class(atlas_mesh_Nodes), intent(in) :: this integer(c_int), intent(in) :: idx field = atlas_Field( atlas__mesh__Nodes__field_by_idx(this%CPTR_PGIBUG_A,int(idx-1_c_long,ATLAS_KIND_IDX) ) ) call field%return() end function function xy(this) result(field) use atlas_nodes_c_binding type(atlas_Field) :: field class(atlas_mesh_Nodes), intent(in) :: this field = atlas_Field( atlas__mesh__Nodes__xy(this%CPTR_PGIBUG_A) ) call field%return() end function function lonlat(this) result(field) use atlas_nodes_c_binding type(atlas_Field) :: field class(atlas_mesh_Nodes), intent(in) :: this field = atlas_Field( atlas__mesh__Nodes__lonlat(this%CPTR_PGIBUG_A) ) call field%return() end function function global_index(this) result(field) use atlas_nodes_c_binding type(atlas_Field) :: field class(atlas_mesh_Nodes), intent(in) :: this field = atlas_Field( atlas__mesh__Nodes__global_index(this%CPTR_PGIBUG_A) ) call field%return() end function function remote_index(this) result(field) use atlas_nodes_c_binding type(atlas_Field) :: field class(atlas_mesh_Nodes), intent(in) :: this field = atlas_Field( atlas__mesh__Nodes__remote_index(this%CPTR_PGIBUG_A) ) call field%return() end function function partition(this) result(field) use atlas_nodes_c_binding type(atlas_Field) :: field class(atlas_mesh_Nodes), intent(in) :: this field = atlas_Field( atlas__mesh__Nodes__partition(this%CPTR_PGIBUG_A) ) call field%return() end function function ghost(this) result(field) use atlas_nodes_c_binding type(atlas_Field) :: field class(atlas_mesh_Nodes), intent(in) :: this field = atlas_Field( atlas__mesh__Nodes__ghost(this%CPTR_PGIBUG_A) ) call field%return() end function function metadata(this) use atlas_nodes_c_binding type(atlas_Metadata) :: metadata class(atlas_mesh_Nodes), intent(in) :: this call metadata%reset_c_ptr( atlas__mesh__Nodes__metadata(this%CPTR_PGIBUG_A) ) end function subroutine resize_int(this,size) use, intrinsic :: iso_c_binding use atlas_nodes_c_binding class(atlas_mesh_Nodes), intent(in) :: this integer(c_int), intent(in) :: size call atlas__mesh__Nodes__resize(this%CPTR_PGIBUG_A,int(size,ATLAS_KIND_IDX)) end subroutine subroutine resize_long(this,size) use, intrinsic :: iso_c_binding use atlas_nodes_c_binding class(atlas_mesh_Nodes), intent(in) :: this integer(c_long), intent(in) :: size call atlas__mesh__Nodes__resize(this%CPTR_PGIBUG_A,int(size,ATLAS_KIND_IDX)) end subroutine function str(this) use atlas_nodes_c_binding use fckit_c_interop_module, only: c_ptr_to_string, c_ptr_free character(len=:), allocatable :: str class(atlas_mesh_Nodes), intent(in) :: this type(c_ptr) :: str_cptr integer(c_int) :: str_size call atlas__mesh__Nodes__str(this%CPTR_PGIBUG_A,str_cptr,str_size) allocate(character(len=str_size) :: str ) str = c_ptr_to_string(str_cptr) call c_ptr_free(str_cptr) end function !------------------------------------------------------------------------------- #if FCKIT_FINAL_NOT_INHERITING ATLAS_FINAL subroutine atlas_mesh_Nodes__final_auto(this) type(atlas_mesh_Nodes), intent(inout) :: this #if FCKIT_FINAL_DEBUGGING write(0,*) "atlas_mesh_Nodes__final_auto" #endif #if FCKIT_FINAL_NOT_PROPAGATING call this%final() #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine #endif ! ---------------------------------------------------------------------------------------- end module atlas_mesh_Nodes_module atlas-0.46.0/src/atlas_f/mesh/atlas_Connectivity_module.F900000664000175000017500000006055315160212070023717 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. #include "atlas/atlas_f.h" module atlas_connectivity_module use, intrinsic :: iso_c_binding, only : c_int, c_ptr, c_null_ptr use fckit_owned_object_module, only : fckit_owned_object use fckit_object_module, only : fckit_object use atlas_kinds_module, only : ATLAS_KIND_IDX use atlas_allocate_module, only : atlas_allocate_managedmem, atlas_deallocate_managedmem use fckit_exception_module, only : fckit_exception implicit none private :: c_ptr private :: c_int private :: c_null_ptr private :: fckit_owned_object private :: fckit_object private :: atlas_allocate_managedmem private :: atlas_deallocate_managedmem public :: atlas_Connectivity public :: atlas_MultiBlockConnectivity public :: atlas_BlockConnectivity private !----------------------------- ! atlas_Connectivity ! !----------------------------- type, extends(fckit_owned_object) :: atlas_Connectivity ! Public members type( atlas_ConnectivityAccess ), public, pointer :: access => null() contains procedure, public :: assignment_operator_hook procedure, public :: set_access ! Public methods procedure, public :: name => atlas_Connectivity__name procedure, private :: value_args_int => atlas_Connectivity__value_args_int procedure, private :: value_args_long => atlas_Connectivity__value_args_long generic, public :: value => value_args_int, value_args_long procedure, public :: rows => atlas_Connectivity__rows procedure, public :: cols => atlas_Connectivity__cols procedure, public :: maxcols => atlas_Connectivity__maxcols procedure, public :: mincols => atlas_Connectivity__mincols procedure, public :: padded_data => atlas_Connectivity__padded_data procedure, private :: atlas_Connectivity__data procedure, private :: atlas_Connectivity__data_int procedure, private :: atlas_Connectivity__data_long generic, public :: data => atlas_Connectivity__data, atlas_Connectivity__data_int, atlas_Connectivity__data_long procedure, private :: atlas_Connectivity__row_int procedure, private :: atlas_Connectivity__row_long generic, public :: row => atlas_Connectivity__row_int, atlas_Connectivity__row_long procedure, public :: missing_value => atlas_Connectivity__missing_value procedure, private :: add_values_args_idx => atlas_Connectivity__add_values_args_idx #if ATLAS_BITS_LOCAL != 32 procedure, private :: add_values_args_int32 => atlas_Connectivity__add_values_args_int32 #define _add_values_args_int32 , add_values_args_int32 #else #define _add_values_args_int32 #endif procedure, private :: add_values_args_long => atlas_Connectivity__add_values_args_long procedure, private :: add_missing_args_int => atlas_Connectivity__add_missing_args_int procedure, private :: add_missing_args_long => atlas_Connectivity__add_missing_args_long generic, public :: add => add_values_args_idx, add_values_args_long, add_missing_args_int, & & add_missing_args_long _add_values_args_int32 #if FCKIT_FINAL_NOT_INHERITING final :: atlas_Connectivity__final_auto #endif end type !--------------------------------------- ! atlas_MultiBlockConnectivity ! !--------------------------------------- type, extends(atlas_Connectivity) :: atlas_MultiBlockConnectivity contains ! Public methods procedure, public :: blocks => atlas_MultiBlockConnectivity__blocks procedure, public :: block => atlas_MultiBlockConnectivity__block ! PGI compiler bug won't accept "assignment_operator_hook" from atlas_Connectivity parent class... grrr procedure, public :: assignment_operator_hook => atlas_MBC__assignment_operator_hook #if FCKIT_FINAL_NOT_INHERITING final :: atlas_MultiBlockConnectivity__final_auto #endif end type !----------------------------! ! atlas_BlockConnectivity ! !----------------------------! type, extends(fckit_object) :: atlas_BlockConnectivity contains procedure, public :: rows => atlas_BlockConnectivity__rows procedure, public :: cols => atlas_BlockConnectivity__cols procedure, public :: data => atlas_BlockConnectivity__data procedure, public :: missing_value => atlas_BlockConnectivity__missing_value end type interface atlas_Connectivity module procedure Connectivity_cptr module procedure Connectivity_constructor end interface interface atlas_MultiBlockConnectivity module procedure MultiBlockConnectivity_cptr module procedure MultiBlockConnectivity_constructor end interface interface atlas_BlockConnectivity module procedure BlockConnectivity_cptr end interface !------------------------------- ! Helper types ! !------------------------------- type :: atlas_ConnectivityAccessRow integer(ATLAS_KIND_IDX), pointer :: col(:) contains end type type :: atlas_ConnectivityAccess integer(ATLAS_KIND_IDX), private, pointer :: values_(:) => null() integer(ATLAS_KIND_IDX), private, pointer :: displs_(:) => null() integer(ATLAS_KIND_IDX), public, pointer :: cols(:) => null() type(atlas_ConnectivityAccessRow), public, pointer :: row(:) => null() integer(ATLAS_KIND_IDX), private :: rows_ integer(ATLAS_KIND_IDX), private, pointer :: padded_(:,:) => null() integer(ATLAS_KIND_IDX), private :: maxcols_, mincols_ integer(ATLAS_KIND_IDX), private :: missing_value_ type(c_ptr), private :: connectivity_ptr = c_null_ptr contains procedure, public :: rows => access_rows procedure, public :: value => access_value end type ! ---------------------------------------------------------------------------- interface subroutine atlas__connectivity__register_ctxt(This,ptr) bind(c,name="atlas__connectivity__register_ctxt") use, intrinsic :: iso_c_binding, only: c_ptr type(c_ptr), value :: This type(c_ptr), value :: ptr end subroutine subroutine atlas__connectivity__register_update(This,funptr) bind(c,name="atlas__connectivity__register_update") use, intrinsic :: iso_c_binding, only: c_ptr, c_funptr type(c_ptr), value :: This type(c_funptr), value :: funptr end subroutine subroutine atlas__connectivity__register_delete(This,funptr) bind(c,name="atlas__connectivity__register_delete") use, intrinsic :: iso_c_binding, only: c_ptr, c_funptr type(c_ptr), value :: This type(c_funptr), value :: funptr end subroutine function atlas__connectivity__ctxt(This,ptr) bind(c,name="atlas__connectivity__ctxt") use, intrinsic :: iso_c_binding, only: c_ptr, c_int integer(c_int) :: atlas__connectivity__ctxt type(c_ptr), value :: This type(c_ptr) :: ptr end function end interface contains subroutine assignment_operator_hook(this,other) class(atlas_Connectivity) :: this class(fckit_owned_object) :: other call this%set_access() FCKIT_SUPPRESS_UNUSED(other) end subroutine ! Following routine is exact copy of "assignment_operator_hook" above, because of bug in PGI compiler (17.7) ! Without it, wrongly the "fckit_owned_object::assignment_operator_hook" is used instead of ! "atlas_Connectivity::assignment_operator_hook". subroutine atlas_MBC__assignment_operator_hook(this,other) class(atlas_MultiBlockConnectivity) :: this class(fckit_owned_object) :: other call this%set_access() FCKIT_SUPPRESS_UNUSED(other) end subroutine function Connectivity_cptr(cptr) result(this) use, intrinsic :: iso_c_binding, only : c_ptr use atlas_connectivity_c_binding type(atlas_Connectivity) :: this type(c_ptr) :: cptr call this%reset_c_ptr( cptr ) call this%set_access() call this%return() end function function Connectivity_constructor(name) result(this) use atlas_connectivity_c_binding use fckit_c_interop_module, only : c_str type(atlas_Connectivity) :: this character(len=*), intent(in), optional :: name call this%reset_c_ptr( atlas__Connectivity__create() ) call this%set_access() if( present(name) ) then call atlas__Connectivity__rename(this%CPTR_PGIBUG_A,c_str(name)) endif call this%return() end function function atlas_Connectivity__name(this) result(name) use, intrinsic :: iso_c_binding, only : c_ptr use atlas_connectivity_c_binding use fckit_c_interop_module, only : c_ptr_to_string class(atlas_Connectivity), intent(in) :: this character(len=:), allocatable :: name type(c_ptr) :: name_c_str name_c_str = atlas__Connectivity__name(this%CPTR_PGIBUG_A) name = c_ptr_to_string(name_c_str) end function pure function access_value(this,c,r) result(val) use, intrinsic :: iso_c_binding, only : c_int integer(ATLAS_KIND_IDX) :: val class(atlas_ConnectivityAccess), intent(in) :: this integer(ATLAS_KIND_IDX), intent(in) :: r,c val = this%values_(c+this%displs_(r)) end function pure function access_rows(this) result(val) use atlas_kinds_module integer(ATLAS_KIND_IDX) :: val class(atlas_ConnectivityAccess), intent(in) :: this val = this%rows_ end function pure function atlas_Connectivity__value_args_long(this,c,r) result(val) use, intrinsic :: iso_c_binding, only : c_long integer(ATLAS_KIND_IDX) :: val class(atlas_Connectivity), intent(in) :: this integer(c_long), intent(in) :: r,c val = this%access%values_(c+this%access%displs_(r)) end function pure function atlas_Connectivity__value_args_int(this,c,r) result(val) use, intrinsic :: iso_c_binding, only : c_int integer(ATLAS_KIND_IDX) :: val class(atlas_Connectivity), intent(in) :: this integer(c_int), intent(in) :: r,c val = this%access%values_(c+this%access%displs_(r)) end function pure function atlas_Connectivity__rows(this) result(val) integer(ATLAS_KIND_IDX) :: val class(atlas_Connectivity), intent(in) :: this val = this%access%rows_ end function function atlas_Connectivity__missing_value(this) result(val) use atlas_connectivity_c_binding integer(ATLAS_KIND_IDX) :: val class(atlas_Connectivity), intent(in) :: this val = atlas__Connectivity__missing_value(this%CPTR_PGIBUG_A) end function pure function atlas_Connectivity__cols(this,r) result(val) integer(ATLAS_KIND_IDX) :: val class(atlas_Connectivity), intent(in) :: this integer(c_int), intent(in) :: r val = this%access%cols(r) end function pure function atlas_Connectivity__mincols(this) result(val) integer(ATLAS_KIND_IDX) :: val class(atlas_Connectivity), intent(in) :: this val = this%access%mincols_ end function pure function atlas_Connectivity__maxcols(this) result(val) integer(ATLAS_KIND_IDX) :: val class(atlas_Connectivity), intent(in) :: this val = this%access%maxcols_ end function subroutine atlas_Connectivity__padded_data(this, padded, cols) use, intrinsic :: iso_c_binding, only : c_int class(atlas_Connectivity), intent(inout) :: this integer(ATLAS_KIND_IDX), pointer, intent(inout) :: padded(:,:) integer(ATLAS_KIND_IDX), pointer, intent(inout), optional :: cols(:) if( .not. associated(this%access%padded_) ) call update_padded(this%access) padded => this%access%padded_ if( present(cols) ) cols => this%access%cols end subroutine function c_loc_idx(x) use, intrinsic :: iso_c_binding integer(ATLAS_KIND_IDX), target :: x type(c_ptr) :: c_loc_idx c_loc_idx = c_loc(x) end function subroutine atlas_Connectivity__data(this, data) use, intrinsic :: iso_c_binding, only : c_f_pointer, c_loc, c_int class(atlas_Connectivity), intent(in) :: this integer(ATLAS_KIND_IDX), pointer, intent(inout) :: data(:,:) integer(ATLAS_KIND_IDX) :: maxcols maxcols = this%maxcols() if( maxcols == this%mincols() ) then if( size(this%access%values_) > 0 ) then call c_f_pointer (c_loc_idx(this%access%values_(1)), data, & & (/maxcols,this%access%rows_/)) endif else call fckit_exception%abort("ERROR: Cannot point connectivity pointer data(:,:) to irregular table",& & "atlas_Connectivity_module.F90", __LINE__ ) endif end subroutine subroutine atlas_Connectivity__data_int(this, data, ncols) use, intrinsic :: iso_c_binding, only : c_f_pointer, c_loc, c_int class(atlas_Connectivity), intent(in) :: this integer(ATLAS_KIND_IDX), pointer, intent(inout) :: data(:,:) integer(c_int), intent(out) :: ncols integer(ATLAS_KIND_IDX) :: maxcols maxcols = this%maxcols() if( maxcols == this%mincols() ) then if( size(this%access%values_) > 0 ) then call c_f_pointer (c_loc_idx(this%access%values_(1)), data, & & (/maxcols,this%access%rows_/)) ncols = maxcols endif else call fckit_exception%abort("ERROR: Cannot point connectivity pointer data(:,:) to irregular table",& & "atlas_Connectivity_module.F90", __LINE__ ) endif end subroutine subroutine atlas_Connectivity__data_long(this, data, ncols) use, intrinsic :: iso_c_binding, only : c_f_pointer, c_loc, c_long class(atlas_Connectivity), intent(in) :: this integer(ATLAS_KIND_IDX), pointer, intent(inout) :: data(:,:) integer(c_long), intent(out) :: ncols integer(ATLAS_KIND_IDX) :: maxcols maxcols = this%maxcols() if( maxcols == this%mincols() ) then if( size(this%access%values_) > 0 ) then call c_f_pointer (c_loc_idx(this%access%values_(1)), data, & & (/maxcols,this%access%rows_/)) ncols = maxcols endif else call fckit_exception%abort("ERROR: Cannot point connectivity pointer data(:,:) to irregular table",& & "atlas_Connectivity_module.F90", __LINE__ ) endif end subroutine subroutine atlas_Connectivity__row_int(this, row_idx, row, cols) use, intrinsic :: iso_c_binding, only : c_int class(atlas_Connectivity), intent(in) :: this integer(c_int), intent(in) :: row_idx integer(ATLAS_KIND_IDX), pointer, intent(inout) :: row(:) integer(c_int), intent(out) :: cols row => this%access%values_(this%access%displs_(row_idx)+1:this%access%displs_(row_idx+1)+1) cols = this%access%cols(row_idx) end subroutine subroutine atlas_Connectivity__row_long(this, row_idx, row, cols) use, intrinsic :: iso_c_binding, only : c_long class(atlas_Connectivity), intent(in) :: this integer(c_long), intent(in) :: row_idx integer(ATLAS_KIND_IDX), pointer, intent(inout) :: row(:) integer(c_long), intent(out) :: cols row => this%access%values_(this%access%displs_(row_idx)+1:this%access%displs_(row_idx+1)+1) cols = this%access%cols(row_idx) end subroutine subroutine atlas_Connectivity__add_values_args_long(this,rows,cols,values) use atlas_connectivity_c_binding use, intrinsic :: iso_c_binding, only : c_long class(atlas_Connectivity), intent(in) :: this integer(c_long), intent(in) :: rows integer(c_long), intent(in) :: cols integer(ATLAS_KIND_IDX) :: values(:) call atlas__connectivity__add_values(this%CPTR_PGIBUG_A,int(rows,ATLAS_KIND_IDX),int(cols,ATLAS_KIND_IDX),values) end subroutine subroutine atlas_Connectivity__add_values_args_idx(this,rows,cols,values) use atlas_connectivity_c_binding use, intrinsic :: iso_c_binding, only : c_int class(atlas_Connectivity), intent(in) :: this integer(c_int), intent(in) :: rows integer(c_int), intent(in) :: cols integer(ATLAS_KIND_IDX), intent(in) :: values(:) call atlas__connectivity__add_values(this%CPTR_PGIBUG_A,int(rows,ATLAS_KIND_IDX),int(cols,ATLAS_KIND_IDX),values) end subroutine #if ATLAS_BITS_LOCAL != 32 subroutine atlas_Connectivity__add_values_args_int32(this,rows,cols,values) use atlas_connectivity_c_binding use, intrinsic :: iso_c_binding, only : c_int class(atlas_Connectivity), intent(in) :: this integer(c_int), intent(in) :: rows integer(c_int), intent(in) :: cols integer(c_int), intent(in) :: values(:) integer(ATLAS_KIND_IDX) :: idx_values(rows*cols) idx_values(:) = values(:) call atlas__connectivity__add_values(this%CPTR_PGIBUG_A,int(rows,ATLAS_KIND_IDX),int(cols,ATLAS_KIND_IDX),idx_values) end subroutine #endif subroutine atlas_Connectivity__add_missing_args_long(this,rows,cols) use atlas_connectivity_c_binding use, intrinsic :: iso_c_binding, only : c_long class(atlas_Connectivity), intent(in) :: this integer(c_long) :: rows integer(c_long) :: cols call atlas__connectivity__add_missing(this%CPTR_PGIBUG_A,int(rows,ATLAS_KIND_IDX),int(cols,ATLAS_KIND_IDX)) end subroutine subroutine atlas_Connectivity__add_missing_args_int(this,rows,cols) use atlas_connectivity_c_binding use, intrinsic :: iso_c_binding, only : c_int class(atlas_Connectivity), intent(in) :: this integer(c_int) :: rows integer(c_int) :: cols call atlas__connectivity__add_missing(this%CPTR_PGIBUG_A,int(rows,ATLAS_KIND_IDX),int(cols,ATLAS_KIND_IDX)) end subroutine !======================================================== function MultiBlockConnectivity_cptr(cptr) result(this) use atlas_connectivity_c_binding use, intrinsic :: iso_c_binding, only : c_ptr type(atlas_MultiBlockConnectivity) :: this type(c_ptr) :: cptr call this%reset_c_ptr( cptr ) call this%set_access() call this%return() end function function MultiBlockConnectivity_constructor(name) result(this) use atlas_connectivity_c_binding use fckit_c_interop_module type(atlas_MultiBlockConnectivity) :: this character(len=*), intent(in), optional :: name call this%reset_c_ptr( atlas__MultiBlockConnectivity__create() ) call this%set_access() if( present(name) ) then call atlas__Connectivity__rename(this%CPTR_PGIBUG_A,c_str(name)) endif call this%return() end function function atlas_MultiBlockConnectivity__blocks(this) result(val) use atlas_connectivity_c_binding integer(ATLAS_KIND_IDX) :: val class(atlas_MultiBlockConnectivity), intent(in) :: this val = atlas__MultiBlockConnectivity__blocks(this%CPTR_PGIBUG_A) end function function atlas_MultiBlockConnectivity__block(this,block_idx) result(block) use atlas_connectivity_c_binding type(atlas_BlockConnectivity) :: block class(atlas_MultiBlockConnectivity), intent(in) :: this integer(ATLAS_KIND_IDX) :: block_idx call block%reset_c_ptr( atlas__MultiBlockConnectivity__block( & this%CPTR_PGIBUG_A,int(block_idx-1_ATLAS_KIND_IDX,ATLAS_KIND_IDX) ) ) end function !======================================================== function BlockConnectivity_cptr(cptr) result(this) use atlas_connectivity_c_binding use, intrinsic :: iso_c_binding, only : c_ptr type(atlas_BlockConnectivity) :: this type(c_ptr) :: cptr call this%reset_c_ptr( cptr ) end function subroutine atlas_BlockConnectivity__data(this,data) use atlas_connectivity_c_binding use, intrinsic :: iso_c_binding, only : c_int, c_ptr, c_f_pointer class(atlas_BlockConnectivity), intent(in) :: this integer(ATLAS_KIND_IDX), pointer, intent(inout) :: data(:,:) type(c_ptr) :: data_cptr integer(ATLAS_KIND_IDX) :: rows integer(ATLAS_KIND_IDX) :: cols call atlas__BlockConnectivity__data(this%CPTR_PGIBUG_A,data_cptr,rows,cols) call c_f_pointer (data_cptr, data, [cols,rows]) end subroutine function atlas_BlockConnectivity__rows(this) result(val) use atlas_connectivity_c_binding integer(ATLAS_KIND_IDX) :: val class(atlas_BlockConnectivity), intent(in) :: this val = atlas__BlockConnectivity__rows(this%CPTR_PGIBUG_A) end function function atlas_BlockConnectivity__cols(this) result(val) use atlas_connectivity_c_binding integer(ATLAS_KIND_IDX) :: val class(atlas_BlockConnectivity), intent(in) :: this val = atlas__BlockConnectivity__cols(this%CPTR_PGIBUG_A) end function function atlas_BlockConnectivity__missing_value(this) result(val) use atlas_connectivity_c_binding integer(ATLAS_KIND_IDX) :: val class(atlas_BlockConnectivity), intent(in) :: this val = atlas__BlockConnectivity__missing_value(this%CPTR_PGIBUG_A) + 1 end function !======================================================== subroutine set_access(this) use, intrinsic :: iso_c_binding, only : c_ptr, c_int, c_f_pointer, c_funloc, c_loc class(atlas_Connectivity), intent(inout) :: this type(c_ptr) :: ctxt integer(c_int) :: have_ctxt have_ctxt = atlas__connectivity__ctxt(this%CPTR_PGIBUG_A,ctxt) if( have_ctxt == 1 ) then call c_f_pointer(ctxt,this%access) else allocate( this%access ) call atlas__connectivity__register_ctxt ( this%CPTR_PGIBUG_A, c_loc(this%access) ) call atlas__connectivity__register_update( this%CPTR_PGIBUG_A, c_funloc(update_access_c) ) call atlas__connectivity__register_delete( this%CPTR_PGIBUG_A, c_funloc(delete_access_c) ) this%access%connectivity_ptr = this%CPTR_PGIBUG_A call update_access(this%access) endif end subroutine subroutine update_access_c(this_ptr) bind(c) use, intrinsic :: iso_c_binding, only : c_ptr, c_f_pointer type(c_ptr), value :: this_ptr type(atlas_ConnectivityAccess), pointer :: this call c_f_pointer(this_ptr,this) call update_access(this) end subroutine subroutine update_access(this) use atlas_connectivity_c_binding use, intrinsic :: iso_c_binding, only : c_ptr, c_f_pointer type(atlas_ConnectivityAccess), intent(inout) :: this integer :: jrow type(c_ptr) :: values_cptr type(c_ptr) :: displs_cptr type(c_ptr) :: counts_cptr integer(ATLAS_KIND_IDX) :: values_size integer(ATLAS_KIND_IDX) :: displs_size integer(ATLAS_KIND_IDX) :: counts_size this%missing_value_ = atlas__Connectivity__missing_value(this%connectivity_ptr) call atlas__Connectivity__values(this%connectivity_ptr,values_cptr,values_size) call atlas__Connectivity__displs(this%connectivity_ptr,displs_cptr,displs_size) call atlas__Connectivity__counts(this%connectivity_ptr,counts_cptr,counts_size) call c_f_pointer(values_cptr, this%values_, (/values_size/)) call c_f_pointer(displs_cptr, this%displs_, (/displs_size/)) call c_f_pointer(counts_cptr, this%cols, (/counts_size/)) this%rows_ = atlas__Connectivity__rows(this%connectivity_ptr) if( associated( this%row ) ) deallocate(this%row) allocate( this%row(this%rows_) ) this%maxcols_ = 0 this%mincols_ = huge(this%maxcols_) do jrow=1,this%rows_ this%row(jrow)%col => this%values_(this%displs_(jrow)+1:this%displs_(jrow+1)+1) this%maxcols_ = max(this%maxcols_, this%cols(jrow) ) this%mincols_ = min(this%mincols_, this%cols(jrow) ) enddo if( associated( this%padded_) ) call update_padded(this) end subroutine subroutine update_padded(this) class(atlas_ConnectivityAccess), intent(inout) :: this integer(ATLAS_KIND_IDX) :: jrow, jcol if( associated(this%padded_) ) call atlas_deallocate_managedmem( this%padded_ ) call atlas_allocate_managedmem( this%padded_, [this%maxcols_,this%rows()] ) if( associated(this%padded_) ) then this%padded_(:,:) = this%missing_value_ do jrow=1,this%rows() do jcol=1,this%cols(jrow) this%padded_(jcol,jrow) = this%value(jcol,jrow) enddo enddo endif end subroutine subroutine delete_access_c(this_ptr) bind(c) use, intrinsic :: iso_c_binding, only : c_ptr, c_f_pointer type(c_ptr), value :: this_ptr type(atlas_ConnectivityAccess), pointer :: this call c_f_pointer(this_ptr,this) call delete_access(this) end subroutine subroutine delete_access(this) use, intrinsic :: iso_c_binding, only : c_int use atlas_connectivity_c_binding type(atlas_ConnectivityAccess), pointer, intent(inout) :: this if( associated( this%row ) ) deallocate(this%row) if( associated( this%padded_) ) call atlas_deallocate_managedmem(this%padded_) #ifndef _CRAYFTN ! Cray compiler bug (CCE/8.7) leads to following runtime error here. ! lib-4412 : UNRECOVERABLE library error ! An argument in the DEALLOCATE statement is a disassociated pointer, an ! unallocated array, or a pointer not allocated as a pointer. ! --> Does this lead to a (tiny) memory leak? deallocate(this) #endif end subroutine !------------------------------------------------------------------------------- #if FCKIT_FINAL_NOT_INHERITING ATLAS_FINAL subroutine atlas_Connectivity__final_auto(this) type(atlas_Connectivity), intent(inout) :: this #if FCKIT_FINAL_DEBUGGING write(0,*) "atlas_Connectivity__final_auto" #endif #if FCKIT_FINAL_NOT_PROPAGATING call this%final() #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine #endif !------------------------------------------------------------------------------- #if FCKIT_FINAL_NOT_INHERITING ATLAS_FINAL subroutine atlas_MultiBlockConnectivity__final_auto(this) type(atlas_MultiBlockConnectivity), intent(inout) :: this #if FCKIT_FINAL_DEBUGGING write(0,*) "atlas_MultiBlockConnectivity__final_auto" #endif #if FCKIT_FINAL_NOT_PROPAGATING call this%final() #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine #endif !------------------------------------------------------------------------------- end module atlas_connectivity_module atlas-0.46.0/src/atlas_f/mesh/atlas_Mesh_module.F900000664000175000017500000001147015160212070022127 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. #include "atlas/atlas_f.h" module atlas_Mesh_module use fckit_owned_object_module, only: fckit_owned_object use atlas_mesh_Cells_module, only: atlas_mesh_Cells use atlas_mesh_Nodes_module, only: atlas_mesh_Nodes use atlas_mesh_Edges_module, only: atlas_mesh_Edges use, intrinsic :: iso_c_binding, only : c_size_t, c_ptr implicit none private :: fckit_owned_object private :: atlas_mesh_Cells private :: atlas_mesh_Nodes private :: atlas_mesh_Edges private :: c_size_t private :: c_ptr public :: atlas_Mesh private !----------------------------- ! atlas_Mesh ! !----------------------------- TYPE, extends(fckit_owned_object) :: atlas_Mesh ! Purpose : ! ------- ! *Mesh* : Container type holding an entire mesh ! Methods : ! ------- ! Author : ! ------ ! 20-Nov-2013 Willem Deconinck *ECMWF* !------------------------------------------------------------------------------ contains procedure, public :: nodes => Mesh__nodes procedure, public :: cells => Mesh__cells procedure, public :: edges => Mesh__edges procedure, public :: footprint procedure, public :: update_device procedure, public :: update_host procedure, public :: sync_host_device #if FCKIT_FINAL_NOT_INHERITING final :: atlas_Mesh__final_auto #endif END TYPE atlas_Mesh interface atlas_Mesh module procedure atlas_Mesh__cptr module procedure atlas_Mesh__ctor end interface !======================================================== contains !======================================================== function atlas_Mesh__cptr(cptr) result(this) use atlas_mesh_c_binding type(atlas_Mesh) :: this type(c_ptr), intent(in) :: cptr call this%reset_c_ptr( cptr ) call this%return() end function atlas_Mesh__cptr !------------------------------------------------------------------------------- function atlas_Mesh__ctor() result(this) use atlas_mesh_c_binding type(atlas_Mesh) :: this call this%reset_c_ptr( atlas__Mesh__new() ) call this%return() end function atlas_Mesh__ctor !------------------------------------------------------------------------------- function Mesh__nodes(this) result(nodes) use atlas_mesh_c_binding class(atlas_Mesh), intent(in) :: this type(atlas_mesh_Nodes) :: nodes nodes = atlas_mesh_Nodes( atlas__Mesh__nodes(this%CPTR_PGIBUG_A) ) call nodes%return() end function !------------------------------------------------------------------------------- function Mesh__cells(this) result(cells) use atlas_mesh_c_binding class(atlas_Mesh), intent(in) :: this type(atlas_mesh_Cells) :: cells cells = atlas_mesh_Cells( atlas__Mesh__cells(this%CPTR_PGIBUG_A) ) call cells%return() end function !------------------------------------------------------------------------------- function Mesh__edges(this) result(edges) use atlas_mesh_c_binding class(atlas_Mesh), intent(in) :: this type(atlas_mesh_Edges) :: edges edges = atlas_mesh_Edges( atlas__Mesh__Edges(this%CPTR_PGIBUG_A) ) call edges%return() end function !------------------------------------------------------------------------------- function footprint(this) use atlas_mesh_c_binding integer(c_size_t) :: footprint class(atlas_Mesh) :: this footprint = atlas__Mesh__footprint(this%CPTR_PGIBUG_A) end function !------------------------------------------------------------------------------- subroutine update_device(this) use atlas_mesh_c_binding class(atlas_Mesh), intent(inout) :: this call atlas__Mesh__update_device(this%CPTR_PGIBUG_A) end subroutine !------------------------------------------------------------------------------- subroutine update_host(this) use atlas_mesh_c_binding class(atlas_Mesh), intent(inout) :: this call atlas__Mesh__update_host(this%CPTR_PGIBUG_A) end subroutine ! ---------------------------------------------------------------------------------------- subroutine sync_host_device(this) use atlas_mesh_c_binding class(atlas_Mesh), intent(inout) :: this call atlas__Mesh__sync_host_device(this%CPTR_PGIBUG_A) end subroutine !------------------------------------------------------------------------------- #if FCKIT_FINAL_NOT_INHERITING ATLAS_FINAL subroutine atlas_Mesh__final_auto(this) type(atlas_Mesh), intent(inout) :: this #if FCKIT_FINAL_DEBUGGING write(0,*) "atlas_Mesh__final_auto" #endif #if FCKIT_FINAL_NOT_PROPAGATING call this%final() #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine #endif !------------------------------------------------------------------------------- end module atlas_Mesh_module atlas-0.46.0/src/atlas_f/mesh/atlas_MeshBuilder_module.F900000664000175000017500000001165415160212070023442 0ustar alastairalastair! (C) Copyright 2023 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. #include "atlas/atlas_f.h" module atlas_MeshBuilder_module use fckit_owned_object_module, only: fckit_owned_object implicit none private :: fckit_owned_object public :: atlas_TriangularMeshBuilder private !-----------------------------! ! atlas_TriangularMeshBuilder ! !-----------------------------! !------------------------------------------------------------------------------ TYPE, extends(fckit_owned_object) :: atlas_TriangularMeshBuilder contains procedure, public :: build => atlas_TriangularMeshBuilder__build #if FCKIT_FINAL_NOT_INHERITING final :: atlas_TriangularMeshBuilder__final_auto #endif END TYPE atlas_TriangularMeshBuilder interface atlas_TriangularMeshBuilder module procedure atlas_TriangularMeshBuilder__cptr module procedure atlas_TriangularMeshBuilder__config end interface !------------------------------------------------------------------------------ !======================================================== contains !======================================================== function atlas_TriangularMeshBuilder__cptr(cptr) result(this) use, intrinsic :: iso_c_binding, only: c_ptr type(atlas_TriangularMeshBuilder) :: this type(c_ptr), intent(in) :: cptr call this%reset_c_ptr( cptr ) call this%return() end function function atlas_TriangularMeshBuilder__config(config) result(this) use fckit_c_interop_module, only: c_str use atlas_MeshBuilder_c_binding use atlas_Config_module, only: atlas_Config type(atlas_TriangularMeshBuilder) :: this type(atlas_Config), intent(in), optional :: config call this%reset_c_ptr( atlas__TriangularMeshBuilder__new() ) call this%return() end function ! Mesh operator()(size_t nb_nodes, const gidx_t node_global_index[], const double x[], const double y[], const double lon[], const double lat[], ! size_t nb_triags, const gidx_t triangle_global_index[], const gidx_t triangle_nodes_global_index[], ! gidx_t global_index_base) const; function atlas_TriangularMeshBuilder__build(this, & nb_nodes, node_global_index, x, y, lon, lat, & nb_triags, triag_global_index, triag_nodes, & global_index_base) result(mesh) use, intrinsic :: iso_c_binding, only: c_double, c_size_t use atlas_MeshBuilder_c_binding use atlas_Mesh_module, only: atlas_Mesh use atlas_kinds_module, only : ATLAS_KIND_GIDX use fckit_array_module, only : array_stride, array_view1d type(atlas_Mesh) :: mesh class(atlas_TriangularMeshBuilder), intent(in) :: this integer, intent(in) :: nb_nodes integer(ATLAS_KIND_GIDX), intent(in) :: node_global_index(nb_nodes) real(c_double), intent(in), target :: x(:), y(:), lon(:), lat(:) integer, intent(in) :: nb_triags integer(ATLAS_KIND_GIDX), intent(in), target :: triag_global_index(nb_triags) integer(ATLAS_KIND_GIDX), intent(in), target :: triag_nodes(3,nb_triags) integer(ATLAS_KIND_GIDX), optional, intent(in) :: global_index_base integer(ATLAS_KIND_GIDX) :: global_index_base_ real(c_double), pointer :: view1d_x(:), view1d_y(:), view1d_lon(:), view1d_lat(:) integer(ATLAS_KIND_GIDX), pointer :: view1d_triag_global_index(:), view1d_triag_nodes(:) global_index_base_ = 1 if (present(global_index_base)) then global_index_base_ = global_index_base endif view1d_x => array_view1d(x) view1d_y => array_view1d(y) view1d_lon => array_view1d(lon) view1d_lat => array_view1d(lat) view1d_triag_global_index => array_view1d(triag_global_index) view1d_triag_nodes => array_view1d(triag_nodes) call mesh%reset_c_ptr() ! Somehow needed with PGI/16.7 and build-type "bit" mesh = atlas_Mesh( atlas__TriangularMeshBuilder__operator(this%CPTR_PGIBUG_A, & & int(nb_nodes,c_size_t), node_global_index, & & view1d_x, view1d_y, int(array_stride(x,1),c_size_t), int(array_stride(y,1),c_size_t), & & view1d_lon, view1d_lat, int(array_stride(lon,1),c_size_t), int(array_stride(lat,1),c_size_t), & & int(nb_triags,c_size_t), view1d_triag_global_index, view1d_triag_nodes, & & global_index_base_ )) call mesh%return() end function !------------------------------------------------------------------------------- #if FCKIT_FINAL_NOT_INHERITING ATLAS_FINAL subroutine atlas_TriangularMeshBuilder__final_auto(this) type(atlas_TriangularMeshBuilder), intent(inout) :: this #if FCKIT_FINAL_DEBUGGING write(0,*) "atlas_MeshBuilder__final_auto" #endif #if FCKIT_FINAL_NOT_PROPAGATING call this%final() #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine #endif ! ---------------------------------------------------------------------------------------- end module atlas_MeshBuilder_module atlas-0.46.0/src/atlas_f/mesh/atlas_mesh_Edges_module.F900000664000175000017500000000375115160212070023301 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. #include "atlas/atlas_f.h" module atlas_mesh_Edges_module use, intrinsic :: iso_c_binding, only: c_ptr use atlas_HybridElements_module, only: atlas_HybridElements implicit none private :: atlas_HybridElements public :: atlas_mesh_Edges private type, extends(atlas_HybridElements) :: atlas_mesh_Edges contains #if FCKIT_FINAL_NOT_INHERITING final :: atlas_mesh_Edges__final_auto #endif end type interface atlas_mesh_Edges module procedure atlas_mesh_Edges__cptr module procedure atlas_mesh_Edges__constructor end interface !======================================================== contains !======================================================== function atlas_mesh_edges__cptr(cptr) result(this) use atlas_hybridelements_c_binding type(atlas_mesh_Edges) :: this type(c_ptr), intent(in) :: cptr call this%reset_c_ptr( cptr ) call this%return() end function function atlas_mesh_edges__constructor() result(this) use atlas_hybridelements_c_binding type(atlas_mesh_Edges) :: this call this%reset_c_ptr( atlas__mesh__HybridElements__create() ) call this%return() end function !------------------------------------------------------------------------------- #if FCKIT_FINAL_NOT_INHERITING ATLAS_FINAL subroutine atlas_mesh_Edges__final_auto(this) type(atlas_mesh_Edges), intent(inout) :: this #if FCKIT_FINAL_DEBUGGING write(0,*) "atlas_mesh_Edges__final_auto" #endif #if FCKIT_FINAL_NOT_PROPAGATING call this%final() #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine #endif ! ---------------------------------------------------------------------------------------- end module atlas_mesh_Edges_module atlas-0.46.0/src/atlas_f/mesh/atlas_mesh_Cells_module.F900000664000175000017500000000377215160212070023317 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. #include "atlas/atlas_f.h" module atlas_mesh_cells_module use, intrinsic :: iso_c_binding, only: c_ptr use atlas_HybridElements_module, only: atlas_HybridElements implicit none private :: atlas_HybridElements private :: c_ptr public :: atlas_mesh_cells private type, extends(atlas_HybridElements) :: atlas_mesh_cells contains #if FCKIT_FINAL_NOT_INHERITING final :: atlas_mesh_Cells__final_auto #endif end type interface atlas_mesh_cells module procedure atlas_mesh_cells__cptr module procedure atlas_mesh_cells__constructor end interface !======================================================== contains !======================================================== function atlas_mesh_cells__cptr(cptr) result(this) use atlas_hybridelements_c_binding type(atlas_mesh_cells) :: this type(c_ptr), intent(in) :: cptr call this%reset_c_ptr( cptr ) call this%return() end function function atlas_mesh_cells__constructor() result(this) use atlas_hybridelements_c_binding type(atlas_mesh_cells) :: this call this%reset_c_ptr( atlas__mesh__HybridElements__create() ) call this%return() end function !------------------------------------------------------------------------------- #if FCKIT_FINAL_NOT_INHERITING ATLAS_FINAL subroutine atlas_mesh_Cells__final_auto(this) type(atlas_mesh_Cells), intent(inout) :: this #if FCKIT_FINAL_DEBUGGING write(0,*) "atlas_mesh_Cells__final_auto" #endif #if FCKIT_FINAL_NOT_PROPAGATING call this%final() #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine #endif ! ---------------------------------------------------------------------------------------- end module atlas_mesh_cells_module atlas-0.46.0/src/atlas_f/atlas_f.h.in0000664000175000017500000000237415160212070017460 0ustar alastairalastair#if 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. // clang-format off #endif #ifndef atlas_f_h #define atlas_f_h #include "fckit/fckit.h" #define ATLAS_HAVE_OMP @atlas_HAVE_OMP_Fortran@ #define ATLAS_HAVE_ACC @atlas_HAVE_ACC@ #define ATLAS_HAVE_GPU @atlas_HAVE_GPU@ #define ATLAS_BITS_GLOBAL @ATLAS_BITS_GLOBAL@ #define ATLAS_BITS_LOCAL @ATLAS_BITS_LOCAL@ #define ATLAS_HAVE_TRANS @atlas_HAVE_ATLAS_TRANS@ #define ATLAS_HAVE_INTERPOLATION @atlas_HAVE_ATLAS_INTERPOLATION@ #define ATLAS_HAVE_NUMERICS @atlas_HAVE_ATLAS_NUMERICS@ #define ATLAS_FCKIT_VERSION_INT @ATLAS_FCKIT_VERSION_INT@ #define ATLAS_FCKIT_DEVELOP @ATLAS_FCKIT_DEVELOP@ #define ATLAS_FINAL FCKIT_FINAL #ifndef PGIBUG_ATLAS_197 #define CPTR_PGIBUG_A c_ptr() #define CPTR_PGIBUG_B c_ptr() #define PGIBUG_ATLAS_197 0 #endif #define ATLAS_FCKIT_VERSION_AT_LEAST(x, y, z) (ATLAS_FCKIT_VERSION_INT >= x * 10000 + y * 100 + z) #endif atlas-0.46.0/src/atlas_f/atlas_f.fypp0000664000175000017500000000461015160212070017575 0ustar alastairalastair! (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. #:mute #:def ENABLE_ATLAS_MACROS() #! Use this macro in the "use " location to enable #! Usage: #! @:ENABLE_ATLAS_MACROS() use fckit_exception_module, only : fckit_exception use atlas_trace_module, only : atlas_Trace #:enddef #:def ATLAS_ABORT( string ) #! Abort with message #! Usage: #! if( condition ) @{ ATLAS_ABORT( "error message" ) }@ #! call fckit_exception%abort( ${string}$, "${_FILE_}$", ${_LINE_}$ ) #:enddef #:def ATLAS_ASSERT( cond ) #! Assert condition evaluates to .True. , otherwise abort #! Usage: #! @:ATLAS_ASSERT( cond ) #! or inline: #! @{ ATLAS_ASSERT( cond ) }@ #! if (.not. (${cond}$)) then call fckit_exception%abort( '${cond.replace("'", "''")}$', "${_FILE_}$", ${_LINE_}$ ) end if #:enddef #:def ATLAS_TRACE( title, labels = None ) #! Create a right-hand-side atlas_Trace object in a nicer way #! Usage: #! type( atlas_Trace ) :: trace #! trace = @{ ATLAS_TRACE( "title" ) }@ #! ... #! call trace%final() #! #! or with labels: #! type( atlas_Trace ) :: trace #! trace = @{ ATLAS_TRACE( "title", {"label1","label2"} ) }@ #! ... #! call trace%final() #! #:if labels is not None atlas_Trace( "${_FILE_}$", ${_LINE_}$, ${title}$, ${labels}$ ) #:else atlas_Trace( "${_FILE_}$", ${_LINE_}$, ${title}$ ) #:endif #:enddef #:def ATLAS_TRACE_BEGIN( trace, title, labels = None ) #! Create a right-hand-side atlas_Trace object in a nicer way #! Usage: #! type( atlas_Trace ) :: trace #! trace = @{ ATLAS_TRACE( "title" ) }@ #! ... #! call trace%final() #! #! or with labels: #! type( atlas_Trace ) :: trace #! trace = @{ ATLAS_TRACE( "title", {"label1","label2"} ) }@ #! ... #! call trace%final() #! #:if labels is not None ${trace}$ = atlas_Trace( "${_FILE_}$", ${_LINE_}$, ${title}$, ${labels}$ ) #:else ${trace}$ = atlas_Trace( "${_FILE_}$", ${_LINE_}$, ${title}$ ) #:endif #:enddef #:def ATLAS_TRACE_END( trace ) call ${trace}$ % final() #:enddef #:endmute atlas-0.46.0/src/atlas_f/atlas_module.F900000664000175000017500000002235315160212070020221 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. #include "atlas/atlas_f.h" module atlas_module ! Purpose : ! ------- ! *atlas* : Object oriented library for flexible parallel data structures ! for structured grids and unstructured meshes ! !------------------------------------------------------------------------------ use atlas_Field_module, only: & & atlas_Field, & & atlas_real, & & atlas_integer, & & atlas_logical use atlas_FunctionSpace_module, only: & & atlas_FunctionSpace use atlas_FieldSet_module, only: & & atlas_FieldSet use atlas_State_module, only: & & atlas_State use atlas_JSON_module, only: & & atlas_PathName, & & atlas_JSON use atlas_Config_module, only: & & atlas_config use atlas_Metadata_module, only: & & atlas_Metadata use atlas_Geometry_module, only: & & atlas_Geometry use atlas_KDTree_module, only: & & atlas_IndexKDTree use atlas_HybridElements_module, only: & & atlas_HybridElements use atlas_mesh_Edges_module, only: & & atlas_mesh_Edges use atlas_mesh_Cells_module, only: & & atlas_mesh_Cells use atlas_Elements_module, only: & & atlas_Elements use atlas_ElementType_module, only: & & atlas_ElementType, & & atlas_Triangle, & & atlas_Quadrilateral, & & atlas_Line use atlas_Connectivity_module, only: & & atlas_Connectivity, & & atlas_MultiBlockConnectivity, & & atlas_BlockConnectivity use atlas_Domain_module, only : & & atlas_Domain, & & atlas_LonLatRectangularDomain use atlas_mesh_Nodes_module, only: & & atlas_mesh_Nodes use atlas_HaloExchange_module, only: & & atlas_HaloExchange #if ATLAS_HAVE_INTERPOLATION use atlas_Interpolation_module, only: & & atlas_Interpolation #endif use atlas_GatherScatter_module, only: & & atlas_GatherScatter use atlas_Checksum_module, only: & & atlas_Checksum use atlas_Mesh_module, only: & & atlas_Mesh use atlas_GridDistribution_module, only: & & atlas_GridDistribution use atlas_Grid_module, only: & & atlas_Grid, & & atlas_UnstructuredGrid, & & atlas_StructuredGrid, & & atlas_GaussianGrid, & & atlas_ReducedGaussianGrid, & & atlas_RegularGaussianGrid, & & atlas_RegularLonLatGrid, & & atlas_ShiftedLonLatGrid, & & atlas_ShiftedLonGrid, & & atlas_ShiftedLatGrid, & & atlas_RegionalGrid use atlas_Vertical_module, only :& & atlas_Vertical use atlas_StencilComputer_module, only: & & atlas_StructuredGrid_ComputeNorth, & & atlas_StructuredGrid_ComputeWest, & & atlas_StructuredGrid_ComputeStencil, & & atlas_StructuredGrid_Stencil use atlas_functionspace_EdgeColumns_module, only: & & atlas_functionspace_EdgeColumns use atlas_functionspace_CellColumns_module, only: & & atlas_functionspace_CellColumns use atlas_functionspace_NodeColumns_module, only: & & atlas_functionspace_NodeColumns use atlas_functionspace_PointCloud_module, only: & & atlas_functionspace_PointCloud use atlas_functionspace_StructuredColumns_module, only: & & atlas_functionspace_StructuredColumns use atlas_functionspace_BlockStructuredColumns_module, only: & & atlas_functionspace_BlockStructuredColumns use atlas_functionspace_Spectral_module, only: & & atlas_functionspace_Spectral use atlas_Projection_module, only : & & atlas_Projection, & & atlas_RotatedLonLatProjection, & & atlas_LambertConformalConicProjection, & & atlas_RotatedSchmidtProjection #if ATLAS_HAVE_TRANS use atlas_Trans_module, only : & & atlas_Trans #endif use atlas_kinds_module, only: & & ATLAS_KIND_GIDX, & & ATLAS_KIND_IDX, & & ATLAS_KIND_REAL64, & & ATLAS_KIND_REAL32, & & ATLAS_KIND_INT64, & & ATLAS_KIND_INT32 use atlas_Partitioner_module, only: & & atlas_Partitioner, & & atlas_MatchingPartitioner, & & atlas_MatchingMeshPartitioner ! Deprecated ! use atlas_MatchingPartitioner instead use atlas_MeshGenerator_module, only: & & atlas_MeshGenerator #if ATLAS_HAVE_NUMERICS use atlas_Method_module, only: & & atlas_Method use atlas_fvm_module, only: & & atlas_fvm_Method use atlas_Nabla_module, only: & & atlas_Nabla #endif use atlas_meshbuilder_module, only : & & atlas_TriangularMeshBuilder use atlas_mesh_actions_module, only: & & atlas_build_parallel_fields, & & atlas_build_nodes_parallel_fields, & & atlas_build_edges_parallel_fields, & & atlas_build_periodic_boundaries, & & atlas_build_halo, & & atlas_build_edges, & & atlas_build_pole_edges, & & atlas_build_node_to_cell_connectivity, & & atlas_build_node_to_edge_connectivity, & & atlas_build_median_dual_mesh, & & atlas_write_load_balance_report, & & atlas_renumber_nodes_glb_idx use atlas_output_module, only: & & atlas_Output, & & atlas_output_Gmsh use atlas_trace_module, only : & & atlas_Trace use atlas_functions_module use atlas_Redistribution_module, only : & & atlas_Redistribution use fckit_log_module, only: atlas_log => fckit_log implicit none public ENUM, bind(c) enumerator :: openmode enumerator :: app = 1 enumerator :: out = 16 end ENUM type, private :: atlas_Library_type contains procedure, public, nopass :: initialise => atlas_init procedure, public, nopass :: finalise => atlas_final procedure, public, nopass :: initialize => atlas_init procedure, public, nopass :: finalize => atlas_final procedure, public, nopass :: version => atlas_version procedure, public, nopass :: gitsha1 => atlas_git_sha1_abbrev end type type(atlas_library_type), public :: atlas_library type, private :: eckit_Library_type contains procedure, public, nopass :: version => eckit_version procedure, public, nopass :: gitsha1 => eckit_git_sha1_abbrev end type type(eckit_library_type), public :: eckit_library interface atlas_initialize module procedure atlas_init end interface interface atlas_initialise module procedure atlas_init end interface interface atlas_finalize module procedure atlas_final end interface interface atlas_finalise module procedure atlas_final end interface ! ============================================================================= CONTAINS ! ============================================================================= ! ----------------------------------------------------------------------------- subroutine atlas_init( comm ) use atlas_library_c_binding use iso_fortran_env, only : stdout => output_unit use fckit_main_module, only: fckit_main use fckit_mpi_module, only : fckit_mpi_setCommDefault integer, intent(in), optional :: comm if( .not. fckit_main%ready() ) then call fckit_main%initialise() if( fckit_main%taskID() == 0 ) then call atlas_log%set_fortran_unit(stdout,style=atlas_log%PREFIX) else call atlas_log%reset() endif call atlas_log%debug("--> Only MPI rank 0 is logging. Please initialise fckit_main"//& & " before to avoid this behaviour",flush=.true.); endif if( present(comm) ) then call fckit_mpi_setCommDefault(comm) endif call atlas__atlas_init_noargs() end subroutine subroutine atlas_final() use fckit_c_interop_module use atlas_library_c_binding call atlas__atlas_finalize() end subroutine function eckit_version() use atlas_library_c_binding use fckit_c_interop_module character(len=40) :: eckit_version eckit_version = c_ptr_to_string(atlas__eckit_version()) end function eckit_version function eckit_git_sha1() use fckit_c_interop_module use atlas_library_c_binding character(len=40) :: eckit_git_sha1 eckit_git_sha1 = c_ptr_to_string(atlas__eckit_git_sha1()) end function eckit_git_sha1 function eckit_git_sha1_abbrev(length) use, intrinsic :: iso_c_binding, only: c_int use atlas_library_c_binding use fckit_c_interop_module character(len=40) :: eckit_git_sha1_abbrev integer(c_int), optional :: length integer(c_int) :: opt_length opt_length = 7 if( present(length) ) opt_length = length eckit_git_sha1_abbrev = c_ptr_to_string(atlas__eckit_git_sha1_abbrev(opt_length)) end function eckit_git_sha1_abbrev function atlas_version() use fckit_c_interop_module use atlas_library_c_binding character(len=40) :: atlas_version atlas_version = c_ptr_to_string(atlas__atlas_version()) end function atlas_version function atlas_git_sha1() use fckit_c_interop_module use atlas_library_c_binding character(len=40) :: atlas_git_sha1 atlas_git_sha1 = c_ptr_to_string(atlas__atlas_git_sha1()) end function atlas_git_sha1 function atlas_git_sha1_abbrev(length) use fckit_c_interop_module use, intrinsic :: iso_c_binding, only: c_int use atlas_library_c_binding character(len=40) :: atlas_git_sha1_abbrev integer(c_int), optional :: length integer(c_int) :: opt_length opt_length = 7 if( present(length) ) opt_length = length atlas_git_sha1_abbrev = c_ptr_to_string(atlas__atlas_git_sha1_abbrev(opt_length)) end function atlas_git_sha1_abbrev ! ----------------------------------------------------------------------------- end module atlas_module atlas-0.46.0/src/atlas_f/grid/0000775000175000017500000000000015160212070016210 5ustar alastairalastairatlas-0.46.0/src/atlas_f/grid/atlas_GridDistribution_module.F900000664000175000017500000001345115160212070024512 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. #include "atlas/atlas_f.h" module atlas_GridDistribution_module use fckit_owned_object_module, only: fckit_owned_object use, intrinsic :: iso_c_binding, only : c_ptr implicit none public :: atlas_GridDistribution private !----------------------------- ! atlas_GridDistribution ! !----------------------------- !------------------------------------------------------------------------------ TYPE, extends(fckit_owned_object) :: atlas_GridDistribution ! Purpose : ! ------- ! *GridDistribution* : Object passed from atlas to inspect grid distribution ! Methods : ! ------- ! Author : ! ------ ! 12-Mar-2014 Willem Deconinck *ECMWF* !------------------------------------------------------------------------------ contains procedure :: nb_partitions => atlas_GridDistribution__nb_partitions procedure :: nb_pts => atlas_GridDistribution__nb_pts procedure, private :: partition_int32 procedure, private :: partition_int64 generic :: partition => partition_int32, partition_int64 #if FCKIT_FINAL_NOT_INHERITING final :: atlas_GridDistribution__final_auto #endif END TYPE atlas_GridDistribution !------------------------------------------------------------------------------ interface atlas_GridDistribution module procedure atlas_GridDistribution__cptr module procedure atlas_GridDistribution__ctor module procedure atlas_GridDistribution__ctor_Grid_Config module procedure atlas_GridDistribution__ctor_Grid_Partitioner end interface private :: c_ptr private :: fckit_owned_object !======================================================== contains !======================================================== ! ----------------------------------------------------------------------------- ! GridDistribution routines function atlas_GridDistribution__cptr( cptr ) result(this) use atlas_distribution_c_binding type(atlas_GridDistribution) :: this type(c_ptr) :: cptr call this%reset_c_ptr( cptr ) call this%return() end function function atlas_GridDistribution__ctor( part, part0 ) result(this) use atlas_distribution_c_binding use atlas_kinds_module, only : ATLAS_KIND_IDX type(atlas_GridDistribution) :: this integer, intent(in) :: part(:) integer, intent(in), optional :: part0 integer(ATLAS_KIND_IDX) :: npts integer :: opt_part0 opt_part0 = 0 if( present(part0) ) opt_part0 = part0 npts = size(part) call this%reset_c_ptr( atlas__GridDistribution__new(npts, part, opt_part0) ) call this%return() end function !fckit_owned_object function atlas_GridDistribution__ctor_Grid_Config( grid, config ) result(this) use atlas_distribution_c_binding use atlas_Grid_module, only : atlas_Grid use atlas_Config_module, only : atlas_Config type(atlas_GridDistribution) :: this class(atlas_Grid), intent (in) :: grid type(atlas_Config), intent(in) :: config call this%reset_c_ptr( atlas__GridDistribution__new__Grid_Config(grid%CPTR_PGIBUG_A, config%CPTR_PGIBUG_B) ) call this%return() end function function atlas_GridDistribution__ctor_Grid_Partitioner( grid, partitioner ) result(this) use atlas_distribution_c_binding use atlas_Grid_module, only : atlas_Grid use atlas_Config_module, only : atlas_Config type(atlas_GridDistribution) :: this class(atlas_Grid), intent (in) :: grid class(fckit_owned_object), intent(in) :: partitioner ! cannot use atlas_Partitioner as it would cause cyclic module dependencies call this%reset_c_ptr( atlas__GridDistribution__new__Grid_Partitioner(grid%CPTR_PGIBUG_A, partitioner%CPTR_PGIBUG_A) ) call this%return() end function function partition_int32(this, i) result(partition) use, intrinsic :: iso_c_binding, only: c_long, c_int use atlas_distribution_c_binding integer(c_int) :: partition class(atlas_GridDistribution), intent(in) :: this integer(c_int), intent(in) :: i partition = atlas__GridDistribution__partition_int32(this%CPTR_PGIBUG_A, i-1) end function function partition_int64(this, i) result(partition) use, intrinsic :: iso_c_binding, only: c_long, c_int use atlas_distribution_c_binding integer(c_int) :: partition class(atlas_GridDistribution), intent(in) :: this integer(c_long), intent(in) :: i partition = atlas__GridDistribution__partition_int64(this%CPTR_PGIBUG_A, i-1) end function function atlas_GridDistribution__nb_pts(this) result(nb_pts) use atlas_distribution_c_binding use atlas_kinds_module, only : ATLAS_KIND_IDX class(atlas_GridDistribution) :: this integer(kind=ATLAS_KIND_IDX), allocatable :: nb_pts(:) allocate (nb_pts (this%nb_partitions ())) call atlas__GridDistribution__nb_pts(this%CPTR_PGIBUG_A, nb_pts) end function function atlas_GridDistribution__nb_partitions(this) result(nb_partitions) use, intrinsic :: iso_c_binding, only: c_long use atlas_distribution_c_binding class(atlas_GridDistribution), intent(in) :: this integer(c_long) :: nb_partitions nb_partitions = atlas__atlas__GridDistribution__nb_partitions(this%CPTR_PGIBUG_A) end function ! ---------------------------------------------------------------------------------------- #if FCKIT_FINAL_NOT_INHERITING ATLAS_FINAL subroutine atlas_GridDistribution__final_auto(this) type(atlas_GridDistribution), intent(inout) :: this #if FCKIT_FINAL_DEBUGGING write(0,*) "atlas_GridDistribution__final_auto" #endif #if FCKIT_FINAL_NOT_PROPAGATING call this%final() #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine #endif ! ---------------------------------------------------------------------------------------- end module atlas_GridDistribution_module atlas-0.46.0/src/atlas_f/grid/atlas_StencilComputer_module.F900000664000175000017500000003375015160212070024351 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. #include "atlas/atlas_f.h" module atlas_StencilComputer_module use fckit_shared_object_module, only: fckit_shared_object, fckit_c_deleter use atlas_kinds_module, only : ATLAS_KIND_IDX use, intrinsic :: iso_c_binding, only : c_ptr, c_int implicit none private :: fckit_shared_object, fckit_c_deleter private :: c_ptr, c_int private :: ATLAS_KIND_IDX public :: atlas_StructuredGrid_ComputeNorth public :: atlas_StructuredGrid_ComputeWest public :: atlas_StructuredGrid_ComputeStencil public :: atlas_StructuredGrid_Stencil private !------------------------------------------------------------------------------ TYPE, extends(fckit_shared_object) :: atlas_StructuredGrid_ComputeNorth ! Purpose : ! ------- ! *atlas_StructuredGrid_ComputeNorth* : To compute latitude index north of latitude ! Methods : ! ------- ! Author : ! ------ ! 9-Jan-2024 Willem Deconinck *ECMWF* !------------------------------------------------------------------------------ contains procedure, private :: atlas_StructuredGrid_ComputeNorth__execute_real32 procedure, private :: atlas_StructuredGrid_ComputeNorth__execute_real64 generic :: execute => atlas_StructuredGrid_ComputeNorth__execute_real32, & & atlas_StructuredGrid_ComputeNorth__execute_real64 #if FCKIT_FINAL_NOT_INHERITING final :: atlas_StructuredGrid_ComputeNorth__final_auto #endif END TYPE atlas_StructuredGrid_ComputeNorth interface atlas_StructuredGrid_ComputeNorth module procedure atlas_StructuredGrid_ComputeNorth__ctor end interface !------------------------------------------------------------------------------ !------------------------------------------------------------------------------ TYPE, extends(fckit_shared_object) :: atlas_StructuredGrid_ComputeWest ! Purpose : ! ------- ! *atlas_StructuredGrid_ComputeWest* : To compute longitude index west of longitude at given latitude index ! Methods : ! ------- ! Author : ! ------ ! 9-Jan-2024 Willem Deconinck *ECMWF* !------------------------------------------------------------------------------ contains procedure, private :: atlas_StructuredGrid_ComputeWest__execute_real32 procedure, private :: atlas_StructuredGrid_ComputeWest__execute_real64 generic :: execute => atlas_StructuredGrid_ComputeWest__execute_real32, & & atlas_StructuredGrid_ComputeWest__execute_real64 #if FCKIT_FINAL_NOT_INHERITING final :: atlas_StructuredGrid_ComputeWest__final_auto #endif END TYPE atlas_StructuredGrid_ComputeWest interface atlas_StructuredGrid_ComputeWest module procedure atlas_StructuredGrid_ComputeWest__ctor end interface !------------------------------------------------------------------------------ integer(c_int), parameter, private :: STENCIL_MAX_WIDTH = 6 !-> maximum 6x6 stencil TYPE atlas_StructuredGrid_Stencil integer(ATLAS_KIND_IDX) :: width integer(ATLAS_KIND_IDX) :: j_begin integer(ATLAS_KIND_IDX) :: i_begin(STENCIL_MAX_WIDTH) ! on stack contains procedure, pass :: write => atlas_StructuredGrid_Stencil__write generic, public :: write(FORMATTED) => write procedure, private :: i_int32 => atlas_StructuredGrid_Stencil__i_int32 procedure, private :: i_int64 => atlas_StructuredGrid_Stencil__i_int64 generic, public :: i => i_int32, i_int64 procedure, public :: j => atlas_StructuredGrid_Stencil__j END TYPE atlas_StructuredGrid_Stencil TYPE :: atlas_StructuredGrid_ComputeStencil integer(c_int) :: halo integer(ATLAS_KIND_IDX) :: stencil_width integer(ATLAS_KIND_IDX) :: stencil_offset type(atlas_StructuredGrid_ComputeNorth) :: compute_north type(atlas_StructuredGrid_ComputeWest) :: compute_west contains procedure :: setup_int32 => atlas_StructuredGrid_ComputeStencil__setup_int32 procedure :: setup_int64 => atlas_StructuredGrid_ComputeStencil__setup_int64 generic, public :: setup => setup_int32, setup_int64 procedure :: execute => atlas_StructuredGrid_ComputeStencil__execute_real64 procedure :: assignment_operator => atlas_StructuredGrid_ComputeStencil__assignment generic, public :: assignment(=) => assignment_operator procedure :: final => atlas_StructuredGrid_ComputeStencil__final END TYPE ! Better not use, use setup member function instead ! !interface atlas_StructuredGrid_ComputeStencil ! module procedure atlas_StructuredGrid_ComputeStencil__ctor !end interface !======================================================== contains !======================================================== ! ----------------------------------------------------------------------------- ! Destructor #if FCKIT_FINAL_NOT_INHERITING ATLAS_FINAL subroutine atlas_StructuredGrid_ComputeNorth__final_auto(this) type(atlas_StructuredGrid_ComputeNorth), intent(inout) :: this #if FCKIT_FINAL_NOT_PROPAGATING call this%final() #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine #endif #if FCKIT_FINAL_NOT_INHERITING ATLAS_FINAL subroutine atlas_StructuredGrid_ComputeWest__final_auto(this) type(atlas_StructuredGrid_ComputeWest), intent(inout) :: this #if FCKIT_FINAL_NOT_PROPAGATING call this%final() #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine #endif ! ----------------------------------------------------------------------------- ! Constructors function atlas_StructuredGrid_ComputeNorth__ctor(grid, halo) result(this) use, intrinsic :: iso_c_binding, only : c_int use fckit_c_interop_module, only: c_str use atlas_grid_StencilComputer_c_binding, only : atlas__grid__ComputeNorth__new, atlas__grid__ComputeNorth__delete use atlas_grid_module, only : atlas_StructuredGrid implicit none type(atlas_StructuredGrid_ComputeNorth) :: this class(atlas_StructuredGrid), intent(in) :: grid integer(c_int), intent(in) :: halo call this%reset_c_ptr( atlas__grid__ComputeNorth__new(grid%CPTR_PGIBUG_B, halo), & & fckit_c_deleter(atlas__grid__ComputeNorth__delete) ) call this%return() end function function atlas_StructuredGrid_ComputeWest__ctor(grid, halo) result(this) use, intrinsic :: iso_c_binding, only : c_int use fckit_c_interop_module, only: c_str use atlas_grid_StencilComputer_c_binding, only : atlas__grid__ComputeWest__new, atlas__grid__ComputeWest__delete use atlas_grid_module, only : atlas_StructuredGrid implicit none type(atlas_StructuredGrid_ComputeWest) :: this class(atlas_StructuredGrid), intent(in) :: grid integer(c_int), intent(in) :: halo call this%reset_c_ptr( atlas__grid__ComputeWest__new(grid%CPTR_PGIBUG_B, halo), & & fckit_c_deleter(atlas__grid__ComputeWest__delete) ) call this%return() end function subroutine atlas_StructuredGrid_ComputeStencil__setup_int32(this, grid, stencil_width) use, intrinsic :: iso_c_binding, only : c_double, c_int use atlas_grid_module, only : atlas_StructuredGrid implicit none class(atlas_StructuredGrid_ComputeStencil) :: this class(atlas_StructuredGrid), intent(in) :: grid integer(c_int), intent(in) :: stencil_width this%stencil_width = stencil_width this%halo = (stencil_width + 1) / 2 this%stencil_offset = stencil_width - floor(real(stencil_width,c_double) / 2._c_double + 1._c_double, ATLAS_KIND_IDX) this%compute_north = atlas_StructuredGrid_ComputeNorth(grid, this%halo) this%compute_west = atlas_StructuredGrid_ComputeWest(grid, this%halo) end subroutine subroutine atlas_StructuredGrid_ComputeStencil__setup_int64(this, grid, stencil_width) use, intrinsic :: iso_c_binding, only : c_double, c_long use atlas_grid_module, only : atlas_StructuredGrid implicit none class(atlas_StructuredGrid_ComputeStencil) :: this class(atlas_StructuredGrid), intent(in) :: grid integer(c_long), intent(in) :: stencil_width this%stencil_width = stencil_width this%halo = (stencil_width + 1) / 2 this%stencil_offset = stencil_width - floor(real(stencil_width,c_double) / 2._c_double + 1._c_double, ATLAS_KIND_IDX) this%compute_north = atlas_StructuredGrid_ComputeNorth(grid, this%halo) this%compute_west = atlas_StructuredGrid_ComputeWest(grid, this%halo) end subroutine ! ---------------------------------------------------------------------------------------- function atlas_StructuredGrid_ComputeNorth__execute_real32(this, y) result(index) use, intrinsic :: iso_c_binding, only : c_float use atlas_grid_StencilComputer_c_binding, only : atlas__grid__ComputeNorth__execute_real32 implicit none integer(ATLAS_KIND_IDX) :: index class(atlas_StructuredGrid_ComputeNorth), intent(in) :: this real(c_float), intent(in) :: y index = atlas__grid__ComputeNorth__execute_real32(this%CPTR_PGIBUG_B, y) + 1 end function function atlas_StructuredGrid_ComputeNorth__execute_real64(this, y) result(index) use, intrinsic :: iso_c_binding, only : c_double use atlas_grid_StencilComputer_c_binding, only : atlas__grid__ComputeNorth__execute_real64 implicit none integer(ATLAS_KIND_IDX) :: index class(atlas_StructuredGrid_ComputeNorth), intent(in) :: this real(c_double), intent(in) :: y index = atlas__grid__ComputeNorth__execute_real64(this%CPTR_PGIBUG_B, y) + 1 end function ! ---------------------------------------------------------------------------------------- function atlas_StructuredGrid_ComputeWest__execute_real32(this, x, j) result(index) use, intrinsic :: iso_c_binding, only : c_float use atlas_grid_StencilComputer_c_binding, only : atlas__grid__ComputeWest__execute_real32 implicit none integer(ATLAS_KIND_IDX) :: index class(atlas_StructuredGrid_ComputeWest), intent(in) :: this real(c_float), intent(in) :: x integer(ATLAS_KIND_IDX), intent(in) :: j index = atlas__grid__ComputeWest__execute_real32(this%CPTR_PGIBUG_B, x, j-int(1,ATLAS_KIND_IDX)) + 1 end function function atlas_StructuredGrid_ComputeWest__execute_real64(this, x, j) result(index) use, intrinsic :: iso_c_binding, only : c_double use atlas_grid_StencilComputer_c_binding, only : atlas__grid__ComputeWest__execute_real64 implicit none integer(ATLAS_KIND_IDX) :: index class(atlas_StructuredGrid_ComputeWest), intent(in) :: this real(c_double), intent(in) :: x integer(ATLAS_KIND_IDX), intent(in) :: j index = atlas__grid__ComputeWest__execute_real64(this%CPTR_PGIBUG_B, x, j-int(1,ATLAS_KIND_IDX)) + 1 end function ! ---------------------------------------------------------------------------------------- subroutine atlas_StructuredGrid_ComputeStencil__execute_real64(this, x, y, stencil) use, intrinsic :: iso_c_binding, only : c_double implicit none class(atlas_StructuredGrid_ComputeStencil), intent(in) :: this real(c_double), intent(in) :: x, y type(atlas_StructuredGrid_Stencil), intent(inout) :: stencil integer(ATLAS_KIND_IDX) :: jj stencil%width = this%stencil_width stencil%j_begin = this%compute_north%execute(y) - this%stencil_offset do jj = 1_ATLAS_KIND_IDX, this%stencil_width stencil%i_begin(jj) = this%compute_west%execute(x, stencil%j_begin + jj - 1) - this%stencil_offset enddo end subroutine ! ---------------------------------------------------------------------------------------- subroutine atlas_StructuredGrid_Stencil__write (stencil, unit, iotype, v_list, iostat, iomsg) implicit none class(atlas_StructuredGrid_Stencil), intent(in) :: stencil INTEGER, INTENT(IN) :: unit CHARACTER(*), INTENT(IN) :: iotype INTEGER, INTENT(IN) :: v_list(:) INTEGER, INTENT(OUT) :: iostat CHARACTER(*), INTENT(INOUT) :: iomsg integer(ATLAS_KIND_IDX) :: jlat, jlon do jlat = 1, stencil%width write(unit,'(a,I0,a)',advance='no',IOSTAT=iostat) " j: ", stencil%j(jlat), " i = " do jlon = 1, stencil%width write(unit,'(I3)',advance='no',IOSTAT=iostat) stencil%i(jlon,jlat) enddo write(0,'(a)',IOSTAT=iostat) new_line('a') enddo end subroutine function atlas_StructuredGrid_Stencil__j(this, j_index) result(j) integer(ATLAS_KIND_IDX) :: j class(atlas_StructuredGrid_Stencil), intent(in) :: this integer(ATLAS_KIND_IDX) :: j_index j = this%j_begin + (j_index-1) end function function atlas_StructuredGrid_Stencil__i_int32(this, i_index, j_index) result(i) use, intrinsic :: iso_c_binding, only : c_int integer(ATLAS_KIND_IDX) :: i class(atlas_StructuredGrid_Stencil), intent(in) :: this integer(c_int) :: i_index integer(ATLAS_KIND_IDX) :: j_index i = this%i_begin(j_index) + (i_index-1) end function function atlas_StructuredGrid_Stencil__i_int64(this, i_index, j_index) result(i) use, intrinsic :: iso_c_binding, only : c_long integer(ATLAS_KIND_IDX) :: i class(atlas_StructuredGrid_Stencil), intent(in) :: this integer(c_long) :: i_index integer(ATLAS_KIND_IDX) :: j_index i = this%i_begin(j_index) + (i_index-1) end function ! ---------------------------------------------------------------------------------------- function atlas_StructuredGrid_ComputeStencil__ctor(grid, stencil_width) result(this) use atlas_grid_module, only : atlas_StructuredGrid implicit none type(atlas_StructuredGrid_ComputeStencil) :: this class(atlas_StructuredGrid), intent(in) :: grid integer(ATLAS_KIND_IDX), intent(in) :: stencil_width call this%setup(grid, stencil_width) end function subroutine atlas_StructuredGrid_ComputeStencil__assignment(this, other) implicit none class(atlas_StructuredGrid_ComputeStencil), intent(inout) :: this class(atlas_StructuredGrid_ComputeStencil), intent(in) :: other call this%final() write(0,*) "owners = ", other%compute_north%owners() this%compute_north = other%compute_north this%compute_west = other%compute_west this%stencil_width = other%stencil_width this%stencil_offset = other%stencil_offset this%halo = other%halo end subroutine subroutine atlas_StructuredGrid_ComputeStencil__final(this) class(atlas_StructuredGrid_ComputeStencil), intent(inout) :: this call this%compute_north%final() call this%compute_west%final() end subroutine ! ---------------------------------------------------------------------------------------- end module atlas_StencilComputer_module atlas-0.46.0/src/atlas_f/grid/atlas_Partitioner_module.F900000664000175000017500000001245115160212070023524 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. #include "atlas/atlas_f.h" module atlas_Partitioner_module use fckit_owned_object_module, only: fckit_owned_object implicit none private :: fckit_owned_object public :: atlas_Partitioner public :: atlas_MatchingPartitioner public :: atlas_MatchingMeshPartitioner ! use MatchingPartitioner instead! private !----------------------------- ! atlas_Partitioner ! !----------------------------- !------------------------------------------------------------------------------ TYPE, extends(fckit_owned_object) :: atlas_Partitioner ! Purpose : ! ------- ! *Partitioner* : Object passed from atlas to inspect grid distribution ! Methods : ! ------- ! Author : ! ------ ! 12-Mar-2014 Willem Deconinck *ECMWF* !------------------------------------------------------------------------------ contains procedure, public :: partition #if FCKIT_FINAL_NOT_INHERITING final :: atlas_Partitioner__final_auto #endif END TYPE atlas_Partitioner !------------------------------------------------------------------------------ interface atlas_Partitioner module procedure atlas_Partitioner__ctor module procedure atlas_Partitioner__ctor_type end interface interface atlas_MatchingPartitioner module procedure atlas_MatchingMeshPartitioner__ctor module procedure atlas_MatchingFunctionSpacePartitioner__ctor end interface interface atlas_MatchingMeshPartitioner module procedure atlas_MatchingMeshPartitioner__ctor end interface !======================================================== contains !======================================================== ! ----------------------------------------------------------------------------- ! Partitioner routines function atlas_Partitioner__ctor( config ) result(this) use atlas_config_module, only : atlas_Config use atlas_partitioner_c_binding type(atlas_Partitioner) :: this type(atlas_Config) :: config call this%reset_c_ptr( atlas__grid__Partitioner__new( config%CPTR_PGIBUG_B ) ) call this%return() end function function atlas_Partitioner__ctor_type( type ) result(this) use atlas_config_module, only : atlas_Config use atlas_partitioner_c_binding use fckit_C_interop_module, only : c_str type(atlas_Partitioner) :: this character(len=*), intent(in) :: type !-------------------------------------- call this%reset_c_ptr( atlas__grid__Partitioner__new_type( c_str(type) ) ) call this%return() end function function atlas_MatchingMeshPartitioner__ctor( mesh, config ) result(this) use atlas_mesh_module, only : atlas_Mesh use atlas_config_module, only : atlas_Config use atlas_partitioner_c_binding type(atlas_Partitioner) :: this type(atlas_Mesh) , intent(in) :: mesh type(atlas_Config), intent(in), optional :: config type(atlas_Config) :: opt_config if( present(config) ) then call this%reset_c_ptr( atlas__grid__MatchingMeshPartitioner__new( & mesh%CPTR_PGIBUG_A, config%CPTR_PGIBUG_B ) ) else opt_config = atlas_Config() call this%reset_c_ptr( atlas__grid__MatchingMeshPartitioner__new( & mesh%CPTR_PGIBUG_A, opt_config%CPTR_PGIBUG_B ) ) call opt_config%final() endif call this%return() end function function atlas_MatchingFunctionSpacePartitioner__ctor( functionspace, config ) result(this) use atlas_functionspace_module, only : atlas_FunctionSpace use atlas_config_module, only : atlas_Config use atlas_partitioner_c_binding type(atlas_Partitioner) :: this class(atlas_FunctionSpace) , intent(in) :: functionspace type(atlas_Config), intent(in), optional :: config type(atlas_Config) :: opt_config if( present(config) ) then call this%reset_c_ptr( atlas__grid__MatchingFunctionSpacePartitioner__new( & functionspace%CPTR_PGIBUG_A, config%CPTR_PGIBUG_B ) ) else opt_config = atlas_Config() call this%reset_c_ptr( atlas__grid__MatchingFunctionSpacePartitioner__new( & functionspace%CPTR_PGIBUG_A, opt_config%CPTR_PGIBUG_B ) ) call opt_config%final() endif call this%return() end function function partition(this,grid) result(distribution) use atlas_partitioner_c_binding use atlas_GridDistribution_module, only : atlas_GridDistribution use atlas_Grid_module, only : atlas_Grid type(atlas_GridDistribution) :: distribution class(atlas_Partitioner), intent(in) :: this class(atlas_Grid), intent(in) :: grid distribution = atlas_GridDistribution( atlas__grid__Partitioner__partition( this%CPTR_PGIBUG_A, grid%CPTR_PGIBUG_A ) ) call distribution%return() end function !------------------------------------------------------------------------------- #if FCKIT_FINAL_NOT_INHERITING ATLAS_FINAL subroutine atlas_Partitioner__final_auto(this) type(atlas_Partitioner), intent(inout) :: this #if FCKIT_FINAL_DEBUGGING write(0,*) "atlas_Partitioner__final_auto" #endif #if FCKIT_FINAL_NOT_PROPAGATING call this%final() #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine #endif ! ---------------------------------------------------------------------------------------- end module atlas_Partitioner_module atlas-0.46.0/src/atlas_f/grid/atlas_Vertical_module.F900000664000175000017500000001075215160212070022777 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. #include "atlas/atlas_f.h" module atlas_Vertical_module use fckit_shared_object_module, only : fckit_shared_object, fckit_c_deleter, fckit_c_nodeleter use atlas_field_module, only : atlas_Field use, intrinsic :: iso_c_binding, only : c_ptr, c_double implicit none public :: atlas_Vertical private !----------------------------- ! atlas_Vertical ! !----------------------------- !------------------------------------------------------------------------------ TYPE, extends(fckit_shared_object) :: atlas_Vertical ! Purpose : ! ------- ! *Vertical* : Object describing vertical levels ! Methods : ! ------- ! Author : ! ------ ! 28-Nov-2018 Willem Deconinck *ECMWF* !------------------------------------------------------------------------------ contains procedure, public :: z procedure, public :: size => vsize #if FCKIT_FINAL_NOT_INHERITING final :: atlas_Vertical__final_auto #endif END TYPE atlas_Vertical !------------------------------------------------------------------------------ interface atlas_Vertical module procedure atlas_Vertical__ctor_from_cptr module procedure atlas_Vertical__ctor_from_array module procedure atlas_Vertical__ctor_from_array_with_interval end interface private :: c_ptr, c_double private :: fckit_shared_object private :: fckit_c_deleter private :: fckit_c_nodeleter private :: atlas_Field !======================================================== contains !======================================================== ! ----------------------------------------------------------------------------- ! Vertical routines function atlas_Vertical__ctor_from_cptr( cptr, delete ) result(this) use atlas_vertical_c_binding type(c_ptr), value :: cptr type(atlas_Vertical) :: this logical, optional :: delete logical :: opt_delete opt_delete = .true. if( present(delete) ) opt_delete = delete if( opt_delete ) then call this%reset_c_ptr( cptr, fckit_c_deleter(atlas__Vertical__delete) ) else call this%reset_c_ptr( cptr, fckit_c_nodeleter() ) endif call this%return() end function function atlas_Vertical__ctor_from_array( levels ) result(this) use atlas_vertical_c_binding use atlas_kinds_module, only : ATLAS_KIND_IDX type(atlas_Vertical) :: this real(c_double), intent(in) :: levels(:) integer(ATLAS_KIND_IDX) :: nb_levels nb_levels = size(levels) call this%reset_c_ptr( atlas__Vertical__new(nb_levels, levels), & & fckit_c_deleter(atlas__Vertical__delete) ) call this%return() end function function atlas_Vertical__ctor_from_array_with_interval( levels, interval ) result(this) use atlas_vertical_c_binding use atlas_kinds_module, only : ATLAS_KIND_IDX type(atlas_Vertical) :: this real(c_double), intent(in) :: levels(:) real(c_double), intent(in) :: interval(2) integer(ATLAS_KIND_IDX) :: nb_levels nb_levels = size(levels) call this%reset_c_ptr( atlas__Vertical__new_interval(nb_levels, levels, interval), & & fckit_c_deleter(atlas__Vertical__delete) ) call this%return() end function ! ---------------------------------------------------------------------------------------- function z( this ) use atlas_vertical_c_binding type(atlas_Field) :: z class(atlas_Vertical), intent(in) :: this z = atlas_Field( atlas__Vertical__z( this%CPTR_PGIBUG_B ) ) call z%return() end function ! ---------------------------------------------------------------------------------------- function vsize( this ) use atlas_vertical_c_binding use, intrinsic :: iso_c_binding, only : c_int integer(c_int) :: vsize class(atlas_Vertical), intent(in) :: this vsize = atlas__Vertical__size( this%CPTR_PGIBUG_B ) end function ! ---------------------------------------------------------------------------------------- #if FCKIT_FINAL_NOT_INHERITING ATLAS_FINAL subroutine atlas_Vertical__final_auto(this) type(atlas_Vertical), intent(inout) :: this #if FCKIT_FINAL_DEBUGGING write(0,*) "atlas_Vertical__final_auto" #endif #if FCKIT_FINAL_NOT_PROPAGATING call this%final() #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine #endif ! ---------------------------------------------------------------------------------------- end module atlas_Vertical_module atlas-0.46.0/src/atlas_f/grid/atlas_Grid_module.F900000664000175000017500000012047015160212070022112 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. #include "atlas/atlas_f.h" module atlas_Grid_module use fckit_owned_object_module, only: fckit_owned_object use atlas_Config_module, only: atlas_Config use atlas_Projection_module, only : atlas_Projection use atlas_Domain_module, only : atlas_LonLatRectangularDomain use atlas_kinds_module, only : ATLAS_KIND_IDX, ATLAS_KIND_GIDX use, intrinsic :: iso_c_binding, only : c_ptr implicit none private :: fckit_owned_object private :: atlas_Config private :: atlas_Projection private :: c_ptr public :: atlas_Grid public :: atlas_UnstructuredGrid public :: atlas_StructuredGrid public :: atlas_GaussianGrid public :: atlas_ReducedGaussianGrid public :: atlas_RegularGaussianGrid public :: atlas_RegularLonLatGrid public :: atlas_ShiftedLonLatGrid public :: atlas_ShiftedLonGrid public :: atlas_ShiftedLatGrid public :: atlas_RegionalGrid private !------------------------------------------------------------------------------ TYPE, extends(fckit_owned_object) :: atlas_Grid ! Purpose : ! ------- ! *atlas_Grid* : Object Grid specifications for Grids ! Methods : ! ------- ! Author : ! ------ ! 9-Oct-2014 Willem Deconinck *ECMWF* !------------------------------------------------------------------------------ contains procedure :: name => atlas_Grid__name procedure :: size => atlas_Grid__size procedure :: spec => atlas_Grid__spec procedure :: uid procedure :: lonlat_bounding_box #if FCKIT_FINAL_NOT_INHERITING final :: atlas_Grid__final_auto #endif END TYPE atlas_Grid interface atlas_Grid module procedure atlas_Grid__ctor_id module procedure atlas_Grid__ctor_config module procedure atlas_Grid__ctor_cptr end interface !------------------------------------------------------------------------------ TYPE, extends(atlas_Grid) :: atlas_UnstructuredGrid ! Purpose : ! ------- ! *atlas_UnstructuredGrid* : Object Grid specifications for Reduced Grids ! Methods : ! ------- ! Author : ! ------ ! 8-Jun-2019 Willem Deconinck *ECMWF* !------------------------------------------------------------------------------ contains #if FCKIT_FINAL_NOT_INHERITING final :: atlas_UnstructuredGrid__final_auto #endif END TYPE atlas_UnstructuredGrid interface atlas_UnstructuredGrid module procedure atlas_UnstructuredGrid__ctor_points module procedure atlas_UnstructuredGrid__ctor_config module procedure atlas_UnstructuredGrid__ctor_cptr end interface !------------------------------------------------------------------------------ TYPE, extends(atlas_Grid) :: atlas_StructuredGrid ! Purpose : ! ------- ! *atlas_StructuredGrid* : Object Grid specifications for Reduced Grids ! Methods : ! ------- ! Author : ! ------ ! 9-Oct-2014 Willem Deconinck *ECMWF* !------------------------------------------------------------------------------ contains procedure :: ny => Structured__ny procedure, private :: nx_int32 => Structured__nx_int32 procedure, private :: nx_int64 => Structured__nx_int64 generic :: nx => nx_int32, nx_int64 procedure, private :: Structured__index_int32 procedure, private :: Structured__index_int64 generic :: index => Structured__index_int32, Structured__index_int64 procedure, private :: Structured__index2ij_int32 procedure, private :: Structured__index2ij_int64 generic :: index2ij => Structured__index2ij_int32, Structured__index2ij_int64 procedure, private :: Structured__ij_int32 procedure, private :: Structured__ij_int64 generic :: ij => Structured__ij_int32, Structured__ij_int64 procedure :: nx_array => Structured__nx_array procedure :: nxmin => Structured__nxmin procedure :: nxmax => Structured__nxmax procedure :: y_array => Structured__y_array procedure, private :: x_32 => Structured__x_32 procedure, private :: x_64 => Structured__x_64 generic :: x => x_32, x_64 procedure, private :: y_32 => Structured__y_32 procedure, private :: y_64 => Structured__y_64 generic :: y => y_32, y_64 procedure, private :: xy_32 => Structured__xy_32 procedure, private :: xy_64 => Structured__xy_64 generic :: xy => xy_32, xy_64 procedure, private :: lonlat_32 => Structured__lonlat_32 procedure, private :: lonlat_64 => Structured__lonlat_64 generic :: lonlat => lonlat_32, lonlat_64 procedure :: reduced => Structured__reduced #if FCKIT_FINAL_NOT_INHERITING final :: atlas_StructuredGrid__final_auto #endif END TYPE atlas_StructuredGrid interface atlas_StructuredGrid module procedure atlas_StructuredGrid__ctor_id module procedure atlas_StructuredGrid__ctor_config module procedure atlas_StructuredGrid__ctor_cptr end interface !------------------------------------------------------------------------------ !------------------------------------------------------------------------------ TYPE, extends(atlas_StructuredGrid) :: atlas_GaussianGrid ! Purpose : ! ------- ! *atlas_ReducedGaussianGrid* : Object Grid specifications for Reduced Gaussian Grids ! Methods : ! ------- ! Author : ! ------ ! 9-Oct-2014 Willem Deconinck *ECMWF* !------------------------------------------------------------------------------ contains procedure :: N => Gaussian__N #if FCKIT_FINAL_NOT_INHERITING final :: atlas_GaussianGrid__final_auto #endif END TYPE atlas_GaussianGrid interface atlas_GaussianGrid module procedure atlas_GaussianGrid__ctor_id end interface !------------------------------------------------------------------------------ !------------------------------------------------------------------------------ TYPE, extends(atlas_StructuredGrid) :: atlas_ReducedGaussianGrid ! Purpose : ! ------- ! *atlas_ReducedGaussianGrid* : Object Grid specifications for Reduced Gaussian Grids ! Methods : ! ------- ! Author : ! ------ ! 9-Oct-2014 Willem Deconinck *ECMWF* !------------------------------------------------------------------------------ contains procedure :: N => ReducedGaussian__N #if FCKIT_FINAL_NOT_INHERITING final :: atlas_ReducedGaussianGrid__final_auto #endif END TYPE atlas_ReducedGaussianGrid interface atlas_ReducedGaussianGrid module procedure atlas_ReducedGaussianGrid__ctor_int32 module procedure atlas_ReducedGaussianGrid__ctor_int64 module procedure atlas_ReducedGaussianGrid__ctor_projection_int32 module procedure atlas_ReducedGaussianGrid__ctor_projection_int64 end interface !------------------------------------------------------------------------------ TYPE, extends(atlas_StructuredGrid) :: atlas_RegularGaussianGrid ! Purpose : ! ------- ! *atlas_RegularGaussianGrid* : Object Grid specifications for Regular Gaussian Grids ! Methods : ! ------- ! Author : ! ------ ! 9-Oct-2014 Willem Deconinck *ECMWF* !------------------------------------------------------------------------------ contains procedure :: N => RegularGaussian__N #if FCKIT_FINAL_NOT_INHERITING final :: atlas_RegularGaussianGrid__final_auto #endif END TYPE atlas_RegularGaussianGrid interface atlas_RegularGaussianGrid module procedure atlas_RegularGaussianGrid__ctor_int32 module procedure atlas_RegularGaussianGrid__ctor_int64 end interface !------------------------------------------------------------------------------ TYPE, extends(atlas_StructuredGrid) :: atlas_RegularLonLatGrid ! Purpose : ! ------- ! *atlas_RegularLonLatGrid* : Object Grid specifications for LonLat Grids ! Methods : ! ------- ! Author : ! ------ ! 9-Oct-2014 Willem Deconinck *ECMWF* !------------------------------------------------------------------------------ contains #if FCKIT_FINAL_NOT_INHERITING final :: atlas_RegularLonLatGrid__final_auto #endif END TYPE atlas_RegularLonLatGrid interface atlas_RegularLonLatGrid module procedure atlas_grid_RegularLonLat__ctor_int32 module procedure atlas_grid_RegularLonLat__ctor_int64 end interface !------------------------------------------------------------------------------ TYPE, extends(atlas_StructuredGrid) :: atlas_ShiftedLonLatGrid ! Purpose : ! ------- ! *atlas_ShiftedLonLatGrid* : Object Grid specifications for LonLat Grids ! Methods : ! ------- ! Author : ! ------ ! 9-Oct-2014 Willem Deconinck *ECMWF* !------------------------------------------------------------------------------ contains #if FCKIT_FINAL_NOT_INHERITING final :: atlas_ShiftedLonLatGrid__final_auto #endif END TYPE atlas_ShiftedLonLatGrid interface atlas_ShiftedLonLatGrid module procedure atlas_grid_ShiftedLonLat__ctor_int32 module procedure atlas_grid_ShiftedLonLat__ctor_int64 end interface !------------------------------------------------------------------------------ TYPE, extends(atlas_StructuredGrid) :: atlas_ShiftedLonGrid ! Purpose : ! ------- ! *atlas_ShiftedLonGrid* : Object Grid specifications for LonLat Grids ! Methods : ! ------- ! Author : ! ------ ! 9-Oct-2014 Willem Deconinck *ECMWF* !------------------------------------------------------------------------------ contains #if FCKIT_FINAL_NOT_INHERITING final :: atlas_ShiftedLonGrid__final_auto #endif END TYPE atlas_ShiftedLonGrid interface atlas_ShiftedLonGrid module procedure atlas_grid_ShiftedLon__ctor_int32 module procedure atlas_grid_ShiftedLon__ctor_int64 end interface !------------------------------------------------------------------------------ TYPE, extends(atlas_StructuredGrid) :: atlas_ShiftedLatGrid ! Purpose : ! ------- ! *atlas_ShiftedLatGrid* : Object Grid specifications for LonLat Grids ! Methods : ! ------- ! Author : ! ------ ! 9-Oct-2014 Willem Deconinck *ECMWF* !------------------------------------------------------------------------------ contains #if FCKIT_FINAL_NOT_INHERITING final :: atlas_ShiftedLatGrid__final_auto #endif END TYPE atlas_ShiftedLatGrid interface atlas_ShiftedLatGrid module procedure atlas_grid_ShiftedLat__ctor_int32 module procedure atlas_grid_ShiftedLat__ctor_int64 end interface !------------------------------------------------------------------------------ interface atlas_RegionalGrid module procedure atlas_RegionalGrid_ctor_int32 module procedure atlas_RegionalGrid_ctor_int64 module procedure atlas_RegionalGrid_ctor_nwse_int32 module procedure atlas_RegionalGrid_ctor_nwse_int64 module procedure atlas_RegionalGrid_ctor_increments_int32 module procedure atlas_RegionalGrid_ctor_increments_int64 end interface !------------------------------------------------------------------------------ interface c_idx module procedure c_idx_32 module procedure c_idx_64 end interface interface c_gidx module procedure c_gidx_32 module procedure c_gidx_64 end interface !------------------------------------------------------------------------------ !======================================================== contains !======================================================== pure function c_idx_32(f_idx) result(c_idx) use, intrinsic :: iso_c_binding, only : c_long integer(ATLAS_KIND_IDX) :: c_idx integer(c_long), intent(in) :: f_idx c_idx = int(f_idx,ATLAS_KIND_IDX) - 1_ATLAS_KIND_IDX end function pure function c_idx_64(f_idx) result(c_idx) use, intrinsic :: iso_c_binding, only : c_long, c_int integer(ATLAS_KIND_IDX) :: c_idx integer(c_int), intent(in) :: f_idx c_idx = int(f_idx,ATLAS_KIND_IDX) - 1_ATLAS_KIND_IDX end function pure function c_gidx_32(f_gidx) result(c_gidx) use, intrinsic :: iso_c_binding, only : c_long integer(ATLAS_KIND_GIDX) :: c_gidx integer(c_long), intent(in) :: f_gidx c_gidx = int(f_gidx,ATLAS_KIND_GIDX) - 1_ATLAS_KIND_GIDX end function pure function c_gidx_64(f_gidx) result(c_gidx) use, intrinsic :: iso_c_binding, only : c_int integer(ATLAS_KIND_GIDX) :: c_gidx integer(c_int), intent(in) :: f_gidx c_gidx = int(f_gidx,ATLAS_KIND_GIDX) - 1_ATLAS_KIND_GIDX end function ! ----------------------------------------------------------------------------- ! Destructor #if FCKIT_FINAL_NOT_INHERITING ATLAS_FINAL subroutine atlas_Grid__final_auto(this) type(atlas_Grid), intent(inout) :: this #if FCKIT_FINAL_NOT_PROPAGATING call this%final() #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine ATLAS_FINAL subroutine atlas_UnstructuredGrid__final_auto(this) type(atlas_UnstructuredGrid), intent(inout) :: this #if FCKIT_FINAL_NOT_PROPAGATING call this%final() #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine ATLAS_FINAL subroutine atlas_StructuredGrid__final_auto(this) type(atlas_StructuredGrid), intent(inout) :: this #if FCKIT_FINAL_NOT_PROPAGATING call this%final() #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine ATLAS_FINAL subroutine atlas_GaussianGrid__final_auto(this) type(atlas_GaussianGrid), intent(inout) :: this #if FCKIT_FINAL_NOT_PROPAGATING call this%final() #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine ATLAS_FINAL subroutine atlas_ReducedGaussianGrid__final_auto(this) type(atlas_ReducedGaussianGrid), intent(inout) :: this #if FCKIT_FINAL_NOT_PROPAGATING call this%final() #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine ATLAS_FINAL subroutine atlas_RegularLonLatGrid__final_auto(this) type(atlas_RegularLonLatGrid), intent(inout) :: this #if FCKIT_FINAL_NOT_PROPAGATING call this%final() #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine ATLAS_FINAL subroutine atlas_ShiftedLonLatGrid__final_auto(this) type(atlas_ShiftedLonLatGrid), intent(inout) :: this #if FCKIT_FINAL_NOT_PROPAGATING call this%final() #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine ATLAS_FINAL subroutine atlas_ShiftedLonGrid__final_auto(this) type(atlas_ShiftedLonGrid), intent(inout) :: this #if FCKIT_FINAL_NOT_PROPAGATING call this%final() #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine ATLAS_FINAL subroutine atlas_ShiftedLatGrid__final_auto(this) type(atlas_ShiftedLatGrid), intent(inout) :: this #if FCKIT_FINAL_NOT_PROPAGATING call this%final() #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine ATLAS_FINAL subroutine atlas_RegularGaussianGrid__final_auto(this) type(atlas_RegularGaussianGrid), intent(inout) :: this #if FCKIT_FINAL_NOT_PROPAGATING call this%final() #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine #endif ! ----------------------------------------------------------------------------- ! Constructors function atlas_Grid__ctor_id(identifier) result(this) use fckit_c_interop_module, only: c_str use atlas_grid_Structured_c_binding type(atlas_Grid) :: this character(len=*), intent(in) :: identifier call this%reset_c_ptr( atlas__grid__Structured(c_str(identifier)) ) call this%return() end function function atlas_Grid__ctor_config(config) result(this) use atlas_grid_Structured_c_binding type(atlas_Grid) :: this type(atlas_Config), intent(in) :: config call this%reset_c_ptr( atlas__grid__Structured__config(config%CPTR_PGIBUG_B) ) call this%return() end function function atlas_Grid__ctor_cptr(cptr) result(this) use fckit_c_interop_module, only: c_str use atlas_grid_Structured_c_binding type(atlas_Grid) :: this type(c_ptr), intent(in) :: cptr call this%reset_c_ptr( cptr ) call this%return() end function ! ----------------------------------------------------------------------------- function atlas_UnstructuredGrid__ctor_config(config) result(this) use atlas_grid_unstructured_c_binding type(atlas_UnstructuredGrid) :: this type(atlas_Config), intent(in) :: config call this%reset_c_ptr( atlas__grid__Unstructured__config(config%CPTR_PGIBUG_B) ) call this%return() end function function atlas_UnstructuredGrid__ctor_cptr(cptr) result(this) use fckit_c_interop_module, only: c_str use atlas_grid_unstructured_c_binding type(atlas_UnstructuredGrid) :: this type(c_ptr), intent(in) :: cptr call this%reset_c_ptr( cptr ) call this%return() end function function atlas_UnstructuredGrid__ctor_points( xy ) result(this) use, intrinsic :: iso_c_binding, only : c_double, c_int use fckit_c_interop_module, only : c_str use fckit_array_module, only : array_strides, array_view1d use atlas_grid_unstructured_c_binding type(atlas_UnstructuredGrid) :: this real(c_double), intent(in), target :: xy(:,:) integer(c_int) :: shapef(2) integer(c_int) :: stridesf(2) real(c_double), pointer :: xy1d(:) xy1d => array_view1d(xy) shapef = shape(xy) stridesf = array_strides(xy) call this%reset_c_ptr( atlas__grid__Unstructured__points( xy1d, shapef, stridesf ) ) call this%return() end function ! ----------------------------------------------------------------------------- function atlas_StructuredGrid__ctor_id(identifier) result(this) use fckit_c_interop_module, only: c_str use atlas_grid_Structured_c_binding type(atlas_StructuredGrid) :: this character(len=*), intent(in) :: identifier call this%reset_c_ptr( atlas__grid__Structured(c_str(identifier)) ) call this%return() end function function atlas_StructuredGrid__ctor_config(config) result(this) use atlas_grid_Structured_c_binding type(atlas_StructuredGrid) :: this type(atlas_Config), intent(in) :: config call this%reset_c_ptr( atlas__grid__Structured__config(config%CPTR_PGIBUG_B) ) call this%return() end function function atlas_StructuredGrid__ctor_cptr(cptr) result(this) use fckit_c_interop_module, only: c_str use atlas_grid_Structured_c_binding type(atlas_StructuredGrid) :: this type(c_ptr), intent(in) :: cptr call this%reset_c_ptr( cptr ) call this%return() end function !----------------------------------------------------------------------------- function atlas_GaussianGrid__ctor_id(identifier) result(this) use fckit_c_interop_module, only: c_str use atlas_grid_Structured_c_binding type(atlas_GaussianGrid) :: this character(len=*), intent(in) :: identifier call this%reset_c_ptr( atlas__grid__Structured(c_str(identifier)) ) call this%return() end function ! ----------------------------------------------------------------------------- function atlas_RegularGaussianGrid__ctor_int32(N) result(this) use, intrinsic :: iso_c_binding, only: c_int, c_long use atlas_grid_Structured_c_binding type(atlas_RegularGaussianGrid) :: this integer(c_int), intent(in) :: N call this%reset_c_ptr( atlas__grid__regular__RegularGaussian(int(N,c_long)) ) call this%return() end function function atlas_RegularGaussianGrid__ctor_int64(N) result(this) use, intrinsic :: iso_c_binding, only: c_long use atlas_grid_Structured_c_binding type(atlas_RegularGaussianGrid) :: this integer(c_long), intent(in) :: N call this%reset_c_ptr( atlas__grid__regular__RegularGaussian(int(N,c_long)) ) call this%return() end function !----------------------------------------------------------------------------- function atlas_ReducedGaussianGrid__ctor_int32(nx) result(this) use, intrinsic :: iso_c_binding, only: c_int, c_long use atlas_grid_Structured_c_binding type(atlas_ReducedGaussianGrid) :: this integer(c_int), intent(in) :: nx(:) call this%reset_c_ptr( & & atlas__grid__reduced__ReducedGaussian_int( nx, int(size(nx),c_long) ) ) call this%return() end function function atlas_ReducedGaussianGrid__ctor_int64(nx) result(this) use, intrinsic :: iso_c_binding, only: c_int, c_long use atlas_grid_Structured_c_binding type(atlas_ReducedGaussianGrid) :: this integer(c_long), intent(in) :: nx(:) call this%reset_c_ptr( & & atlas__grid__reduced__ReducedGaussian_long( nx, int(size(nx),c_long) ) ) call this%return() end function function atlas_ReducedGaussianGrid__ctor_projection_int32(nx, projection) result(this) use, intrinsic :: iso_c_binding, only: c_int, c_long use atlas_grid_Structured_c_binding type(atlas_ReducedGaussianGrid) :: this integer(c_int), intent(in) :: nx(:) type(atlas_Projection), intent(in) :: projection call this%reset_c_ptr( & & atlas__grid__reduced__ReducedGaussian_int_projection( nx, int(size(nx),c_long), projection%CPTR_PGIBUG_A ) ) call this%return() end function function atlas_ReducedGaussianGrid__ctor_projection_int64(nx, projection) result(this) use, intrinsic :: iso_c_binding, only: c_int, c_long use atlas_grid_Structured_c_binding type(atlas_ReducedGaussianGrid) :: this integer(c_long), intent(in) :: nx(:) type(atlas_Projection), intent(in) :: projection call this%reset_c_ptr( & & atlas__grid__reduced__ReducedGaussian_long_projection( nx, int(size(nx),c_long), projection%CPTR_PGIBUG_A ) ) call this%return() end function !----------------------------------------------------------------------------- function atlas_grid_RegularLonLat__ctor_int32(nlon,nlat) result(this) use, intrinsic :: iso_c_binding, only: c_int, c_long use atlas_grid_Structured_c_binding type(atlas_RegularLonLatGrid) :: this integer(c_int), intent(in) :: nlon, nlat call this%reset_c_ptr( atlas__grid__regular__RegularLonLat(int(nlon,c_long),int(nlat,c_long)) ) call this%return() end function function atlas_grid_RegularLonLat__ctor_int64(nlon,nlat) result(this) use, intrinsic :: iso_c_binding, only: c_long use atlas_grid_Structured_c_binding type(atlas_RegularLonLatGrid) :: this integer(c_long), intent(in) :: nlon, nlat call this%reset_c_ptr( atlas__grid__regular__RegularLonLat( nlon, nlat ) ) call this%return() end function !----------------------------------------------------------------------------- function atlas_grid_ShiftedLonLat__ctor_int32(nlon,nlat) result(this) use, intrinsic :: iso_c_binding, only: c_int, c_long use atlas_grid_Structured_c_binding type(atlas_ShiftedLonLatGrid) :: this integer(c_int), intent(in) :: nlon, nlat call this%reset_c_ptr( atlas__grid__regular__ShiftedLonLat(int(nlon,c_long),int(nlat,c_long)) ) call this%return() end function function atlas_grid_ShiftedLonLat__ctor_int64(nlon,nlat) result(this) use, intrinsic :: iso_c_binding, only: c_long use atlas_grid_Structured_c_binding type(atlas_ShiftedLonLatGrid) :: this integer(c_long), intent(in) :: nlon, nlat call this%reset_c_ptr( atlas__grid__regular__ShiftedLonLat( nlon, nlat ) ) call this%return() end function !----------------------------------------------------------------------------- function atlas_grid_ShiftedLon__ctor_int32(nlon,nlat) result(this) use, intrinsic :: iso_c_binding, only: c_int, c_long use atlas_grid_Structured_c_binding type(atlas_ShiftedLonGrid) :: this integer(c_int), intent(in) :: nlon, nlat call this%reset_c_ptr( atlas__grid__regular__ShiftedLon(int(nlon,c_long),int(nlat,c_long)) ) call this%return() end function function atlas_grid_ShiftedLon__ctor_int64(nlon,nlat) result(this) use, intrinsic :: iso_c_binding, only: c_long use atlas_grid_Structured_c_binding type(atlas_ShiftedLonGrid) :: this integer(c_long), intent(in) :: nlon, nlat call this%reset_c_ptr( atlas__grid__regular__ShiftedLon( nlon, nlat ) ) call this%return() end function !----------------------------------------------------------------------------- function atlas_grid_ShiftedLat__ctor_int32(nlon,nlat) result(this) use, intrinsic :: iso_c_binding, only: c_int, c_long use atlas_grid_Structured_c_binding type(atlas_ShiftedLatGrid) :: this integer(c_int), intent(in) :: nlon, nlat call this%reset_c_ptr( atlas__grid__regular__ShiftedLat(int(nlon,c_long),int(nlat,c_long)) ) call this%return() end function function atlas_grid_ShiftedLat__ctor_int64(nlon,nlat) result(this) use, intrinsic :: iso_c_binding, only: c_long use atlas_grid_Structured_c_binding type(atlas_ShiftedLatGrid) :: this integer(c_long), intent(in) :: nlon, nlat call this%reset_c_ptr( atlas__grid__regular__ShiftedLat( nlon, nlat ) ) call this%return() end function ! ----------------------------------------------------------------------------- ! Structured members function atlas_Grid__name(this) result(name) use atlas_grid_Grid_c_binding use fckit_c_interop_module, only : c_ptr_to_string, c_ptr_free use, intrinsic :: iso_c_binding, only : c_ptr class(atlas_Grid), intent(in) :: this character(len=:), allocatable :: name type(c_ptr) :: name_c_str integer :: size call atlas__grid__Grid__name(this%CPTR_PGIBUG_A, name_c_str, size ) name = c_ptr_to_string(name_c_str) call c_ptr_free(name_c_str) end function function atlas_Grid__size(this) result(npts) use, intrinsic :: iso_c_binding, only: c_int use atlas_grid_Grid_c_binding class(atlas_Grid), intent(in) :: this integer(ATLAS_KIND_IDX) :: npts npts = int(atlas__grid__Grid__size(this%CPTR_PGIBUG_A),ATLAS_KIND_IDX) end function function atlas_Grid__spec(this) result(spec) use atlas_grid_Grid_c_binding class(atlas_Grid), intent(in) :: this type(atlas_Config) :: spec spec = atlas_Config( atlas__grid__Grid__spec(this%CPTR_PGIBUG_A) ) call spec%return () end function function uid(this) use atlas_grid_Grid_c_binding use fckit_c_interop_module, only : c_ptr_to_string, c_ptr_free use, intrinsic :: iso_c_binding, only : c_ptr class(atlas_Grid), intent(in) :: this character(len=:), allocatable :: uid type(c_ptr) :: uid_c_str integer :: size call atlas__grid__Grid__uid(this%CPTR_PGIBUG_A, uid_c_str, size ) uid = c_ptr_to_string(uid_c_str) call c_ptr_free(uid_c_str) end function function lonlat_bounding_box (this) result (bb) use, intrinsic :: iso_c_binding, only : c_double use atlas_grid_Grid_c_binding class(atlas_Grid), intent(in) :: this type(atlas_LonLatRectangularDomain) :: bb bb = atlas_LonLatRectangularDomain(atlas__grid__Grid__lonlat_bounding_box (this%CPTR_PGIBUG_A)) call bb%return() end function function Gaussian__N(this) result(N) use, intrinsic :: iso_c_binding, only: c_long use atlas_grid_Structured_c_binding class(atlas_GaussianGrid), intent(in) :: this integer(c_long) :: N N = atlas__grid__Gaussian__N(this%CPTR_PGIBUG_A) end function function ReducedGaussian__N(this) result(N) use, intrinsic :: iso_c_binding, only: c_long use atlas_grid_Structured_c_binding class(atlas_ReducedGaussianGrid), intent(in) :: this integer(c_long) :: N N = atlas__grid__Gaussian__N(this%CPTR_PGIBUG_A) end function function RegularGaussian__N(this) result(N) use, intrinsic :: iso_c_binding, only: c_long use atlas_grid_Structured_c_binding class(atlas_RegularGaussianGrid), intent(in) :: this integer(c_long) :: N N = atlas__grid__Gaussian__N(this%CPTR_PGIBUG_A) end function function Structured__ny(this) result(ny) use, intrinsic :: iso_c_binding, only: c_long use atlas_grid_Structured_c_binding class(atlas_StructuredGrid), intent(in) :: this integer(ATLAS_KIND_IDX) :: ny ny = atlas__grid__Structured__ny(this%CPTR_PGIBUG_A) end function subroutine Structured__index2ij_int32(this, gidx, i, j) use, intrinsic :: iso_c_binding, only: c_int32_t use atlas_grid_Structured_c_binding integer(c_int32_t), intent (in) :: gidx class(atlas_StructuredGrid), intent(in) :: this integer(ATLAS_KIND_IDX), intent(out) :: i, j call atlas__grid__Structured__index2ij(this%CPTR_PGIBUG_A, c_gidx(gidx), i, j) i = i + 1 j = j + 1 end subroutine subroutine Structured__index2ij_int64(this, gidx, i, j) use, intrinsic :: iso_c_binding, only: c_long, c_int64_t use atlas_grid_Structured_c_binding integer(c_int64_t), intent (in) :: gidx class(atlas_StructuredGrid), intent(in) :: this integer(ATLAS_KIND_IDX), intent(out) :: i, j call atlas__grid__Structured__index2ij(this%CPTR_PGIBUG_A, c_gidx(gidx), i, j) i = i + 1 j = j + 1 end subroutine function Structured__ij_int32(this, gidx) result(ij) use, intrinsic :: iso_c_binding, only: c_int use atlas_grid_Structured_c_binding integer(c_int), intent (in) :: gidx class(atlas_StructuredGrid), intent(in) :: this integer(ATLAS_KIND_IDX) :: ij (2) call atlas__grid__Structured__index2ij(this%CPTR_PGIBUG_A, c_gidx(gidx), ij(1), ij(2)) ij = ij + 1 end function function Structured__ij_int64(this, gidx) result(ij) use, intrinsic :: iso_c_binding, only: c_int, c_long use atlas_grid_Structured_c_binding integer(c_long), intent (in) :: gidx class(atlas_StructuredGrid), intent(in) :: this integer(ATLAS_KIND_IDX) :: ij (2) call atlas__grid__Structured__index2ij(this%CPTR_PGIBUG_A, c_gidx(gidx), ij(1), ij(2)) ij = ij + 1 end function function Structured__index_int32(this, i, j) result(gidx) use, intrinsic :: iso_c_binding, only: c_int use atlas_grid_Structured_c_binding integer(ATLAS_KIND_IDX) :: gidx class(atlas_StructuredGrid), intent(in) :: this integer(c_int), intent(in) :: i, j gidx = int(1 + atlas__grid__Structured__index(this%CPTR_PGIBUG_A, c_idx(i), c_idx(j) ), ATLAS_KIND_IDX ) end function function Structured__index_int64(this, i, j) result(gidx) use, intrinsic :: iso_c_binding, only: c_long use atlas_grid_Structured_c_binding integer(ATLAS_KIND_IDX) :: gidx class(atlas_StructuredGrid), intent(in) :: this integer(c_long), intent(in) :: i, j gidx = int( 1 + atlas__grid__Structured__index(this%CPTR_PGIBUG_A, c_idx(i), c_idx(j) ), ATLAS_KIND_IDX ) end function function Structured__nx_int32(this, j) result(nx) use, intrinsic :: iso_c_binding, only: c_int use atlas_grid_Structured_c_binding integer(ATLAS_KIND_IDX) :: nx class(atlas_StructuredGrid), intent(in) :: this integer(c_int), intent(in) :: j nx = atlas__grid__Structured__nx(this%CPTR_PGIBUG_A, c_idx(j) ) end function function Structured__nx_int64(this, j) result(nx) use, intrinsic :: iso_c_binding, only: c_long use atlas_grid_Structured_c_binding integer(ATLAS_KIND_IDX) :: nx class(atlas_StructuredGrid), intent(in) :: this integer(c_long), intent(in) :: j nx = atlas__grid__Structured__nx(this%CPTR_PGIBUG_A, c_idx(j) ) end function function Structured__reduced(this) result(reduced) use atlas_grid_Structured_c_binding class(atlas_StructuredGrid), intent(in) :: this logical :: reduced if( atlas__grid__Structured__reduced(this%CPTR_PGIBUG_A) == 1 ) then reduced = .true. else reduced = .false. endif end function function Structured__nx_array(this) result(nx) use atlas_grid_Structured_c_binding use, intrinsic :: iso_c_binding , only : c_long, c_f_pointer class(atlas_StructuredGrid), intent(in) :: this integer(ATLAS_KIND_IDX), pointer :: nx(:) type (c_ptr) :: nx_c_ptr integer(ATLAS_KIND_IDX) :: nx_size call atlas__grid__Structured__nx_array(this%CPTR_PGIBUG_A, nx_c_ptr, nx_size) call c_f_pointer(nx_c_ptr , nx , (/nx_size/)) end function function Structured__nxmax(this) result(nxmax) use, intrinsic :: iso_c_binding, only: c_long use atlas_grid_Structured_c_binding class(atlas_StructuredGrid), intent(in) :: this integer(c_long) :: nxmax nxmax = atlas__grid__Structured__nxmax(this%CPTR_PGIBUG_A) end function function Structured__nxmin(this) result(nxmin) use, intrinsic :: iso_c_binding, only: c_long use atlas_grid_Structured_c_binding class(atlas_StructuredGrid), intent(in) :: this integer(c_long) :: nxmin nxmin = atlas__grid__Structured__nxmin(this%CPTR_PGIBUG_A) end function function Structured__y_32(this, j) result(y) use, intrinsic :: iso_c_binding, only: c_double, c_int use atlas_grid_Structured_c_binding real(c_double) :: y class(atlas_StructuredGrid), intent(in) :: this integer(c_int), intent(in) :: j y = atlas__grid__Structured__y(this%CPTR_PGIBUG_A, c_idx(j) ) end function function Structured__y_64(this, j) result(y) use, intrinsic :: iso_c_binding, only: c_double, c_long use atlas_grid_Structured_c_binding real(c_double) :: y class(atlas_StructuredGrid), intent(in) :: this integer(c_long), intent(in) :: j y = atlas__grid__Structured__y(this%CPTR_PGIBUG_A, c_idx(j) ) end function function Structured__y_array(this) result(y) use atlas_grid_Structured_c_binding use, intrinsic :: iso_c_binding , only : c_double, c_f_pointer class(atlas_StructuredGrid), intent(in) :: this real (c_double) , pointer :: y(:) type (c_ptr) :: y_c_ptr integer(ATLAS_KIND_IDX) :: y_size call atlas__grid__Structured__y_array(this%CPTR_PGIBUG_A, & & y_c_ptr, y_size) call c_f_pointer (y_c_ptr, y, (/y_size/)) end function function Structured__x_32(this, i,j) result(x) use, intrinsic :: iso_c_binding, only: c_double, c_int use atlas_grid_Structured_c_binding class(atlas_StructuredGrid), intent(in) :: this real(c_double) :: x integer(c_int) :: i,j x = atlas__grid__Structured__x(this%CPTR_PGIBUG_A, c_idx(i), c_idx(j)) end function function Structured__x_64(this, i,j) result(x) use, intrinsic :: iso_c_binding, only: c_double, c_long use atlas_grid_Structured_c_binding class(atlas_StructuredGrid), intent(in) :: this real(c_double) :: x integer(c_long) :: i,j x = atlas__grid__Structured__x(this%CPTR_PGIBUG_A, c_idx(i), c_idx(j)) end function function Structured__xy_32(this, i,j) result(xy) use, intrinsic :: iso_c_binding, only: c_double, c_int use atlas_grid_Structured_c_binding real(c_double) :: xy(2) class(atlas_StructuredGrid), intent(in) :: this integer(c_int) , intent(in) :: i,j call atlas__grid__Structured__xy(this%CPTR_PGIBUG_A, c_idx(i), c_idx(j), xy) end function function Structured__xy_64(this, i,j) result(xy) use, intrinsic :: iso_c_binding, only: c_double, c_long use atlas_grid_Structured_c_binding real(c_double) :: xy(2) class(atlas_StructuredGrid), intent(in) :: this integer(c_long) , intent(in) :: i,j call atlas__grid__Structured__xy(this%CPTR_PGIBUG_A, c_idx(i), c_idx(j), xy) end function function Structured__lonlat_32(this, i,j) result(lonlat) use, intrinsic :: iso_c_binding, only: c_double, c_int use atlas_grid_Structured_c_binding real(c_double) :: lonlat(2) class(atlas_StructuredGrid), intent(in) :: this integer(c_int) , intent(in) :: i,j call atlas__grid__Structured__lonlat(this%CPTR_PGIBUG_A, c_idx(i), c_idx(j), lonlat) end function function Structured__lonlat_64(this, i,j) result(lonlat) use, intrinsic :: iso_c_binding, only: c_double, c_long use atlas_grid_Structured_c_binding real(c_double) :: lonlat(2) class(atlas_StructuredGrid), intent(in) :: this integer(c_long) , intent(in) :: i,j call atlas__grid__Structured__lonlat(this%CPTR_PGIBUG_A, c_idx(i), c_idx(j), lonlat) end function ! ---------------------------------------------------------------------------------------- function atlas_RegionalGrid_ctor_int32( nx, ny, xy_min, xy_max, projection, y_numbering ) result(this) use, intrinsic :: iso_c_binding, only : c_int, c_double type(atlas_StructuredGrid) :: this integer(c_int), intent(in) :: nx, ny real(c_double), intent(in) :: xy_min(2), xy_max(2) type(atlas_Projection), intent(in), optional :: projection integer(c_int), intent(in), optional :: y_numbering type(atlas_Config) :: config config = atlas_Config() call config%set("type","regional") call config%set("nx",nx) call config%set("ny",ny) call config%set("xmin",xy_min(1)) call config%set("ymin",xy_min(2)) call config%set("xmax",xy_max(1)) call config%set("ymax",xy_max(2)) if( present(projection) ) then call config%set("projection",projection%spec()) endif if( present(y_numbering) ) then call config%set("y_numbering",y_numbering) endif this = atlas_StructuredGrid(config) call config%final() call this%return() end function function atlas_RegionalGrid_ctor_int64( nx, ny, xy_min, xy_max, projection, y_numbering ) result(this) use, intrinsic :: iso_c_binding, only : c_int, c_long, c_double type(atlas_StructuredGrid) :: this integer(c_long), intent(in) :: nx, ny real(c_double), intent(in) :: xy_min(2), xy_max(2) type(atlas_Projection), intent(in), optional :: projection integer(c_int), intent(in), optional :: y_numbering type(atlas_Config) :: config config = atlas_Config() call config%set("type","regional") call config%set("nx",nx) call config%set("ny",ny) call config%set("xmin",xy_min(1)) call config%set("ymin",xy_min(2)) call config%set("xmax",xy_max(1)) call config%set("ymax",xy_max(2)) if( present(projection) ) then call config%set("projection",projection%spec()) endif if( present(y_numbering) ) then call config%set("y_numbering",y_numbering) endif this = atlas_StructuredGrid(config) call config%final() call this%return() end function function atlas_RegionalGrid_ctor_nwse_int32( nx, ny, north, west, south, east, projection, y_numbering ) result(this) use, intrinsic :: iso_c_binding, only : c_int, c_double type(atlas_StructuredGrid) :: this integer(c_int), intent(in) :: nx, ny real(c_double), intent(in) :: north, west, south, east type(atlas_Projection), intent(in), optional :: projection integer(c_int), intent(in), optional :: y_numbering type(atlas_Config) :: config config = atlas_Config() call config%set("type","regional") call config%set("nx",nx) call config%set("ny",ny) call config%set("north",north) call config%set("west",west) call config%set("south",south) call config%set("east",east) if( present(projection) ) then call config%set("projection",projection%spec()) endif if( present(y_numbering) ) then call config%set("y_numbering",y_numbering) endif this = atlas_StructuredGrid(config) call config%final() call this%return() end function function atlas_RegionalGrid_ctor_nwse_int64( nx, ny, north, west, south, east, projection, y_numbering ) result(this) use, intrinsic :: iso_c_binding, only : c_int, c_long, c_double type(atlas_Grid) :: this integer(c_long), intent(in) :: nx, ny real(c_double), intent(in) :: north, west, south, east type(atlas_Projection), intent(in), optional :: projection integer(c_int), intent(in), optional :: y_numbering type(atlas_Config) :: config config = atlas_Config() call config%set("type","regional") call config%set("nx",nx) call config%set("ny",ny) call config%set("north",north) call config%set("west",west) call config%set("south",south) call config%set("east",east) if( present(projection) ) then call config%set("projection",projection%spec()) endif if( present(y_numbering) ) then call config%set("y_numbering",y_numbering) endif this = atlas_StructuredGrid(config) call config%final() call this%return() end function function atlas_RegionalGrid_ctor_increments_int32( nx, ny, dx, dy, xy_min, projection, y_numbering ) result(this) use iso_c_binding, only : c_int, c_double type(atlas_StructuredGrid) :: this integer(c_int), intent(in) :: nx integer(c_int), intent(in) :: ny real(c_double), intent(in) :: xy_min(2) real(c_double), intent(in) :: dx real(c_double), intent(in) :: dy type(atlas_Projection), intent(in), optional :: projection integer(c_int), intent(in), optional :: y_numbering type(atlas_Config) :: config config = atlas_Config() call config%set("type","regional") call config%set("nx",nx) call config%set("ny",ny) call config%set("xmin",xy_min(1)) call config%set("ymin",xy_min(2)) call config%set("dx",dx) call config%set("dy",dy) if( present(projection) ) then call config%set("projection",projection%spec()) endif if( present(y_numbering) ) then call config%set("y_numbering",y_numbering) endif this = atlas_StructuredGrid(config) call config%final() call this%return() end function function atlas_RegionalGrid_ctor_increments_int64( nx, ny, dx, dy, xy_min, projection, y_numbering ) result(this) use iso_c_binding, only : c_int, c_long, c_double type(atlas_StructuredGrid) :: this integer(c_long), intent(in) :: nx integer(c_long), intent(in) :: ny real(c_double), intent(in) :: xy_min(2) real(c_double), intent(in) :: dx real(c_double), intent(in) :: dy type(atlas_Projection), intent(in), optional :: projection integer(c_int), intent(in), optional :: y_numbering type(atlas_Config) :: config config = atlas_Config() call config%set("type","regional") call config%set("nx",nx) call config%set("ny",ny) call config%set("xmin",xy_min(1)) call config%set("ymin",xy_min(2)) call config%set("dx",dx) call config%set("dy",dy) if( present(projection) ) then call config%set("projection",projection%spec()) endif if( present(y_numbering) ) then call config%set("y_numbering",y_numbering) endif this = atlas_StructuredGrid(config) call config%final() call this%return() end function ! ---------------------------------------------------------------------------------------- end module atlas_Grid_module atlas-0.46.0/src/atlas_f/internals/0000775000175000017500000000000015160212070017262 5ustar alastairalastairatlas-0.46.0/src/atlas_f/internals/atlas_read_file.cc0000664000175000017500000000337715160212070022701 0ustar alastairalastair/* * (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/filesystem/PathName.h" #include "eckit/runtime/Main.h" #include "atlas/runtime/Exception.h" #include "atlas_f/internals/atlas_read_file.h" namespace atlas { void read_file(const eckit::PathName& p, std::ostream& out) { if (p.exists()) { std::ifstream in; in.open(p.asString().c_str()); if (!in) { throw_CantOpenFile(p.asString(), Here()); } else { out << in.rdbuf(); in.close(); } } } } // namespace atlas extern "C" { //----------------------------------------------------------------------------- int atlas__read_file(const char* path, char*& content, int& size) { // eckit::FileReadPolicy p = // eckit::Main::instance().behavior().fileReadPolicy(); // std::stringstream ss; // if( read( p, path, ss ) ) // { // std::string s = ss.str(); // size = s.size()+1; // content = new char[size]; // strcpy(content,s.c_str()); // return true; // } std::stringstream ss; atlas::read_file(path, ss); std::string s = ss.str(); size = static_cast(s.size()); content = new char[size + 1]; std::strncpy(content, s.c_str(), size + 1); return true; } //----------------------------------------------------------------------------- } atlas-0.46.0/src/atlas_f/internals/Library.h0000664000175000017500000000141215160212070021035 0ustar alastairalastair/* * (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 // C wrapper interfaces to C++ routines extern "C" { void atlas__atlas_init_noargs(); void atlas__atlas_finalize(); const char* atlas__eckit_version(); const char* atlas__eckit_git_sha1(); const char* atlas__eckit_git_sha1_abbrev(int length); const char* atlas__atlas_version(); const char* atlas__atlas_git_sha1(); const char* atlas__atlas_git_sha1_abbrev(int length); } atlas-0.46.0/src/atlas_f/internals/atlas_read_file.h0000664000175000017500000000077115160212070022536 0ustar alastairalastair/* * (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. */ #define Char char extern "C" { int atlas__read_file(const char* path, Char*& content, int& size); } #undef Char atlas-0.46.0/src/atlas_f/internals/atlas_generics.fypp0000664000175000017500000000500715160212070023147 0ustar alastairalastair! (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. #:mute #:set ranks = [1,2,3,4] #:set dim = ['',':',':,:',':,:,:',':,:,:,:',':,:,:,:,:'] #:set dimr = ['','',':',':,:',':,:,:',':,:,:,:'] #:set ftypes = ['integer(c_int)','integer(c_long)','real(c_float)','real(c_double)', 'logical(4)'] #:set ctypes = ['int','long','float','double', 'int'] #:set dtypes = ['int32', 'int64', 'real32', 'real64', 'logical32'] #:set types = list(zip(dtypes,ftypes,ctypes)) #:set integer_ftypes = ['integer(c_int)','integer(c_long)'] #:set integer_ctypes = ['int','long'] #:set integer_dtypes = ['int32', 'int64'] #:set integer_types = list(zip(integer_dtypes,integer_ftypes,integer_ctypes)) #:set real_ftypes = ['real(c_float)','real(c_double)'] #:set real_ctypes = ['float','double'] #:set real_dtypes = ['real32', 'real64'] #:set real_types = list(zip(real_dtypes,real_ftypes,real_ctypes)) #:def generic_public_interface( interface, prefix = None ) #:if prefix is None #:set prefix = interface #:endif #:set counter = 0 #:for rank in ranks #:for dtype in dtypes[:4] procedure, private :: ${prefix}$_${dtype}$_r${rank}$ #:set counter = counter + 1 #:endfor #:endfor #:set last = counter #:set counter = 1 generic, public :: ${interface}$ => & #:for rank in ranks #:for dtype in dtypes[:4] & ${prefix}$_${dtype}$_r${rank}$#{if counter < last}#, & #{endif}# #:set counter = counter + 1 #:endfor #:endfor #:enddef #:def generic_public_interface_2( interface, prefix1, prefix2 ) #:set counter = 0 #:for rank in ranks #:for dtype in dtypes[:4] procedure, private :: ${prefix1}$_${dtype}$_r${rank}$ #:set counter = counter + 1 #:endfor #:endfor #:for rank in ranks #:for dtype in dtypes[:4] procedure, private :: ${prefix2}$_${dtype}$_r${rank}$ #:set counter = counter + 1 #:endfor #:endfor #:set last = counter #:set counter = 1 generic, public :: ${interface}$ => & #:for rank in ranks #:for dtype in dtypes[:4] & ${prefix1}$_${dtype}$_r${rank}$#{if counter < last}#, & #{endif}# #:set counter = counter + 1 #:endfor #:endfor #:for rank in ranks #:for dtype in dtypes[:4] & ${prefix2}$_${dtype}$_r${rank}$#{if counter < last}#, & #{endif}# #:set counter = counter + 1 #:endfor #:endfor #:enddef #:endmute atlas-0.46.0/src/atlas_f/internals/Library.cc0000664000175000017500000000311215160212070021172 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "eckit/eckit_version.h" #include "atlas/library/Library.h" #include "atlas_f/internals/Library.h" //---------------------------------------------------------------------------------------------------------------------- extern "C" { void atlas__atlas_init_noargs() { atlas::initialize(); } void atlas__atlas_finalize() { atlas::finalize(); } const char* atlas__eckit_version() { return eckit_version(); } const char* atlas__eckit_git_sha1() { return eckit_git_sha1(); } const char* atlas__eckit_git_sha1_abbrev(int length) { static std::string git_sha1(eckit_git_sha1()); if (git_sha1.empty()) { git_sha1 = "not available"; } else { git_sha1 = git_sha1.substr(0, std::min(length, 40)); } return git_sha1.c_str(); } const char* atlas__atlas_version() { static std::string str = atlas::Library::instance().version(); return str.c_str(); } const char* atlas__atlas_git_sha1() { static std::string str = atlas::Library::instance().gitsha1(); return str.c_str(); } const char* atlas__atlas_git_sha1_abbrev(int length) { static std::string s = atlas::Library::instance().gitsha1(length); return s.c_str(); } } // extern "C" atlas-0.46.0/src/atlas_f/field/0000775000175000017500000000000015160212070016346 5ustar alastairalastairatlas-0.46.0/src/atlas_f/field/atlas_MultiField_module.F900000664000175000017500000001243215160212070023417 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. #include "atlas/atlas_f.h" module atlas_multifield_module use fckit_owned_object_module, only : fckit_owned_object use atlas_Config_module, only: atlas_Config use atlas_field_module, only: atlas_field, array_c_to_f use atlas_fieldset_module, only: atlas_fieldset implicit none public :: atlas_MultiField private !------------------------------------------------------------------------------ TYPE, extends(fckit_owned_object) :: atlas_MultiField ! Purpose : ! ------- ! *MultiField* : Object containing field data and Metadata ! Methods : ! ------- ! name : The name or tag this field was created with ! data : Return the field as a fortran array of specified shape ! Metadata : Return object that can contain a variety of Metadata ! Author : ! ------ ! 20-Nov-2013 Willem Deconinck *ECMWF* ! 29-Aug-2023 Slavko Brdar *ECMWF* !------------------------------------------------------------------------------ contains procedure, public :: MultiField__fieldset procedure, public :: MultiField__size generic :: fieldset => MultiField__fieldset generic :: size => MultiField__size #if FCKIT_FINAL_NOT_INHERITING final :: atlas_MultiField__final_auto #endif END TYPE interface atlas_MultiField module procedure atlas_MultiField__cptr module procedure atlas_MultiField__create module procedure atlas_MultiField__create_names end interface private :: fckit_owned_object private :: atlas_Config !======================================================== contains !======================================================== !------------------------------------------------------------------------------- function atlas_MultiField__cptr(cptr) result(field) use, intrinsic :: iso_c_binding, only : c_ptr type(atlas_MultiField) :: field type(c_ptr), intent(in) :: cptr call field%reset_c_ptr( cptr ) call field%return() end function !------------------------------------------------------------------------------- function atlas_MultiField__create(params) result(field) use atlas_multifield_c_binding type(atlas_MultiField) :: field class(atlas_Config), intent(in) :: params field = atlas_MultiField__cptr( atlas__MultiField__create(params%CPTR_PGIBUG_B) ) call field%return() end function !------------------------------------------------------------------------------- function atlas_MultiField__create_names(kind, shape, field_names) result(field) use, intrinsic :: iso_c_binding, only : c_char, c_int, c_int32_t, c_size_t use atlas_multifield_c_binding type(atlas_MultiField) :: field integer(c_int), intent(in) :: kind integer, intent(in) :: shape(:) character(*), intent(in) :: field_names(:) character(kind=c_char,len=:), allocatable :: flat_field_names integer(c_size_t) :: length integer(c_int32_t) :: ii integer(c_int32_t), allocatable :: field_name_sizes(:) if (size(field_names) == 0 .or. size(shape) < 3) then print *, "atlas_MultiField__create_names: must have at least one field name, and the size of shape", & & " is minimum 3, e.g. [nproma,-1,nblk]" stop -1 end if length = len(field_names(1)) allocate(field_name_sizes(size(field_names))) field_name_sizes = len(field_names(:)) if (any(field_name_sizes /= length)) then print *, "atlas_MultiField__create_names: field_names have to have same length in characters" stop -1 end if allocate(character(len=length*size(field_names) ) :: flat_field_names) do ii = 1, size(field_names) flat_field_names((ii-1)*length+1:ii*length) = field_names(ii) enddo field = atlas_MultiField__cptr( atlas__MultiField__create_shape(kind, size(shape), shape, & & flat_field_names, length, size(field_names,kind=c_size_t)) ) call field%return() end function !------------------------------------------------------------------------------- function MultiField__size(this) result(size) use atlas_multifield_c_binding class(atlas_MultiField), intent(in) :: this integer :: size size = atlas__MultiField__size(this%CPTR_PGIBUG_B) end function !------------------------------------------------------------------------------- function MultiField__fieldset(this) result(fset) use, intrinsic :: iso_c_binding, only : c_ptr use atlas_multifield_c_binding class(atlas_MultiField), intent(in) :: this type(c_ptr) :: fset_cptr type(atlas_FieldSet) :: fset fset_cptr = atlas__MultiField__fieldset(this%CPTR_PGIBUG_B) fset = atlas_FieldSet( fset_cptr ) call fset%return() end function !------------------------------------------------------------------------------- #if FCKIT_FINAL_NOT_INHERITING ATLAS_FINAL subroutine atlas_MultiField__final_auto(this) type(atlas_MultiField), intent(inout) :: this #if FCKIT_FINAL_DEBUGGING write(0,*) "atlas_MultiField__final_auto" #endif #if FCKIT_FINAL_NOT_PROPAGATING call this%final() #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine #endif !------------------------------------------------------------------------------- end module atlas_multifield_module atlas-0.46.0/src/atlas_f/field/atlas_Field_module.fypp0000664000175000017500000010142115160212070023021 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. #include "atlas/atlas_f.h" #:include "atlas/atlas_f.fypp" #:include "internals/atlas_generics.fypp" module atlas_field_module use fckit_owned_object_module, only : fckit_owned_object use atlas_Config_module, only: atlas_Config implicit none public :: atlas_Field public :: atlas_real public :: atlas_integer public :: atlas_logical public :: atlas_data_type public :: array_c_to_f private !------------------------------------------------------------------------------ TYPE, extends(fckit_owned_object) :: atlas_Field ! Purpose : ! ------- ! *Field* : Object containing field data and Metadata ! Methods : ! ------- ! name : The name or tag this field was created with ! data : Return the field as a fortran array of specified shape ! Metadata : Return object that can contain a variety of Metadata ! Author : ! ------ ! 20-Nov-2013 Willem Deconinck *ECMWF* !------------------------------------------------------------------------------ contains procedure :: name => Field__name procedure :: functionspace => Field__functionspace procedure :: datatype => Field__datatype procedure :: metadata => Field__metadata procedure, private :: shape_array => Field__shape_array procedure, private :: shape_idx => Field__shape_idx procedure :: size => Field__size procedure :: rank => Field__rank procedure :: bytes => Field__bytes procedure :: levels => Field__levels procedure :: kind => Field__kind generic :: shape => shape_array, shape_idx procedure :: contiguous procedure, public :: strides_array => Field__strides_array procedure, public :: strides_idx => Field__strides_idx generic :: strides => strides_array, strides_idx generic :: stride => strides_idx procedure :: halo_exchange procedure :: dirty procedure :: set_dirty procedure :: rename => rename_ procedure :: set_levels procedure :: set_functionspace #:for rank in ranks #:for dtype in dtypes procedure, private :: access_data_${dtype}$_r${rank}$ procedure, private :: access_device_data_${dtype}$_r${rank}$ procedure, private :: access_data_${dtype}$_r${rank}$_shape procedure, private :: access_data_${dtype}$_r${rank}$_slice #:endfor #:endfor generic, public :: data => & #:for rank in ranks #:for dtype in dtypes & access_data_${dtype}$_r${rank}$, & & access_data_${dtype}$_r${rank}$_shape, & & access_data_${dtype}$_r${rank}$_slice, & #:endfor #:endfor & dummy generic, public :: device_data => & #:for rank in ranks #:for dtype in dtypes & access_device_data_${dtype}$_r${rank}$, & #:endfor #:endfor & dummy procedure, public :: device_allocated procedure, public :: allocate_device procedure, public :: deallocate_device procedure, public :: set_host_needs_update procedure, public :: set_device_needs_update procedure, public :: host_needs_update procedure, public :: device_needs_update procedure, public :: update_device procedure, public :: update_host procedure, public :: sync_host_device procedure, public :: sync_host procedure, public :: sync_device procedure, private :: dummy #if FCKIT_FINAL_NOT_INHERITING final :: atlas_Field__final_auto #endif END TYPE interface atlas_Field module procedure atlas_Field__cptr module procedure atlas_Field__create #:for dtype in integer_dtypes module procedure atlas_Field__create_name_kind_shape_${dtype}$ module procedure atlas_Field__create_kind_shape_${dtype}$ #:endfor #:for rank in ranks #:for dtype in dtypes module procedure atlas_Field__wrap_${dtype}$_r${rank}$ module procedure atlas_Field__wrap_name_${dtype}$_r${rank}$ #:endfor #:endfor end interface ! ---------------------------------------------------- ! ENUM DataType integer, private, parameter :: ATLAS_KIND_INT32 = -4 integer, private, parameter :: ATLAS_KIND_INT64 = -8 integer, private, parameter :: ATLAS_KIND_REAL32 = 4 integer, private, parameter :: ATLAS_KIND_REAL64 = 8 ! ---------------------------------------------------- interface array_c_to_f #:for rank in ranks #:for dtype in dtypes module procedure array_c_to_f_${dtype}$_r${rank}$ #:endfor #:endfor end interface !------------------------------------------------------------------------------- private :: fckit_owned_object private :: atlas_Config !======================================================== contains !======================================================== #:for rank in ranks #:for dtype,ftype,ctype in types !------------------------------------------------------------------------------- subroutine array_c_to_f_${dtype}$_r${rank}$(array_cptr,rank,shape_cptr,strides_cptr,array_fptr) use, intrinsic :: iso_c_binding, only : c_ptr, c_int, c_double, c_long, c_float, c_f_pointer @:ENABLE_ATLAS_MACROS() type(c_ptr), intent(in) :: array_cptr integer(c_int), intent(in) :: rank type(c_ptr), intent(in) :: shape_cptr type(c_ptr), intent(in) :: strides_cptr ${ftype}$, pointer, intent(inout) :: array_fptr(${dim[rank]}$) ${ftype}$, pointer :: tmp(${dim[rank]}$) integer, pointer :: shape(:) integer, pointer :: strides(:) integer :: eshape(${rank}$) integer :: j if( rank /= ${rank}$ ) then write(0,*) rank, "!=", ${rank}$ @:ATLAS_ABORT("Rank mismatch") endif call c_f_pointer ( shape_cptr, shape , [rank] ) call c_f_pointer ( strides_cptr, strides , [rank] ) do j=1,rank-1 if( strides(j) /= 0 ) then eshape(j) = strides(j+1)/strides(j) else eshape(j) = shape(j) endif enddo eshape(rank) = shape(rank) call c_f_pointer ( array_cptr , tmp , shape=eshape ) if (associated(tmp)) then #{if rank == 1}# array_fptr => tmp(1:shape(1)) #{endif}# #{if rank == 2}# array_fptr => tmp(1:shape(1),1:shape(2)) #{endif}# #{if rank == 3}# array_fptr => tmp(1:shape(1),1:shape(2),1:shape(3)) #{endif}# #{if rank == 4}# array_fptr => tmp(1:shape(1),1:shape(2),1:shape(3),1:shape(4)) #{endif}# else #{if rank == 1}# allocate(array_fptr(0)) #{endif}# #{if rank == 2}# allocate(array_fptr(0,0)) #{endif}# #{if rank == 3}# allocate(array_fptr(0,0,0)) #{endif}# #{if rank == 4}# allocate(array_fptr(0,0,0,0)) #{endif}# endif end subroutine !------------------------------------------------------------------------------- #:endfor #:endfor #:for rank in ranks #:for dtype,ftype,ctype in types subroutine access_data_${dtype}$_r${rank}$(this, field) use atlas_field_c_binding use, intrinsic :: iso_c_binding, only : c_ptr, c_int, c_long, c_float, c_double class(atlas_Field), intent(in) :: this ${ftype}$, pointer, intent(inout) :: field(${dim[rank]}$) type(c_ptr) :: field_cptr type(c_ptr) :: shape_cptr type(c_ptr) :: strides_cptr integer(c_int) :: rank call atlas__Field__data_${ctype}$_specf(this%CPTR_PGIBUG_A, field_cptr, rank, shape_cptr, strides_cptr) call array_c_to_f(field_cptr,rank,shape_cptr,strides_cptr, field) end subroutine subroutine access_device_data_${dtype}$_r${rank}$(this, field) use atlas_field_c_binding use, intrinsic :: iso_c_binding, only : c_ptr, c_int, c_long, c_float, c_double class(atlas_Field), intent(in) :: this ${ftype}$, pointer, intent(inout) :: field(${dim[rank]}$) type(c_ptr) :: field_cptr type(c_ptr) :: shape_cptr type(c_ptr) :: strides_cptr integer(c_int) :: rank call atlas__Field__device_data_${ctype}$_specf(this%CPTR_PGIBUG_A, field_cptr, rank, shape_cptr, strides_cptr) call array_c_to_f(field_cptr,rank,shape_cptr,strides_cptr, field) end subroutine subroutine access_data_${dtype}$_r${rank}$_slice(this, slice, iblk) use atlas_field_c_binding use, intrinsic :: iso_c_binding, only : c_ptr, c_int, c_long, c_float, c_double class(atlas_Field), intent(in) :: this ${ftype}$, pointer :: field(${dim[rank]}$) #:if rank > 1 ${ftype}$, pointer, intent(inout) :: slice(${dimr[rank]}$) #:else ${ftype}$, pointer, intent(inout) :: slice #:endif integer, intent(in) :: iblk type(c_ptr) :: field_cptr type(c_ptr) :: shape_cptr type(c_ptr) :: strides_cptr integer(c_int) :: rank call atlas__Field__data_${ctype}$_specf(this%CPTR_PGIBUG_A, field_cptr, rank, shape_cptr, strides_cptr) call array_c_to_f(field_cptr, rank, shape_cptr, strides_cptr, field) #:if rank > 1 slice => field(${dimr[rank]}$, iblk) #:else slice => field(iblk) #:endif end subroutine !------------------------------------------------------------------------------- #:endfor #:endfor #:for rank in ranks #:for dtype,ftype,ctype in types subroutine access_data_${dtype}$_r${rank}$_shape(this, field, shape) use atlas_field_c_binding use, intrinsic :: iso_c_binding, only : c_ptr, c_int, c_long, c_float, c_double, c_f_pointer class(atlas_Field), intent(in) :: this ${ftype}$, pointer, intent(inout) :: field(${dim[rank]}$) integer(c_int), intent(in) :: shape(:) type(c_ptr) :: field_cptr type(c_ptr) :: shape_cptr type(c_ptr) :: strides_cptr integer(c_int) :: rank call atlas__Field__data_${ctype}$_specf(this%CPTR_PGIBUG_A, field_cptr, rank, shape_cptr, strides_cptr) call c_f_pointer( field_cptr, field, shape ) end subroutine !------------------------------------------------------------------------------- #:endfor #:endfor subroutine dummy(this) use atlas_field_c_binding class(atlas_Field), intent(in) :: this FCKIT_SUPPRESS_UNUSED(this) end subroutine !------------------------------------------------------------------------------- integer function atlas_real(kind) use, intrinsic :: iso_c_binding, only : c_double, c_float @:ENABLE_ATLAS_MACROS() integer :: kind if (kind == c_double) then atlas_real = ATLAS_KIND_REAL64 else if (kind == c_float) then atlas_real = ATLAS_KIND_REAL32 else @:ATLAS_ABORT("Unsupported real kind") end if end function !------------------------------------------------------------------------------- integer function atlas_integer(kind) use, intrinsic :: iso_c_binding, only : c_int, c_long @:ENABLE_ATLAS_MACROS() integer, optional :: kind atlas_integer = ATLAS_KIND_INT32 if ( present(kind) ) then if (kind == c_int) then atlas_integer = ATLAS_KIND_INT32 else if (kind == c_long) then atlas_integer = ATLAS_KIND_INT64 else @:ATLAS_ABORT("Unsupported integer kind") end if end if end function !------------------------------------------------------------------------------- integer function atlas_logical(kind) integer, optional :: kind atlas_logical = ATLAS_KIND_INT32 FCKIT_SUPPRESS_UNUSED(kind) end function !------------------------------------------------------------------------------- function atlas_data_type(kind) @:ENABLE_ATLAS_MACROS() character(len=6) :: atlas_data_type integer, intent(in) :: kind if( kind == ATLAS_KIND_INT32 ) then atlas_data_type = "int32" else if( kind == ATLAS_KIND_INT64 ) then atlas_data_type = "int64" else if( kind == ATLAS_KIND_REAL32 ) then atlas_data_type = "real32" else if( kind == ATLAS_KIND_REAL64 ) then atlas_data_type = "real64" else @:ATLAS_ABORT("cannot convert kind to data_type") endif end function !------------------------------------------------------------------------------- function atlas_Field__cptr(cptr) result(field) use, intrinsic :: iso_c_binding, only : c_ptr type(atlas_Field) :: field type(c_ptr), intent(in) :: cptr call field%reset_c_ptr( cptr ) call field%return() end function !------------------------------------------------------------------------------- function atlas_Field__create(params) result(field) use atlas_field_c_binding type(atlas_Field) :: field class(atlas_Config), intent(in) :: params field = atlas_Field__cptr( atlas__Field__create(params%CPTR_PGIBUG_B) ) call field%return() end function !------------------------------------------------------------------------------- #:for dtype, ftype, ctype in integer_types function atlas_Field__create_name_kind_shape_${dtype}$(name,kind,shape,alignment) result(field) use atlas_field_c_binding use, intrinsic :: iso_c_binding, only : c_int, c_long type(atlas_Field) :: field character(len=*), intent(in) :: name integer(c_int), intent(in) :: kind ${ftype}$, intent(in) :: shape(:) integer(c_int), intent(in), optional :: alignment type(atlas_Config) :: params params = atlas_Config() call params%set("creator","ArraySpec") call params%set("shape",shape) call params%set("fortran",.True.) call params%set("kind",kind) call params%set("name",name) if( present(alignment) ) call params%set("alignment",alignment) field = atlas_Field__cptr( atlas__Field__create(params%CPTR_PGIBUG_B) ) call params%final() call field%return() end function !------------------------------------------------------------------------------- function atlas_Field__create_kind_shape_${dtype}$(kind,shape,alignment) result(field) use atlas_field_c_binding use, intrinsic :: iso_c_binding, only : c_int, c_long type(atlas_Field) :: field integer(c_int), intent(in) :: kind ${ftype}$, intent(in) :: shape(:) integer(c_int), intent(in), optional :: alignment type(atlas_Config) :: params params = atlas_Config() call params%set("creator","ArraySpec") call params%set("shape",shape) call params%set("fortran",.True.) call params%set("kind",kind) if( present(alignment) ) call params%set("alignment",alignment) field = atlas_Field__cptr( atlas__Field__create(params%CPTR_PGIBUG_B) ) call params%final() call field%return() end function #:endfor !------------------------------------------------------------------------------- #:for rank in ranks #:for dtype, ftype, ctype in types function atlas_Field__wrap_name_${dtype}$_r${rank}$(name,data) result(field) use atlas_field_c_binding use fckit_array_module, only : array_strides, array_view1d use, intrinsic :: iso_c_binding, only : c_int, c_long, c_float, c_double, c_loc, c_int32_t, c_sizeof use fckit_c_interop_module, only : c_str, c_ptr_to_loc type(atlas_Field) :: field character(len=*), intent(in) :: name ${ftype}$, intent(in), target :: data(${dim[rank]}$) ${ftype}$, pointer :: data_ptr(${dim[rank]}$) integer(c_int) :: shapef(${rank}$) integer(c_int) :: stridesf(${rank}$) integer(c_int32_t) :: value_size ! bytes of one data value #:if ftype != "logical(4)" ${ftype}$ :: val ! dummy to compute value_size ${ftype}$, pointer :: data1d(:) ! For some reason, with nvhpc/22.11 we need create a pointer to pass to array_view1d ! This is not reproducible all the time. ! Test atlas_fctest_functionspace started failing when fckit was compiled with -Mnotarget_temps (nvidia/22.11) data_ptr => data data1d => array_view1d(data_ptr) #:else ! Case for logical only integer(c_int) :: val ! dummy to compute value_size integer(c_int), pointer :: data1d(:) data_ptr => data data1d => array_view1d( data_ptr, int(0,c_int) ) #:endif value_size = int(c_sizeof(val),c_int32_t) shapef = shape(data) if( size(data)>0 ) then !!! See issue https://github.com/ecmwf/atlas/pull/213 : problem with array_strides(data) with NAG compiler. #:if rank == 4 stridesf = 1 if (ubound(data,1) > 1) stridesf(1) = & int(c_ptr_to_loc(c_loc(data(2,1,1,1)))-c_ptr_to_loc(c_loc(data(1,1,1,1))),c_int32_t)/value_size if (ubound(data,2) > 1) stridesf(2) = & int(c_ptr_to_loc(c_loc(data(1,2,1,1)))-c_ptr_to_loc(c_loc(data(1,1,1,1))),c_int32_t)/value_size if (ubound(data,3) > 1) stridesf(3) = & int(c_ptr_to_loc(c_loc(data(1,1,2,1)))-c_ptr_to_loc(c_loc(data(1,1,1,1))),c_int32_t)/value_size if (ubound(data,4) > 1) stridesf(4) = & int(c_ptr_to_loc(c_loc(data(1,1,1,2)))-c_ptr_to_loc(c_loc(data(1,1,1,1))),c_int32_t)/value_size #:elif rank == 3 stridesf = 1 if (ubound(data,1) > 1) stridesf(1) = & int(c_ptr_to_loc(c_loc(data(2,1,1)))-c_ptr_to_loc(c_loc(data(1,1,1))),c_int32_t)/value_size if (ubound(data,2) > 1) stridesf(2) = & int(c_ptr_to_loc(c_loc(data(1,2,1)))-c_ptr_to_loc(c_loc(data(1,1,1))),c_int32_t)/value_size if (ubound(data,3) > 1) stridesf(3) = & int(c_ptr_to_loc(c_loc(data(1,1,2)))-c_ptr_to_loc(c_loc(data(1,1,1))),c_int32_t)/value_size #:elif rank == 2 stridesf = 1 if (ubound(data,1) > 1) stridesf(1) = & int(c_ptr_to_loc(c_loc(data(2,1)))-c_ptr_to_loc(c_loc(data(1,1))),c_int32_t)/value_size if (ubound(data,2) > 1) stridesf(2) = & int(c_ptr_to_loc(c_loc(data(1,2)))-c_ptr_to_loc(c_loc(data(1,1))),c_int32_t)/value_size #:elif rank == 1 stridesf = 1 if (ubound(data,1) > 1) stridesf(1) = & int(c_ptr_to_loc(c_loc(data(2)))-c_ptr_to_loc(c_loc(data(1))),c_int32_t)/value_size #:else stridesf = array_strides(data) #:endif else stridesf = 0 endif field = atlas_Field__cptr( & atlas__Field__wrap_${ctype}$_specf( c_str(name), data1d, size(shapef), shapef, stridesf ) ) call field%return() end function function atlas_Field__wrap_${dtype}$_r${rank}$(data) result(field) use, intrinsic :: iso_c_binding, only : c_int, c_long, c_float, c_double, c_loc, c_int32_t, c_sizeof use :: fckit_c_interop_module, only : c_str, c_ptr_to_loc use atlas_field_c_binding use fckit_array_module, only : array_strides, array_view1d type(atlas_Field) :: field ${ftype}$, intent(in), target :: data(${dim[rank]}$) ${ftype}$, pointer :: data_ptr(${dim[rank]}$) integer(c_int) :: shapef(${rank}$) integer(c_int) :: stridesf(${rank}$) integer(c_int32_t) :: value_size ! bytes of one data value #:if ftype != "logical(4)" ${ftype}$ :: val ! dummy to compute value_size ${ftype}$, pointer :: data1d(:) data_ptr => data data1d => array_view1d(data_ptr) #:else integer(c_int) :: val ! dummy to compute value_size integer(c_int), pointer :: data1d(:) data_ptr => data data1d => array_view1d( data_ptr, int(0,c_int) ) #:endif value_size = int(c_sizeof(val),c_int32_t) shapef = shape(data) if( size(data)>0 ) then !!! See issue https://github.com/ecmwf/atlas/pull/213 : problem with array_strides(data) with NAG compiler. #:if rank == 4 stridesf = 1 if (ubound(data,1) > 1) stridesf(1) = & int(c_ptr_to_loc(c_loc(data(2,1,1,1)))-c_ptr_to_loc(c_loc(data(1,1,1,1))),c_int32_t)/value_size if (ubound(data,2) > 1) stridesf(2) = & int(c_ptr_to_loc(c_loc(data(1,2,1,1)))-c_ptr_to_loc(c_loc(data(1,1,1,1))),c_int32_t)/value_size if (ubound(data,3) > 1) stridesf(3) = & int(c_ptr_to_loc(c_loc(data(1,1,2,1)))-c_ptr_to_loc(c_loc(data(1,1,1,1))),c_int32_t)/value_size if (ubound(data,4) > 1) stridesf(4) = & int(c_ptr_to_loc(c_loc(data(1,1,1,2)))-c_ptr_to_loc(c_loc(data(1,1,1,1))),c_int32_t)/value_size #:elif rank == 3 stridesf = 1 if (ubound(data,1) > 1) stridesf(1) = & int(c_ptr_to_loc(c_loc(data(2,1,1)))-c_ptr_to_loc(c_loc(data(1,1,1))),c_int32_t)/value_size if (ubound(data,2) > 1) stridesf(2) = & int(c_ptr_to_loc(c_loc(data(1,2,1)))-c_ptr_to_loc(c_loc(data(1,1,1))),c_int32_t)/value_size if (ubound(data,3) > 1) stridesf(3) = & int(c_ptr_to_loc(c_loc(data(1,1,2)))-c_ptr_to_loc(c_loc(data(1,1,1))),c_int32_t)/value_size #:elif rank == 2 stridesf = 1 if (ubound(data,1) > 1) stridesf(1) = & int(c_ptr_to_loc(c_loc(data(2,1)))-c_ptr_to_loc(c_loc(data(1,1))),c_int32_t)/value_size if (ubound(data,2) > 1) stridesf(2) = & int(c_ptr_to_loc(c_loc(data(1,2)))-c_ptr_to_loc(c_loc(data(1,1))),c_int32_t)/value_size #:elif rank == 1 stridesf = 1 if (ubound(data,1) > 1) stridesf(1) = & int(c_ptr_to_loc(c_loc(data(2)))-c_ptr_to_loc(c_loc(data(1))),c_int32_t)/value_size #:else stridesf = array_strides(data) #:endif else stridesf = 0 endif field = atlas_Field__cptr( & atlas__Field__wrap_${ctype}$_specf( c_str(""),data1d,size(shapef),shapef, stridesf) ) call field%return() end function #:endfor #:endfor !------------------------------------------------------------------------------- function Field__name(this) result(field_name) use atlas_field_c_binding use, intrinsic :: iso_c_binding, only : c_ptr use fckit_c_interop_module, only : c_ptr_to_string, c_str class(atlas_Field), intent(in) :: this character(len=:), allocatable :: field_name type(c_ptr) :: field_name_c_str field_name_c_str = atlas__Field__name(this%CPTR_PGIBUG_A) field_name = c_ptr_to_string(field_name_c_str) end function Field__name !------------------------------------------------------------------------------- function Field__functionspace(this) result(functionspace) use atlas_field_c_binding type(fckit_owned_object) :: functionspace class(atlas_Field), intent(in) :: this call functionspace%reset_c_ptr( atlas__Field__functionspace(this%CPTR_PGIBUG_A) ) call functionspace%return() end function Field__functionspace !------------------------------------------------------------------------------- function Field__datatype(this) result(datatype) use atlas_field_c_binding use, intrinsic :: iso_c_binding, only : c_ptr, c_int use fckit_c_interop_module, only : c_ptr_free, c_ptr_to_string class(atlas_Field), intent(in) :: this character(len=:), allocatable :: datatype type(c_ptr) :: datatype_cptr integer(c_int) :: datatype_size integer(c_int) :: datatype_allocated call atlas__Field__datatype(this%CPTR_PGIBUG_A,datatype_cptr,datatype_size,datatype_allocated) allocate(character(len=datatype_size) :: datatype ) datatype= c_ptr_to_string(datatype_cptr) if( datatype_allocated == 1 ) call c_ptr_free(datatype_cptr) end function Field__datatype !------------------------------------------------------------------------------- function Field__size(this) result(fieldsize) use atlas_field_c_binding class(atlas_Field), intent(in) :: this integer :: fieldsize fieldsize = atlas__Field__size(this%CPTR_PGIBUG_A) end function Field__size !------------------------------------------------------------------------------- function Field__rank(this) result(rank) use atlas_field_c_binding class(atlas_Field), intent(in) :: this integer :: rank rank = atlas__Field__rank(this%CPTR_PGIBUG_A) end function Field__rank !------------------------------------------------------------------------------- function Field__bytes(this) result(bytes) use atlas_field_c_binding use, intrinsic :: iso_c_binding, only : c_double class(atlas_Field), intent(in) :: this real(c_double) :: bytes bytes = atlas__Field__bytes(this%CPTR_PGIBUG_A) end function Field__bytes !------------------------------------------------------------------------------- function Field__kind(this) result(kind) use atlas_field_c_binding class(atlas_Field), intent(in) :: this integer :: kind kind = atlas__Field__kind(this%CPTR_PGIBUG_A) end function Field__kind !------------------------------------------------------------------------------- function Field__levels(this) result(levels) use atlas_field_c_binding class(atlas_Field), intent(in) :: this integer :: levels levels = atlas__Field__levels(this%CPTR_PGIBUG_A) end function Field__levels !------------------------------------------------------------------------------- function Field__metadata(this) result(metadata) use atlas_field_c_binding use atlas_metadata_module class(atlas_Field), intent(in) :: this type(atlas_Metadata) :: Metadata call metadata%reset_c_ptr( atlas__Field__metadata(this%CPTR_PGIBUG_A) ) end function Field__metadata !------------------------------------------------------------------------------- function Field__shape_array(this) result(shape) use atlas_field_c_binding use, intrinsic :: iso_c_binding, only : c_ptr, c_int, c_f_pointer class(atlas_Field), intent(in) :: this integer, allocatable :: shape(:) type(c_ptr) :: shape_c_ptr integer, pointer :: shape_f_ptr(:) integer(c_int) :: field_rank call atlas__Field__shapef(this%CPTR_PGIBUG_A, shape_c_ptr, field_rank) call c_f_pointer ( shape_c_ptr , shape_f_ptr , (/field_rank/) ) allocate( shape(field_rank) ) shape(:) = shape_f_ptr(:) end function Field__shape_array !------------------------------------------------------------------------------- function Field__shape_idx(this,idx) result(shape_val) use atlas_field_c_binding use, intrinsic :: iso_c_binding, only : c_ptr, c_int, c_f_pointer @:ENABLE_ATLAS_MACROS() integer :: shape_val class(atlas_Field), intent(in) :: this integer, intent(in) :: idx type(c_ptr) :: shape_c_ptr integer, pointer :: shape_f_ptr(:) integer(c_int) :: field_rank call atlas__Field__shapef(this%CPTR_PGIBUG_A, shape_c_ptr, field_rank) call c_f_pointer ( shape_c_ptr , shape_f_ptr , (/field_rank/) ) @:ATLAS_ASSERT( idx <= field_rank ) shape_val = shape_f_ptr(idx) end function Field__shape_idx !------------------------------------------------------------------------------- function Field__strides_array(this) result(strides) use atlas_field_c_binding use, intrinsic :: iso_c_binding, only : c_ptr, c_int, c_f_pointer class(atlas_Field), intent(in) :: this integer, allocatable :: strides(:) type(c_ptr) :: strides_c_ptr integer, pointer :: strides_f_ptr(:) integer(c_int) :: field_rank call atlas__Field__stridesf(this%CPTR_PGIBUG_A, strides_c_ptr, field_rank) call c_f_pointer ( strides_c_ptr , strides_f_ptr , (/field_rank/) ) allocate( strides(field_rank) ) strides(:) = strides_f_ptr(:) end function !------------------------------------------------------------------------------- function Field__strides_idx(this,idx) result(strides_val) use atlas_field_c_binding use, intrinsic :: iso_c_binding, only : c_ptr, c_int, c_f_pointer @:ENABLE_ATLAS_MACROS() integer :: strides_val class(atlas_Field), intent(in) :: this integer, intent(in) :: idx type(c_ptr) :: strides_c_ptr integer, pointer :: strides_f_ptr(:) integer(c_int) :: field_rank call atlas__Field__stridesf(this%CPTR_PGIBUG_A, strides_c_ptr, field_rank) call c_f_pointer ( strides_c_ptr , strides_f_ptr , (/field_rank/) ) @:ATLAS_ASSERT( idx <= field_rank ) strides_val = strides_f_ptr(idx) end function !------------------------------------------------------------------------------- subroutine set_levels(this,nb_levels) use atlas_field_c_binding class(atlas_Field), intent(inout) :: this integer, intent(in) :: nb_levels call atlas__field__set_levels(this%CPTR_PGIBUG_A,nb_levels) end subroutine !------------------------------------------------------------------------------- subroutine rename_(this,name) use atlas_field_c_binding use fckit_c_interop_module, only : c_str class(atlas_Field), intent(inout) :: this character(len=*), intent(in) :: name call atlas__field__rename(this%CPTR_PGIBUG_A,c_str(name)) end subroutine !------------------------------------------------------------------------------- subroutine set_functionspace(this,functionspace) use atlas_field_c_binding class(atlas_Field), intent(inout) :: this class(fckit_owned_object), intent(in) :: functionspace call atlas__field__set_functionspace(this%CPTR_PGIBUG_A,functionspace%CPTR_PGIBUG_A) end subroutine !------------------------------------------------------------------------------- subroutine set_host_needs_update(this,value) use atlas_field_c_binding class(atlas_Field), intent(in) :: this logical, optional, intent(in) :: value integer :: value_int value_int = 1 if (present(value)) then if (.not. value) then value_int = 0 endif endif call atlas__field__set_host_needs_update(this%CPTR_PGIBUG_A,value_int) end subroutine !------------------------------------------------------------------------------- subroutine set_device_needs_update(this,value) use atlas_field_c_binding class(atlas_Field), intent(in) :: this logical, optional, intent(in) :: value integer :: value_int value_int = 1 if (present(value)) then if (.not. value) then value_int = 0 endif endif call atlas__field__set_device_needs_update(this%CPTR_PGIBUG_A,value_int) end subroutine !------------------------------------------------------------------------------- function host_needs_update(this) use atlas_field_c_binding logical :: host_needs_update class(atlas_Field), intent(in) :: this if( atlas__Field__host_needs_update(this%CPTR_PGIBUG_A) == 1 ) then host_needs_update = .true. else host_needs_update = .false. endif end function !------------------------------------------------------------------------------- function device_needs_update(this) use atlas_field_c_binding logical :: device_needs_update class(atlas_Field), intent(in) :: this if( atlas__Field__device_needs_update(this%CPTR_PGIBUG_A) == 1 ) then device_needs_update = .true. else device_needs_update = .false. endif end function !------------------------------------------------------------------------------- subroutine update_device(this) use atlas_field_c_binding class(atlas_Field), intent(inout) :: this call atlas__Field__update_device(this%CPTR_PGIBUG_A) end subroutine !------------------------------------------------------------------------------- subroutine update_host(this) use atlas_field_c_binding class(atlas_Field), intent(inout) :: this call atlas__Field__update_host(this%CPTR_PGIBUG_A) end subroutine !------------------------------------------------------------------------------- subroutine sync_host_device(this) use atlas_field_c_binding class(atlas_Field), intent(inout) :: this call atlas__Field__sync_host_device(this%CPTR_PGIBUG_A) end subroutine !------------------------------------------------------------------------------- subroutine sync_host(this) use atlas_field_c_binding class(atlas_Field), intent(inout) :: this call atlas__Field__sync_host(this%CPTR_PGIBUG_A) end subroutine !------------------------------------------------------------------------------- subroutine sync_device(this) use atlas_field_c_binding class(atlas_Field), intent(inout) :: this call atlas__Field__sync_device(this%CPTR_PGIBUG_A) end subroutine !------------------------------------------------------------------------------- subroutine allocate_device(this) use atlas_field_c_binding class(atlas_Field), intent(inout) :: this call atlas__Field__allocate_device(this%CPTR_PGIBUG_A) end subroutine !------------------------------------------------------------------------------- subroutine deallocate_device(this) use atlas_field_c_binding class(atlas_Field), intent(inout) :: this call atlas__Field__deallocate_device(this%CPTR_PGIBUG_A) end subroutine !------------------------------------------------------------------------------- function device_allocated(this) use atlas_field_c_binding logical :: device_allocated class(atlas_Field), intent(in) :: this if( atlas__Field__device_allocated(this%CPTR_PGIBUG_A) == 1 ) then device_allocated = .true. else device_allocated = .false. endif end function !------------------------------------------------------------------------------- subroutine halo_exchange(this,on_device) use, intrinsic :: iso_c_binding, only : c_int use atlas_field_c_binding class(atlas_Field), intent(in) :: this logical, optional :: on_device integer(c_int) :: on_device_int on_device_int = 0 if( present(on_device) ) then if( on_device ) on_device_int = 1 endif call atlas__Field__halo_exchange(this%CPTR_PGIBUG_A, on_device_int) end subroutine !------------------------------------------------------------------------------- subroutine set_dirty(this,value) use, intrinsic :: iso_c_binding, only : c_int use atlas_field_c_binding class(atlas_Field), intent(in) :: this logical, optional, intent(in) :: value integer(c_int) :: value_int if( present(value) ) then if( value ) then value_int = 1 else value_int = 0 endif else value_int = 1 endif call atlas__Field__set_dirty(this%CPTR_PGIBUG_A, value_int) end subroutine !------------------------------------------------------------------------------- function dirty(this) result(value) use, intrinsic :: iso_c_binding, only : c_int use atlas_field_c_binding class(atlas_Field), intent(inout) :: this logical :: value integer(c_int) :: value_int value_int = atlas__Field__dirty(this%CPTR_PGIBUG_A) if( value_int == 0 ) then value = .false. else value = .true. endif end function !------------------------------------------------------------------------------- function contiguous(this) use atlas_field_c_binding logical :: contiguous class(atlas_Field), intent(in) :: this if( atlas__Field__contiguous(this%CPTR_PGIBUG_A) == 1 ) then contiguous = .true. else contiguous = .false. endif end function !------------------------------------------------------------------------------- #if FCKIT_FINAL_NOT_INHERITING ATLAS_FINAL subroutine atlas_Field__final_auto(this) type(atlas_Field), intent(inout) :: this #if FCKIT_FINAL_DEBUGGING write(0,*) "atlas_Field__final_auto" #endif #if FCKIT_FINAL_NOT_PROPAGATING call this%final() #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine #endif !------------------------------------------------------------------------------- end module atlas_field_module atlas-0.46.0/src/atlas_f/field/atlas_FieldSet_module.fypp0000664000175000017500000006765515160212070023521 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. #include "atlas/atlas_f.h" #:include "atlas/atlas_f.fypp" #:include "internals/atlas_generics.fypp" module atlas_FieldSet_module use fckit_owned_object_module, only: fckit_owned_object use atlas_field_module, only: atlas_field, array_c_to_f use atlas_kinds_module, only : ATLAS_KIND_IDX implicit none private :: fckit_owned_object public :: atlas_FieldSet private !------------------------------------------------------------------------------ TYPE, extends(fckit_owned_object) :: atlas_FieldSet ! Purpose : ! ------- ! *FieldSet* : Object that groups Fields that go together ! Fields can belong to several fieldsets simultaneously. ! The actual ownership of the field lies in a FunctionSpace ! Methods : ! ------- ! add_field : The name or tag this field was created with ! field : Return the field as a fortran array of specified shape ! Author : ! ------ ! 20-Nov-2013 Willem Deconinck *ECMWF* !------------------------------------------------------------------------------ contains procedure, public :: name => FieldSet__name procedure, public :: size => FieldSet__size procedure, public :: has procedure, private :: field_by_name procedure, private :: field_by_idx_int procedure, private :: field_by_idx_long procedure, public :: add_field procedure, public :: add_fieldset generic :: add => add_field, add_fieldset generic :: field => field_by_name, field_by_idx_int, field_by_idx_long procedure, public :: set_dirty procedure, public :: halo_exchange #:for rank in ranks #:for dtype in dtypes procedure, private :: access_data_${dtype}$_r${rank}$_by_name procedure, private :: access_data_${dtype}$_r${rank}$_by_idx procedure, private :: access_data_${dtype}$_r${rank}$_slice_by_name procedure, private :: access_data_${dtype}$_r${rank}$_slice_by_idx procedure, private :: access_device_data_${dtype}$_r${rank}$_by_name procedure, private :: access_device_data_${dtype}$_r${rank}$_by_idx #:endfor #:endfor generic, public :: data => & #:for rank in ranks #:for dtype in dtypes & access_data_${dtype}$_r${rank}$_by_name, & & access_data_${dtype}$_r${rank}$_by_idx, & & access_data_${dtype}$_r${rank}$_slice_by_name, & & access_data_${dtype}$_r${rank}$_slice_by_idx, & #:endfor #:endfor & dummy generic, public :: device_data => & #:for rank in ranks #:for dtype in dtypes & access_device_data_${dtype}$_r${rank}$_by_name, & & access_device_data_${dtype}$_r${rank}$_by_idx, & #:endfor #:endfor & dummy procedure, private :: dummy procedure, public :: set_host_needs_update_idx procedure, public :: set_host_needs_update_value procedure, public :: set_host_needs_update_name generic :: set_host_needs_update => set_host_needs_update_idx, set_host_needs_update_value, & set_host_needs_update_name procedure, public :: set_device_needs_update_idx procedure, public :: set_device_needs_update_value procedure, public :: set_device_needs_update_name generic :: set_device_needs_update => set_device_needs_update_idx, set_device_needs_update_value, & set_device_needs_update_name procedure, public :: sync_host_device_idx procedure, public :: sync_host_device_name generic :: sync_host_device => sync_host_device_idx, sync_host_device_name procedure, public :: sync_host_idx procedure, public :: sync_host_name generic :: sync_host => sync_host_idx, sync_host_name procedure, public :: sync_device_idx procedure, public :: sync_device_name generic :: sync_device => sync_device_idx, sync_device_name procedure, public :: allocate_device_idx procedure, public :: allocate_device_name generic :: allocate_device => allocate_device_idx, allocate_device_name procedure, public :: update_device_idx procedure, public :: update_device_name generic :: update_device => update_device_idx, update_device_name procedure, public :: update_host_idx procedure, public :: update_host_name generic :: update_host => update_host_idx, update_host_name procedure, public :: deallocate_device_idx procedure, public :: deallocate_device_name generic :: deallocate_device => deallocate_device_idx, deallocate_device_name #if FCKIT_FINAL_NOT_INHERITING final :: atlas_FieldSet__final_auto #endif procedure, public :: has_field => has ! deprecated ! END TYPE atlas_FieldSet !------------------------------------------------------------------------------ interface atlas_FieldSet module procedure atlas_FieldSet__cptr module procedure atlas_FieldSet__ctor end interface !------------------------------------------------------------------------------ !======================================================== contains !======================================================== ! ----------------------------------------------------------------------------- ! FieldSet routines #:for rank in ranks #:for dtype,ftype,ctype in types subroutine access_data_${dtype}$_r${rank}$_by_name(this, name, field) use fckit_c_interop_module, only: c_str use atlas_fieldset_c_binding use, intrinsic :: iso_c_binding, only : c_ptr, c_int, c_long, c_float, c_double class(atlas_FieldSet), intent(in) :: this character(len=*), intent(in) :: name ${ftype}$, pointer, intent(inout) :: field(${dim[rank]}$) type(c_ptr) :: field_cptr type(c_ptr) :: shape_cptr type(c_ptr) :: strides_cptr integer(c_int) :: rank call atlas__FieldSet__data_${ctype}$_specf(this%CPTR_PGIBUG_A, c_str(name), field_cptr, rank, shape_cptr, strides_cptr) call array_c_to_f(field_cptr, rank, shape_cptr, strides_cptr, field) end subroutine subroutine access_data_${dtype}$_r${rank}$_by_idx(this, idx, field) use fckit_c_interop_module, only: c_str use atlas_fieldset_c_binding use, intrinsic :: iso_c_binding, only : c_ptr, c_int, c_long, c_float, c_double class(atlas_FieldSet), intent(in) :: this integer, intent(in) :: idx ${ftype}$, pointer, intent(inout) :: field(${dim[rank]}$) type(c_ptr) :: field_cptr type(c_ptr) :: shape_cptr type(c_ptr) :: strides_cptr integer(c_int) :: rank call atlas__FieldSet__data_${ctype}$_specf_by_idx(this%CPTR_PGIBUG_A, idx-1, field_cptr, rank, shape_cptr, strides_cptr) call array_c_to_f(field_cptr, rank, shape_cptr, strides_cptr, field) end subroutine subroutine access_data_${dtype}$_r${rank}$_slice_by_name(this, name, slice, iblk) use fckit_c_interop_module, only: c_str use atlas_fieldset_c_binding use, intrinsic :: iso_c_binding, only : c_ptr, c_int, c_long, c_float, c_double class(atlas_FieldSet), intent(in) :: this character(len=*), intent(in) :: name #:if rank > 1 ${ftype}$, pointer, intent(inout) :: slice(${dimr[rank]}$) #:else ${ftype}$, pointer, intent(inout) :: slice #:endif integer, intent(in) :: iblk ${ftype}$, pointer :: field(${dim[rank]}$) call access_data_${dtype}$_r${rank}$_by_name(this, c_str(name), field) #:if rank > 1 slice => field(${dimr[rank]}$, iblk) #:else slice => field(iblk) #:endif end subroutine subroutine access_data_${dtype}$_r${rank}$_slice_by_idx(this, idx, slice, iblk) use fckit_c_interop_module, only: c_str use atlas_fieldset_c_binding use, intrinsic :: iso_c_binding, only : c_ptr, c_int, c_long, c_float, c_double class(atlas_FieldSet), intent(in) :: this integer, intent(in) :: idx #:if rank > 1 ${ftype}$, pointer, intent(inout) :: slice(${dimr[rank]}$) #:else ${ftype}$, pointer, intent(inout) :: slice #:endif integer, intent(in) :: iblk ${ftype}$, pointer :: field(${dim[rank]}$) call access_data_${dtype}$_r${rank}$_by_idx(this, idx, field) #:if rank > 1 slice => field(${dimr[rank]}$, iblk) #:else slice => field(iblk) #:endif end subroutine !------------------------------------------------------------------------------- subroutine access_device_data_${dtype}$_r${rank}$_by_name(this, name, field) use fckit_c_interop_module, only: c_str use atlas_field_c_binding use atlas_Field_module, only: atlas_Field use, intrinsic :: iso_c_binding, only : c_ptr, c_int, c_long, c_float, c_double class(atlas_FieldSet), intent(in) :: this character(len=*), intent(in) :: name ${ftype}$, pointer, intent(inout) :: field(${dim[rank]}$) type(c_ptr) :: field_cptr type(c_ptr) :: shape_cptr type(c_ptr) :: strides_cptr integer(c_int) :: rank type(atlas_Field) :: field_sel field_sel = this%field(name) call atlas__Field__device_data_${ctype}$_specf(field_sel%CPTR_PGIBUG_A, field_cptr, rank, shape_cptr, strides_cptr) call array_c_to_f(field_cptr, rank, shape_cptr, strides_cptr, field) end subroutine !------------------------------------------------------------------------------- subroutine access_device_data_${dtype}$_r${rank}$_by_idx(this, idx, field) use fckit_c_interop_module, only: c_str use atlas_field_c_binding use atlas_Field_module, only: atlas_Field use, intrinsic :: iso_c_binding, only : c_ptr, c_int, c_long, c_float, c_double class(atlas_FieldSet), intent(in) :: this integer, intent(in) :: idx ${ftype}$, pointer, intent(inout) :: field(${dim[rank]}$) type(c_ptr) :: field_cptr type(c_ptr) :: shape_cptr type(c_ptr) :: strides_cptr integer(c_int) :: rank type(atlas_Field) :: field_loc field_loc = this%field(idx) call atlas__Field__device_data_${ctype}$_specf(field_loc%CPTR_PGIBUG_A, field_cptr, rank, shape_cptr, strides_cptr) call array_c_to_f(field_cptr, rank, shape_cptr, strides_cptr, field) end subroutine !------------------------------------------------------------------------------- #:endfor #:endfor subroutine dummy(this) use atlas_fieldset_c_binding class(atlas_FieldSet), intent(in) :: this FCKIT_SUPPRESS_UNUSED(this) end subroutine function atlas_FieldSet__cptr(cptr) result(fieldset) use, intrinsic :: iso_c_binding, only: c_ptr type(atlas_FieldSet) :: fieldset type(c_ptr), intent(in) :: cptr call fieldset%reset_c_ptr( cptr ) call fieldset%return() end function function atlas_FieldSet__ctor(name) result(fieldset) use fckit_c_interop_module, only: c_str use atlas_fieldset_c_binding character(len=*), intent(in), optional :: name type(atlas_FieldSet) :: fieldset if( present(name) ) then fieldset = atlas_FieldSet__cptr( atlas__FieldSet__new( c_str(name) ) ) else fieldset = atlas_FieldSet__cptr( atlas__FieldSet__new( c_str("") ) ) endif call fieldset%return() end function function FieldSet__name(this) result(fieldset_name) use, intrinsic :: iso_c_binding, only : c_ptr use fckit_c_interop_module, only : c_ptr_to_string, c_str use atlas_fieldset_c_binding class(atlas_FieldSet), intent(in) :: this character(len=:), allocatable :: fieldset_name type(c_ptr) :: fieldset_name_c_str fieldset_name_c_str = atlas__FieldSet__name(this%CPTR_PGIBUG_A) fieldset_name = c_ptr_to_string(fieldset_name_c_str) end function FieldSet__name subroutine add_field(this,field) use atlas_fieldset_c_binding use atlas_Field_module, only: atlas_Field class(atlas_FieldSet), intent(in) :: this type(atlas_Field), intent(in) :: field call atlas__FieldSet__add_field(this%CPTR_PGIBUG_A, field%CPTR_PGIBUG_A) end subroutine subroutine add_fieldset(this,fset) use atlas_fieldset_c_binding class(atlas_FieldSet), intent(inout) :: this class(atlas_FieldSet), intent(in) :: fset call atlas__FieldSet__add_fieldset(this%CPTR_PGIBUG_A, fset%CPTR_PGIBUG_A) end subroutine function has(this,name) result(flag) use, intrinsic :: iso_c_binding, only: c_int use fckit_c_interop_module, only: c_str use atlas_fieldset_c_binding class(atlas_FieldSet), intent(in) :: this character(len=*), intent(in) :: name logical :: flag integer(c_int) :: rc rc = atlas__FieldSet__has_field(this%CPTR_PGIBUG_A, c_str(name)) if( rc == 0 ) then flag = .False. else flag = .True. end if end function function FieldSet__size(this) result(nb_fields) use atlas_fieldset_c_binding class(atlas_FieldSet), intent(in) :: this integer(ATLAS_KIND_IDX) :: nb_fields nb_fields = atlas__FieldSet__size(this%CPTR_PGIBUG_A) end function function field_by_name(this,name) result(field) use fckit_c_interop_module, only: c_str use atlas_fieldset_c_binding use atlas_Field_module, only: atlas_Field class(atlas_FieldSet), intent(in) :: this character(len=*), intent(in) :: name type(atlas_Field) :: field field = atlas_Field( atlas__FieldSet__field_by_name(this%CPTR_PGIBUG_A, c_str(name) ) ) call field%return() end function function field_by_idx_long(this,idx) result(field) use, intrinsic :: iso_c_binding, only: c_long use atlas_fieldset_c_binding use atlas_Field_module, only: atlas_Field class(atlas_FieldSet), intent(in) :: this integer(c_long), intent(in) :: idx type(atlas_Field) :: field field = atlas_Field( atlas__FieldSet__field_by_idx(this%CPTR_PGIBUG_A, int(idx-1_c_long,ATLAS_KIND_IDX) ) ) ! C index call field%return() end function function field_by_idx_int(this,idx) result(field) use, intrinsic :: iso_c_binding, only: c_int use atlas_fieldset_c_binding use atlas_Field_module, only: atlas_Field class(atlas_FieldSet), intent(in) :: this integer(c_int), intent(in) :: idx type(atlas_Field) :: field field = atlas_Field( atlas__FieldSet__field_by_idx(this%CPTR_PGIBUG_A, int(idx-1_c_int,ATLAS_KIND_IDX) ) ) ! C index call field%return() end function !------------------------------------------------------------------------------- subroutine halo_exchange(this,on_device) use, intrinsic :: iso_c_binding, only : c_int use atlas_fieldset_c_binding class(atlas_FieldSet), intent(inout) :: this logical, optional :: on_device integer(c_int) :: on_device_int on_device_int = 0 if( present(on_device) ) then if( on_device ) on_device_int = 1 endif call atlas__FieldSet__halo_exchange(this%CPTR_PGIBUG_A, on_device_int) end subroutine !------------------------------------------------------------------------------- subroutine set_host_needs_update_value(this, value) use, intrinsic :: iso_c_binding, only : c_int use atlas_field_c_binding use atlas_Field_module, only: atlas_Field class(atlas_FieldSet), intent(inout) :: this logical, intent(in), optional :: value type(atlas_Field) :: field integer(c_int) :: i, value_int value_int = 1 if (present(value)) then if (.not. value) then value_int = 0 end if end if do i = 1, this%size() field = this%field(i) call atlas__Field__set_host_needs_update(field%CPTR_PGIBUG_A, value_int) end do end subroutine !------------------------------------------------------------------------------- subroutine set_host_needs_update_idx(this, field_indices, value) use, intrinsic :: iso_c_binding, only : c_int use atlas_field_c_binding use atlas_Field_module, only: atlas_Field class(atlas_FieldSet), intent(inout) :: this integer, intent(in) :: field_indices(:) logical, intent(in), optional :: value type(atlas_Field) :: field integer(c_int) :: i, value_int value_int = 1 if (present(value)) then if (.not. value) then value_int = 0 end if end if do i = 1, size(field_indices) field = this%field(field_indices(i)) call atlas__Field__set_host_needs_update(field%CPTR_PGIBUG_A, value_int) end do end subroutine !------------------------------------------------------------------------------- subroutine set_host_needs_update_name(this, field_names, value) use, intrinsic :: iso_c_binding, only : c_int use atlas_field_c_binding use atlas_Field_module, only: atlas_Field class(atlas_FieldSet), intent(inout) :: this character(*), intent(in) :: field_names(:) logical, intent(in), optional :: value type(atlas_Field) :: field integer(c_int) :: i, value_int value_int = 1 if (present(value)) then if (.not. value) then value_int = 0 end if end if do i = 1, size(field_names) field = this%field(field_names(i)) call atlas__Field__set_host_needs_update(field%CPTR_PGIBUG_A, value_int) end do end subroutine !------------------------------------------------------------------------------- subroutine set_device_needs_update_value(this, value) use, intrinsic :: iso_c_binding, only : c_int use atlas_field_c_binding use atlas_Field_module, only: atlas_Field class(atlas_FieldSet), intent(inout) :: this logical, intent(in), optional :: value type(atlas_Field) :: field integer(c_int) :: i, value_int value_int = 1 if (present(value)) then if (.not. value) then value_int = 0 end if end if do i = 1, this%size() field = this%field(i) call atlas__Field__set_device_needs_update(field%CPTR_PGIBUG_A, value_int) end do end subroutine !------------------------------------------------------------------------------- subroutine set_device_needs_update_idx(this, field_indices, value) use, intrinsic :: iso_c_binding, only : c_int use atlas_field_c_binding use atlas_Field_module, only: atlas_Field class(atlas_FieldSet), intent(inout) :: this integer, intent(in) :: field_indices(:) logical, intent(in), optional :: value type(atlas_Field) :: field integer(c_int) :: i, value_int value_int = 1 if (present(value)) then if (.not. value) then value_int = 0 end if end if do i = 1, size(field_indices) field = this%field(field_indices(i)) call atlas__Field__set_device_needs_update(field%CPTR_PGIBUG_A, value_int) end do end subroutine !------------------------------------------------------------------------------- subroutine set_device_needs_update_name(this, field_names, value) use, intrinsic :: iso_c_binding, only : c_int use atlas_field_c_binding use atlas_Field_module, only: atlas_Field class(atlas_FieldSet), intent(inout) :: this character(*), intent(in) :: field_names(:) logical, intent(in), optional :: value type(atlas_Field) :: field integer(c_int) :: i, value_int value_int = 1 if (present(value)) then if (.not. value) then value_int = 0 end if end if do i = 1, size(field_names) field = this%field(field_names(i)) call atlas__Field__set_device_needs_update(field%CPTR_PGIBUG_A, value_int) end do end subroutine !------------------------------------------------------------------------------- subroutine sync_host_device_idx(this, field_indices) use, intrinsic :: iso_c_binding, only : c_int use atlas_field_c_binding use atlas_Field_module, only: atlas_Field class(atlas_FieldSet), intent(inout) :: this integer, intent(in), optional :: field_indices(:) type(atlas_Field) :: field integer(c_int) :: i if (present(field_indices)) then do i = 1, size(field_indices) field = this%field(field_indices(i)) call atlas__Field__sync_host_device(field%CPTR_PGIBUG_A) end do else do i = 1, this%size() field = this%field(i) call atlas__Field__sync_host_device(field%CPTR_PGIBUG_A) end do end if end subroutine !------------------------------------------------------------------------------- subroutine sync_host_device_name(this, field_names) use, intrinsic :: iso_c_binding, only : c_int use atlas_field_c_binding use atlas_Field_module, only: atlas_Field class(atlas_FieldSet), intent(inout) :: this character(*), intent(in) :: field_names(:) type(atlas_Field) :: field integer(c_int) :: i do i = 1, size(field_names) field = this%field(field_names(i)) call atlas__Field__sync_host_device(field%CPTR_PGIBUG_A) end do end subroutine !------------------------------------------------------------------------------- subroutine sync_host_idx(this, field_indices) use, intrinsic :: iso_c_binding, only : c_int use atlas_field_c_binding use atlas_Field_module, only: atlas_Field class(atlas_FieldSet), intent(inout) :: this integer, intent(in), optional :: field_indices(:) type(atlas_Field) :: field integer(c_int) :: i if (present(field_indices)) then do i = 1, size(field_indices) field = this%field(field_indices(i)) call atlas__Field__sync_host(field%CPTR_PGIBUG_A) end do else do i = 1, this%size() field = this%field(i) call atlas__Field__sync_host(field%CPTR_PGIBUG_A) end do end if end subroutine !------------------------------------------------------------------------------- subroutine sync_host_name(this, field_names) use, intrinsic :: iso_c_binding, only : c_int use atlas_field_c_binding use atlas_Field_module, only: atlas_Field class(atlas_FieldSet), intent(inout) :: this character(*), intent(in) :: field_names(:) type(atlas_Field) :: field integer(c_int) :: i do i = 1, size(field_names) field = this%field(field_names(i)) call atlas__Field__sync_host(field%CPTR_PGIBUG_A) end do end subroutine !------------------------------------------------------------------------------- subroutine sync_device_idx(this, field_indices) use, intrinsic :: iso_c_binding, only : c_int use atlas_field_c_binding use atlas_Field_module, only: atlas_Field class(atlas_FieldSet), intent(inout) :: this integer, intent(in), optional :: field_indices(:) type(atlas_Field) :: field integer(c_int) :: i if (present(field_indices)) then do i = 1, size(field_indices) field = this%field(field_indices(i)) call atlas__Field__sync_device(field%CPTR_PGIBUG_A) end do else do i = 1, this%size() field = this%field(i) call atlas__Field__sync_device(field%CPTR_PGIBUG_A) end do end if end subroutine !------------------------------------------------------------------------------- subroutine sync_device_name(this, field_names) use, intrinsic :: iso_c_binding, only : c_int use atlas_field_c_binding use atlas_Field_module, only: atlas_Field class(atlas_FieldSet), intent(inout) :: this character(*), intent(in) :: field_names(:) type(atlas_Field) :: field integer(c_int) :: i do i = 1, size(field_names) field = this%field(field_names(i)) call atlas__Field__sync_device(field%CPTR_PGIBUG_A) end do end subroutine !------------------------------------------------------------------------------- subroutine allocate_device_idx(this, field_indices) use, intrinsic :: iso_c_binding, only : c_int use atlas_field_c_binding use atlas_Field_module, only: atlas_Field class(atlas_FieldSet), intent(inout) :: this integer, intent(in), optional :: field_indices(:) type(atlas_Field) :: field integer(c_int) :: i if (present(field_indices)) then do i = 1, size(field_indices) field = this%field(field_indices(i)) call atlas__Field__allocate_device(field%CPTR_PGIBUG_A) end do else do i = 1, this%size() field = this%field(i) call atlas__Field__allocate_device(field%CPTR_PGIBUG_A) end do end if end subroutine !------------------------------------------------------------------------------- subroutine allocate_device_name(this, field_names) use, intrinsic :: iso_c_binding, only : c_int use atlas_field_c_binding use atlas_Field_module, only: atlas_Field class(atlas_FieldSet), intent(inout) :: this character(*), intent(in) :: field_names(:) type(atlas_Field) :: field integer(c_int) :: i do i = 1, size(field_names) field = this%field(field_names(i)) call atlas__Field__allocate_device(field%CPTR_PGIBUG_A) end do end subroutine !------------------------------------------------------------------------------- subroutine update_device_idx(this, field_indices) use, intrinsic :: iso_c_binding, only : c_int use atlas_field_c_binding use atlas_Field_module, only: atlas_Field class(atlas_FieldSet), intent(inout) :: this integer, intent(in), optional :: field_indices(:) type(atlas_Field) :: field integer(c_int) :: i if (present(field_indices)) then do i = 1, size(field_indices) field = this%field(field_indices(i)) call atlas__Field__update_device(field%CPTR_PGIBUG_A) end do else do i = 1, this%size() field = this%field(i) call atlas__Field__update_device(field%CPTR_PGIBUG_A) end do end if end subroutine !------------------------------------------------------------------------------- subroutine update_device_name(this, field_names) use, intrinsic :: iso_c_binding, only : c_int use atlas_field_c_binding use atlas_Field_module, only: atlas_Field class(atlas_FieldSet), intent(inout) :: this character(*), intent(in) :: field_names(:) type(atlas_Field) :: field integer(c_int) :: i do i = 1, size(field_names) field = this%field(field_names(i)) call atlas__Field__update_device(field%CPTR_PGIBUG_A) end do end subroutine !------------------------------------------------------------------------------- subroutine update_host_idx(this, field_indices) use, intrinsic :: iso_c_binding, only : c_int use atlas_field_c_binding use atlas_Field_module, only: atlas_Field class(atlas_FieldSet), intent(inout) :: this integer, intent(in), optional :: field_indices(:) type(atlas_Field) :: field integer(c_int) :: i if (present(field_indices)) then do i = 1, size(field_indices) field = this%field(field_indices(i)) call atlas__Field__update_host(field%CPTR_PGIBUG_A) end do else do i = 1, this%size() field = this%field(i) call atlas__Field__update_host(field%CPTR_PGIBUG_A) end do end if end subroutine !------------------------------------------------------------------------------- subroutine update_host_name(this, field_names) use, intrinsic :: iso_c_binding, only : c_int use atlas_field_c_binding use atlas_Field_module, only: atlas_Field class(atlas_FieldSet), intent(inout) :: this character(*), intent(in) :: field_names(:) type(atlas_Field) :: field integer(c_int) :: i do i = 1, size(field_names) field = this%field(field_names(i)) call atlas__Field__update_host(field%CPTR_PGIBUG_A) end do end subroutine !------------------------------------------------------------------------------- subroutine deallocate_device_idx(this, field_indices) use, intrinsic :: iso_c_binding, only : c_int use atlas_field_c_binding use atlas_Field_module, only: atlas_Field class(atlas_FieldSet), intent(inout) :: this integer, optional :: field_indices(:) type(atlas_Field) :: field integer(c_int) :: i if (present(field_indices)) then do i = 1, size(field_indices) field = this%field(field_indices(i)) call atlas__Field__deallocate_device(field%CPTR_PGIBUG_A) end do else do i = 1, this%size() field = this%field(i) call atlas__Field__deallocate_device(field%CPTR_PGIBUG_A) end do end if end subroutine !------------------------------------------------------------------------------- subroutine deallocate_device_name(this, field_names) use, intrinsic :: iso_c_binding, only : c_int use atlas_field_c_binding use atlas_Field_module, only: atlas_Field class(atlas_FieldSet), intent(inout) :: this character(*), intent(in) :: field_names(:) type(atlas_Field) :: field integer(c_int) :: i do i = 1, size(field_names) field = this%field(field_names(i)) call atlas__Field__deallocate_device(field%CPTR_PGIBUG_A) end do end subroutine !------------------------------------------------------------------------------- subroutine set_dirty(this,value) use, intrinsic :: iso_c_binding, only : c_int use atlas_fieldset_c_binding class(atlas_FieldSet), intent(inout) :: this logical, optional, intent(in) :: value integer(c_int) :: value_int if( present(value) ) then if( value ) then value_int = 1 else value_int = 0 endif else value_int = 1 endif call atlas__FieldSet__set_dirty(this%CPTR_PGIBUG_A, value_int) end subroutine !------------------------------------------------------------------------------- #if FCKIT_FINAL_NOT_INHERITING ATLAS_FINAL subroutine atlas_FieldSet__final_auto(this) type(atlas_FieldSet), intent(inout) :: this #if FCKIT_FINAL_DEBUGGING write(0,*) "atlas_FieldSet__final_auto" #endif #if FCKIT_FINAL_NOT_PROPAGATING call this%final() #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine #endif ! ---------------------------------------------------------------------------------------- end module atlas_FieldSet_module atlas-0.46.0/src/atlas_f/field/atlas_State_module.F900000664000175000017500000001400015160212070022432 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. #include "atlas/atlas_f.h" module atlas_State_module use fckit_owned_object_module, only: fckit_owned_object use atlas_Field_module, only: atlas_Field use atlas_kinds_module, only: ATLAS_KIND_IDX implicit none private :: fckit_owned_object private :: atlas_Field public :: atlas_State private !----------------------------! ! atlas_State ! !----------------------------! ! (C) Copyright 2013-2015 ECMWF. !------------------------------------------------------------------------------ TYPE, extends(fckit_owned_object) :: atlas_State ! Purpose : ! ------- ! *atlas_State* : ! Container type of fields that are defined on the same points ! Describes how nodes are ordered ! Describes how parallelisation for fields is done ! Describes interpolation between nodes ! Methods : ! ------- ! name : The name or tag this function space was created with ! create_field : Create a new real field in this function space with given name ! remove : Remove a field with given name ! field : Access to a field with given name ! parallelise : Setup halo-exchange information ! halo_exchange : Perform halo exchange on field_data ! Author : ! ------ ! 20-Nov-2013 Willem Deconinck *ECMWF* !------------------------------------------------------------------------------ contains !-- Field procedure, public :: add => atlas_State__add procedure, public :: remove => atlas_State__remove procedure, public :: has => atlas_State__has procedure, public :: size => atlas_State__size procedure, private :: field_by_name => atlas_State__field_by_name procedure, private :: field_by_index => atlas_State__field_by_index generic, public :: field => field_by_name, field_by_index procedure, public :: metadata => atlas_State__metadata #if FCKIT_FINAL_NOT_INHERITING final :: atlas_State__final_auto #endif END TYPE atlas_State interface atlas_State module procedure atlas_State__new module procedure atlas_State__generate end interface !------------------------------------------------------------------------------ !======================================================== contains !======================================================== ! ----------------------------------------------------------------------------- ! State routines function atlas_State__new() result(this) use atlas_state_c_binding type(atlas_State) :: this call this%reset_c_ptr( atlas__State__new() ) call this%return() end function function atlas_State__generate(generator, params) result(this) use fckit_c_interop_module, only: c_str use atlas_state_c_binding use atlas_Config_module, only: atlas_Config type(atlas_State) :: this character(len=*), intent(in) :: generator class(atlas_Config), intent(in), optional :: params type(atlas_Config) :: p call this%reset_c_ptr( atlas__State__new() ) if( present(params) ) then call atlas__State__initialize(this%CPTR_PGIBUG_A,c_str(generator),params%CPTR_PGIBUG_B) else p = atlas_Config() call atlas__State__initialize(this%CPTR_PGIBUG_A,c_str(generator),p%CPTR_PGIBUG_B) call p%final() endif call this%return() end function subroutine atlas_State__add(this,field) use atlas_state_c_binding class(atlas_State), intent(inout) :: this class(atlas_Field), intent(in) :: field call atlas__State__add(this%CPTR_PGIBUG_A,field%CPTR_PGIBUG_A) end subroutine subroutine atlas_State__remove(this,name) use fckit_c_interop_module, only: c_str use atlas_state_c_binding class(atlas_State), intent(inout) :: this character(len=*), intent(in) :: name call atlas__State__remove(this%CPTR_PGIBUG_A,c_str(name)) end subroutine function atlas_State__has(this,name) result(has) use fckit_c_interop_module, only: c_str use atlas_state_c_binding logical :: has class(atlas_State), intent(in) :: this character(len=*), intent(in) :: name integer :: has_int has_int = atlas__State__has(this%CPTR_PGIBUG_A,c_str(name)) has = .False. if( has_int == 1 ) has = .True. end function function atlas_State__size(this) result(size) use atlas_state_c_binding integer(ATLAS_KIND_IDX) :: size class(atlas_State), intent(in) :: this size = atlas__State__size(this%CPTR_PGIBUG_A) end function function atlas_State__field_by_name(this,name) result(field) use fckit_c_interop_module, only: c_str use atlas_state_c_binding type(atlas_Field) :: field class(atlas_State), intent(inout) :: this character(len=*), intent(in) :: name field = atlas_Field( atlas__State__field_by_name(this%CPTR_PGIBUG_A,c_str(name)) ) call field%return() end function function atlas_State__field_by_index(this,index) result(field) use atlas_state_c_binding type(atlas_Field) :: field class(atlas_State), intent(in) :: this integer, intent(in) :: index field = atlas_Field( atlas__State__field_by_index(this%CPTR_PGIBUG_A,int(index-1,ATLAS_KIND_IDX)) ) call field%return() end function function atlas_State__metadata(this) result(metadata) use atlas_state_c_binding use atlas_Metadata_module, only: atlas_Metadata type(atlas_Metadata) :: metadata class(atlas_State), intent(in) :: this call metadata%reset_c_ptr( atlas__State__metadata(this%CPTR_PGIBUG_A) ) end function !------------------------------------------------------------------------------- #if FCKIT_FINAL_NOT_INHERITING ATLAS_FINAL subroutine atlas_State__final_auto(this) type(atlas_State), intent(inout) :: this #if FCKIT_FINAL_DEBUGGING write(0,*) "atlas_State__final_auto" #endif #if FCKIT_FINAL_NOT_PROPAGATING call this%final() #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine #endif ! ---------------------------------------------------------------------------------------- end module atlas_State_module atlas-0.46.0/src/atlas_f/CMakeLists.txt0000664000175000017500000003245715160212070020036 0ustar alastairalastair# (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. configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/atlas_f.h.in ${CMAKE_CURRENT_BINARY_DIR}/../atlas/atlas_f.h @ONLY ) execute_process( COMMAND ${CMAKE_COMMAND} -E create_symlink ${CMAKE_CURRENT_SOURCE_DIR}/atlas_f.fypp ${CMAKE_CURRENT_BINARY_DIR}/../atlas/atlas_f.fypp) install( FILES ${CMAKE_CURRENT_SOURCE_DIR}/atlas_f.fypp ${CMAKE_CURRENT_BINARY_DIR}/../atlas/atlas_f.h DESTINATION ${INSTALL_INCLUDE_DIR}/atlas ) ### fortran bindings function(generate_fortran_bindings output filename) set( options "" ) set( single_value_args OUTPUT MODULE ) set( multi_value_args "" ) cmake_parse_arguments( _PAR "${options}" "${single_value_args}" "${multi_value_args}" ${_FIRST_ARG} ${ARGN} ) get_filename_component(base ${filename} NAME_WE) set(base_abs ${CMAKE_CURRENT_SOURCE_DIR}/${base}) set(outfile ${CMAKE_CURRENT_BINARY_DIR}/${base}_c_binding.f90) if( _PAR_OUTPUT ) set(outfile ${_PAR_OUTPUT}) endif() set(${output} ${${output}} ${outfile} PARENT_SCOPE) if( NOT _PAR_MODULE ) set( _PAR_MODULE "atlas_${base}_c_binding" ) endif() set( FINT32 "integer(c_int)" ) set( FINT64 "integer(c_long)" ) set( F_GIDX ${FINT${ATLAS_BITS_GLOBAL}} ) set( F_IDX ${FINT${ATLAS_BITS_LOCAL}} ) add_custom_command( OUTPUT ${outfile} COMMAND ${PYTHON_EXECUTABLE} ${PROJECT_SOURCE_DIR}/tools/c2f.py ${CMAKE_CURRENT_SOURCE_DIR}/${filename} -o ${outfile} -m ${_PAR_MODULE} -t '{"idx_t":"${F_IDX}","gidx_t":"${F_GIDX}","atlas::idx_t":"${F_IDX}","atlas::gidx_t":"${F_GIDX}"}' DEPENDS ${filename} ) set_source_files_properties(${outfile} PROPERTIES GENERATED TRUE) endfunction() generate_fortran_bindings(FORTRAN_BINDINGS ../atlas/grid.h) generate_fortran_bindings(FORTRAN_BINDINGS ../atlas/grid/detail/grid/Grid.h MODULE atlas_grid_Grid_c_binding OUTPUT grid_Grid_c_binding.f90 ) generate_fortran_bindings(FORTRAN_BINDINGS ../atlas/grid/detail/grid/CubedSphere.h MODULE atlas_grid_CubedSphere_c_binding OUTPUT grid_CubedSphere_c_binding.f90 ) generate_fortran_bindings(FORTRAN_BINDINGS ../atlas/grid/detail/grid/Structured.h MODULE atlas_grid_Structured_c_binding OUTPUT grid_Structured_c_binding.f90 ) generate_fortran_bindings(FORTRAN_BINDINGS ../atlas/grid/detail/grid/Unstructured.h MODULE atlas_grid_Unstructured_c_binding OUTPUT grid_Unstructured_c_binding.f90 ) generate_fortran_bindings(FORTRAN_BINDINGS ../atlas/grid/detail/distribution/DistributionImpl.h MODULE atlas_distribution_c_binding OUTPUT distribution_c_binding.f90 ) generate_fortran_bindings(FORTRAN_BINDINGS ../atlas/grid/detail/vertical/VerticalInterface.h MODULE atlas_vertical_c_binding OUTPUT vertical_c_binding.f90 ) generate_fortran_bindings(FORTRAN_BINDINGS ../atlas/grid/Partitioner.h) generate_fortran_bindings(FORTRAN_BINDINGS ../atlas/projection/detail/ProjectionImpl.h MODULE atlas_projection_c_binding OUTPUT projection_c_binding.f90 ) generate_fortran_bindings(FORTRAN_BINDINGS ../atlas/domain/detail/Domain.h MODULE atlas_domain_c_binding OUTPUT domain_c_binding.f90 ) generate_fortran_bindings(FORTRAN_BINDINGS ../atlas/mesh/detail/MeshIntf.h MODULE atlas_mesh_c_binding OUTPUT atlas_mesh_c_binding.f90 ) generate_fortran_bindings(FORTRAN_BINDINGS ../atlas/mesh/detail/MeshBuilderIntf.h MODULE atlas_meshbuilder_c_binding OUTPUT atlas_meshbuilder_c_binding.f90 ) generate_fortran_bindings(FORTRAN_BINDINGS ../atlas/mesh/Nodes.h) generate_fortran_bindings(FORTRAN_BINDINGS ../atlas/mesh/Connectivity.h) generate_fortran_bindings(FORTRAN_BINDINGS ../atlas/mesh/HybridElements.h) generate_fortran_bindings(FORTRAN_BINDINGS ../atlas/mesh/Elements.h) generate_fortran_bindings(FORTRAN_BINDINGS ../atlas/mesh/ElementType.h) generate_fortran_bindings(FORTRAN_BINDINGS ../atlas/mesh/actions/BuildParallelFields.h) generate_fortran_bindings(FORTRAN_BINDINGS ../atlas/mesh/actions/BuildPeriodicBoundaries.h) generate_fortran_bindings(FORTRAN_BINDINGS ../atlas/mesh/actions/BuildHalo.h) generate_fortran_bindings(FORTRAN_BINDINGS ../atlas/mesh/actions/BuildEdges.h) generate_fortran_bindings(FORTRAN_BINDINGS ../atlas/mesh/actions/BuildDualMesh.h) generate_fortran_bindings(FORTRAN_BINDINGS ../atlas/mesh/actions/BuildNode2CellConnectivity.h) generate_fortran_bindings(FORTRAN_BINDINGS ../atlas/mesh/actions/WriteLoadBalanceReport.h) generate_fortran_bindings(FORTRAN_BINDINGS ../atlas/meshgenerator/detail/MeshGeneratorInterface.h MODULE atlas_meshgenerator_c_binding OUTPUT meshgenerator_c_binding.f90 ) generate_fortran_bindings(FORTRAN_BINDINGS ../atlas/output/Output.h) generate_fortran_bindings(FORTRAN_BINDINGS ../atlas/output/detail/GmshInterface.h MODULE atlas_output_gmsh_c_binding OUTPUT output_Gmsh_c_binding.f90) generate_fortran_bindings(FORTRAN_BINDINGS ../atlas/field/State.h) generate_fortran_bindings(FORTRAN_BINDINGS ../atlas/field/detail/FieldInterface.h MODULE atlas_field_c_binding OUTPUT field_c_binding.f90 ) generate_fortran_bindings(FORTRAN_BINDINGS ../atlas/field/detail/MultiFieldInterface.h MODULE atlas_multifield_c_binding OUTPUT multifield_c_binding.f90 ) generate_fortran_bindings(FORTRAN_BINDINGS ../atlas/field/FieldSet.h) generate_fortran_bindings(FORTRAN_BINDINGS ../atlas/functionspace/detail/FunctionSpaceInterface.h MODULE atlas_functionspace_c_binding OUTPUT functionspace_c_binding.f90 ) generate_fortran_bindings(FORTRAN_BINDINGS ../atlas/functionspace/detail/SpectralInterface.h MODULE atlas_functionspace_Spectral_c_binding OUTPUT functionspace_Spectral_c_binding.f90 ) generate_fortran_bindings(FORTRAN_BINDINGS ../atlas/functionspace/detail/StructuredColumnsInterface.h MODULE atlas_functionspace_StructuredColumns_c_binding OUTPUT functionspace_StructuredColumns_c_binding.f90 ) generate_fortran_bindings(FORTRAN_BINDINGS ../atlas/functionspace/detail/BlockStructuredColumnsInterface.h MODULE atlas_functionspace_BlockStructuredColumns_c_binding OUTPUT functionspace_BlockStructuredColumns_c_binding.f90 ) generate_fortran_bindings(FORTRAN_BINDINGS ../atlas/functionspace/detail/CellColumnsInterface.h MODULE atlas_functionspace_CellColumns_c_binding OUTPUT functionspace_CellColumns_c_binding.f90) generate_fortran_bindings(FORTRAN_BINDINGS ../atlas/functionspace/detail/NodeColumnsInterface.h MODULE atlas_functionspace_NodeColumns_c_binding OUTPUT functionspace_NodeColumns_c_binding.f90) generate_fortran_bindings(FORTRAN_BINDINGS ../atlas/functionspace/EdgeColumns.h MODULE atlas_functionspace_EdgeColumns_c_binding OUTPUT functionspace_EdgeColumns_c_binding.f90) generate_fortran_bindings(FORTRAN_BINDINGS ../atlas/functionspace/detail/PointCloudInterface.h MODULE atlas_functionspace_PointCloud_c_binding OUTPUT functionspace_PointCloud_c_binding.f90) generate_fortran_bindings(FORTRAN_BINDINGS ../atlas/redistribution/detail/RedistributionInterface.h MODULE atlas_redistribution_c_binding OUTPUT redistribution_c_binding.f90) generate_fortran_bindings(FORTRAN_BINDINGS ../atlas/grid/detail/grid/StencilComputerInterface.h MODULE atlas_grid_StencilComputer_c_binding OUTPUT grid_StencilComputer_c_binding.f90 ) if( atlas_HAVE_ATLAS_NUMERICS ) generate_fortran_bindings(FORTRAN_BINDINGS ../atlas/numerics/Nabla.h) generate_fortran_bindings(FORTRAN_BINDINGS ../atlas/numerics/Nabla.h) generate_fortran_bindings(FORTRAN_BINDINGS ../atlas/numerics/Method.h ) generate_fortran_bindings(FORTRAN_BINDINGS ../atlas/numerics/fvm/Method.h MODULE atlas_fvm_method_c_binding OUTPUT fvm_method_c_binding.f90 ) endif() if( atlas_HAVE_ATLAS_INTERPOLATION ) generate_fortran_bindings(FORTRAN_BINDINGS ../atlas/interpolation/Interpolation.h MODULE atlas_interpolation_c_binding OUTPUT interpolation_c_binding.f90 ) endif() if( atlas_HAVE_ATLAS_TRANS ) generate_fortran_bindings(FORTRAN_BINDINGS ../atlas/trans/detail/TransInterface.h MODULE atlas_trans_c_binding OUTPUT trans_c_binding.f90 ) endif() generate_fortran_bindings(FORTRAN_BINDINGS ../atlas/util/Allocate.h) generate_fortran_bindings(FORTRAN_BINDINGS ../atlas/util/Metadata.h) generate_fortran_bindings(FORTRAN_BINDINGS ../atlas/util/Config.h) generate_fortran_bindings(FORTRAN_BINDINGS ../atlas/util/Geometry.h) generate_fortran_bindings(FORTRAN_BINDINGS ../atlas/util/KDTree.h) generate_fortran_bindings(FORTRAN_BINDINGS ../atlas/output/detail/GmshIO.h) generate_fortran_bindings(FORTRAN_BINDINGS ../atlas/parallel/HaloExchange.h) generate_fortran_bindings(FORTRAN_BINDINGS ../atlas/parallel/GatherScatter.h) generate_fortran_bindings(FORTRAN_BINDINGS ../atlas/parallel/Checksum.h) generate_fortran_bindings(FORTRAN_BINDINGS internals/atlas_read_file.h) generate_fortran_bindings(FORTRAN_BINDINGS internals/Library.h) generate_fortran_bindings(FORTRAN_BINDINGS runtime/atlas_trace.h MODULE atlas_trace_c_binding OUTPUT atlas_trace_c_binding.f90 ) list( APPEND atlas_trans_srcs trans/atlas_Trans_module.F90 ) list( APPEND atlas_interpolation_srcs interpolation/atlas_Interpolation_module.F90 ) list( APPEND atlas_numerics_srcs numerics/atlas_Method_module.F90 numerics/atlas_fvm_module.F90 numerics/atlas_Nabla_module.F90 ) if( NOT atlas_HAVE_ATLAS_TRANS ) unset( atlas_trans_srcs ) endif() if( NOT atlas_HAVE_ATLAS_INTERPOLATION ) unset( atlas_interpolation_srcs ) endif() if( NOT atlas_HAVE_ATLAS_NUMERICS ) unset( atlas_numerics_srcs ) endif() ### atlas fortran lib ecbuild_add_library( TARGET atlas_f AUTO_VERSION SOURCES ${FORTRAN_BINDINGS} atlas_module.F90 util/atlas_functions_module.F90 util/atlas_kinds_module.F90 util/atlas_JSON_module.F90 util/atlas_Config_module.F90 util/atlas_Geometry_module.F90 util/atlas_KDTree_module.F90 util/atlas_Metadata_module.F90 util/atlas_allocate_module.F90 output/atlas_output_module.F90 domain/atlas_Domain_module.F90 functionspace/atlas_FunctionSpace_module.F90 functionspace/atlas_functionspace_EdgeColumns_module.F90 functionspace/atlas_functionspace_CellColumns_module.F90 functionspace/atlas_functionspace_NodeColumns_module.fypp functionspace/atlas_functionspace_StructuredColumns_module.F90 functionspace/atlas_functionspace_BlockStructuredColumns_module.F90 functionspace/atlas_functionspace_Spectral_module.F90 functionspace/atlas_functionspace_PointCloud_module.F90 field/atlas_FieldSet_module.fypp field/atlas_State_module.F90 field/atlas_Field_module.fypp field/atlas_MultiField_module.F90 grid/atlas_Grid_module.F90 grid/atlas_GridDistribution_module.F90 grid/atlas_Vertical_module.F90 grid/atlas_Partitioner_module.F90 grid/atlas_StencilComputer_module.F90 mesh/atlas_MeshBuilder_module.F90 mesh/atlas_MeshGenerator_module.F90 mesh/atlas_Mesh_module.F90 mesh/atlas_mesh_Nodes_module.F90 mesh/atlas_mesh_Edges_module.F90 mesh/atlas_mesh_Cells_module.F90 mesh/atlas_Connectivity_module.F90 mesh/atlas_HybridElements_module.F90 mesh/atlas_Elements_module.F90 mesh/atlas_ElementType_module.F90 mesh/atlas_mesh_actions_module.F90 parallel/atlas_GatherScatter_module.fypp parallel/atlas_Checksum_module.fypp parallel/atlas_HaloExchange_module.fypp projection/atlas_Projection_module.F90 redistribution/atlas_Redistribution_module.F90 internals/atlas_read_file.h internals/atlas_read_file.cc internals/Library.h internals/Library.cc runtime/atlas_trace.cc runtime/atlas_Trace_module.F90 ${atlas_trans_srcs} ${atlas_interpolation_srcs} ${atlas_numerics_srcs} PUBLIC_LIBS $ fckit PRIVATE_LIBS $<${atlas_HAVE_OMP_Fortran}:OpenMP::OpenMP_Fortran> eckit hic PUBLIC_INCLUDES $ $ $ $ $ $ PRIVATE_INCLUDES $ $ ) if( HAVE_ACC AND CMAKE_Fortran_COMPILER_ID MATCHES NVHPC ) target_link_options( atlas_f INTERFACE $<$:SHELL:${ACC_LINK_OPTIONS}> $<$:SHELL:${ACC_LINK_OPTIONS}> $<$:SHELL:${ACC_LINK_OPTIONS}> $<$:SHELL:${ACC_LINK_OPTIONS}> ) endif() fckit_target_preprocess_fypp( atlas_f DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/atlas_f.h.in ${CMAKE_CURRENT_SOURCE_DIR}/atlas_f.fypp ${CMAKE_CURRENT_SOURCE_DIR}/internals/atlas_generics.fypp ) atlas-0.46.0/src/atlas_f/domain/0000775000175000017500000000000015160212070016532 5ustar alastairalastairatlas-0.46.0/src/atlas_f/domain/atlas_Domain_module.F900000664000175000017500000001335615160212070022762 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. #include "atlas/atlas_f.h" module atlas_Domain_module use, intrinsic :: iso_c_binding, only : c_double use fckit_owned_object_module, only : fckit_owned_object use atlas_config_module, only : atlas_Config implicit none private :: fckit_owned_object private :: atlas_Config public :: atlas_Domain public :: atlas_LonLatRectangularDomain private !------------------------------------------------------------------------------ TYPE, extends(fckit_owned_object) :: atlas_Domain ! Purpose : ! ------- ! *atlas_Domain* : !------------------------------------------------------------------------------ contains procedure, public :: type => atlas_Domain__type procedure, public :: hash procedure, public :: spec #if FCKIT_FINAL_NOT_INHERITING final :: atlas_Domain__final_auto #endif END TYPE atlas_Domain interface atlas_Domain module procedure atlas_Domain__ctor_cptr module procedure atlas_Domain__ctor_config end interface !------------------------------------------------------------------------------ TYPE, extends(atlas_Domain) :: atlas_LonLatRectangularDomain ! Purpose : ! ------- ! *atlas_LonLatRectangularDomain* : !------------------------------------------------------------------------------ contains procedure, public :: north procedure, public :: west procedure, public :: south procedure, public :: east #if FCKIT_FINAL_NOT_INHERITING final :: atlas_LonLatRectangularDomain__final_auto #endif END TYPE atlas_LonLatRectangularDomain interface atlas_LonLatRectangularDomain module procedure atlas_LonLatRectangularDomain__ctor_cptr end interface !------------------------------------------------------------------------------ !======================================================== contains !======================================================== function atlas_Domain__ctor_cptr(cptr) result(this) use, intrinsic :: iso_c_binding, only : c_ptr type(atlas_Domain) :: this type(c_ptr), intent(in) :: cptr call this%reset_c_ptr( cptr ) call this%return() end function function atlas_Domain__ctor_config(config) result(this) use atlas_Domain_c_binding use, intrinsic :: iso_c_binding, only : c_ptr type(atlas_Domain) :: this type(atlas_Config) :: config call this%reset_c_ptr( atlas__Domain__ctor_config(config%CPTR_PGIBUG_B) ) call this%return() end function function atlas_Domain__type(this) result(type_) use atlas_Domain_c_binding use fckit_c_interop_module, only : c_ptr_to_string, c_ptr_free use, intrinsic :: iso_c_binding, only : c_ptr class(atlas_Domain), intent(in) :: this character(len=:), allocatable :: type_ type(c_ptr) :: type_c_str integer :: size call atlas__Domain__type(this%CPTR_PGIBUG_A, type_c_str, size ) type_ = c_ptr_to_string(type_c_str) call c_ptr_free(type_c_str) end function function hash(this) use atlas_Domain_c_binding use fckit_c_interop_module, only : c_ptr_to_string, c_ptr_free use, intrinsic :: iso_c_binding, only : c_ptr class(atlas_Domain), intent(in) :: this character(len=:), allocatable :: hash type(c_ptr) :: hash_c_str integer :: size call atlas__Domain__hash(this%CPTR_PGIBUG_A, hash_c_str, size ) hash = c_ptr_to_string(hash_c_str) call c_ptr_free(hash_c_str) end function function spec(this) use atlas_Domain_c_binding class(atlas_Domain), intent(in) :: this type(atlas_Config) :: spec spec = atlas_Config( atlas__Domain__spec(this%CPTR_PGIBUG_A) ) call spec%return() end function !------------------------------------------------------------------------------ function atlas_LonLatRectangularDomain__ctor_cptr(cptr) result(this) use, intrinsic :: iso_c_binding, only : c_ptr type(atlas_LonLatRectangularDomain) :: this type(c_ptr), intent(in) :: cptr call this%reset_c_ptr( cptr ) call this%return() end function function north(this) result(value) use atlas_Domain_c_binding class(atlas_LonLatRectangularDomain), intent(in) :: this real(c_double) :: value value = atlas__LonLatRectangularDomain__north(this%CPTR_PGIBUG_A) end function function west(this) result(value) use atlas_Domain_c_binding class(atlas_LonLatRectangularDomain), intent(in) :: this real(c_double) :: value value = atlas__LonLatRectangularDomain__west(this%CPTR_PGIBUG_A) end function function south(this) result(value) use atlas_Domain_c_binding class(atlas_LonLatRectangularDomain), intent(in) :: this real(c_double) :: value value = atlas__LonLatRectangularDomain__south(this%CPTR_PGIBUG_A) end function function east(this) result(value) use atlas_Domain_c_binding class(atlas_LonLatRectangularDomain), intent(in) :: this real(c_double) :: value value = atlas__LonLatRectangularDomain__east(this%CPTR_PGIBUG_A) end function !------------------------------------------------------------------------------ #if FCKIT_FINAL_NOT_INHERITING ATLAS_FINAL subroutine atlas_Domain__final_auto(this) type(atlas_Domain), intent(inout) :: this #if FCKIT_FINAL_DEBUGGING write(0,*) "atlas_Domain__final_auto" #endif #if FCKIT_FINAL_NOT_PROPAGATING call this%final() #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine ATLAS_FINAL subroutine atlas_LonLatRectangularDomain__final_auto(this) type(atlas_LonLatRectangularDomain), intent(inout) :: this #if FCKIT_FINAL_DEBUGGING write(0,*) "atlas_LonLatRectangularDomain__final_auto" #endif #if FCKIT_FINAL_NOT_PROPAGATING call this%final() #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine #endif end module atlas_Domain_module atlas-0.46.0/src/atlas_f/functionspace/0000775000175000017500000000000015160212070020124 5ustar alastairalastairatlas-0.46.0/src/atlas_f/functionspace/atlas_functionspace_PointCloud_module.F900000664000175000017500000001205115160212070030135 0ustar alastairalastair! (C) Copyright 2020 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. #include "atlas/atlas_f.h" module atlas_functionspace_PointCloud_module use fckit_c_interop_module, only : c_str, c_ptr_to_string, c_ptr_free use atlas_functionspace_module, only : atlas_FunctionSpace use atlas_Grid_module, only: atlas_Grid use atlas_Field_module, only: atlas_Field use atlas_FieldSet_module, only: atlas_FieldSet use atlas_kinds_module, only: ATLAS_KIND_GIDX implicit none private :: c_str, c_ptr_to_string, c_ptr_free private :: atlas_FunctionSpace private :: atlas_Field private :: atlas_FieldSet private :: atlas_Grid private :: ATLAS_KIND_GIDX public :: atlas_functionspace_PointCloud private !------------------------------------------------------------------------------ TYPE, extends(atlas_FunctionSpace) :: atlas_functionspace_PointCloud ! Purpose : ! ------- ! *atlas_functionspace_PointCloud* : Interpretes point cloud fields ! Methods : ! ------- ! Author : ! ------ ! March-2020 Benjamin Menetrier *IRIT-JCSDA* !------------------------------------------------------------------------------ contains procedure, public :: size => size_ procedure, public :: lonlat END TYPE atlas_functionspace_PointCloud interface atlas_functionspace_PointCloud module procedure ctor_cptr module procedure ctor_lonlat module procedure ctor_lonlat_ghost module procedure ctor_fieldset module procedure ctor_grid end interface !------------------------------------------------------------------------------ !======================================================== contains !======================================================== !------------------------------------------------------------------------------ function ctor_cptr(cptr) result(this) use, intrinsic :: iso_c_binding, only : c_ptr type(atlas_functionspace_PointCloud) :: this type(c_ptr), intent(in) :: cptr call this%reset_c_ptr( cptr ) call this%return() end function !------------------------------------------------------------------------------ function ctor_lonlat(lonlat) result(this) use atlas_functionspace_PointCloud_c_binding type(atlas_functionspace_PointCloud) :: this class(atlas_Field), intent(in) :: lonlat call this%reset_c_ptr( atlas__functionspace__PointCloud__new__lonlat( lonlat%CPTR_PGIBUG_A ) ) call this%return() end function !------------------------------------------------------------------------------ function ctor_lonlat_ghost(lonlat,ghost) result(this) use atlas_functionspace_PointCloud_c_binding type(atlas_functionspace_PointCloud) :: this class(atlas_Field), intent(in) :: lonlat class(atlas_Field), intent(in) :: ghost call this%reset_c_ptr( atlas__functionspace__PointCloud__new__lonlat_ghost( lonlat%CPTR_PGIBUG_A, ghost%CPTR_PGIBUG_A ) ) call this%return() end function !------------------------------------------------------------------------------ function ctor_fieldset(fset) result(this) use atlas_functionspace_PointCloud_c_binding type(atlas_functionspace_PointCloud) :: this class(atlas_FieldSet), intent(in) :: fset call this%reset_c_ptr( atlas__functionspace__PointCloud__new__fieldset( fset%CPTR_PGIBUG_A ) ) call this%return() end function !------------------------------------------------------------------------------ function ctor_grid(grid) result(this) use atlas_functionspace_PointCloud_c_binding type(atlas_functionspace_PointCloud) :: this class(atlas_Grid), intent(in) :: grid call this%reset_c_ptr( atlas__functionspace__PointCloud__new__grid( grid%CPTR_PGIBUG_A ) ) call this%return() end function !------------------------------------------------------------------------------ function size_(this) use atlas_functionspace_PointCloud_c_binding integer :: size_ class(atlas_functionspace_PointCloud), intent(in) :: this size_ = atlas__fs__PointCloud__size(this%CPTR_PGIBUG_A) end function !------------------------------------------------------------------------------ function lonlat(this) result(field) use atlas_functionspace_PointCloud_c_binding type(atlas_Field) :: field class(atlas_functionspace_PointCloud), intent(in) :: this field = atlas_Field( atlas__fs__PointCloud__lonlat(this%CPTR_PGIBUG_A) ) call field%return() end function !------------------------------------------------------------------------------- #if FCKIT_FINAL_NOT_INHERITING ATLAS_FINAL subroutine atlas_functionspace_PointCloud__final_auto(this) type(atlas_functionspace_PointCloud), intent(inout) :: this #if FCKIT_FINAL_DEBUGGING write(0,*) "atlas_functionspace_PointCloud__final_auto" #endif #if FCKIT_FINAL_NOT_PROPAGATING call this%final() #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine #endif !------------------------------------------------------------------------------ end module atlas_functionspace_PointCloud_module atlas-0.46.0/src/atlas_f/functionspace/atlas_functionspace_StructuredColumns_module.F900000664000175000017500000005001215160212070031561 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. #include "atlas/atlas_f.h" module atlas_functionspace_StructuredColumns_module use, intrinsic :: iso_c_binding, only : c_ptr, c_double use fckit_c_interop_module, only : c_str, c_ptr_to_string, c_ptr_free use atlas_functionspace_module, only : atlas_FunctionSpace use atlas_Field_module, only: atlas_Field use atlas_FieldSet_module, only: atlas_FieldSet use atlas_Grid_module, only: atlas_Grid use atlas_Config_module, only: atlas_Config use atlas_kinds_module, only : ATLAS_KIND_IDX use fckit_owned_object_module, only : fckit_owned_object use atlas_GridDistribution_module, only : atlas_GridDistribution use atlas_Partitioner_module, only : atlas_Partitioner use atlas_Vertical_module, only : atlas_Vertical implicit none private :: c_ptr, c_double private :: c_str, c_ptr_to_string, c_ptr_free private :: atlas_FunctionSpace private :: atlas_Field private :: atlas_FieldSet private :: atlas_Grid private :: atlas_GridDistribution private :: atlas_Partitioner private :: atlas_Vertical private :: atlas_Config private :: fckit_owned_object public :: atlas_functionspace_StructuredColumns private !------------------------------------------------------------------------------ TYPE, extends(atlas_FunctionSpace) :: atlas_functionspace_StructuredColumns ! Purpose : ! ------- ! *atlas_functionspace_StructuredColumns* : Interpretes spectral fields ! Methods : ! ------- ! Author : ! ------ ! August-2015 Willem Deconinck *ECMWF* !------------------------------------------------------------------------------ integer(ATLAS_KIND_IDX), pointer, public :: index(:,:) contains procedure, public :: assignment_operator_hook procedure, private :: gather_fieldset procedure, private :: gather_field generic, public :: gather => gather_fieldset, gather_field procedure, private :: scatter_fieldset procedure, private :: scatter_field generic, public :: scatter => scatter_fieldset, scatter_field procedure, private :: checksum_fieldset procedure, private :: checksum_field generic, public :: checksum => checksum_fieldset, checksum_field procedure :: j_begin procedure :: j_end procedure :: i_begin procedure :: i_end procedure :: j_begin_halo procedure :: j_end_halo procedure :: i_begin_halo procedure :: i_end_halo procedure :: size => get_size procedure :: size_owned => get_size_owned procedure :: levels procedure :: xy !! Return xy coordinate field procedure :: z !! Return z coordinate field procedure :: partition !! Return partition field procedure :: global_index !! Return global_index field procedure :: index_i !! Return index_i field procedure :: index_j !! Return index_j field procedure :: grid procedure, private :: set_index #if FCKIT_FINAL_NOT_INHERITING final :: StructuredColumns__final_auto #endif END TYPE atlas_functionspace_StructuredColumns interface atlas_functionspace_StructuredColumns module procedure ctor_cptr module procedure ctor_grid module procedure ctor_grid_dist module procedure ctor_grid_dist_config module procedure ctor_grid_dist_levels module procedure ctor_grid_dist_vertical module procedure ctor_grid_part module procedure ctor_grid_part_levels module procedure ctor_grid_part_vertical end interface !------------------------------------------------------------------------------ !======================================================== contains !======================================================== subroutine assignment_operator_hook(this,other) class(atlas_functionspace_StructuredColumns) :: this class(fckit_owned_object) :: other call this%set_index() FCKIT_SUPPRESS_UNUSED(other) end subroutine function ctor_cptr(cptr) result(this) type(atlas_functionspace_StructuredColumns) :: this type(c_ptr), intent(in) :: cptr call this%reset_c_ptr( cptr ) call this%set_index() call this%return() end function function empty_config() result(config) type(atlas_Config) :: config config = atlas_Config() call config%return() end function function ctor_grid(grid, halo, levels) result(this) use atlas_functionspace_StructuredColumns_c_binding type(atlas_functionspace_StructuredColumns) :: this class(atlas_Grid), intent(in) :: grid integer, optional :: halo integer, optional :: levels type(atlas_Config) :: config config = empty_config() ! Due to PGI compiler bug, we have to do this instead of "config = atlas_Config()"" if( present(halo) ) call config%set("halo",halo) if( present(levels) ) call config%set("levels",levels) call this%reset_c_ptr( atlas__functionspace__StructuredColumns__new__grid( grid%CPTR_PGIBUG_A, & & config%CPTR_PGIBUG_B ) ) call this%set_index() call config%final() call this%return() end function function ctor_grid_dist(grid, distribution, halo, levels) result(this) use atlas_functionspace_StructuredColumns_c_binding type(atlas_functionspace_StructuredColumns) :: this class(atlas_Grid), intent(in) :: grid type(atlas_griddistribution), intent(in) :: distribution integer, optional :: halo integer, optional :: levels type(atlas_Config) :: config config = empty_config() ! Due to PGI compiler bug, we have to do this instead of "config = atlas_Config()"" if( present(halo) ) call config%set("halo",halo) if( present(levels) ) call config%set("levels",levels) call this%reset_c_ptr( atlas__functionspace__StructuredColumns__new__grid_dist( & & grid%CPTR_PGIBUG_A, distribution%CPTR_PGIBUG_A, config%CPTR_PGIBUG_B ) ) call this%set_index() call config%final() call this%return() end function function ctor_grid_dist_levels(grid, distribution, levels, halo) result(this) use atlas_functionspace_StructuredColumns_c_binding type(atlas_functionspace_StructuredColumns) :: this class(atlas_Grid), intent(in) :: grid type(atlas_griddistribution), intent(in) :: distribution integer, optional :: halo real(c_double) :: levels(:) type(atlas_Config) :: config type(atlas_Vertical) :: vertical config = empty_config() ! Due to PGI compiler bug, we have to do this insted of "config = atlas_Config()"" if( present(halo) ) call config%set("halo",halo) call config%set("levels",size(levels)) vertical = atlas_Vertical(levels) call this%reset_c_ptr( atlas__functionspace__StructuredColumns__new__grid_dist_vert( & & grid%CPTR_PGIBUG_A, distribution%CPTR_PGIBUG_A, vertical%CPTR_PGIBUG_B, & & config%CPTR_PGIBUG_B ) ) call this%set_index() call config%final() call vertical%final() call this%return() end function function ctor_grid_dist_vertical(grid, distribution, vertical, halo) result(this) use atlas_functionspace_StructuredColumns_c_binding type(atlas_functionspace_StructuredColumns) :: this class(atlas_Grid), intent(in) :: grid type(atlas_griddistribution), intent(in) :: distribution integer, optional :: halo type(atlas_Vertical) :: vertical type(atlas_Config) :: config config = empty_config() ! Due to PGI compiler bug, we have to do this insted of "config = atlas_Config()"" if( present(halo) ) call config%set("halo",halo) call config%set("levels",vertical%size()) call this%reset_c_ptr( atlas__functionspace__StructuredColumns__new__grid_dist_vert( & & grid%CPTR_PGIBUG_A, distribution%CPTR_PGIBUG_A, vertical%CPTR_PGIBUG_B, & & config%CPTR_PGIBUG_B ) ) call this%set_index() call config%final() call this%return() end function function ctor_grid_dist_config(grid, distribution, config) result(this) use atlas_functionspace_StructuredColumns_c_binding type(atlas_functionspace_StructuredColumns) :: this class(atlas_Grid), intent(in) :: grid type(atlas_griddistribution), intent(in) :: distribution type(atlas_Config), intent (in) :: config call this%reset_c_ptr(atlas__functionspace__StructuredColumns__new__grid_dist_config( & & grid%CPTR_PGIBUG_A, distribution%CPTR_PGIBUG_A, & & config%CPTR_PGIBUG_B ) ) call this%set_index() call this%return() end function function ctor_grid_part(grid, partitioner, halo, levels) result(this) use atlas_functionspace_StructuredColumns_c_binding type(atlas_functionspace_StructuredColumns) :: this class(atlas_Grid), intent(in) :: grid type(atlas_Partitioner), intent(in) :: partitioner integer, optional :: halo integer, optional :: levels type(atlas_Config) :: config config = empty_config() ! Due to PGI compiler bug, we have to do this instead of "config = atlas_Config()"" if( present(halo) ) call config%set("halo",halo) if( present(levels) ) call config%set("levels",levels) call this%reset_c_ptr( atlas__functionspace__StructuredColumns__new__grid_part( & & grid%CPTR_PGIBUG_A, partitioner%CPTR_PGIBUG_A, config%CPTR_PGIBUG_B ) ) call this%set_index() call config%final() call this%return() end function function ctor_grid_part_levels(grid, partitioner, levels, halo) result(this) use atlas_functionspace_StructuredColumns_c_binding type(atlas_functionspace_StructuredColumns) :: this class(atlas_Grid), intent(in) :: grid type(atlas_Partitioner), intent(in) :: partitioner integer, optional :: halo real(c_double) :: levels(:) type(atlas_Config) :: config type(atlas_Vertical) :: vertical config = empty_config() ! Due to PGI compiler bug, we have to do this insted of "config = atlas_Config()"" if( present(halo) ) call config%set("halo",halo) call config%set("levels",size(levels)) vertical = atlas_Vertical(levels) call this%reset_c_ptr( atlas__functionspace__StructuredColumns__new__grid_part_vert( & & grid%CPTR_PGIBUG_A, partitioner%CPTR_PGIBUG_A, vertical%CPTR_PGIBUG_B, & & config%CPTR_PGIBUG_B ) ) call this%set_index() call config%final() call vertical%final() call this%return() end function function ctor_grid_part_vertical(grid, partitioner, vertical, halo) result(this) use atlas_functionspace_StructuredColumns_c_binding type(atlas_functionspace_StructuredColumns) :: this class(atlas_Grid), intent(in) :: grid type(atlas_Partitioner), intent(in) :: partitioner integer, optional :: halo type(atlas_Vertical) :: vertical type(atlas_Config) :: config config = empty_config() ! Due to PGI compiler bug, we have to do this insted of "config = atlas_Config()"" if( present(halo) ) call config%set("halo",halo) call config%set("levels",vertical%size()) call this%reset_c_ptr( atlas__functionspace__StructuredColumns__new__grid_part_vert( & & grid%CPTR_PGIBUG_A, partitioner%CPTR_PGIBUG_A, vertical%CPTR_PGIBUG_B, & & config%CPTR_PGIBUG_B ) ) call this%set_index() call config%final() call this%return() end function subroutine gather_field(this,local,global) use atlas_functionspace_StructuredColumns_c_binding class(atlas_functionspace_StructuredColumns), intent(in) :: this type(atlas_Field), intent(in) :: local type(atlas_Field), intent(inout) :: global call atlas__functionspace__StructuredColumns__gather_field(this%CPTR_PGIBUG_A,local%CPTR_PGIBUG_A,global%CPTR_PGIBUG_A) end subroutine subroutine gather_fieldset(this,local,global) use atlas_functionspace_StructuredColumns_c_binding class(atlas_functionspace_StructuredColumns), intent(in) :: this type(atlas_FieldSet), intent(in) :: local type(atlas_FieldSet), intent(inout) :: global call atlas__functionspace__StructuredColumns__gather_fieldset(this%CPTR_PGIBUG_A,local%CPTR_PGIBUG_A,global%CPTR_PGIBUG_A) end subroutine subroutine scatter_field(this,global,local) use atlas_functionspace_StructuredColumns_c_binding class(atlas_functionspace_StructuredColumns), intent(in) :: this type(atlas_Field), intent(in) :: global type(atlas_Field), intent(inout) :: local call atlas__functionspace__StructuredColumns__scatter_field(this%CPTR_PGIBUG_A,global%CPTR_PGIBUG_A,local%CPTR_PGIBUG_A) end subroutine subroutine scatter_fieldset(this,global,local) use atlas_functionspace_StructuredColumns_c_binding class(atlas_functionspace_StructuredColumns), intent(in) :: this type(atlas_FieldSet), intent(in) :: global type(atlas_FieldSet), intent(inout) :: local call atlas__functionspace__StructuredColumns__scatter_fieldset(this%CPTR_PGIBUG_A,global%CPTR_PGIBUG_A,local%CPTR_PGIBUG_A) end subroutine function checksum_fieldset(this,fieldset) result(checksum) use, intrinsic :: iso_c_binding use atlas_functionspace_StructuredColumns_c_binding character(len=:), allocatable :: checksum class(atlas_functionspace_StructuredColumns), intent(in) :: this type(atlas_FieldSet), intent(in) :: fieldset type(c_ptr) :: checksum_cptr integer(ATLAS_KIND_IDX) :: checksum_size integer(c_int) :: checksum_allocated call atlas__fs__StructuredColumns__checksum_fieldset( & & this%CPTR_PGIBUG_A,fieldset%CPTR_PGIBUG_A,checksum_cptr,checksum_size,checksum_allocated) allocate(character(len=checksum_size) :: checksum ) checksum = c_ptr_to_string(checksum_cptr) if( checksum_allocated == 1 ) call c_ptr_free(checksum_cptr) end function function checksum_field(this,field) result(checksum) use, intrinsic :: iso_c_binding use atlas_functionspace_StructuredColumns_c_binding character(len=:), allocatable :: checksum class(atlas_functionspace_StructuredColumns), intent(in) :: this type(atlas_Field), intent(in) :: field type(c_ptr) :: checksum_cptr integer(ATLAS_KIND_IDX) :: checksum_size integer(c_int) :: checksum_allocated call atlas__fs__StructuredColumns__checksum_field( & & this%CPTR_PGIBUG_A,field%CPTR_PGIBUG_A,checksum_cptr,checksum_size,checksum_allocated) allocate(character(len=checksum_size) :: checksum ) checksum = c_ptr_to_string(checksum_cptr) if( checksum_allocated == 1 ) call c_ptr_free(checksum_cptr) end function subroutine set_index(this) use, intrinsic :: iso_c_binding, only : c_ptr, c_f_pointer use atlas_functionspace_StructuredColumns_c_binding class(atlas_functionspace_StructuredColumns), intent(inout) :: this type(c_ptr) :: index_cptr integer(ATLAS_KIND_IDX), pointer :: index_fptr(:) integer(ATLAS_KIND_IDX) :: i_min, i_max, j_min, j_max integer(ATLAS_KIND_IDX) :: ni, nj call atlas__fs__StructuredColumns__index_host( this%CPTR_PGIBUG_A, index_cptr, i_min, i_max, j_min, j_max ) ni = i_max-i_min+1; nj = j_max-j_min+1; call c_f_pointer( index_cptr, index_fptr, (/ni*nj/) ) this%index(i_min:i_max,j_min:j_max) => index_fptr(:) end subroutine function j_begin(this) result(j) use atlas_functionspace_StructuredColumns_c_binding integer(ATLAS_KIND_IDX) :: j class(atlas_functionspace_StructuredColumns), intent(in) :: this j = atlas__fs__StructuredColumns__j_begin(this%CPTR_PGIBUG_A) end function function j_end(this) result(j) use atlas_functionspace_StructuredColumns_c_binding integer(ATLAS_KIND_IDX) :: j class(atlas_functionspace_StructuredColumns), intent(in) :: this j = atlas__fs__StructuredColumns__j_end(this%CPTR_PGIBUG_A) end function function i_begin(this,j) result(i) use atlas_functionspace_StructuredColumns_c_binding integer(ATLAS_KIND_IDX) :: i integer(ATLAS_KIND_IDX), intent(in) :: j class(atlas_functionspace_StructuredColumns), intent(in) :: this i = atlas__fs__StructuredColumns__i_begin(this%CPTR_PGIBUG_A,j) end function function i_end(this,j) result(i) use atlas_functionspace_StructuredColumns_c_binding integer(ATLAS_KIND_IDX) :: i integer(ATLAS_KIND_IDX), intent(in) :: j class(atlas_functionspace_StructuredColumns), intent(in) :: this i = atlas__fs__StructuredColumns__i_end(this%CPTR_PGIBUG_A,j) end function function j_begin_halo(this) result(j) use atlas_functionspace_StructuredColumns_c_binding integer(ATLAS_KIND_IDX) :: j class(atlas_functionspace_StructuredColumns), intent(in) :: this j = atlas__fs__StructuredColumns__j_begin_halo(this%CPTR_PGIBUG_A) end function function j_end_halo(this) result(j) use atlas_functionspace_StructuredColumns_c_binding integer(ATLAS_KIND_IDX) :: j class(atlas_functionspace_StructuredColumns), intent(in) :: this j = atlas__fs__StructuredColumns__j_end_halo(this%CPTR_PGIBUG_A) end function function i_begin_halo(this,j) result(i) use atlas_functionspace_StructuredColumns_c_binding integer(ATLAS_KIND_IDX) :: i integer(ATLAS_KIND_IDX), intent(in) :: j class(atlas_functionspace_StructuredColumns), intent(in) :: this i = atlas__fs__StructuredColumns__i_begin_halo(this%CPTR_PGIBUG_A,j) end function function i_end_halo(this,j) result(i) use atlas_functionspace_StructuredColumns_c_binding integer(ATLAS_KIND_IDX) :: i integer(ATLAS_KIND_IDX), intent(in) :: j class(atlas_functionspace_StructuredColumns), intent(in) :: this i = atlas__fs__StructuredColumns__i_end_halo(this%CPTR_PGIBUG_A,j) end function function get_size(this) result(size) use atlas_functionspace_StructuredColumns_c_binding integer(ATLAS_KIND_IDX) :: size class(atlas_functionspace_StructuredColumns), intent(in) :: this size = atlas__fs__StructuredColumns__size(this%CPTR_PGIBUG_A) end function function get_size_owned(this) result(size) use atlas_functionspace_StructuredColumns_c_binding integer(ATLAS_KIND_IDX) :: size class(atlas_functionspace_StructuredColumns), intent(in) :: this size = atlas__fs__StructuredColumns__sizeOwned(this%CPTR_PGIBUG_A) end function function levels(this) use atlas_functionspace_StructuredColumns_c_binding integer(ATLAS_KIND_IDX) :: levels class(atlas_functionspace_StructuredColumns), intent(in) :: this levels = atlas__fs__StructuredColumns__levels(this%CPTR_PGIBUG_A) end function function xy(this) result(field) use atlas_functionspace_StructuredColumns_c_binding type(atlas_Field) :: field class(atlas_functionspace_StructuredColumns), intent(in) :: this field = atlas_Field( atlas__fs__StructuredColumns__xy(this%CPTR_PGIBUG_A) ) call field%return() end function function z(this) result(field) use atlas_functionspace_StructuredColumns_c_binding type(atlas_Field) :: field class(atlas_functionspace_StructuredColumns), intent(in) :: this field = atlas_Field( atlas__fs__StructuredColumns__z(this%CPTR_PGIBUG_A) ) call field%return() end function function partition(this) result(field) use atlas_functionspace_StructuredColumns_c_binding type(atlas_Field) :: field class(atlas_functionspace_StructuredColumns), intent(in) :: this field = atlas_Field( atlas__fs__StructuredColumns__partition(this%CPTR_PGIBUG_A) ) call field%return() end function function global_index(this) result(field) use atlas_functionspace_StructuredColumns_c_binding type(atlas_Field) :: field class(atlas_functionspace_StructuredColumns), intent(in) :: this field = atlas_Field( atlas__fs__StructuredColumns__global_index(this%CPTR_PGIBUG_A) ) call field%return() end function function index_i(this) result(field) use atlas_functionspace_StructuredColumns_c_binding type(atlas_Field) :: field class(atlas_functionspace_StructuredColumns), intent(in) :: this field = atlas_Field( atlas__fs__StructuredColumns__index_i(this%CPTR_PGIBUG_A) ) call field%return() end function function index_j(this) result(field) use atlas_functionspace_StructuredColumns_c_binding type(atlas_Field) :: field class(atlas_functionspace_StructuredColumns), intent(in) :: this field = atlas_Field( atlas__fs__StructuredColumns__index_j(this%CPTR_PGIBUG_A) ) call field%return() end function function grid(this) use atlas_functionspace_StructuredColumns_c_binding type(atlas_Grid) :: grid class(atlas_functionspace_StructuredColumns), intent(in) :: this grid = atlas_Grid( atlas__fs__StructuredColumns__grid(this%CPTR_PGIBUG_A) ) call grid%return() end function !------------------------------------------------------------------------------- #if FCKIT_FINAL_NOT_INHERITING ATLAS_FINAL subroutine StructuredColumns__final_auto(this) type(atlas_functionspace_StructuredColumns), intent(inout) :: this #if FCKIT_FINAL_DEBUGGING write(0,*) "atlas_functionspace_StructuredColumns__final_auto" #endif #if FCKIT_FINAL_NOT_PROPAGATING call this%final() #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine #endif end module atlas_functionspace_StructuredColumns_module atlas-0.46.0/src/atlas_f/functionspace/atlas_FunctionSpace_module.F900000664000175000017500000002221515160212070025700 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. #include "atlas/atlas_f.h" module atlas_functionspace_module use fckit_owned_object_module, only : fckit_owned_object use atlas_field_module, only : atlas_Field use atlas_fieldset_module, only : atlas_FieldSet use atlas_config_module, only : atlas_Config implicit none private :: fckit_owned_object private :: atlas_Field private :: atlas_FieldSet private :: atlas_Config public :: atlas_FunctionSpace private !------------------------------------------------------------------------------ TYPE, extends(fckit_owned_object) :: atlas_FunctionSpace ! Purpose : ! ------- ! *FunctionSpace* : ! Describes how nodes are ordered ! Describes how parallelisation for fields is done ! Methods : ! ------- ! name : The name or tag this function space was created with ! Author : ! ------ ! 20-Nov-2013 Willem Deconinck *ECMWF* !------------------------------------------------------------------------------ contains procedure, public :: name => atlas_FunctionSpace__name procedure, private :: create_field_args procedure, private :: create_field_template procedure, private :: deprecated_create_field_1 ! deprecated procedure, private :: deprecated_create_field_2 ! deprecated procedure, private :: halo_exchange_field procedure, private :: halo_exchange_fieldset procedure, private :: adjoint_halo_exchange_field procedure, private :: adjoint_halo_exchange_fieldset generic, public :: create_field => & & create_field_args, & & create_field_template, & & deprecated_create_field_1, & & deprecated_create_field_2 generic, public :: halo_exchange => halo_exchange_field, halo_exchange_fieldset generic, public :: adjoint_halo_exchange => adjoint_halo_exchange_field, adjoint_halo_exchange_fieldset #if FCKIT_FINAL_NOT_INHERITING final :: atlas_FunctionSpace__final_auto #endif END TYPE atlas_FunctionSpace interface atlas_FunctionSpace module procedure atlas_FunctionSpace__cptr end interface !======================================================== contains !======================================================== function atlas_FunctionSpace__cptr(cptr) result(this) use, intrinsic :: iso_c_binding, only : c_ptr type(atlas_FunctionSpace) :: this type(c_ptr), intent(in) :: cptr call this%reset_c_ptr( cptr ) call this%return() end function function atlas_FunctionSpace__name(this) result(name) use atlas_functionspace_c_binding use fckit_c_interop_module, only : c_ptr_to_string, c_ptr_free use, intrinsic :: iso_c_binding, only : c_ptr class(atlas_FunctionSpace), intent(in) :: this character(len=:), allocatable :: name type(c_ptr) :: name_c_str integer :: size call atlas__FunctionSpace__name(this%CPTR_PGIBUG_A, name_c_str, size ) name = c_ptr_to_string(name_c_str) call c_ptr_free(name_c_str) end function function create_field_args(this,kind,name,levels,variables,type,alignment,global,owner) result(field) use atlas_functionspace_c_binding use, intrinsic :: iso_c_binding, only : c_int type(atlas_Field) :: field class(atlas_Functionspace), intent(in) :: this integer, intent(in) :: kind character(len=*), intent(in), optional :: name integer(c_int), intent(in), optional :: levels integer(c_int), intent(in), optional :: variables character(len=*), intent(in), optional :: type integer(c_int), intent(in), optional :: alignment logical, intent(in), optional :: global integer(c_int), intent(in), optional :: owner type(atlas_Config) :: options options = atlas_Config() call options%set("datatype",kind) if( present(name) ) call options%set("name",name) if( present(owner) ) call options%set("owner",owner) if( present(global) ) call options%set("global",global) if( present(levels) ) call options%set("levels",levels) if( present(variables) ) call options%set("variables",variables) if( present(type) ) call options%set("type",type) if( present(alignment) ) call options%set("alignment",alignment) field = atlas_Field( atlas__FunctionSpace__create_field( this%CPTR_PGIBUG_A, options%CPTR_PGIBUG_B ) ) call field%return() call options%final() end function !------------------------------------------------------------------------------ function create_field_template(this,template,name,global,owner) result(field) use atlas_functionspace_c_binding use, intrinsic :: iso_c_binding, only : c_int type(atlas_Field) :: field class(atlas_Functionspace), intent(in) :: this type(atlas_Field), intent(in) :: template character(len=*), intent(in), optional :: name logical, intent(in), optional :: global integer(c_int), intent(in), optional :: owner type(atlas_Config) :: options options = atlas_Config() if( present(name) ) call options%set("name",name) if( present(owner) ) call options%set("owner",owner) if( present(global) ) call options%set("global",global) field = atlas_Field( atlas__FunctionSpace__create_field_template( & & this%CPTR_PGIBUG_A, template%CPTR_PGIBUG_A,options%CPTR_PGIBUG_B) ) call options%final() call field%return() end function !------------------------------------------------------------------------------ subroutine halo_exchange_fieldset(this,fieldset) use atlas_functionspace_c_binding class(atlas_Functionspace), intent(in) :: this type(atlas_FieldSet), intent(inout) :: fieldset call atlas__FunctionSpace__halo_exchange_fieldset(this%CPTR_PGIBUG_A,fieldset%CPTR_PGIBUG_A) end subroutine !------------------------------------------------------------------------------ subroutine halo_exchange_field(this,field) use atlas_functionspace_c_binding class(atlas_Functionspace), intent(in) :: this type(atlas_Field), intent(inout) :: field call atlas__FunctionSpace__halo_exchange_field(this%CPTR_PGIBUG_A,field%CPTR_PGIBUG_A) end subroutine !------------------------------------------------------------------------------ subroutine adjoint_halo_exchange_fieldset(this,fieldset) use atlas_functionspace_c_binding class(atlas_Functionspace), intent(in) :: this type(atlas_FieldSet), intent(inout) :: fieldset call atlas__FunctionSpace__adjoint_halo_exchange_fieldset(this%CPTR_PGIBUG_A,fieldset%CPTR_PGIBUG_A) end subroutine !------------------------------------------------------------------------------ subroutine adjoint_halo_exchange_field(this,field) use atlas_functionspace_c_binding class(atlas_Functionspace), intent(in) :: this type(atlas_Field), intent(inout) :: field call atlas__FunctionSpace__adjoint_halo_exchange_field(this%CPTR_PGIBUG_A,field%CPTR_PGIBUG_A) end subroutine !------------------------------------------------------------------------------ !------------------------------------------------------------------------------ ! Deprecated versions compatible to support IFS CY45R1 function deprecated_create_field_1(this,name,kind,levels,vars) result(field) use atlas_functionspace_c_binding use, intrinsic :: iso_c_binding, only : c_int type(atlas_Field) :: field class(atlas_Functionspace), intent(in) :: this character(len=*), intent(in) :: name integer, intent(in) :: kind integer(c_int), intent(in) :: levels integer(c_int), intent(in) :: vars(:) integer(c_int) :: opt_variables type(atlas_Config) :: options options = atlas_Config() call options%set("datatype",kind) call options%set("name",name) call options%set("levels",levels) opt_variables = sum(vars) call options%set("variables",opt_variables) field = atlas_Field( atlas__FunctionSpace__create_field( this%CPTR_PGIBUG_A, options%CPTR_PGIBUG_B ) ) call options%final() call field%return() end function function deprecated_create_field_2(this,require_name,kind,levels) result(field) use atlas_functionspace_c_binding use, intrinsic :: iso_c_binding, only : c_int type(atlas_Field) :: field class(atlas_Functionspace), intent(in) :: this character(len=*), intent(in) :: require_name integer, intent(in) :: kind integer(c_int), intent(in) :: levels type(atlas_Config) :: options options = atlas_Config() call options%set("datatype",kind) call options%set("name",require_name) call options%set("levels",levels) field = atlas_Field( atlas__FunctionSpace__create_field( this%CPTR_PGIBUG_A, options%CPTR_PGIBUG_B ) ) call options%final() call field%return() end function !------------------------------------------------------------------------------- #if FCKIT_FINAL_NOT_INHERITING ATLAS_FINAL subroutine atlas_FunctionSpace__final_auto(this) type(atlas_FunctionSpace), intent(inout) :: this #if FCKIT_FINAL_DEBUGGING write(0,*) "atlas_FunctionSpace__final_auto" #endif #if FCKIT_FINAL_NOT_PROPAGATING call this%final() #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine #endif !------------------------------------------------------------------------------ end module atlas_functionspace_module atlas-0.46.0/src/atlas_f/functionspace/atlas_functionspace_Spectral_module.F900000664000175000017500000002165715160212070027646 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. #include "atlas/atlas_f.h" module atlas_functionspace_Spectral_module use, intrinsic :: iso_c_binding, only : c_ptr, c_int use fckit_c_interop_module, only : c_str, c_ptr_to_string, c_ptr_free use atlas_functionspace_module, only : atlas_FunctionSpace use atlas_Field_module, only: atlas_Field use atlas_FieldSet_module, only: atlas_FieldSet use atlas_Config_module, only: atlas_Config implicit none private :: c_ptr, c_int private :: c_str, c_ptr_to_string, c_ptr_free private :: atlas_FunctionSpace private :: atlas_Field private :: atlas_FieldSet private :: atlas_Config public :: atlas_functionspace_Spectral private !------------------------------------------------------------------------------ TYPE, extends(atlas_FunctionSpace) :: atlas_functionspace_Spectral ! Purpose : ! ------- ! *atlas_functionspace_Spectral* : Interpretes spectral fields ! Methods : ! ------- ! Author : ! ------ ! August-2015 Willem Deconinck *ECMWF* !------------------------------------------------------------------------------ contains procedure, private :: gather_field procedure, private :: scatter_field procedure, private :: gather_fieldset procedure, private :: scatter_fieldset generic, public :: gather => gather_field, gather_fieldset generic, public :: scatter => scatter_field, scatter_fieldset procedure, private :: norm_scalar procedure, private :: norm_array generic, public :: norm => norm_scalar, norm_array procedure, public :: truncation procedure, public :: nb_spectral_coefficients procedure, public :: nb_spectral_coefficients_global procedure, public :: levels procedure, public :: nump procedure, public :: nmyms procedure, public :: nasm0 procedure, public :: nvalue #if FCKIT_FINAL_NOT_INHERITING final :: atlas_functionspace_Spectral__final_auto #endif END TYPE atlas_functionspace_Spectral interface atlas_functionspace_Spectral module procedure atlas_functionspace_Spectral__cptr module procedure atlas_functionspace_Spectral__config end interface !------------------------------------------------------------------------------ !======================================================== contains !======================================================== function atlas_functionspace_Spectral__cptr(cptr) result(this) type(atlas_functionspace_Spectral) :: this type(c_ptr), intent(in) :: cptr call this%reset_c_ptr( cptr ) call this%return() end function function atlas_functionspace_Spectral__config(truncation,levels) result(this) use atlas_functionspace_spectral_c_binding type(atlas_functionspace_Spectral) :: this integer(c_int), intent(in) :: truncation integer(c_int), intent(in), optional :: levels type(atlas_Config) :: options options = atlas_Config() call options%set("truncation",truncation) if( present(levels) ) call options%set("levels",levels) call this%reset_c_ptr( atlas__SpectralFunctionSpace__new__config(options%CPTR_PGIBUG_B) ) call options%final() call this%return() end function subroutine gather_field(this,local,global) use atlas_functionspace_spectral_c_binding class(atlas_functionspace_Spectral), intent(in) :: this type(atlas_Field), intent(in) :: local type(atlas_Field), intent(inout) :: global call atlas__SpectralFunctionSpace__gather(this%CPTR_PGIBUG_A,local%CPTR_PGIBUG_A,global%CPTR_PGIBUG_A) end subroutine subroutine scatter_field(this,global,local) use atlas_functionspace_spectral_c_binding class(atlas_functionspace_Spectral), intent(in) :: this type(atlas_Field), intent(in) :: global type(atlas_Field), intent(inout) :: local call atlas__SpectralFunctionSpace__scatter(this%CPTR_PGIBUG_A,global%CPTR_PGIBUG_A,local%CPTR_PGIBUG_A) end subroutine subroutine gather_fieldset(this,local,global) use atlas_functionspace_spectral_c_binding class(atlas_functionspace_Spectral), intent(in) :: this type(atlas_FieldSet), intent(in) :: local type(atlas_FieldSet), intent(inout) :: global call atlas__SpectralFunctionSpace__gather_fieldset(this%CPTR_PGIBUG_A,local%CPTR_PGIBUG_A,global%CPTR_PGIBUG_A) end subroutine subroutine scatter_fieldset(this,global,local) use atlas_functionspace_spectral_c_binding class(atlas_functionspace_Spectral), intent(in) :: this type(atlas_FieldSet), intent(in) :: global type(atlas_FieldSet), intent(inout) :: local call atlas__SpectralFunctionSpace__scatter_fieldset(this%CPTR_PGIBUG_A,global%CPTR_PGIBUG_A,local%CPTR_PGIBUG_A) end subroutine subroutine norm_scalar(this,field,norm,rank) use, intrinsic :: iso_c_binding, only : c_int, c_double use atlas_functionspace_spectral_c_binding class(atlas_functionspace_Spectral), intent(in) :: this type(atlas_Field), intent(in) :: field real(c_double), intent(out) :: norm integer(c_int), optional :: rank integer :: opt_rank real(c_double) :: norm_array(1) opt_rank = 0 if( present(rank) ) opt_rank = rank call atlas__SpectralFunctionSpace__norm(this%CPTR_PGIBUG_A,field%CPTR_PGIBUG_A,norm_array,opt_rank) norm = norm_array(1) end subroutine subroutine norm_array(this,field,norm,rank) use, intrinsic :: iso_c_binding, only : c_int, c_double use atlas_functionspace_spectral_c_binding class(atlas_functionspace_Spectral), intent(in) :: this type(atlas_Field), intent(in) :: field real(c_double), intent(inout) :: norm(:) integer(c_int), optional :: rank integer :: opt_rank opt_rank = 0 if( present(rank) ) opt_rank = rank call atlas__SpectralFunctionSpace__norm(this%CPTR_PGIBUG_A,field%CPTR_PGIBUG_A,norm,opt_rank) end subroutine function levels( this ) use atlas_functionspace_spectral_c_binding integer :: levels class(atlas_functionspace_Spectral) :: this call atlas__SpectralFunctionSpace__levels( this%c_ptr(), levels ) end function !------------------------------------------------------------------------------- ! Experimental function truncation( this ) use atlas_functionspace_spectral_c_binding integer :: truncation class(atlas_functionspace_Spectral) :: this call atlas__SpectralFunctionSpace__truncation( this%c_ptr(), truncation ) end function function nb_spectral_coefficients( this ) use atlas_functionspace_spectral_c_binding integer :: nb_spectral_coefficients class(atlas_functionspace_Spectral) :: this call atlas__SpectralFunctionSpace__nspec2( this%c_ptr(), nb_spectral_coefficients ) end function function nb_spectral_coefficients_global( this ) use atlas_functionspace_spectral_c_binding integer :: nb_spectral_coefficients_global class(atlas_functionspace_Spectral) :: this call atlas__SpectralFunctionSpace__nspec2g( this%c_ptr(), nb_spectral_coefficients_global ) end function function nump( this ) use atlas_functionspace_spectral_c_binding integer :: nump class(atlas_functionspace_Spectral) :: this call atlas__SpectralFunctionSpace__nump( this%c_ptr(), nump ) end function function nmyms(this) use atlas_functionspace_spectral_c_binding use, intrinsic :: iso_c_binding, only : c_int, c_f_pointer, c_ptr integer(c_int), pointer :: nmyms(:) class(atlas_functionspace_Spectral), intent(in) :: this type(c_ptr) :: nmyms_c_ptr integer(c_int) :: size call atlas__SpectralFunctionSpace__nmyms(this%c_ptr(), nmyms_c_ptr, size) call c_f_pointer ( nmyms_c_ptr , nmyms , (/size/) ) end function function nasm0(this) use atlas_functionspace_spectral_c_binding use, intrinsic :: iso_c_binding, only : c_int, c_f_pointer, c_ptr integer(c_int), pointer :: nasm0(:) class(atlas_functionspace_Spectral), intent(in) :: this type(c_ptr) :: nasm0_c_ptr integer(c_int), pointer :: nasm0_f_ptr(:) integer(c_int) :: size call atlas__SpectralFunctionSpace__nasm0(this%c_ptr(), nasm0_c_ptr, size) call c_f_pointer ( nasm0_c_ptr , nasm0_f_ptr , (/size/) ) nasm0(0:) => nasm0_f_ptr(:) end function function nvalue(this) use atlas_functionspace_spectral_c_binding use, intrinsic :: iso_c_binding, only : c_int, c_f_pointer, c_ptr integer(c_int), pointer :: nvalue(:) class(atlas_functionspace_Spectral), intent(in) :: this type(c_ptr) :: nvalue_c_ptr integer(c_int) :: size call atlas__SpectralFunctionSpace__nvalue(this%c_ptr(), nvalue_c_ptr, size) call c_f_pointer ( nvalue_c_ptr , nvalue , (/size/) ) end function !------------------------------------------------------------------------------- #if FCKIT_FINAL_NOT_INHERITING ATLAS_FINAL subroutine atlas_functionspace_Spectral__final_auto(this) type(atlas_functionspace_Spectral), intent(inout) :: this #if FCKIT_FINAL_DEBUGGING write(0,*) "atlas_functionspace_Spectral__final_auto" #endif #if FCKIT_FINAL_NOT_PROPAGATING call this%final() #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine #endif end module atlas_functionspace_Spectral_module atlas-0.46.0/src/atlas_f/functionspace/atlas_functionspace_CellColumns_module.F900000664000175000017500000002434315160212070030304 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. #include "atlas/atlas_f.h" module atlas_functionspace_CellColumns_module use fckit_c_interop_module, only : c_str, c_ptr_to_string, c_ptr_free use atlas_functionspace_module, only : atlas_FunctionSpace use atlas_Field_module, only: atlas_Field use atlas_FieldSet_module, only: atlas_FieldSet use atlas_Mesh_module, only: atlas_Mesh use atlas_mesh_Cells_module, only: atlas_mesh_Cells use atlas_GatherScatter_module, only: atlas_GatherScatter use atlas_HaloExchange_module, only: atlas_HaloExchange use atlas_Checksum_module, only: atlas_Checksum use atlas_Config_module, only: atlas_Config use atlas_kinds_module, only: ATLAS_KIND_GIDX implicit none private :: c_str, c_ptr_to_string, c_ptr_free private :: atlas_FunctionSpace private :: atlas_Field private :: atlas_FieldSet private :: atlas_mesh_Cells private :: atlas_GatherScatter private :: atlas_HaloExchange private :: atlas_Checksum private :: atlas_Mesh private :: atlas_Config private :: ATLAS_KIND_GIDX public :: atlas_functionspace_CellColumns private !------------------------------------------------------------------------------ TYPE, extends(atlas_FunctionSpace) :: atlas_functionspace_CellColumns ! Purpose : ! ------- ! *atlas_functionspace_CellColumns* : Interpretes fields defined in cells ! Methods : ! ------- ! Author : ! ------ ! August-2015 Willem Deconinck *ECMWF* !------------------------------------------------------------------------------ contains procedure, public :: nb_cells procedure, public :: mesh procedure, public :: cells procedure, public :: get_halo_exchange procedure, private :: gather_fieldset procedure, private :: gather_field generic, public :: gather => gather_fieldset, gather_field procedure, public :: get_gather procedure, private :: scatter_fieldset procedure, private :: scatter_field generic, public :: scatter => scatter_fieldset, scatter_field procedure, public :: get_scatter procedure, private :: checksum_fieldset procedure, private :: checksum_field generic, public :: checksum => checksum_fieldset, checksum_field procedure, public :: get_checksum #if FCKIT_FINAL_NOT_INHERITING final :: atlas_functionspace_CellColumns__final_auto #endif END TYPE atlas_functionspace_CellColumns interface atlas_functionspace_CellColumns module procedure constructor__cptr module procedure constructor end interface !------------------------------------------------------------------------------ !======================================================== contains !======================================================== !------------------------------------------------------------------------------ function constructor__cptr(cptr) result(this) use, intrinsic :: iso_c_binding, only : c_ptr type(atlas_functionspace_CellColumns) :: this type(c_ptr), intent(in) :: cptr call this%reset_c_ptr( cptr ) call this%return() end function !------------------------------------------------------------------------------ function constructor(mesh,halo,levels) result(this) use atlas_functionspace_CellColumns_c_binding type(atlas_functionspace_CellColumns) :: this type(atlas_Mesh), intent(inout) :: mesh integer, intent(in), optional :: halo integer, intent(in), optional :: levels type(atlas_Config) :: config config = atlas_Config() if( present(halo) ) call config%set("halo",halo) if( present(levels) ) call config%set("levels",levels) call this%reset_c_ptr( atlas__CellsFunctionSpace__new(mesh%CPTR_PGIBUG_A,config%CPTR_PGIBUG_B) ) call config%final() call this%return() end function !------------------------------------------------------------------------------ function nb_cells(this) use atlas_functionspace_CellColumns_c_binding integer :: nb_cells class(atlas_functionspace_CellColumns), intent(in) :: this nb_cells = atlas__CellsFunctionSpace__nb_cells(this%CPTR_PGIBUG_A) end function !------------------------------------------------------------------------------ function mesh(this) use atlas_functionspace_CellColumns_c_binding type(atlas_Mesh) :: mesh class(atlas_functionspace_CellColumns), intent(in) :: this call mesh%reset_c_ptr( atlas__CellsFunctionSpace__mesh(this%CPTR_PGIBUG_A) ) call mesh%return() end function !------------------------------------------------------------------------------ function cells(this) use atlas_functionspace_CellColumns_c_binding type(atlas_mesh_Cells) :: cells class(atlas_functionspace_CellColumns), intent(in) :: this call cells%reset_c_ptr( atlas__CellsFunctionSpace__cells(this%CPTR_PGIBUG_A) ) call cells%return() end function !------------------------------------------------------------------------------ function get_gather(this) result(gather) use atlas_functionspace_CellColumns_c_binding type(atlas_GatherScatter) :: gather class(atlas_functionspace_CellColumns), intent(in) :: this call gather%reset_c_ptr( atlas__CellsFunctioNSpace__get_gather(this%CPTR_PGIBUG_A) ) end function !------------------------------------------------------------------------------ function get_scatter(this) result(gather) use atlas_functionspace_CellColumns_c_binding type(atlas_GatherScatter) :: gather class(atlas_functionspace_CellColumns), intent(in) :: this call gather%reset_c_ptr( atlas__CellsFunctioNSpace__get_scatter(this%CPTR_PGIBUG_A) ) end function !------------------------------------------------------------------------------ subroutine gather_fieldset(this,local,global) use atlas_functionspace_CellColumns_c_binding class(atlas_functionspace_CellColumns), intent(in) :: this type(atlas_FieldSet), intent(in) :: local type(atlas_FieldSet), intent(inout) :: global call atlas__CellsFunctionSpace__gather_fieldset(this%CPTR_PGIBUG_A,local%CPTR_PGIBUG_A,global%CPTR_PGIBUG_A) end subroutine !------------------------------------------------------------------------------ subroutine gather_field(this,local,global) use atlas_functionspace_CellColumns_c_binding class(atlas_functionspace_CellColumns), intent(in) :: this type(atlas_Field), intent(in) :: local type(atlas_Field), intent(inout) :: global call atlas__CellsFunctionSpace__gather_field(this%CPTR_PGIBUG_A,local%CPTR_PGIBUG_A,global%CPTR_PGIBUG_A) end subroutine !------------------------------------------------------------------------------ subroutine scatter_fieldset(this,global,local) use atlas_functionspace_CellColumns_c_binding class(atlas_functionspace_CellColumns), intent(in) :: this type(atlas_FieldSet), intent(in) :: global type(atlas_FieldSet), intent(inout) :: local call atlas__CellsFunctionSpace__scatter_fieldset(this%CPTR_PGIBUG_A,global%CPTR_PGIBUG_A,local%CPTR_PGIBUG_A) end subroutine !------------------------------------------------------------------------------ subroutine scatter_field(this,global,local) use atlas_functionspace_CellColumns_c_binding class(atlas_functionspace_CellColumns), intent(in) :: this type(atlas_Field), intent(in) :: global type(atlas_Field), intent(inout) :: local call atlas__CellsFunctionSpace__scatter_field(this%CPTR_PGIBUG_A,global%CPTR_PGIBUG_A,local%CPTR_PGIBUG_A) end subroutine !------------------------------------------------------------------------------ function get_halo_exchange(this) result(halo_exchange) use atlas_functionspace_CellColumns_c_binding type(atlas_HaloExchange) :: halo_exchange class(atlas_functionspace_CellColumns), intent(in) :: this call halo_exchange%reset_c_ptr( atlas__CellsFunctioNSpace__get_halo_exchange(this%CPTR_PGIBUG_A) ) end function !------------------------------------------------------------------------------ function get_checksum(this) result(checksum) use atlas_functionspace_CellColumns_c_binding type(atlas_Checksum) :: checksum class(atlas_functionspace_CellColumns), intent(in) :: this call checksum%reset_c_ptr( atlas__CellsFunctioNSpace__get_checksum(this%CPTR_PGIBUG_A) ) end function !------------------------------------------------------------------------------ function checksum_fieldset(this,fieldset) result(checksum) use atlas_functionspace_CellColumns_c_binding use, intrinsic :: iso_c_binding, only : c_ptr character(len=:), allocatable :: checksum class(atlas_functionspace_CellColumns), intent(in) :: this type(atlas_FieldSet), intent(in) :: fieldset type(c_ptr) :: checksum_cptr integer :: checksum_size, checksum_allocated call atlas__CellsFunctionSpace__checksum_fieldset( & this%CPTR_PGIBUG_A,fieldset%CPTR_PGIBUG_A,checksum_cptr,checksum_size,checksum_allocated) allocate(character(len=checksum_size) :: checksum ) checksum = c_ptr_to_string(checksum_cptr) if( checksum_allocated == 1 ) call c_ptr_free(checksum_cptr) end function !------------------------------------------------------------------------------ function checksum_field(this,field) result(checksum) use atlas_functionspace_CellColumns_c_binding use, intrinsic :: iso_c_binding, only : c_ptr character(len=:), allocatable :: checksum class(atlas_functionspace_CellColumns), intent(in) :: this type(atlas_Field), intent(in) :: field type(c_ptr) :: checksum_cptr integer :: checksum_size, checksum_allocated call atlas__CellsFunctionSpace__checksum_field( & this%CPTR_PGIBUG_A,field%CPTR_PGIBUG_A,checksum_cptr,checksum_size,checksum_allocated) allocate(character(len=checksum_size) :: checksum ) checksum = c_ptr_to_string(checksum_cptr) if( checksum_allocated == 1 ) call c_ptr_free(checksum_cptr) end function !------------------------------------------------------------------------------ #if FCKIT_FINAL_NOT_INHERITING ATLAS_FINAL subroutine atlas_functionspace_CellColumns__final_auto(this) type(atlas_functionspace_CellColumns), intent(inout) :: this #if FCKIT_FINAL_DEBUGGING write(0,*) "atlas_functionspace_CellColumns__final_auto" #endif #if FCKIT_FINAL_NOT_PROPAGATING call this%final() #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine #endif !------------------------------------------------------------------------------ end module atlas_functionspace_CellColumns_module atlas-0.46.0/src/atlas_f/functionspace/atlas_functionspace_BlockStructuredColumns_module.F900000664000175000017500000005442215160212070032545 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. #include "atlas/atlas_f.h" module atlas_functionspace_BlockStructuredColumns_module use, intrinsic :: iso_c_binding, only : c_ptr, c_double use fckit_c_interop_module, only : c_str, c_ptr_to_string, c_ptr_free use atlas_functionspace_module, only : atlas_FunctionSpace use atlas_Field_module, only: atlas_Field use atlas_FieldSet_module, only: atlas_FieldSet use atlas_Grid_module, only: atlas_Grid use atlas_Config_module, only: atlas_Config use atlas_kinds_module, only : ATLAS_KIND_IDX use fckit_owned_object_module, only : fckit_owned_object use atlas_GridDistribution_module, only : atlas_GridDistribution use atlas_Partitioner_module, only : atlas_Partitioner use atlas_Vertical_module, only : atlas_Vertical implicit none private :: c_ptr, c_double private :: c_str, c_ptr_to_string, c_ptr_free private :: atlas_FunctionSpace private :: atlas_Field private :: atlas_FieldSet private :: atlas_Grid private :: atlas_GridDistribution private :: atlas_Partitioner private :: atlas_Vertical private :: atlas_Config private :: fckit_owned_object public :: atlas_functionspace_BlockStructuredColumns private !------------------------------------------------------------------------------ TYPE, extends(atlas_FunctionSpace) :: atlas_functionspace_BlockStructuredColumns ! Purpose : ! ------- ! *atlas_functionspace_BlockStructuredColumns* : Interpretes spectral fields ! Methods : ! ------- ! Author : ! ------ ! August-2015 Willem Deconinck *ECMWF* !------------------------------------------------------------------------------ integer(ATLAS_KIND_IDX), pointer, public :: index(:,:) contains procedure, public :: assignment_operator_hook procedure, private :: gather_fieldset procedure, private :: gather_field generic, public :: gather => gather_fieldset, gather_field procedure, private :: scatter_fieldset procedure, private :: scatter_field generic, public :: scatter => scatter_fieldset, scatter_field procedure, private :: checksum_fieldset procedure, private :: checksum_field generic, public :: checksum => checksum_fieldset, checksum_field procedure :: j_begin procedure :: j_end procedure :: i_begin procedure :: i_end procedure :: j_begin_halo procedure :: j_end_halo procedure :: i_begin_halo procedure :: i_end_halo procedure :: size => get_size procedure :: size_owned => get_size_owned procedure :: levels procedure :: block_begin procedure :: block_size procedure :: nproma procedure :: nblks procedure :: xy !! Return xy coordinate field procedure :: z !! Return z coordinate field procedure :: partition !! Return partition field procedure :: global_index !! Return global_index field procedure :: index_i !! Return index_i field procedure :: index_j !! Return index_j field procedure :: grid procedure, private :: set_index #if FCKIT_FINAL_NOT_INHERITING final :: BStructuredColumns__final_auto #endif END TYPE atlas_functionspace_BlockStructuredColumns interface atlas_functionspace_BlockStructuredColumns module procedure ctor_cptr module procedure ctor_grid module procedure ctor_grid_config module procedure ctor_grid_dist module procedure ctor_grid_dist_config module procedure ctor_grid_dist_levels module procedure ctor_grid_dist_vertical module procedure ctor_grid_part module procedure ctor_grid_part_levels module procedure ctor_grid_part_vertical end interface !------------------------------------------------------------------------------ !======================================================== contains !======================================================== subroutine assignment_operator_hook(this,other) class(atlas_functionspace_BlockStructuredColumns) :: this class(fckit_owned_object) :: other call this%set_index() FCKIT_SUPPRESS_UNUSED(other) end subroutine function ctor_cptr(cptr) result(this) type(atlas_functionspace_BlockStructuredColumns) :: this type(c_ptr), intent(in) :: cptr call this%reset_c_ptr( cptr ) call this%set_index() call this%return() end function function empty_config() result(config) type(atlas_Config) :: config config = atlas_Config() call config%return() end function function ctor_grid(grid, halo, nproma, levels) result(this) use atlas_functionspace_BlockStructuredColumns_c_binding type(atlas_functionspace_BlockStructuredColumns) :: this class(atlas_Grid), intent(in) :: grid integer, optional :: halo integer, optional :: nproma integer, optional :: levels type(atlas_Config) :: config config = empty_config() ! Due to PGI compiler bug, we have to do this instead of "config = atlas_Config()"" if( present(halo) ) call config%set("halo",halo) if( present(nproma) ) call config%set("nproma",nproma) if( present(levels) ) call config%set("levels",levels) call this%reset_c_ptr( atlas__functionspace__BStructuredColumns__new__grid( grid%CPTR_PGIBUG_A, & & config%CPTR_PGIBUG_B ) ) call this%set_index() call config%final() call this%return() end function function ctor_grid_config(grid, config) result(this) use atlas_functionspace_BlockStructuredColumns_c_binding type(atlas_functionspace_BlockStructuredColumns) :: this class(atlas_Grid), intent(in) :: grid type(atlas_Config), intent (in) :: config call this%reset_c_ptr(atlas__functionspace__BStructuredColumns__new__grid( & & grid%CPTR_PGIBUG_A, & & config%CPTR_PGIBUG_B ) ) call this%set_index() call this%return() end function function ctor_grid_dist(grid, distribution, halo, levels) result(this) use atlas_functionspace_BlockStructuredColumns_c_binding type(atlas_functionspace_BlockStructuredColumns) :: this class(atlas_Grid), intent(in) :: grid type(atlas_griddistribution), intent(in) :: distribution integer, optional :: halo integer, optional :: levels type(atlas_Config) :: config config = empty_config() ! Due to PGI compiler bug, we have to do this instead of "config = atlas_Config()"" if( present(halo) ) call config%set("halo",halo) if( present(levels) ) call config%set("levels",levels) call this%reset_c_ptr( atlas__functionspace__BStructuredColumns__new__grid_dist( & & grid%CPTR_PGIBUG_A, distribution%CPTR_PGIBUG_A, config%CPTR_PGIBUG_B ) ) call this%set_index() call config%final() call this%return() end function function ctor_grid_dist_levels(grid, distribution, levels, halo) result(this) use atlas_functionspace_BlockStructuredColumns_c_binding type(atlas_functionspace_BlockStructuredColumns) :: this class(atlas_Grid), intent(in) :: grid type(atlas_griddistribution), intent(in) :: distribution integer, optional :: halo real(c_double) :: levels(:) type(atlas_Config) :: config type(atlas_Vertical) :: vertical config = empty_config() ! Due to PGI compiler bug, we have to do this insted of "config = atlas_Config()"" if( present(halo) ) call config%set("halo",halo) call config%set("levels",size(levels)) vertical = atlas_Vertical(levels) call this%reset_c_ptr( atlas__functionspace__BStructuredColumns__new__grid_dist_vert( & & grid%CPTR_PGIBUG_A, distribution%CPTR_PGIBUG_A, vertical%CPTR_PGIBUG_B, & & config%CPTR_PGIBUG_B ) ) call this%set_index() call config%final() call vertical%final() call this%return() end function function ctor_grid_dist_vertical(grid, distribution, vertical, halo) result(this) use atlas_functionspace_BlockStructuredColumns_c_binding type(atlas_functionspace_BlockStructuredColumns) :: this class(atlas_Grid), intent(in) :: grid type(atlas_griddistribution), intent(in) :: distribution integer, optional :: halo type(atlas_Vertical) :: vertical type(atlas_Config) :: config config = empty_config() ! Due to PGI compiler bug, we have to do this insted of "config = atlas_Config()"" if( present(halo) ) call config%set("halo",halo) call config%set("levels",vertical%size()) call this%reset_c_ptr( atlas__functionspace__BStructuredColumns__new__grid_dist_vert( & & grid%CPTR_PGIBUG_A, distribution%CPTR_PGIBUG_A, vertical%CPTR_PGIBUG_B, & & config%CPTR_PGIBUG_B ) ) call this%set_index() call config%final() call this%return() end function function ctor_grid_dist_config(grid, distribution, config) result(this) use atlas_functionspace_BlockStructuredColumns_c_binding type(atlas_functionspace_BlockStructuredColumns) :: this class(atlas_Grid), intent(in) :: grid type(atlas_griddistribution), intent(in) :: distribution type(atlas_Config), intent (in) :: config call this%reset_c_ptr(atlas__functionspace__BStructuredColumns__new__grid_dist_config( & & grid%CPTR_PGIBUG_A, distribution%CPTR_PGIBUG_A, & & config%CPTR_PGIBUG_B ) ) call this%set_index() call this%return() end function function ctor_grid_part(grid, partitioner, halo, levels) result(this) use atlas_functionspace_BlockStructuredColumns_c_binding type(atlas_functionspace_BlockStructuredColumns) :: this class(atlas_Grid), intent(in) :: grid type(atlas_Partitioner), intent(in) :: partitioner integer, optional :: halo integer, optional :: levels type(atlas_Config) :: config config = empty_config() ! Due to PGI compiler bug, we have to do this instead of "config = atlas_Config()"" if( present(halo) ) call config%set("halo",halo) if( present(levels) ) call config%set("levels",levels) call this%reset_c_ptr( atlas__functionspace__BStructuredColumns__new__grid_part( & & grid%CPTR_PGIBUG_A, partitioner%CPTR_PGIBUG_A, config%CPTR_PGIBUG_B ) ) call this%set_index() call config%final() call this%return() end function function ctor_grid_part_levels(grid, partitioner, levels, halo) result(this) use atlas_functionspace_BlockStructuredColumns_c_binding type(atlas_functionspace_BlockStructuredColumns) :: this class(atlas_Grid), intent(in) :: grid type(atlas_Partitioner), intent(in) :: partitioner integer, optional :: halo real(c_double) :: levels(:) type(atlas_Config) :: config type(atlas_Vertical) :: vertical config = empty_config() ! Due to PGI compiler bug, we have to do this insted of "config = atlas_Config()"" if( present(halo) ) call config%set("halo",halo) call config%set("levels",size(levels)) vertical = atlas_Vertical(levels) call this%reset_c_ptr( atlas__functionspace__BStructuredColumns__new__grid_part_vert( & & grid%CPTR_PGIBUG_A, partitioner%CPTR_PGIBUG_A, vertical%CPTR_PGIBUG_B, & & config%CPTR_PGIBUG_B ) ) call this%set_index() call config%final() call vertical%final() call this%return() end function function ctor_grid_part_vertical(grid, partitioner, vertical, halo) result(this) use atlas_functionspace_BlockStructuredColumns_c_binding type(atlas_functionspace_BlockStructuredColumns) :: this class(atlas_Grid), intent(in) :: grid type(atlas_Partitioner), intent(in) :: partitioner integer, optional :: halo type(atlas_Vertical) :: vertical type(atlas_Config) :: config config = empty_config() ! Due to PGI compiler bug, we have to do this insted of "config = atlas_Config()"" if( present(halo) ) call config%set("halo",halo) call config%set("levels",vertical%size()) call this%reset_c_ptr( atlas__functionspace__BStructuredColumns__new__grid_part_vert( & & grid%CPTR_PGIBUG_A, partitioner%CPTR_PGIBUG_A, vertical%CPTR_PGIBUG_B, & & config%CPTR_PGIBUG_B ) ) call this%set_index() call config%final() call this%return() end function subroutine gather_field(this,local,global) use atlas_functionspace_BlockStructuredColumns_c_binding class(atlas_functionspace_BlockStructuredColumns), intent(in) :: this type(atlas_Field), intent(in) :: local type(atlas_Field), intent(inout) :: global call atlas__functionspace__BStructuredColumns__gather_field(this%CPTR_PGIBUG_A,local%CPTR_PGIBUG_A,global%CPTR_PGIBUG_A) end subroutine subroutine gather_fieldset(this,local,global) use atlas_functionspace_BlockStructuredColumns_c_binding class(atlas_functionspace_BlockStructuredColumns), intent(in) :: this type(atlas_FieldSet), intent(in) :: local type(atlas_FieldSet), intent(inout) :: global call atlas__functionspace__BStructuredColumns__gather_fieldset(this%CPTR_PGIBUG_A,local%CPTR_PGIBUG_A,global%CPTR_PGIBUG_A) end subroutine subroutine scatter_field(this,global,local) use atlas_functionspace_BlockStructuredColumns_c_binding class(atlas_functionspace_BlockStructuredColumns), intent(in) :: this type(atlas_Field), intent(in) :: global type(atlas_Field), intent(inout) :: local call atlas__functionspace__BStructuredColumns__scatter_field(this%CPTR_PGIBUG_A,global%CPTR_PGIBUG_A,local%CPTR_PGIBUG_A) end subroutine subroutine scatter_fieldset(this,global,local) use atlas_functionspace_BlockStructuredColumns_c_binding class(atlas_functionspace_BlockStructuredColumns), intent(in) :: this type(atlas_FieldSet), intent(in) :: global type(atlas_FieldSet), intent(inout) :: local call atlas__functionspace__BStructuredColumns__scatter_fieldset(this%CPTR_PGIBUG_A,global%CPTR_PGIBUG_A,local%CPTR_PGIBUG_A) end subroutine function checksum_fieldset(this,fieldset) result(checksum) use, intrinsic :: iso_c_binding use atlas_functionspace_BlockStructuredColumns_c_binding character(len=:), allocatable :: checksum class(atlas_functionspace_BlockStructuredColumns), intent(in) :: this type(atlas_FieldSet), intent(in) :: fieldset type(c_ptr) :: checksum_cptr integer(ATLAS_KIND_IDX) :: checksum_size integer(c_int) :: checksum_allocated call atlas__fs__BStructuredColumns__checksum_fieldset( & & this%CPTR_PGIBUG_A,fieldset%CPTR_PGIBUG_A,checksum_cptr,checksum_size,checksum_allocated) allocate(character(len=checksum_size) :: checksum ) checksum = c_ptr_to_string(checksum_cptr) if( checksum_allocated == 1 ) call c_ptr_free(checksum_cptr) end function function checksum_field(this,field) result(checksum) use, intrinsic :: iso_c_binding use atlas_functionspace_BlockStructuredColumns_c_binding character(len=:), allocatable :: checksum class(atlas_functionspace_BlockStructuredColumns), intent(in) :: this type(atlas_Field), intent(in) :: field type(c_ptr) :: checksum_cptr integer(ATLAS_KIND_IDX) :: checksum_size integer(c_int) :: checksum_allocated call atlas__fs__BStructuredColumns__checksum_field( & & this%CPTR_PGIBUG_A,field%CPTR_PGIBUG_A,checksum_cptr,checksum_size,checksum_allocated) allocate(character(len=checksum_size) :: checksum ) checksum = c_ptr_to_string(checksum_cptr) if( checksum_allocated == 1 ) call c_ptr_free(checksum_cptr) end function subroutine set_index(this) use, intrinsic :: iso_c_binding, only : c_ptr, c_f_pointer use atlas_functionspace_BlockStructuredColumns_c_binding class(atlas_functionspace_BlockStructuredColumns), intent(inout) :: this type(c_ptr) :: index_cptr integer(ATLAS_KIND_IDX), pointer :: index_fptr(:) integer(ATLAS_KIND_IDX) :: i_min, i_max, j_min, j_max integer(ATLAS_KIND_IDX) :: ni, nj call atlas__fs__BStructuredColumns__index_host( this%CPTR_PGIBUG_A, index_cptr, i_min, i_max, j_min, j_max ) ni = i_max-i_min+1; nj = j_max-j_min+1; call c_f_pointer( index_cptr, index_fptr, (/ni*nj/) ) this%index(i_min:i_max,j_min:j_max) => index_fptr(:) end subroutine function j_begin(this) result(j) use atlas_functionspace_BlockStructuredColumns_c_binding integer(ATLAS_KIND_IDX) :: j class(atlas_functionspace_BlockStructuredColumns), intent(in) :: this j = atlas__fs__BStructuredColumns__j_begin(this%CPTR_PGIBUG_A) end function function j_end(this) result(j) use atlas_functionspace_BlockStructuredColumns_c_binding integer(ATLAS_KIND_IDX) :: j class(atlas_functionspace_BlockStructuredColumns), intent(in) :: this j = atlas__fs__BStructuredColumns__j_end(this%CPTR_PGIBUG_A) end function function i_begin(this,j) result(i) use atlas_functionspace_BlockStructuredColumns_c_binding integer(ATLAS_KIND_IDX) :: i integer(ATLAS_KIND_IDX), intent(in) :: j class(atlas_functionspace_BlockStructuredColumns), intent(in) :: this i = atlas__fs__BStructuredColumns__i_begin(this%CPTR_PGIBUG_A,j) end function function i_end(this,j) result(i) use atlas_functionspace_BlockStructuredColumns_c_binding integer(ATLAS_KIND_IDX) :: i integer(ATLAS_KIND_IDX), intent(in) :: j class(atlas_functionspace_BlockStructuredColumns), intent(in) :: this i = atlas__fs__BStructuredColumns__i_end(this%CPTR_PGIBUG_A,j) end function function j_begin_halo(this) result(j) use atlas_functionspace_BlockStructuredColumns_c_binding integer(ATLAS_KIND_IDX) :: j class(atlas_functionspace_BlockStructuredColumns), intent(in) :: this j = atlas__fs__BStructuredColumns__j_begin_halo(this%CPTR_PGIBUG_A) end function function j_end_halo(this) result(j) use atlas_functionspace_BlockStructuredColumns_c_binding integer(ATLAS_KIND_IDX) :: j class(atlas_functionspace_BlockStructuredColumns), intent(in) :: this j = atlas__fs__BStructuredColumns__j_end_halo(this%CPTR_PGIBUG_A) end function function i_begin_halo(this,j) result(i) use atlas_functionspace_BlockStructuredColumns_c_binding integer(ATLAS_KIND_IDX) :: i integer(ATLAS_KIND_IDX), intent(in) :: j class(atlas_functionspace_BlockStructuredColumns), intent(in) :: this i = atlas__fs__BStructuredColumns__i_begin_halo(this%CPTR_PGIBUG_A,j) end function function i_end_halo(this,j) result(i) use atlas_functionspace_BlockStructuredColumns_c_binding integer(ATLAS_KIND_IDX) :: i integer(ATLAS_KIND_IDX), intent(in) :: j class(atlas_functionspace_BlockStructuredColumns), intent(in) :: this i = atlas__fs__BStructuredColumns__i_end_halo(this%CPTR_PGIBUG_A,j) end function function get_size(this) result(size) use atlas_functionspace_BlockStructuredColumns_c_binding integer(ATLAS_KIND_IDX) :: size class(atlas_functionspace_BlockStructuredColumns), intent(in) :: this size = atlas__fs__BStructuredColumns__size(this%CPTR_PGIBUG_A) end function function get_size_owned(this) result(size) use atlas_functionspace_BlockStructuredColumns_c_binding integer(ATLAS_KIND_IDX) :: size class(atlas_functionspace_BlockStructuredColumns), intent(in) :: this size = atlas__fs__BStructuredColumns__sizeOwned(this%CPTR_PGIBUG_A) end function function levels(this) use atlas_functionspace_BlockStructuredColumns_c_binding integer(ATLAS_KIND_IDX) :: levels class(atlas_functionspace_BlockStructuredColumns), intent(in) :: this levels = atlas__fs__BStructuredColumns__levels(this%CPTR_PGIBUG_A) end function function xy(this) result(field) use atlas_functionspace_BlockStructuredColumns_c_binding type(atlas_Field) :: field class(atlas_functionspace_BlockStructuredColumns), intent(in) :: this field = atlas_Field( atlas__fs__BStructuredColumns__xy(this%CPTR_PGIBUG_A) ) call field%return() end function function z(this) result(field) use atlas_functionspace_BlockStructuredColumns_c_binding type(atlas_Field) :: field class(atlas_functionspace_BlockStructuredColumns), intent(in) :: this field = atlas_Field( atlas__fs__BStructuredColumns__z(this%CPTR_PGIBUG_A) ) call field%return() end function function partition(this) result(field) use atlas_functionspace_BlockStructuredColumns_c_binding type(atlas_Field) :: field class(atlas_functionspace_BlockStructuredColumns), intent(in) :: this field = atlas_Field( atlas__fs__BStructuredColumns__partition(this%CPTR_PGIBUG_A) ) call field%return() end function function global_index(this) result(field) use atlas_functionspace_BlockStructuredColumns_c_binding type(atlas_Field) :: field class(atlas_functionspace_BlockStructuredColumns), intent(in) :: this field = atlas_Field( atlas__fs__BStructuredColumns__global_index(this%CPTR_PGIBUG_A) ) call field%return() end function function index_i(this) result(field) use atlas_functionspace_BlockStructuredColumns_c_binding type(atlas_Field) :: field class(atlas_functionspace_BlockStructuredColumns), intent(in) :: this field = atlas_Field( atlas__fs__BStructuredColumns__index_i(this%CPTR_PGIBUG_A) ) call field%return() end function function index_j(this) result(field) use atlas_functionspace_BlockStructuredColumns_c_binding type(atlas_Field) :: field class(atlas_functionspace_BlockStructuredColumns), intent(in) :: this field = atlas_Field( atlas__fs__BStructuredColumns__index_j(this%CPTR_PGIBUG_A) ) call field%return() end function function grid(this) use atlas_functionspace_BlockStructuredColumns_c_binding type(atlas_Grid) :: grid class(atlas_functionspace_BlockStructuredColumns), intent(in) :: this grid = atlas_Grid( atlas__fs__BStructuredColumns__grid(this%CPTR_PGIBUG_A) ) call grid%return() end function function block_begin(this,j) result(i) use atlas_functionspace_BlockStructuredColumns_c_binding integer(ATLAS_KIND_IDX) :: i integer(ATLAS_KIND_IDX), intent(in) :: j class(atlas_functionspace_BlockStructuredColumns), intent(in) :: this i = atlas__fs__BStructuredColumns__block_begin(this%CPTR_PGIBUG_A,j-1) + 1 end function function block_size(this,j) result(i) use atlas_functionspace_BlockStructuredColumns_c_binding integer(ATLAS_KIND_IDX) :: i integer(ATLAS_KIND_IDX), intent(in) :: j class(atlas_functionspace_BlockStructuredColumns), intent(in) :: this i = atlas__fs__BStructuredColumns__block_size(this%CPTR_PGIBUG_A,j-1) end function function nproma(this) result(i) use atlas_functionspace_BlockStructuredColumns_c_binding integer(ATLAS_KIND_IDX) :: i class(atlas_functionspace_BlockStructuredColumns), intent(in) :: this i = atlas__fs__BStructuredColumns__nproma(this%CPTR_PGIBUG_A) end function function nblks(this) result(i) use atlas_functionspace_BlockStructuredColumns_c_binding integer(ATLAS_KIND_IDX) :: i class(atlas_functionspace_BlockStructuredColumns), intent(in) :: this i = atlas__fs__BStructuredColumns__nblks(this%CPTR_PGIBUG_A) end function !------------------------------------------------------------------------------- #if FCKIT_FINAL_NOT_INHERITING ATLAS_FINAL subroutine BStructuredColumns__final_auto(this) type(atlas_functionspace_BlockStructuredColumns), intent(inout) :: this #if FCKIT_FINAL_DEBUGGING write(0,*) "atlas_functionspace_BlockStructuredColumns__final_auto" #endif #if FCKIT_FINAL_NOT_PROPAGATING call this%final() #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine #endif end module atlas_functionspace_BlockStructuredColumns_module atlas-0.46.0/src/atlas_f/functionspace/atlas_functionspace_EdgeColumns_module.F900000664000175000017500000002415415160212070030271 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. #include "atlas/atlas_f.h" module atlas_functionspace_EdgeColumns_module use, intrinsic :: iso_c_binding, only : c_ptr, c_int use fckit_c_interop_module, only : c_str, c_ptr_to_string, c_ptr_free use atlas_functionspace_module, only : atlas_FunctionSpace use atlas_Field_module, only: atlas_Field use atlas_FieldSet_module, only: atlas_FieldSet use atlas_Mesh_module, only: atlas_Mesh use atlas_mesh_Edges_module, only: atlas_mesh_Edges use atlas_GatherScatter_module, only: atlas_GatherScatter use atlas_HaloExchange_module, only: atlas_HaloExchange use atlas_Checksum_module, only: atlas_Checksum use atlas_Config_module, only: atlas_Config implicit none private :: c_ptr, c_int private :: c_str, c_ptr_to_string, c_ptr_free private :: atlas_FunctionSpace private :: atlas_Field private :: atlas_FieldSet private :: atlas_mesh_Edges private :: atlas_GatherScatter private :: atlas_HaloExchange private :: atlas_Checksum private :: atlas_Mesh private :: atlas_Config public :: atlas_functionspace_EdgeColumns private !------------------------------------------------------------------------------ TYPE, extends(atlas_FunctionSpace) :: atlas_functionspace_EdgeColumns ! Purpose : ! ------- ! *atlas_functionspace_EdgeColumns* : Interpretes fields defined in edges ! Methods : ! ------- ! Author : ! ------ ! February-2016 Willem Deconinck *ECMWF* !------------------------------------------------------------------------------ contains procedure, public :: nb_edges procedure, public :: mesh procedure, public :: edges procedure, public :: get_halo_exchange procedure, private :: gather_fieldset procedure, private :: gather_field generic, public :: gather => gather_field, gather_fieldset procedure, public :: get_gather procedure, private :: scatter_fieldset procedure, private :: scatter_field generic, public :: scatter => scatter_field, scatter_fieldset procedure, public :: get_scatter procedure, private :: checksum_fieldset procedure, private :: checksum_field generic, public :: checksum => checksum_field, checksum_fieldset procedure, public :: get_checksum #if FCKIT_FINAL_NOT_INHERITING final :: atlas_functionspace_EdgeColumns__final_auto #endif END TYPE atlas_functionspace_EdgeColumns interface atlas_functionspace_EdgeColumns module procedure constructor__cptr module procedure constructor end interface !------------------------------------------------------------------------------ !======================================================== contains !======================================================== function constructor__cptr(cptr) result(this) type(atlas_functionspace_EdgeColumns) :: this type(c_ptr), intent(in) :: cptr call this%reset_c_ptr( cptr ) call this%return() end function !------------------------------------------------------------------------------ function constructor(mesh,halo,levels) result(this) use atlas_functionspace_EdgeColumns_c_binding type(atlas_functionspace_EdgeColumns) :: this type(atlas_Mesh), intent(inout) :: mesh integer, intent(in), optional :: halo integer, intent(in), optional :: levels type(atlas_Config) :: config config = atlas_Config() if( present(halo) ) call config%set("halo",halo) if( present(levels) ) call config%set("levels",levels) call this%reset_c_ptr( atlas__fs__EdgeColumns__new( & mesh%CPTR_PGIBUG_A,config%CPTR_PGIBUG_B) ) call config%final() call this%return() end function !------------------------------------------------------------------------------ function nb_edges(this) use atlas_functionspace_EdgeColumns_c_binding integer :: nb_edges class(atlas_functionspace_EdgeColumns), intent(in) :: this nb_edges = atlas__fs__EdgeColumns__nb_edges(this%CPTR_PGIBUG_A) end function !------------------------------------------------------------------------------ function mesh(this) use atlas_functionspace_EdgeColumns_c_binding type(atlas_Mesh) :: mesh class(atlas_functionspace_EdgeColumns), intent(in) :: this call mesh%reset_c_ptr( atlas__fs__EdgeColumns__mesh(this%CPTR_PGIBUG_A) ) call mesh%return() end function !------------------------------------------------------------------------------ function edges(this) use atlas_functionspace_EdgeColumns_c_binding type(atlas_mesh_Edges) :: edges class(atlas_functionspace_EdgeColumns), intent(in) :: this call edges%reset_c_ptr( atlas__fs__EdgeColumns__edges(this%CPTR_PGIBUG_A) ) call edges%return() end function !------------------------------------------------------------------------------ function get_gather(this) result(gather) use atlas_functionspace_EdgeColumns_c_binding type(atlas_GatherScatter) :: gather class(atlas_functionspace_EdgeColumns), intent(in) :: this call gather%reset_c_ptr( atlas__fs__EdgeColumns__get_gather(this%CPTR_PGIBUG_A) ) ! call gather%return() end function !------------------------------------------------------------------------------ function get_scatter(this) result(scatter) use atlas_functionspace_EdgeColumns_c_binding type(atlas_GatherScatter) :: scatter class(atlas_functionspace_EdgeColumns), intent(in) :: this call scatter%reset_c_ptr( atlas__fs__EdgeColumns__get_scatter(this%CPTR_PGIBUG_A) ) ! call scatter%return() end function !------------------------------------------------------------------------------ subroutine gather_fieldset(this,local,global) use atlas_functionspace_EdgeColumns_c_binding class(atlas_functionspace_EdgeColumns), intent(in) :: this type(atlas_FieldSet), intent(in) :: local type(atlas_FieldSet), intent(inout) :: global call atlas__fs__EdgeColumns__gather_fieldset(this%CPTR_PGIBUG_A, & local%CPTR_PGIBUG_A,global%CPTR_PGIBUG_A) end subroutine !------------------------------------------------------------------------------ subroutine gather_field(this,local,global) use atlas_functionspace_EdgeColumns_c_binding class(atlas_functionspace_EdgeColumns), intent(in) :: this type(atlas_Field), intent(in) :: local type(atlas_Field), intent(inout) :: global call atlas__fs__EdgeColumns__gather_field(this%CPTR_PGIBUG_A,local%CPTR_PGIBUG_A,global%CPTR_PGIBUG_A) end subroutine !------------------------------------------------------------------------------ subroutine scatter_fieldset(this,global,local) use atlas_functionspace_EdgeColumns_c_binding class(atlas_functionspace_EdgeColumns), intent(in) :: this type(atlas_FieldSet), intent(in) :: global type(atlas_FieldSet), intent(inout) :: local call atlas__fs__EdgeColumns__scatter_fieldset(this%CPTR_PGIBUG_A, & global%CPTR_PGIBUG_A,local%CPTR_PGIBUG_A) end subroutine !------------------------------------------------------------------------------ subroutine scatter_field(this,global,local) use atlas_functionspace_EdgeColumns_c_binding class(atlas_functionspace_EdgeColumns), intent(in) :: this type(atlas_Field), intent(in) :: global type(atlas_Field), intent(inout) :: local call atlas__fs__EdgeColumns__scatter_field(this%CPTR_PGIBUG_A, & global%CPTR_PGIBUG_A,local%CPTR_PGIBUG_A) end subroutine !------------------------------------------------------------------------------ function get_halo_exchange(this) result(halo_exchange) use atlas_functionspace_EdgeColumns_c_binding type(atlas_HaloExchange) :: halo_exchange class(atlas_functionspace_EdgeColumns), intent(in) :: this call halo_exchange%reset_c_ptr( atlas__fs__EdgeColumns__get_halo_exchange(this%CPTR_PGIBUG_A) ) ! call halo_exchange%return() end function !------------------------------------------------------------------------------ function get_checksum(this) result(checksum) use atlas_functionspace_EdgeColumns_c_binding type(atlas_Checksum) :: checksum class(atlas_functionspace_EdgeColumns), intent(in) :: this call checksum%reset_c_ptr( atlas__fs__EdgeColumns__get_checksum(this%CPTR_PGIBUG_A) ) ! call checksum%return() end function !------------------------------------------------------------------------------ function checksum_fieldset(this,fieldset) result(checksum) use atlas_functionspace_EdgeColumns_c_binding character(len=:), allocatable :: checksum class(atlas_functionspace_EdgeColumns), intent(in) :: this type(atlas_FieldSet), intent(in) :: fieldset type(c_ptr) :: checksum_cptr integer :: checksum_size, checksum_allocated call atlas__fs__EdgeColumns__checksum_fieldset(this%CPTR_PGIBUG_A, & fieldset%CPTR_PGIBUG_A,checksum_cptr,checksum_size,checksum_allocated) allocate(character(len=checksum_size) :: checksum ) checksum = c_ptr_to_string(checksum_cptr) if( checksum_allocated == 1 ) call c_ptr_free(checksum_cptr) end function !------------------------------------------------------------------------------ function checksum_field(this,field) result(checksum) use atlas_functionspace_EdgeColumns_c_binding character(len=:), allocatable :: checksum class(atlas_functionspace_EdgeColumns), intent(in) :: this type(atlas_Field), intent(in) :: field type(c_ptr) :: checksum_cptr integer :: checksum_size, checksum_allocated call atlas__fs__EdgeColumns__checksum_field(this%CPTR_PGIBUG_A, & field%CPTR_PGIBUG_A,checksum_cptr,checksum_size,checksum_allocated) allocate(character(len=checksum_size) :: checksum ) checksum = c_ptr_to_string(checksum_cptr) if( checksum_allocated == 1 ) call c_ptr_free(checksum_cptr) end function !------------------------------------------------------------------------------- #if FCKIT_FINAL_NOT_INHERITING ATLAS_FINAL subroutine atlas_functionspace_EdgeColumns__final_auto(this) type(atlas_functionspace_EdgeColumns), intent(inout) :: this #if FCKIT_FINAL_DEBUGGING write(0,*) "atlas_functionspace_EdgeColumns__final_auto" #endif #if FCKIT_FINAL_NOT_PROPAGATING call this%final() #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine #endif !------------------------------------------------------------------------------ end module atlas_functionspace_EdgeColumns_module atlas-0.46.0/src/atlas_f/functionspace/atlas_functionspace_NodeColumns_module.fypp0000664000175000017500000007634015160212070030736 0ustar alastairalastair! (C) Copyright 2013 ECMWF. ! ! This software is licensed under the terms of the Apache Licence Version 2.0 ! which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. ! In applying this licence, ECMWF does not waive the privileges and immunities ! granted to it by virtue of its status as an intergovernmental organisation nor ! does it submit to any jurisdiction. #include "atlas/atlas_f.h" #:include "internals/atlas_generics.fypp" #:set ranks = [0,1] module atlas_functionspace_NodeColumns_module use fckit_c_interop_module, only : c_str, c_ptr_to_string, c_ptr_free use atlas_functionspace_module, only : atlas_FunctionSpace use atlas_Field_module, only: atlas_Field use atlas_FieldSet_module, only: atlas_FieldSet use atlas_Mesh_module, only: atlas_Mesh use atlas_mesh_Nodes_module, only: atlas_mesh_Nodes use atlas_GatherScatter_module, only: atlas_GatherScatter use atlas_HaloExchange_module, only: atlas_HaloExchange use atlas_Checksum_module, only: atlas_Checksum use atlas_Config_module, only: atlas_Config use atlas_kinds_module, only: ATLAS_KIND_GIDX implicit none private :: c_str, c_ptr_to_string, c_ptr_free private :: atlas_FunctionSpace private :: atlas_Field private :: atlas_FieldSet private :: atlas_mesh_Nodes private :: atlas_GatherScatter private :: atlas_HaloExchange private :: atlas_Checksum private :: atlas_Mesh private :: atlas_Config private :: ATLAS_KIND_GIDX public :: atlas_functionspace_NodeColumns private !------------------------------------------------------------------------------ TYPE, extends(atlas_FunctionSpace) :: atlas_functionspace_NodeColumns ! Purpose : ! ------- ! *atlas_functionspace_NodeColumns* : Interpretes fields defined in nodes ! Methods : ! ------- ! Author : ! ------ ! August-2015 Willem Deconinck *ECMWF* !------------------------------------------------------------------------------ contains procedure, public :: nb_nodes procedure, public :: mesh procedure, public :: nodes procedure, public :: get_halo_exchange procedure, private :: gather_fieldset procedure, private :: gather_field generic, public :: gather => gather_fieldset, gather_field procedure, public :: get_gather procedure, private :: scatter_fieldset procedure, private :: scatter_field generic, public :: scatter => scatter_fieldset, scatter_field procedure, public :: get_scatter procedure, private :: checksum_fieldset procedure, private :: checksum_field generic, public :: checksum => checksum_fieldset, checksum_field procedure, public :: get_checksum procedure, public :: minimum_per_level procedure, public :: maximum_per_level procedure, public :: minimum_and_location_per_level => & & minloc_per_level procedure, public :: maximum_and_location_per_level => & & maxloc_per_level procedure, public :: sum_per_level procedure, public :: order_independent_sum_per_level procedure, public :: mean_per_level @:generic_public_interface( minimum ) @:generic_public_interface( maximum ) @:generic_public_interface_2( minimum_and_location, prefix1=minloc, prefix2=minloclev ) @:generic_public_interface_2( maximum_and_location, prefix1=maxloc, prefix2=maxloclev ) @:generic_public_interface( sum ) @:generic_public_interface( order_independent_sum ) @:generic_public_interface( mean ) @:generic_public_interface( mean_and_standard_deviation, prefix=mean_and_stddev ) procedure, public :: mean_and_standard_deviation_per_level => & & mean_and_stddev_per_level #if FCKIT_FINAL_NOT_INHERITING final :: atlas_functionspace_NodeColumns__final_auto #endif END TYPE atlas_functionspace_NodeColumns interface atlas_functionspace_NodeColumns module procedure constructor__cptr module procedure constructor end interface !------------------------------------------------------------------------------ !======================================================== contains !======================================================== !------------------------------------------------------------------------------ function constructor__cptr(cptr) result(this) use, intrinsic :: iso_c_binding, only : c_ptr type(atlas_functionspace_NodeColumns) :: this type(c_ptr), intent(in) :: cptr call this%reset_c_ptr( cptr ) call this%return() end function !------------------------------------------------------------------------------ function constructor(mesh,halo,levels) result(this) use atlas_functionspace_NodeColumns_c_binding type(atlas_functionspace_NodeColumns) :: this type(atlas_Mesh), intent(inout) :: mesh integer, intent(in), optional :: halo integer, intent(in), optional :: levels type(atlas_Config) :: config config = atlas_Config() if( present(halo) ) call config%set("halo",halo) if( present(levels) ) call config%set("levels",levels) call this%reset_c_ptr( atlas__NodesFunctionSpace__new(mesh%CPTR_PGIBUG_A,config%CPTR_PGIBUG_B) ) call config%final() call this%return() end function !------------------------------------------------------------------------------ function nb_nodes(this) use atlas_functionspace_NodeColumns_c_binding integer :: nb_nodes class(atlas_functionspace_NodeColumns), intent(in) :: this nb_nodes = atlas__NodesFunctionSpace__nb_nodes(this%CPTR_PGIBUG_A) end function !------------------------------------------------------------------------------ function mesh(this) use atlas_functionspace_NodeColumns_c_binding type(atlas_Mesh) :: mesh class(atlas_functionspace_NodeColumns), intent(in) :: this call mesh%reset_c_ptr( atlas__NodesFunctionSpace__mesh(this%CPTR_PGIBUG_A) ) call mesh%return() end function !------------------------------------------------------------------------------ function nodes(this) use atlas_functionspace_NodeColumns_c_binding type(atlas_mesh_Nodes) :: nodes class(atlas_functionspace_NodeColumns), intent(in) :: this call nodes%reset_c_ptr( atlas__NodesFunctionSpace__nodes(this%CPTR_PGIBUG_A) ) call nodes%return() end function !------------------------------------------------------------------------------ function get_gather(this) result(gather) use atlas_functionspace_NodeColumns_c_binding type(atlas_GatherScatter) :: gather class(atlas_functionspace_NodeColumns), intent(in) :: this call gather%reset_c_ptr( atlas__NodesFunctioNSpace__get_gather(this%CPTR_PGIBUG_A) ) end function !------------------------------------------------------------------------------ function get_scatter(this) result(gather) use atlas_functionspace_NodeColumns_c_binding type(atlas_GatherScatter) :: gather class(atlas_functionspace_NodeColumns), intent(in) :: this call gather%reset_c_ptr( atlas__NodesFunctioNSpace__get_scatter(this%CPTR_PGIBUG_A) ) end function !------------------------------------------------------------------------------ subroutine gather_fieldset(this,local,global) use atlas_functionspace_NodeColumns_c_binding class(atlas_functionspace_NodeColumns), intent(in) :: this type(atlas_FieldSet), intent(in) :: local type(atlas_FieldSet), intent(inout) :: global call atlas__NodesFunctionSpace__gather_fieldset(this%CPTR_PGIBUG_A,local%CPTR_PGIBUG_A,global%CPTR_PGIBUG_A) end subroutine !------------------------------------------------------------------------------ subroutine gather_field(this,local,global) use atlas_functionspace_NodeColumns_c_binding class(atlas_functionspace_NodeColumns), intent(in) :: this type(atlas_Field), intent(in) :: local type(atlas_Field), intent(inout) :: global call atlas__NodesFunctionSpace__gather_field(this%CPTR_PGIBUG_A,local%CPTR_PGIBUG_A,global%CPTR_PGIBUG_A) end subroutine !------------------------------------------------------------------------------ subroutine scatter_fieldset(this,global,local) use atlas_functionspace_NodeColumns_c_binding class(atlas_functionspace_NodeColumns), intent(in) :: this type(atlas_FieldSet), intent(in) :: global type(atlas_FieldSet), intent(inout) :: local call atlas__NodesFunctionSpace__scatter_fieldset(this%CPTR_PGIBUG_A,global%CPTR_PGIBUG_A,local%CPTR_PGIBUG_A) end subroutine !------------------------------------------------------------------------------ subroutine scatter_field(this,global,local) use atlas_functionspace_NodeColumns_c_binding class(atlas_functionspace_NodeColumns), intent(in) :: this type(atlas_Field), intent(in) :: global type(atlas_Field), intent(inout) :: local call atlas__NodesFunctionSpace__scatter_field(this%CPTR_PGIBUG_A,global%CPTR_PGIBUG_A,local%CPTR_PGIBUG_A) end subroutine !------------------------------------------------------------------------------ function get_halo_exchange(this) result(halo_exchange) use atlas_functionspace_NodeColumns_c_binding type(atlas_HaloExchange) :: halo_exchange class(atlas_functionspace_NodeColumns), intent(in) :: this call halo_exchange%reset_c_ptr( atlas__NodesFunctioNSpace__get_halo_exchange(this%CPTR_PGIBUG_A) ) end function !------------------------------------------------------------------------------ function get_checksum(this) result(checksum) use atlas_functionspace_NodeColumns_c_binding type(atlas_Checksum) :: checksum class(atlas_functionspace_NodeColumns), intent(in) :: this call checksum%reset_c_ptr( atlas__NodesFunctioNSpace__get_checksum(this%CPTR_PGIBUG_A) ) end function !------------------------------------------------------------------------------ function checksum_fieldset(this,fieldset) result(checksum) use atlas_functionspace_NodeColumns_c_binding use, intrinsic :: iso_c_binding, only : c_ptr character(len=:), allocatable :: checksum class(atlas_functionspace_NodeColumns), intent(in) :: this type(atlas_FieldSet), intent(in) :: fieldset type(c_ptr) :: checksum_cptr integer :: checksum_size, checksum_allocated call atlas__NodesFunctionSpace__checksum_fieldset( & this%CPTR_PGIBUG_A,fieldset%CPTR_PGIBUG_A,checksum_cptr,checksum_size,checksum_allocated) allocate(character(len=checksum_size) :: checksum ) checksum = c_ptr_to_string(checksum_cptr) if( checksum_allocated == 1 ) call c_ptr_free(checksum_cptr) end function !------------------------------------------------------------------------------ function checksum_field(this,field) result(checksum) use atlas_functionspace_NodeColumns_c_binding use, intrinsic :: iso_c_binding, only : c_ptr character(len=:), allocatable :: checksum class(atlas_functionspace_NodeColumns), intent(in) :: this type(atlas_Field), intent(in) :: field type(c_ptr) :: checksum_cptr integer :: checksum_size, checksum_allocated call atlas__NodesFunctionSpace__checksum_field( & this%CPTR_PGIBUG_A,field%CPTR_PGIBUG_A,checksum_cptr,checksum_size,checksum_allocated) allocate(character(len=checksum_size) :: checksum ) checksum = c_ptr_to_string(checksum_cptr) if( checksum_allocated == 1 ) call c_ptr_free(checksum_cptr) end function !------------------------------------------------------------------------------ #:for dtype,ftype,ctype in types[:4] subroutine minimum_${dtype}$_r0(this,field,minimum) use atlas_functionspace_NodeColumns_c_binding use, intrinsic :: iso_c_binding, only : c_${ctype}$ class(atlas_functionspace_NodeColumns), intent(in) :: this type(atlas_Field) :: field ${ftype}$, intent(out) :: minimum call atlas__NodesFunctionSpace__min_${ctype}$(this%CPTR_PGIBUG_A,field%CPTR_PGIBUG_A,minimum) end subroutine !------------------------------------------------------------------------------ subroutine minimum_${dtype}$_r1(this,field,minimum) use atlas_functionspace_NodeColumns_c_binding use, intrinsic :: iso_c_binding, only : c_${ctype}$,c_ptr,c_f_pointer class(atlas_functionspace_NodeColumns), intent(in) :: this type(atlas_Field) :: field ${ftype}$, allocatable, intent(out) :: minimum(:) type(c_ptr) :: min_cptr ${ftype}$, pointer :: min_fptr(:) integer :: min_size call atlas__NodesFunctionSpace__min_arr_${ctype}$(this%CPTR_PGIBUG_A,field%CPTR_PGIBUG_A,min_cptr,min_size) call c_f_pointer(min_cptr,min_fptr,(/min_size/)) allocate(minimum(min_size)) minimum(:) = min_fptr(:) call c_ptr_free(min_cptr) end subroutine !------------------------------------------------------------------------------ subroutine maximum_${dtype}$_r0(this,field,maximum) use atlas_functionspace_NodeColumns_c_binding use, intrinsic :: iso_c_binding, only : c_${ctype}$ class(atlas_functionspace_NodeColumns), intent(in) :: this type(atlas_Field) :: field ${ftype}$, intent(out) :: maximum call atlas__NodesFunctionSpace__max_${ctype}$(this%CPTR_PGIBUG_A,field%CPTR_PGIBUG_A,maximum) end subroutine !------------------------------------------------------------------------------ subroutine maximum_${dtype}$_r1(this,field,maximum) use atlas_functionspace_NodeColumns_c_binding use, intrinsic :: iso_c_binding, only : c_${ctype}$, c_ptr, c_f_pointer class(atlas_functionspace_NodeColumns), intent(in) :: this type(atlas_Field) :: field ${ftype}$, allocatable, intent(out) :: maximum(:) type(c_ptr) :: max_cptr ${ftype}$, pointer :: max_fptr(:) integer :: max_size call atlas__NodesFunctionSpace__max_arr_${ctype}$(this%CPTR_PGIBUG_A,field%CPTR_PGIBUG_A,max_cptr,max_size) call c_f_pointer(max_cptr,max_fptr,(/max_size/)) allocate(maximum(max_size)) maximum(:) = max_fptr(:) call c_ptr_free(max_cptr) end subroutine !------------------------------------------------------------------------------ subroutine minloc_${dtype}$_r0(this,field,minimum,location) use atlas_functionspace_NodeColumns_c_binding use, intrinsic :: iso_c_binding, only : c_${ctype}$, c_long class(atlas_functionspace_NodeColumns), intent(in) :: this type(atlas_Field) :: field ${ftype}$, intent(out) :: minimum integer(ATLAS_KIND_GIDX), intent(out) :: location integer(c_long) :: loc call atlas__NodesFunctionSpace__minloc_${ctype}$(this%CPTR_PGIBUG_A,field%CPTR_PGIBUG_A,minimum,loc) location = loc end subroutine !------------------------------------------------------------------------------ subroutine maxloc_${dtype}$_r0(this,field,maximum,location) use atlas_functionspace_NodeColumns_c_binding use, intrinsic :: iso_c_binding, only : c_${ctype}$, c_long class(atlas_functionspace_NodeColumns), intent(in) :: this type(atlas_Field) :: field ${ftype}$, intent(out) :: maximum integer(ATLAS_KIND_GIDX), intent(out) :: location integer(c_long) :: loc call atlas__NodesFunctionSpace__maxloc_${ctype}$(this%CPTR_PGIBUG_A,field%CPTR_PGIBUG_A,maximum,loc) location = loc end subroutine !------------------------------------------------------------------------------ subroutine minloc_${dtype}$_r1(this,field,minimum,location) use atlas_functionspace_NodeColumns_c_binding use, intrinsic :: iso_c_binding, only : c_${ctype}$, c_ptr, c_f_pointer, c_long class(atlas_functionspace_NodeColumns), intent(in) :: this type(atlas_Field) :: field ${ftype}$, allocatable, intent(out) :: minimum(:) integer(ATLAS_KIND_GIDX), allocatable, intent(out) :: location(:) type(c_ptr) :: min_cptr, loc_cptr ${ftype}$, pointer :: min_fptr(:) integer(c_long),pointer :: loc_fptr(:) integer :: min_size call atlas__NodesFunctionSpace__minloc_arr_${ctype}$(this%CPTR_PGIBUG_A,field%CPTR_PGIBUG_A,min_cptr,loc_cptr,min_size) call c_f_pointer(min_cptr,min_fptr,(/min_size/)) call c_f_pointer(loc_cptr,loc_fptr,(/min_size/)) allocate(minimum(min_size)) allocate(location(min_size)) minimum(:) = min_fptr(:) location(:) = loc_fptr(:) call c_ptr_free(min_cptr) call c_ptr_free(loc_cptr) end subroutine !------------------------------------------------------------------------------ subroutine maxloc_${dtype}$_r1(this,field,maximum,location) use atlas_functionspace_NodeColumns_c_binding use, intrinsic :: iso_c_binding, only : c_${ctype}$, c_ptr, c_f_pointer, c_long class(atlas_functionspace_NodeColumns), intent(in) :: this type(atlas_Field) :: field ${ftype}$, allocatable, intent(out) :: maximum(:) integer(ATLAS_KIND_GIDX), allocatable, intent(out) :: location(:) type(c_ptr) :: max_cptr, loc_cptr ${ftype}$, pointer :: max_fptr(:) integer(c_long),pointer :: loc_fptr(:) integer :: max_size call atlas__NodesFunctionSpace__maxloc_arr_${ctype}$(this%CPTR_PGIBUG_A,field%CPTR_PGIBUG_A,max_cptr,loc_cptr,max_size) call c_f_pointer(max_cptr,max_fptr,(/max_size/)) call c_f_pointer(loc_cptr,loc_fptr,(/max_size/)) allocate(maximum(max_size)) allocate(location(max_size)) maximum(:) = max_fptr(:) location(:) = loc_fptr(:) call c_ptr_free(max_cptr) call c_ptr_free(loc_cptr) end subroutine !------------------------------------------------------------------------------ subroutine sum_${dtype}$_r0(this,field,sum,N) use atlas_functionspace_NodeColumns_c_binding use, intrinsic :: iso_c_binding, only : c_${ctype}$, c_int class(atlas_functionspace_NodeColumns), intent(in) :: this type(atlas_Field) :: field ${ftype}$, intent(out) :: sum integer(c_int), intent(out), optional :: N integer(c_int) :: opt_N call atlas__NodesFunctionSpace__sum_${ctype}$(this%CPTR_PGIBUG_A,field%CPTR_PGIBUG_A,sum,opt_N) if( present(N) ) N = opt_N end subroutine !------------------------------------------------------------------------------ subroutine sum_${dtype}$_r1(this,field,sum,N) use atlas_functionspace_NodeColumns_c_binding use, intrinsic :: iso_c_binding, only : c_${ctype}$, c_int, c_ptr, c_f_pointer use fckit_c_interop_module, only : c_ptr_free class(atlas_functionspace_NodeColumns), intent(in) :: this type(atlas_Field) :: field ${ftype}$, allocatable, intent(out) :: sum(:) integer(c_int), intent(out), optional :: N type(c_ptr) :: sum_cptr ${ftype}$, pointer :: sum_fptr(:) integer :: sum_size integer(c_int) :: opt_N call atlas__NodesFunctionSpace__sum_arr_${ctype}$(this%CPTR_PGIBUG_A,field%CPTR_PGIBUG_A,sum_cptr,sum_size,opt_N) call c_f_pointer(sum_cptr,sum_fptr,(/sum_size/)) allocate(sum(sum_size)) sum(:) = sum_fptr(:) call c_ptr_free(sum_cptr) if( present(N) ) N = opt_N end subroutine !------------------------------------------------------------------------------ subroutine mean_${dtype}$_r0(this,field,mean,N) use atlas_functionspace_NodeColumns_c_binding use, intrinsic :: iso_c_binding class(atlas_functionspace_NodeColumns), intent(in) :: this type(atlas_Field) :: field ${ftype}$, intent(out) :: mean integer(c_int), intent(out), optional :: N integer(c_int) :: opt_N call atlas__NodesFunctionSpace__mean_${ctype}$(this%CPTR_PGIBUG_A,field%CPTR_PGIBUG_A,mean,opt_N) if( present(N) ) N = opt_N end subroutine !------------------------------------------------------------------------------ subroutine mean_${dtype}$_r1(this,field,mean,N) use atlas_functionspace_NodeColumns_c_binding use, intrinsic :: iso_c_binding, only : c_${ctype}$, c_int, c_ptr, c_f_pointer use fckit_c_interop_module, only : c_ptr_free class(atlas_functionspace_NodeColumns), intent(in) :: this type(atlas_Field) :: field ${ftype}$, allocatable, intent(out) :: mean(:) integer(c_int), intent(out), optional :: N type(c_ptr) :: mean_cptr ${ftype}$, pointer :: mean_fptr(:) integer :: mean_size integer(c_int) :: opt_N call atlas__NodesFunctionSpace__mean_arr_${ctype}$(this%CPTR_PGIBUG_A,field%CPTR_PGIBUG_A,mean_cptr,mean_size,opt_N) call c_f_pointer(mean_cptr,mean_fptr,(/mean_size/)) allocate(mean(mean_size)) mean(:) = mean_fptr(:) call c_ptr_free(mean_cptr) if( present(N) ) N = opt_N end subroutine !------------------------------------------------------------------------------ subroutine mean_and_stddev_${dtype}$_r0(this,field,mean,stddev,N) use atlas_functionspace_NodeColumns_c_binding use, intrinsic :: iso_c_binding, only : c_${ctype}$, c_int class(atlas_functionspace_NodeColumns), intent(in) :: this type(atlas_Field) :: field ${ftype}$, intent(out) :: mean ${ftype}$, intent(out) :: stddev integer(c_int), intent(out), optional :: N integer(c_int) :: opt_N call atlas__NodesFunctionSpace__mean_and_stddev_${ctype}$( & this%CPTR_PGIBUG_A,field%CPTR_PGIBUG_A,mean,stddev,opt_N) if( present(N) ) N = opt_N end subroutine !------------------------------------------------------------------------------ subroutine mean_and_stddev_${dtype}$_r1(this,field,mean,stddev,N) use atlas_functionspace_NodeColumns_c_binding use, intrinsic :: iso_c_binding, only : c_${ctype}$, c_int ,c_ptr, c_f_pointer use fckit_c_interop_module, only : c_ptr_free class(atlas_functionspace_NodeColumns), intent(in) :: this type(atlas_Field) :: field ${ftype}$, allocatable, intent(out) :: mean(:) ${ftype}$, allocatable, intent(out) :: stddev(:) integer(c_int), intent(out), optional :: N type(c_ptr) :: mean_cptr, stddev_cptr ${ftype}$, pointer :: mean_fptr(:), stddev_fptr(:) integer :: varsize integer(c_int) :: opt_N call atlas__NodesFunctionSpace__mean_and_stddev_arr_${ctype}$( & this%CPTR_PGIBUG_A,field%CPTR_PGIBUG_A,mean_cptr,stddev_cptr,varsize,opt_N) call c_f_pointer(mean_cptr,mean_fptr,(/varsize/)) call c_f_pointer(stddev_cptr,stddev_fptr,(/varsize/)) allocate(mean(varsize)) allocate(stddev(varsize)) mean(:) = mean_fptr(:) stddev(:) = stddev_fptr(:) call c_ptr_free(mean_cptr) call c_ptr_free(stddev_cptr) if( present(N) ) N = opt_N end subroutine !------------------------------------------------------------------------------ subroutine minloclev_${dtype}$_r0(this,field,minimum,location,level) use atlas_functionspace_NodeColumns_c_binding use, intrinsic :: iso_c_binding class(atlas_functionspace_NodeColumns), intent(in) :: this type(atlas_Field) :: field ${ftype}$, intent(out) :: minimum integer(ATLAS_KIND_GIDX), intent(out) :: location integer(c_int), intent(out) :: level integer(c_long) :: loc integer(c_int) :: opt_lev call atlas__NodesFunctionSpace__minloclev_${ctype}$(this%CPTR_PGIBUG_A,field%CPTR_PGIBUG_A,minimum,loc,opt_lev) location = loc level = opt_lev end subroutine !------------------------------------------------------------------------------ subroutine maxloclev_${dtype}$_r0(this,field,maximum,location,level) use atlas_functionspace_NodeColumns_c_binding use, intrinsic :: iso_c_binding class(atlas_functionspace_NodeColumns), intent(in) :: this type(atlas_Field) :: field ${ftype}$, intent(out) :: maximum integer(ATLAS_KIND_GIDX), intent(out) :: location integer(c_int), intent(out) :: level integer(c_long) :: loc integer(c_int) :: opt_lev call atlas__NodesFunctionSpace__maxloclev_${ctype}$(this%CPTR_PGIBUG_A,field%CPTR_PGIBUG_A,maximum,loc,opt_lev) location = loc level = opt_lev end subroutine !------------------------------------------------------------------------------ subroutine minloclev_${dtype}$_r1(this,field,minimum,location,level) use atlas_functionspace_NodeColumns_c_binding use, intrinsic :: iso_c_binding, only : c_int, c_${ctype}$, c_long, c_ptr, c_f_pointer class(atlas_functionspace_NodeColumns), intent(in) :: this type(atlas_Field) :: field ${ftype}$, allocatable, intent(out) :: minimum(:) integer(ATLAS_KIND_GIDX), allocatable, intent(out) :: location(:) integer(c_int), allocatable, intent(out) :: level(:) type(c_ptr) :: min_cptr, loc_cptr, lev_cptr ${ftype}$, pointer :: min_fptr(:) integer(c_long),pointer :: loc_fptr(:) integer(c_long),pointer :: lev_fptr(:) integer :: min_size, jlev call atlas__NodesFunctionSpace__minloclev_arr_${ctype}$(this%CPTR_PGIBUG_A,field%CPTR_PGIBUG_A,min_cptr,loc_cptr,lev_cptr,min_size) call c_f_pointer(min_cptr,min_fptr,(/min_size/)) call c_f_pointer(loc_cptr,loc_fptr,(/min_size/)) allocate(minimum(min_size)) allocate(location(min_size)) minimum(:) = min_fptr(:) location(:) = loc_fptr(:) call c_f_pointer(lev_cptr,lev_fptr,(/min_size/)) allocate(level(min_size)) do jlev=1,min_size level(jlev) = int(lev_fptr(jlev),c_int) enddo call c_ptr_free(min_cptr) call c_ptr_free(loc_cptr) call c_ptr_free(lev_cptr) end subroutine !------------------------------------------------------------------------------ subroutine maxloclev_${dtype}$_r1(this,field,maximum,location,level) use atlas_functionspace_NodeColumns_c_binding use, intrinsic :: iso_c_binding, only : c_${ctype}$, c_int, c_long , c_f_pointer, c_ptr class(atlas_functionspace_NodeColumns), intent(in) :: this type(atlas_Field) :: field ${ftype}$, allocatable, intent(out) :: maximum(:) integer(ATLAS_KIND_GIDX), allocatable, intent(out) :: location(:) integer(c_int), allocatable, intent(out) :: level(:) type(c_ptr) :: max_cptr, loc_cptr, lev_cptr ${ftype}$, pointer :: max_fptr(:) integer(c_long),pointer :: loc_fptr(:) integer(c_long),pointer :: lev_fptr(:) integer :: max_size, jlev call atlas__NodesFunctionSpace__maxloclev_arr_${ctype}$(this%CPTR_PGIBUG_A,field%CPTR_PGIBUG_A,max_cptr,loc_cptr,lev_cptr,max_size) call c_f_pointer(max_cptr,max_fptr,(/max_size/)) call c_f_pointer(loc_cptr,loc_fptr,(/max_size/)) allocate(maximum(max_size)) allocate(location(max_size)) maximum(:) = max_fptr(:) location(:) = loc_fptr(:) call c_f_pointer(lev_cptr,lev_fptr,(/max_size/)) allocate(level(max_size)) do jlev=1,max_size level(jlev) = int(lev_fptr(jlev),c_int) enddo call c_ptr_free(max_cptr) call c_ptr_free(loc_cptr) call c_ptr_free(lev_cptr) end subroutine !------------------------------------------------------------------------------ subroutine order_independent_sum_${dtype}$_r0(this,field,sum,N) use atlas_functionspace_NodeColumns_c_binding use, intrinsic :: iso_c_binding, only : c_${ctype}$, c_int class(atlas_functionspace_NodeColumns), intent(in) :: this type(atlas_Field) :: field ${ftype}$, intent(out) :: sum integer(c_int), intent(out), optional :: N integer(c_int) :: opt_N call atlas__NodesFunctionSpace__oisum_${ctype}$(this%CPTR_PGIBUG_A,field%CPTR_PGIBUG_A,sum,opt_N) if( present(N) ) N = opt_N end subroutine !------------------------------------------------------------------------------ subroutine order_independent_sum_${dtype}$_r1(this,field,sum,N) use atlas_functionspace_NodeColumns_c_binding use, intrinsic :: iso_c_binding, only : c_${ctype}$, c_int, c_ptr, c_f_pointer use fckit_c_interop_module, only : c_ptr_free class(atlas_functionspace_NodeColumns), intent(in) :: this type(atlas_Field) :: field ${ftype}$, allocatable, intent(out) :: sum(:) integer(c_int), intent(out), optional :: N type(c_ptr) :: sum_cptr ${ftype}$, pointer :: sum_fptr(:) integer :: sum_size integer(c_int) :: opt_N call atlas__NodesFunctionSpace__oisum_arr_${ctype}$(this%CPTR_PGIBUG_A,field%CPTR_PGIBUG_A,sum_cptr,sum_size,opt_N) call c_f_pointer(sum_cptr,sum_fptr,(/sum_size/)) allocate(sum(sum_size)) sum(:) = sum_fptr(:) call c_ptr_free(sum_cptr) if( present(N) ) N = opt_N end subroutine #:endfor !------------------------------------------------------------------------------ subroutine minloc_per_level(this,field,minimum,location) use atlas_functionspace_NodeColumns_c_binding class(atlas_functionspace_NodeColumns), intent(in) :: this type(atlas_Field), intent(in) :: field type(atlas_Field), intent(inout) :: minimum type(atlas_Field), intent(inout) :: location call atlas__NodesFunctionSpace__minloc_per_level( & this%CPTR_PGIBUG_A,field%CPTR_PGIBUG_A,minimum%CPTR_PGIBUG_A,location%CPTR_PGIBUG_A) end subroutine !------------------------------------------------------------------------------ subroutine maxloc_per_level(this,field,maximum,location) use atlas_functionspace_NodeColumns_c_binding class(atlas_functionspace_NodeColumns), intent(in) :: this type(atlas_Field), intent(in) :: field type(atlas_Field), intent(inout) :: maximum type(atlas_Field), intent(inout) :: location call atlas__NodesFunctionSpace__maxloc_per_level( & this%CPTR_PGIBUG_A,field%CPTR_PGIBUG_A,maximum%CPTR_PGIBUG_A,location%CPTR_PGIBUG_A) end subroutine !------------------------------------------------------------------------------ subroutine minimum_per_level(this,field,minimum) use atlas_functionspace_NodeColumns_c_binding class(atlas_functionspace_NodeColumns), intent(in) :: this type(atlas_Field), intent(in) :: field type(atlas_Field), intent(inout) :: minimum call atlas__NodesFunctionSpace__min_per_level( & this%CPTR_PGIBUG_A,field%CPTR_PGIBUG_A,minimum%CPTR_PGIBUG_A) end subroutine !------------------------------------------------------------------------------ subroutine maximum_per_level(this,field,maximum) use atlas_functionspace_NodeColumns_c_binding class(atlas_functionspace_NodeColumns), intent(in) :: this type(atlas_Field), intent(in) :: field type(atlas_Field), intent(inout) :: maximum call atlas__NodesFunctionSpace__max_per_level( & this%CPTR_PGIBUG_A,field%CPTR_PGIBUG_A,maximum%CPTR_PGIBUG_A) end subroutine !------------------------------------------------------------------------------ subroutine sum_per_level(this,field,sum,N) use atlas_functionspace_NodeColumns_c_binding use, intrinsic :: iso_c_binding, only : c_int class(atlas_functionspace_NodeColumns), intent(in) :: this type(atlas_Field), intent(in) :: field type(atlas_Field), intent(inout) :: sum integer(c_int), intent(out), optional :: N integer(c_int) :: opt_N call atlas__NodesFunctionSpace__sum_per_level( & this%CPTR_PGIBUG_A,field%CPTR_PGIBUG_A,sum%CPTR_PGIBUG_A,opt_N) if( present(N) ) N = opt_N end subroutine !------------------------------------------------------------------------------ subroutine order_independent_sum_per_level(this,field,sum,N) use atlas_functionspace_NodeColumns_c_binding use, intrinsic :: iso_c_binding, only : c_int class(atlas_functionspace_NodeColumns), intent(in) :: this type(atlas_Field), intent(in) :: field type(atlas_Field), intent(inout) :: sum integer(c_int), intent(out), optional :: N integer(c_int) :: opt_N call atlas__NodesFunctionSpace__oisum_per_level( & this%CPTR_PGIBUG_A,field%CPTR_PGIBUG_A,sum%CPTR_PGIBUG_A,opt_N) if( present(N) ) N = opt_N end subroutine !------------------------------------------------------------------------------ subroutine mean_per_level(this,field,mean,N) use atlas_functionspace_NodeColumns_c_binding use, intrinsic :: iso_c_binding, only : c_int class(atlas_functionspace_NodeColumns), intent(in) :: this type(atlas_Field), intent(in) :: field type(atlas_Field), intent(inout) :: mean integer(c_int), intent(out), optional :: N integer(c_int) :: opt_N call atlas__NodesFunctionSpace__mean_per_level( & this%CPTR_PGIBUG_A,field%CPTR_PGIBUG_A,mean%CPTR_PGIBUG_A,opt_N) if( present(N) ) N = opt_N end subroutine !------------------------------------------------------------------------------ subroutine mean_and_stddev_per_level(this,field,mean,stddev,N) use atlas_functionspace_NodeColumns_c_binding use, intrinsic :: iso_c_binding class(atlas_functionspace_NodeColumns), intent(in) :: this type(atlas_Field), intent(in) :: field type(atlas_Field), intent(inout) :: mean type(atlas_Field), intent(inout) :: stddev integer(c_int), intent(out), optional :: N integer(c_int) :: opt_N call atlas__NodesFunctionSpace__mean_and_stddev_per_level( & & this%CPTR_PGIBUG_A,field%CPTR_PGIBUG_A,mean%CPTR_PGIBUG_A,stddev%CPTR_PGIBUG_A,opt_N) if( present(N) ) N = opt_N end subroutine !------------------------------------------------------------------------------- #if FCKIT_FINAL_NOT_INHERITING ATLAS_FINAL subroutine atlas_functionspace_NodeColumns__final_auto(this) type(atlas_functionspace_NodeColumns), intent(inout) :: this #if FCKIT_FINAL_DEBUGGING write(0,*) "atlas_functionspace_NodeColumns__final_auto" #endif #if FCKIT_FINAL_NOT_PROPAGATING call this%final() #endif FCKIT_SUPPRESS_UNUSED( this ) end subroutine #endif !------------------------------------------------------------------------------ end module atlas_functionspace_NodeColumns_module atlas-0.46.0/src/apps/0000775000175000017500000000000015160212070014615 5ustar alastairalastairatlas-0.46.0/src/apps/atlas-gaussian-latitudes.cc0000664000175000017500000001041315160212070022033 0ustar alastairalastair/* * (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 #include #include #include "eckit/config/Resource.h" #include "eckit/runtime/Main.h" #include "eckit/runtime/Tool.h" #include "atlas/grid/detail/spacing/GaussianSpacing.h" #include "atlas/library.h" #include "atlas/runtime/Exception.h" //------------------------------------------------------------------------------------------------------ using namespace eckit; using namespace atlas; using atlas::grid::spacing::GaussianSpacing; //------------------------------------------------------------------------------------------------------ class AtlasGaussianLatitudes : public eckit::Tool { void run() override; public: AtlasGaussianLatitudes(int argc, char** argv): eckit::Tool(argc, argv) { atlas::initialise(argc,argv); do_run = false; bool help = Resource("--help", false); std::string help_str = "NAME\n" " atlas-gaussian-latitudes - Compute gaussian latitudes, given " "the N number\n" "\n" "SYNOPSIS\n" " atlas-gaussian-latitudes [--help] [-N] [OPTION]...\n" "\n" "DESCRIPTION\n" " Compute gaussian latitudes, given the N number.\n" " Latitudes start at the pole (+90), and decrease in value.\n" "\n" " -N Number of points between pole and equator\n" "\n" " --full If set, all latitudes will be given, otherwise " "only\n" " between North pole and equator.\n" "\n" " --format \"table\" (default)\n" " \"C\"\n" "\n" " --compact Write 5 latitudes per line if the format supports " "it\n" "\n" "AUTHOR\n" " Written by Willem Deconinck.\n" "\n" "ECMWF December 2014"; N = Resource("-N", 0); full = Resource("--full", false); compact = Resource("--compact", false); format = Resource("--format", std::string("table")); if (N > 0) { do_run = true; } if (do_run == false) { if (help) { Log::info() << help_str << std::endl; } else { Log::info() << "usage: atlas-gaussian-latitudes [--help] [-N] [OPTION]..." << std::endl; } } } private: int N; bool full; bool compact; std::string format; bool do_run; }; //------------------------------------------------------------------------------ void AtlasGaussianLatitudes::run() { if (!do_run) { return; } GaussianSpacing lats(2 * N); int end = full ? 2 * N : N; if (format == "table") { for (int jlat = 0; jlat < end; ++jlat) { std::cout << std::setw(4) << jlat + 1 << std::setw(17) << std::setprecision(12) << std::fixed << lats[jlat] << std::endl; } } if (format == "C") { std::cout << "double lat[] = {" << std::endl; for (int jlat = 0; jlat < end; ++jlat) { std::cout << std::setw(16) << std::setprecision(12) << std::fixed << lats[jlat]; if (jlat != end - 1) { std::cout << ","; } if ((compact && (jlat + 1) % 5 == 0) || !compact || jlat == end - 1) { std::cout << std::endl; } } std::cout << "};" << std::endl; } atlas::finalise(); } //------------------------------------------------------------------------------ int main(int argc, char** argv) { AtlasGaussianLatitudes tool(argc, argv); return tool.start(); } atlas-0.46.0/src/apps/atlas-grids.cc0000664000175000017500000007067715160212070017357 0ustar alastairalastair/* * (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 "eckit/config/Resource.h" #include "eckit/filesystem/PathName.h" #include "eckit/log/Bytes.h" #include "eckit/log/JSON.h" #include "eckit/log/Log.h" #include "eckit/types/FloatCompare.h" #include "atlas/grid.h" #include "atlas/grid/detail/grid/GridBuilder.h" #include "atlas/grid/detail/grid/GridFactory.h" #include "atlas/projection/detail/ProjectionFactory.h" #include "atlas/runtime/AtlasTool.h" #include "atlas/util/NormaliseLongitude.h" namespace atlas { template class FixedFormat { public: using value_type = Value; FixedFormat(value_type x, long precision): x_(x), precision_(precision > 0 ? precision : 20) {} void print(std::ostream& out) const { for (long precision = 0; precision <= precision_; ++precision) { if (is_precision(precision) || precision == precision_) { out << std::setprecision(precision); out << std::fixed << x_; break; } } } bool is_precision(long precision) const { std::stringstream ss; ss << std::setprecision(precision); ss << std::fixed << x_; value_type _x; ss >> _x; return std::abs(x_ - _x) < 1.e-20; } friend std::ostream& operator<<(std::ostream& out, const FixedFormat& This) { This.print(out); return out; } private: float x_; long precision_; }; FixedFormat fixed_format(double x, long precision) { return FixedFormat(x, precision); } FixedFormat fixed_format(float x, long precision) { return FixedFormat(x, precision); } } // namespace atlas //---------------------------------------------------------------------------------------------------------------------- struct AtlasGrids : public atlas::AtlasTool { 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() + " [OPTION]... [--help,-h]"; } std::string longDescription() override { return "Catalogue of available built-in grids\n" "\n" " Browse catalogue of grids\n" "\n" " GRID: unique identifier for grid \n" " Example values: N80, F40, O24, L32, CS-ED-12\n"; } AtlasGrids(int argc, char** argv): AtlasTool(argc, argv) { add_option( new SimpleOption("list", "List all grids. The names are possible values for the argument")); add_option(new SimpleOption("info", "List information about ")); add_option(new SimpleOption("json", "Export json")); add_option(new SimpleOption("rtable", "Export IFS rtable")); add_option(new SimpleOption("check", "Check grid")); add_option(new SimpleOption("check-uid", "Check grid uid required")); add_option(new SimpleOption("check-boundingbox", "Check grid bounding_box(n,w,s,e) required")); add_option(new SimpleOption("precision", "Precision used for float output")); add_option(new SimpleOption("approximate-resolution", "Approximate resolution in degrees (North-South)")); } }; //------------------------------------------------------------------------------------------------------ int AtlasGrids::execute(const Args& args) { using namespace atlas; std::string key = args.count() ? args(0) : ""; bool info = false; args.get("info", info); bool json = false; args.get("json", json); bool rtable = false; args.get("rtable", rtable); bool check = false; args.get("check", check); bool check_uid = false; args.get("check-uid", check_uid); bool check_bbox = false; args.get("check-boundingbox", check_bbox); bool list = false; args.get("list", list); bool approximate_resolution = false; args.get("approximate-resolution", approximate_resolution); bool do_run = list || (!key.empty() && (info || json || rtable || check || approximate_resolution)); if (!key.empty() && !do_run) { Log::error() << "Option wrong or missing after '" << key << "'" << std::endl; } if (list) { Log::info() << "usage: atlas-grids [OPTION]... [--help]\n" << std::endl; Log::info() << "\n"; Log::info() << "Available grid types:" << std::endl; std::set grid_types; for (const auto& [key, builder] : grid::GridBuilder::typeRegistry()) { grid_types.insert(builder->type()); } for (const auto& b : grid_types) { Log::info() << " -- " << b << '\n'; } Log::info() << "\n"; Log::info() << "Available named grids:" << std::endl; size_t maxlen = 0; for (auto b : grid::GridBuilder::nameRegistry()) { for (const auto& name : b.second->names()) { maxlen = std::max(maxlen, name.size()); } } for (const auto& [key, builder] : grid::GridBuilder::typeRegistry()) { int c = 0; for (const auto& name : builder->names()) { if (c == 0) { Log::info() << " -- " << std::left << std::setw(maxlen + 8) << name; if (!builder->type().empty()) { Log::info() << "type: " << builder->type(); } Log::info() << std::endl; } else { Log::info() << " " << std::left << std::setw(maxlen + 8) << name << std::endl; } c++; } } } if (!key.empty()) { eckit::PathName path{key}; Grid grid = path.exists() ? Grid(Grid::Spec{path}) : Grid(key); if (!grid) { return failed(); } if (info) { Log::info() << "Grid " << key << std::endl; Log::info() << " name: " << grid.name() << std::endl; Log::info() << " uid: " << grid.uid() << std::endl; if (auto gaussian = GaussianGrid(grid)) { Log::info() << " Gaussian N number: " << gaussian.N() << std::endl; } if (auto cubedsphere = CubedSphereGrid(grid)) { Log::info() << " Cubedsphere faces: " << cubedsphere.N() << "x" << cubedsphere.N() << "x6" << std::endl; } Log::info() << " number of points: " << grid.size() << std::endl; Log::info() << " memory footprint of grid: " << eckit::Bytes(grid.footprint()) << std::endl; size_t memsize = grid.size() * sizeof(double); Log::info() << " memory footprint per field (dp): " << eckit::Bytes(memsize) << std::endl; if (auto structuredgrid = StructuredGrid(grid)) { if (not grid.projection()) { double deg, km; Log::info() << " number of latitudes (N-S): " << structuredgrid.ny() << std::endl; Log::info() << " number of longitudes (max): " << structuredgrid.nxmax() << std::endl; deg = (structuredgrid.y().front() - structuredgrid.y().back()) / (structuredgrid.ny() - 1); km = deg * 40075. / 360.; Log::info() << " approximate resolution N-S: " << std::setw(10) << std::fixed << deg << " deg " << km << " km " << std::endl; deg = 360. / static_cast(structuredgrid.nx(structuredgrid.ny() / 2)); km = deg * 40075. / 360.; Log::info() << " approximate resolution E-W equator: " << std::setw(10) << std::fixed << deg << " deg " << km << " km " << std::endl; deg = 360. * std::cos(structuredgrid.y(structuredgrid.ny() / 4) * M_PI / 180.) / static_cast(structuredgrid.nx(structuredgrid.ny() / 4)); km = deg * 40075. / 360.; Log::info() << " approximate resolution E-W midlat: " << std::setw(10) << std::fixed << deg << " deg " << km << " km " << std::endl; deg = 360. * std::cos(structuredgrid.y().front() * M_PI / 180.) / static_cast(structuredgrid.nx().front()); km = deg * 40075. / 360.; Log::info() << " approximate resolution E-W pole: " << std::setw(10) << std::fixed << deg << " deg " << km << " km " << std::endl; Log::info() << " spectral truncation -- linear: " << structuredgrid.ny() - 1 << std::endl; Log::info() << " spectral truncation -- quadratic: " << static_cast(std::floor(2. / 3. * structuredgrid.ny() + 0.5)) - 1 << std::endl; Log::info() << " spectral truncation -- cubic: " << static_cast(std::floor(0.5 * structuredgrid.ny() + 0.5)) - 1 << std::endl; } auto precision = Log::info().precision(3); if (grid.projection().units() == "meters") { Log::info() << " x : [ " << std::setw(10) << std::fixed << structuredgrid.xspace().min() / 1000. << " , " << std::setw(10) << std::fixed << structuredgrid.xspace().max() / 1000. << " ] km" << std::endl; Log::info() << " y : [ " << std::setw(10) << std::fixed << structuredgrid.yspace().min() / 1000. << " , " << std::setw(10) << std::fixed << structuredgrid.yspace().max() / 1000. << " ] km" << std::endl; if (structuredgrid.xspace().nxmax() == structuredgrid.xspace().nxmin()) { Log::info() << " dx : " << structuredgrid.xspace().dx()[0] / 1000. << " km" << std::endl; } Log::info() << " dy : " << std::abs(structuredgrid.y(1) - structuredgrid.y(0)) / 1000. << " km" << std::endl; Log::info() << " lonlat(centre) : " << grid.projection().lonlat( {0.5 * (structuredgrid.xspace().max() + structuredgrid.xspace().min()), 0.5 * (structuredgrid.yspace().max() + structuredgrid.yspace().min())}) << std::endl; Log::info() << " lonlat(xmin,ymax) : " << grid.projection().lonlat( {structuredgrid.xspace().min(), structuredgrid.yspace().max()}) << std::endl; Log::info() << " lonlat(xmin,ymin) : " << grid.projection().lonlat( {structuredgrid.xspace().min(), structuredgrid.yspace().min()}) << std::endl; Log::info() << " lonlat(xmax,ymin) : " << grid.projection().lonlat( {structuredgrid.xspace().max(), structuredgrid.yspace().min()}) << std::endl; Log::info() << " lonlat(xmax,ymax) : " << grid.projection().lonlat( {structuredgrid.xspace().max(), structuredgrid.yspace().max()}) << std::endl; } if (grid.projection().units() == "degrees") { Log::info() << " x : [ " << std::setw(10) << std::fixed << structuredgrid.xspace().min() << " , " << std::setw(10) << std::fixed << structuredgrid.xspace().max() << " ] deg" << std::endl; double ymin = std::min(structuredgrid.yspace().front(), structuredgrid.yspace().back()); double ymax = std::max(structuredgrid.yspace().front(), structuredgrid.yspace().back()); Log::info() << " y : [ " << std::setw(10) << std::fixed << ymin << " , " << std::setw(10) << std::fixed << ymax << " ] deg" << std::endl; } auto it = grid.lonlat().begin(); Log::info() << " lonlat(first) : " << *it << std::endl; it += grid.size() - 1; Log::info() << " lonlat(last) : " << *it << std::endl; if (auto bb = grid.lonlatBoundingBox()) { Log::info() << " bounding_box(n,w,s,e) : { " << bb.north() << ", " << bb.west() << ", " << bb.south() << ", " << bb.east() << " }" << std::endl; } Log::info().precision(precision); } } if (approximate_resolution) { if (auto structuredgrid = StructuredGrid(grid)) { if (structuredgrid.domain().global()) { auto deg = (structuredgrid.y().front() - structuredgrid.y().back()) / (structuredgrid.ny() - 1); long precision = -1; args.get("precision", precision); Log::info() << fixed_format(deg, precision) << std::endl; } else { ATLAS_NOTIMPLEMENTED; } } else { ATLAS_NOTIMPLEMENTED; } } if (json) { std::stringstream stream; eckit::JSON js(stream); js.precision(16); js << grid.spec(); std::cout << stream.str() << std::endl; } if (rtable) { if (auto structuredgrid = StructuredGrid(grid)) { std::stringstream stream; stream << "&NAMRGRI\n"; for (idx_t j = 0; j < structuredgrid.ny(); ++j) { stream << " NRGRI(" << std::setfill('0') << std::setw(5) << 1 + j << ")=" << std::setfill(' ') << std::setw(5) << structuredgrid.nx(j) << ",\n"; } stream << "/" << std::flush; std::cout << stream.str() << std::endl; } } if (check) { bool check_failed = false; Log::Channel out; out.setStream(Log::error()); eckit::PathName path{key}; if (not path.exists()) { out << "Check failed: " << key << " is not a file" << std::endl; return failed(); } util::Config config_check; if (not util::Config{path}.get("check", config_check)) { out << "Check failed: no \"check\" section in " << key << std::endl; return failed(); } bool strict = config_check.getBool("strict", true); idx_t size; if (config_check.get("size", size)) { if (grid.size() != size) { out << "Check failed: grid size " << grid.size() << " expected to be " << size << std::endl; check_failed = true; } } else { Log::warning() << "Check for size skipped" << std::endl; } std::string uid; if (config_check.get("uid", uid)) { if (uid == "ignore") { out << "WARNING: ignoring uid explicitly" << std::endl; } else if (grid.uid() != uid) { out << "Check failed: grid uid " << grid.uid() << " expected to be " << uid << std::endl; check_failed = true; } } else if (check_uid && uid.empty()) { out << "Check failed: grid uid " << grid.uid() << " was not encoded in the check" << std::endl; check_failed = true; } else { Log::warning() << "Check for uid skipped" << std::endl; } auto equal = [](double a, double b) { return eckit::types::is_approximately_equal(a, b, 5.e-4); }; auto point_equal = [&equal](const PointLonLat& a, const PointLonLat& b) -> bool { return equal(a.lon(), b.lon()) && equal(a.lat(), b.lat()); }; auto difference_normalised = [](double a, double ref) { util::NormaliseLongitude normalised{ref - 180}; return normalised(a) - ref; }; auto point_equal_normalised = [&](const PointLonLat& a, const PointLonLat& b) -> bool { return equal(a.lat(), b.lat()) && equal(difference_normalised(a.lon(), b.lon()), 0.); }; auto equal_normalised = [&](double a, double b) { return equal(difference_normalised(a, b), 0.); }; auto check_lonlat = [&](const std::string& key, std::function&& get_lonlat) { std::vector lonlat_config; if (config_check.get(key, lonlat_config)) { PointLonLat lonlat_check = {lonlat_config.data()}; PointLonLat lonlat = get_lonlat(); if (not point_equal_normalised(lonlat, lonlat_check)) { out << std::setprecision(4) << std::fixed << "Check failed: " << key << " " << lonlat << " expected to be " << lonlat_check; out << " ( normalised difference: {" << difference_normalised(lonlat.lon(), lonlat_check.lon()) << "," << lonlat.lat() - lonlat_check.lat() << "} )" << std::endl; check_failed = true; } else if (strict && not point_equal(lonlat, lonlat_check)) { out << std::setprecision(4) << std::fixed << "Check failed: " << key << " " << lonlat << " expected to be " << lonlat_check; out << " ( normalised difference: {" << difference_normalised(lonlat.lon(), lonlat_check.lon()) << "," << lonlat.lat() - lonlat_check.lat() << "} )" << std::endl; check_failed = true; } } else { Log::warning() << "Check for " << key << " skipped" << std::endl; } }; check_lonlat("lonlat(first)", [&]() { return grid.lonlat().front(); }); check_lonlat("lonlat(last)", [&]() { return grid.lonlat().back(); }); std::vector bbox; if (config_check.get("bounding_box(n,w,s,e)", bbox) && bbox.size() == 4) { auto bb = grid.lonlatBoundingBox(); if (!bb) { check_failed = true; out << "Check failed: cannot calculate bounding box for " << grid.spec() << std::endl; } else { bool any_value_failed = false; if (!equal(bb.north(), bbox[0])) { any_value_failed = true; out << "Check failed: n=" << bb.north() << " expected to be " << bbox[0] << std::endl; } if (!equal_normalised(bb.west(), bbox[1])) { any_value_failed = true; out << "Check failed: w=" << bb.west() << " expected to be " << bbox[1] << " ( normalised difference : " << difference_normalised(bb.west(), bbox[1]) << " )" << std::endl; } else if (strict && not equal(bb.west(), bbox[1])) { out << "Check failed: w=" << bb.west() << " expected to be " << bbox[1] << " ( normalised difference : " << difference_normalised(bb.west(), bbox[1]) << " )" << std::endl; } if (!equal(bb.south(), bbox[2])) { any_value_failed = true; out << "Check failed: s=" << bb.south() << " expected to be " << bbox[2] << std::endl; } if (!equal_normalised(bb.east(), bbox[3])) { any_value_failed = true; out << "Check failed: e=" << bb.east() << " expected to be " << bbox[3] << " ( normalised difference : " << difference_normalised(bb.east(), bbox[3]) << " )" << std::endl; } else if (strict && not equal(bb.east(), bbox[3])) { any_value_failed = true; out << "Check failed: e=" << bb.east() << " expected to be " << bbox[3] << " ( normalised difference : " << difference_normalised(bb.east(), bbox[3]) << " )" << std::endl; } if (any_value_failed) { check_failed = true; out << "Check failed: bounding_box(n,w,s,e) [" << bb.north() << ", " << bb.west() << ", " << bb.south() << ", " << bb.east() << "] expected to be [" << bbox[0] << ", " << bbox[1] << ", " << bbox[2] << ", " << bbox[3] << "]" << std::endl; } } } else if (check_bbox && bbox.size() != 4) { out << "Check failed: grid bounding_box(n,w,s,e) " << grid.lonlatBoundingBox() << " was not encoded in the check" << std::endl; check_failed = true; } else { 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 (projection::ProjectionFactory::has("proj")) { std::string proj_str; if (config_check.get("proj", proj_str)) { Projection proj(util::Config("type", "proj") | util::Config("proj", proj_str)); auto check_proj = [&](const PointLonLat& lonlat, const PointLonLat& proj_lonlat, const std::string& point = "") { if (not point_equal(lonlat, proj_lonlat)) { if (point_equal_normalised(lonlat, proj_lonlat)) { Log::warning() << "WARNING: Projection of " << point << " grid point is different from Proj only due to different normalisation: " << lonlat << " != " << proj_lonlat << std::endl; } else { Log::info() << "Check failed: Projection of " << point << " grid point is different from Proj: " << lonlat << " != " << proj_lonlat << " normalised difference = {" << std::fixed << std::setprecision(12) << difference_normalised(lonlat.lon(), proj_lonlat.lon()) << "," << lonlat.lat() - proj_lonlat.lat() << "}" << std::endl; check_failed = true; } } }; check_proj(grid.lonlat().front(), proj.lonlat(grid.xy().front()), "first"); check_proj(grid.lonlat().back(), proj.lonlat(grid.xy().back()), "last"); } } auto check_roundtrip = [&](const PointLonLat& point, bool strict = false) { using eckit::types::is_approximately_equal; const auto projection = grid.projection(); PointLonLat point_roundtrip = projection.lonlat(projection.xy(point)); bool roundtrip = is_approximately_equal(point_roundtrip.lat(), point.lat(), 1.e-6) && is_approximately_equal(point_roundtrip.lon(), point.lon(), 1.e-6); constexpr util::NormaliseLongitude normalised; bool normalised_roundtrip = is_approximately_equal(point_roundtrip.lat(), point.lat(), 1.e-6) && is_approximately_equal(normalised(point_roundtrip.lon()), normalised(point.lon())); if (not normalised_roundtrip) { check_failed = true; Log::info() << "Check failed: Roundtrip of point " << point << " failed, even with normalisation: " << point_roundtrip << " != " << point << " normalised difference = {" << std::fixed << std::setprecision(12) << difference_normalised(point_roundtrip.lon(), point.lon()) << "," << point_roundtrip.lat() - point.lat() << "}" << std::endl; } if (strict && not roundtrip) { check_failed = true; Log::info() << "Check failed: Roundtrip of point " << point << " failed"; if (normalised_roundtrip) { Log::info() << " but was OK with normalisation"; } Log::info() << ": " << point_roundtrip << " != " << point << " normalised difference = {" << std::fixed << std::setprecision(12) << difference_normalised(point_roundtrip.lon(), point.lon()) << "," << point_roundtrip.lat() - point.lat() << "}" << std::endl; } }; std::vector roundtrip; if (config_check.get("roundtrip", roundtrip)) { for (auto& entry : roundtrip) { std::vector lonlat; entry.get("lonlat", lonlat); check_roundtrip({lonlat[0], lonlat[1]}, strict); } } if (check_failed) { return failed(); } Log::info() << "SUCCESS: All checks passed" << std::endl; } } return success(); } //------------------------------------------------------------------------------------------------------ int main(int argc, char** argv) { AtlasGrids tool(argc, argv); return tool.start(); } atlas-0.46.0/src/apps/atlas-grid-points.cc0000664000175000017500000001126515160212070020472 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include #include "atlas/grid.h" #include "atlas/library.h" #include "atlas/runtime/AtlasTool.h" #include "atlas/util/GridPointsJSONWriter.h" //------------------------------------------------------------------------------ using namespace atlas; using eckit::PathName; //------------------------------------------------------------------------------ class Program : public AtlasTool { int execute(const Args& args) override; std::string briefDescription() override { return "Write grid points to file"; } std::string usage() override { return name() + " GRID [OPTION]... [--help]"; } std::string longDescription() override { return " The 'GRID' argument can be either the name of a named grid, or the path to a" " YAML configuration file that describes the grid.\n" "Example values for grid names are: N80, F40, O24, L64x33, CS-ED-12. See the program " "'atlas-grids' for a list of named grids.\n" "\n"; } public: Program(int argc, char** argv); private: }; //----------------------------------------------------------------------------- Program::Program(int argc, char** argv): AtlasTool(argc, argv) { add_option(new SimpleOption("output.file", "Output file. If not specified, output is directed to stdout")); add_option(new SimpleOption("output.format", "Output format. If not specified: json")); add_option(new SimpleOption("field_base", "Base used for field output. Default=0")); add_option(new SimpleOption("index_base", "Base used for index input. Default=0")); add_option(new SimpleOption("index", "Select grid point indices (first_index=, last_index = + - 1). " "If not provided, all points are selected. " "Format: comma separated list, where '-' can be used to represent a range. e.g. '[1-3,5,7-10]'." "Square brackets are optional. white-spaces and newline characters are allowed as in a valid JSON array.")); add_option(new SimpleOption("field","Field to output. [\"lonlat\"(D),\"index\",\"partition\"]")); add_option(new Separator("Advanced")); add_option(new SimpleOption("partitioner.type", "Partitioner [equal_regions,equal_area,checkerboard,equal_bands,regular_bands]")); add_option(new SimpleOption("partition", "partition [0:partitions-1]")); add_option(new SimpleOption("partitions", "Number of partitions")); add_option(new SimpleOption("json.precision", "Number of digits after decimal in output")); add_option(new SimpleOption("json.pretty", "Pretty printing of json output")); add_option(new SimpleOption("verbose", "Output progress to stdout, default=false")); } //----------------------------------------------------------------------------- std::string get_arg(const AtlasTool::Args& args, const std::string& flag, const std::string& default_value = "") { for (int i = 0; i < args.count() - 1; ++i) { if (args(i) == flag) { return args(i + 1); } } if (not default_value.empty()) { return default_value; } throw_Exception("Could not find argument for flag " + flag); } int Program::execute(const Args& args) { Grid grid; { std::string key = args.count() ? args(0) : ""; if (!key.empty()) { eckit::PathName path{key}; grid = path.exists() ? Grid(Grid::Spec{path}) : Grid(key); } } if (!grid) { Log::error() << "Grid not specified as positional argument" << std::endl; return failed(); } util::GridPointsJSONWriter writer{grid,args}; if (mpi::rank() == 0 ) { std::string output_file; if (args.get("output.file",output_file)) { Log::info() << "Grid contains " << grid.size() << " points." << std::endl; std::ofstream out(output_file); writer.write(out, Log::info()); Log::info() << "File " << output_file << " written." << std::endl; } else { writer.write(std::cout); } } return success(); } //------------------------------------------------------------------------------ int main(int argc, char** argv) { Program tool(argc, argv); return tool.start(); } atlas-0.46.0/src/apps/atlas-meshgen.cc0000664000175000017500000003462415160212070017665 0ustar alastairalastair/* * (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 #include #include "atlas/functionspace/EdgeColumns.h" #include "atlas/functionspace/NodeColumns.h" #include "atlas/grid.h" #include "atlas/library.h" #include "atlas/mesh/Mesh.h" #include "atlas/mesh/Nodes.h" #include "atlas/mesh/actions/BuildDualMesh.h" #include "atlas/mesh/actions/BuildEdges.h" #include "atlas/mesh/actions/BuildHalo.h" #include "atlas/mesh/actions/BuildParallelFields.h" #include "atlas/mesh/actions/BuildPeriodicBoundaries.h" #include "atlas/mesh/actions/BuildStatistics.h" #include "atlas/mesh/actions/BuildTorusXYZField.h" #include "atlas/mesh/actions/BuildXYZField.h" #include "atlas/meshgenerator.h" #include "atlas/output/Gmsh.h" #include "atlas/output/detail/GmshIO.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/runtime/AtlasTool.h" #include "atlas/runtime/Log.h" #include "atlas/util/Config.h" #include "eckit/exception/Exceptions.h" #include "eckit/filesystem/PathName.h" #include "eckit/log/Bytes.h" #include "eckit/runtime/Main.h" #include "eckit/runtime/Tool.h" //------------------------------------------------------------------------------ using namespace atlas; using namespace atlas::mesh::actions; using namespace atlas::grid; using namespace atlas::functionspace; using namespace atlas::mesh; using atlas::util::Config; using eckit::PathName; //------------------------------------------------------------------------------ MeshGenerator make_meshgenerator(const Grid& grid, const AtlasTool::Args& args) { auto config = grid.meshgenerator(); // recommended by the grid itself if (args.has("generator")) { config.set("type", args.getString("generator")); } if (args.has("3d")) { config.set("3d", true); } if (mpi::comm().size() > 1 || args.getBool("edges", false)) { config.set("3d", false); } config.set("include_pole", args.getBool("include-pole", false)); config.set("patch_pole", args.getBool("patch-pole", false)); if (args.has("fixup")) { config.set("fixup", args.getBool("fixup")); } if (args.has("partition")) { config.set("partition", args.getInt("partition")); config.set("part", args.getInt("partition")); } if (args.has("partitions")) { config.set("partitions", args.getInt("partitions")); config.set("nb_parts", args.getInt("partitions")); } if (args.has("halo")) { config.set("halo", args.getInt("halo")); } if (args.has("angle")) { config.set("angle", args.getDouble("angle")); } return MeshGenerator{config}; } Partitioner make_partitioner(const Grid& grid, const AtlasTool::Args& args) { auto config = grid.partitioner(); // recommended by the grid itself if (args.has("partitioner")) { config.set("type", args.getString("partitioner")); } if (args.has("regular")) { config.set("regular", args.getBool("regular")); } if (args.has("partitions")) { config.set("partitions", args.getInt("partitions")); } return Partitioner{config}; } //------------------------------------------------------------------------------ class Meshgen2Gmsh : public AtlasTool { int execute(const Args& args) override; std::string briefDescription() override { return "Mesh generator and output to Gmsh format"; } std::string usage() override { return name() + " GRID [OUTPUT] [OPTION]... [--help]"; } std::string longDescription() override { return " The 'GRID' argument can be either the name of a named grid, orthe path to a" " YAML configuration file that describes the grid.\n" "Example values for grid names are: N80, F40, O24, L64x33, CS-ED-12. See the program " "'atlas-grids' for a list of named grids.\n" "\n" " The optional 'OUTPUT' argument contains the path to the output file. " "If not given, a default value 'mesh.msh' is employed."; } public: Meshgen2Gmsh(int argc, char** argv); private: std::string key; long halo; bool nodes; bool edges; bool cells; bool brick; bool stats; bool info; bool ghost; std::string identifier; PathName path_in; PathName path_out; }; //----------------------------------------------------------------------------- Meshgen2Gmsh::Meshgen2Gmsh(int argc, char** argv): AtlasTool(argc, argv) { add_option(new SimpleOption("coordinates", "Output mesh in given coordinates")); add_option( new SimpleOption("lonlat", "Output mesh in lon,lat coordinates (shorthand for --coordinates=lonlat)")); add_option(new SimpleOption("ij", "Output mesh in i,j coordinates (shorthand for --coordinates=ij)")); add_option(new SimpleOption("3d", "Output mesh as sphere, and generate " "mesh connecting East and West in " "case serial")); add_option(new SimpleOption("ghost", "Output ghost elements")); add_option(new SimpleOption( "generator", "Mesh generator [structured,regular,delaunay,cubedsphere] (default = structured)")); add_option(new SimpleOption("partitioner", "Mesh partitioner [equal_regions,checkerboard,equal_bands,regular_bands")); add_option(new Separator("Options for `--generator=structured`")); add_option(new SimpleOption("include-pole", "Include pole point")); add_option(new SimpleOption("patch-pole", "Patch poles with elements.")); add_option(new SimpleOption( "angle", "Maximum element-edge slant deviation from meridian in degrees. \n" + indent() + " Value range between 0 and 30\n" + indent() + " 0: Mostly triangular, with only perfect quads (=default)\n" + indent() + " 30: Mostly skewed quads with only triags when skewness becomes too large\n" + indent() + " -1: Only triangles")); add_option(new Separator("Options for `--partitioner=checkerboard`")); add_option(new SimpleOption("regular", "regular checkerboard partitioner")); add_option(new Separator("Advanced")); add_option(new SimpleOption("halo", "Halo size")); add_option(new SimpleOption("nodes", "Build nodes datastructure")); add_option(new SimpleOption("edges", "Build edges datastructure")); add_option(new SimpleOption("cells", "Build cells datastructure")); add_option(new SimpleOption("brick", "Build brick dual mesh")); add_option(new SimpleOption("stats", "Write statistics file")); add_option(new SimpleOption("info", "Write Info")); add_option(new SimpleOption("periodic_x", "periodic mesh in x-direction")); add_option(new SimpleOption("periodic_y", "periodic mesh in y-direction")); add_option(new SimpleOption("torus", "Output mesh as torus")); add_option(new SimpleOption( "water", "Output elements containing water points (not specifying --water or --land enables both)")); add_option(new SimpleOption( "land", "Output elements containing land points (not specifying --water or --land enables both)")); add_option(new SimpleOption("fixup", "Apply custom fixes to the mesh where it applies")); add_option(new SimpleOption("gmsh", "Output gmsh (default=true)")); add_option(new SimpleOption("partition", "partition [0:partitions]")); add_option(new SimpleOption("partitions", "Number of partitions")); add_option(new SimpleOption("partition-graph", "Output partition graph")); add_option(new SimpleOption("partition-polygons", "Output partition polygons python visualization scripts")); } //----------------------------------------------------------------------------- std::string get_arg(const AtlasTool::Args& args, const std::string& flag, const std::string& default_value = "") { for (int i = 0; i < args.count() - 1; ++i) { if (args(i) == flag) { return args(i + 1); } } if (not default_value.empty()) { return default_value; } throw_Exception("Could not find argument for flag " + flag); } int Meshgen2Gmsh::execute(const Args& args) { ATLAS_TRACE(); key = ""; args.get("grid.name", key); nodes = false; args.get("nodes", nodes); edges = false; args.get("edges", edges); cells = false; args.get("cells", cells); stats = false; args.get("stats", stats); info = false; args.get("info", info); halo = 0; args.get("halo", halo); bool dim_3d = false; args.get("3d", dim_3d); brick = false; args.get("brick", brick); ghost = false; args.get("ghost", ghost); key = get_positional_arg(args, 0); path_out = get_arg(args, "-o", args.count() > 1 ? get_positional_arg(args, 1) : "mesh.msh"); // if ( path_in_str.empty() && key.empty() ) { // Log::warning() << "missing argument --grid.name or --grid.json" << std::endl; // Log::warning() << "Usage: " << usage() << std::endl; // return failed(); // } if (edges) { halo = std::max(halo, 1l); } Grid grid; if (key.size()) { eckit::PathName path_in{key}; try { if (path_in.exists()) { Log::info() << "Creating grid from file " << path_in << std::endl; Log::debug() << Config(path_in) << std::endl; grid = Grid(Config{path_in}); } else { Log::info() << "Creating grid from name " << key << std::endl; grid = Grid(key); } } catch (eckit::Exception& e) { Log::error() << e.what() << std::endl; if (path_in.exists()) { Log::error() << "Could not generate mesh for grid defined in file \"" << path_in << "\"" << std::endl; } else { Log::error() << "Could not generate mesh for grid \"" << key << "\"" << std::endl; } return failed(); } } else { Log::error() << "No grid specified." << std::endl; return failed(); } Log::debug() << "Domain: " << grid.domain() << std::endl; if (auto g = StructuredGrid(grid)) { Log::debug() << "Periodic: " << g.periodic() << std::endl; } Log::debug() << "Spec: " << grid.spec() << std::endl; auto meshgenerator = make_meshgenerator(grid, args); auto partitioner = make_partitioner(grid, args); Mesh mesh; try { Log::info() << "Generating mesh using " << meshgenerator.type() << " generator and partitioner " << partitioner.type() << std::endl; mesh = meshgenerator.generate(grid, partitioner); } catch (eckit::Exception& e) { Log::error() << e.what() << std::endl; Log::error() << e.callStack() << std::endl; throw; } if ((grid.projection().units() == "degrees" && halo > 0) || nodes) { functionspace::NodeColumns nodes_fs(mesh, option::halo(halo)); } else { Log::warning() << "Not yet implemented: building halo's with projections " "not defined in degrees" << std::endl; Log::warning() << "units: " << grid.projection().units() << std::endl; } if (brick) { build_brick_dual_mesh(grid, mesh); } if (edges && grid.projection().units() == "degrees") { functionspace::EdgeColumns edges_fs(mesh, option::halo(halo)); if (not brick) { build_median_dual_mesh(mesh); } } if (cells) { functionspace::CellColumns cells_fs(mesh, option::halo(halo)); } if (stats) { build_statistics(mesh); } bool lonlat = args.getBool("lonlat", false); bool ij = args.getBool("ij", false); std::string coordinates = dim_3d ? "xyz" : lonlat ? "lonlat" : ij ? "ij" : "xy"; args.get("coordinates", coordinates); if (args.getBool("gmsh", true)) { bool torus = false; args.get("torus", torus); if (torus) { if (auto g = StructuredGrid(grid)) { Log::debug() << "Building xyz representation for nodes on torus" << std::endl; mesh::actions::BuildTorusXYZField("xyz")(mesh, g.domain(), 5., 2., g.nxmax(), g.ny()); } else { Log::error() << "Cannot output non StructuredGrid grids as torus at the moment" << std::endl; } } Config gmsh_config; gmsh_config.set("coordinates", coordinates); gmsh_config.set("edges", edges); gmsh_config.set("ghost", ghost); gmsh_config.set("info", info); if (args.has("land") || args.has("water")) { gmsh_config.set("land", args.getBool("land", false)); gmsh_config.set("water", args.getBool("water", false)); } atlas::output::Gmsh gmsh(path_out, gmsh_config); Log::info() << "Writing mesh to gmsh file \"" << path_out << "\" generated from grid \"" << grid.name() << "\"" << std::endl; gmsh.write(mesh); } if (info) { Log::info() << "Mesh partition footprint: " << eckit::Bytes(mesh.footprint()) << std::endl; } if (args.getBool("partition-graph", false)) { Log::info() << "Partitioning graph: \n" << mesh.partitionGraph() << std::endl; } if (args.getBool("partition-polygons", false)) { for (idx_t jhalo = 0; jhalo <= halo; ++jhalo) { mesh.polygon(jhalo).outputPythonScript("polygon_halo" + std::to_string(jhalo) + ".py", Config("nodes", false)("coordinates", coordinates)); } } return success(); } //------------------------------------------------------------------------------ int main(int argc, char** argv) { Meshgen2Gmsh tool(argc, argv); return tool.start(); } atlas-0.46.0/src/apps/CMakeLists.txt0000664000175000017500000000211315160212070017352 0ustar alastairalastair# (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. ecbuild_add_executable( TARGET atlas-main OUTPUT_NAME atlas SOURCES atlas.cc LIBS atlas ) ecbuild_add_executable( TARGET atlas-meshgen SOURCES atlas-meshgen.cc LIBS atlas CONDITION atlas_HAVE_ATLAS_FUNCTIONSPACE ) ecbuild_add_executable( TARGET atlas-grids SOURCES atlas-grids.cc LIBS atlas CONDITION atlas_HAVE_ATLAS_GRID ) ecbuild_add_executable( TARGET atlas-grid-points SOURCES atlas-grid-points.cc LIBS atlas ) ecbuild_add_executable( TARGET atlas-gaussian-latitudes SOURCES atlas-gaussian-latitudes.cc LIBS atlas CONDITION atlas_HAVE_ATLAS_GRID ) add_subdirectory(atlas-interpolations) atlas-0.46.0/src/apps/atlas.cc0000664000175000017500000000653115160212070016235 0ustar alastairalastair/* * (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/library.h" #include "atlas/runtime/Log.h" #include "eckit/config/Resource.h" #include "eckit/runtime/Tool.h" using namespace eckit; namespace atlas { //---------------------------------------------------------------------------------------------------------------------- class Version : public Tool { public: Version(int argc, char** argv): Tool(argc, argv) {} ~Version() override = default; void run() override; }; void Version::run() { if (Resource("--version", false)) { Log::info() << atlas::Library::instance().version() << std::endl; return; } else if (Resource("--git", false)) { Log::info() << atlas::Library::instance().gitsha1(12) << std::endl; return; } else if (Resource("--info", false)) { Log::info() << atlas::Library::instance().information() << std::endl; return; } else if (Resource("--init", false)) { Log::info() << "+ atlas::initialize()" << std::endl; atlas::initialize(); Log::info() << "+ atlas::finalize()" << std::endl; atlas::finalize(); return; } else if (Resource("--help", false)) { Log::info() << "NAME\n" " atlas - Framework for parallel flexible data structures on " "the sphere\n" "\n" "SYNOPSIS\n" " atlas [--help] [--version] [--git] [--info]\n" "\n" "DESCRIPTION\n" " Framework for parallel flexible data structures on the " "sphere.\n" "\n" "OPTIONS\n" " --help\n" " Print this help\n" "\n" " --version\n" " Print short version string 'MAJOR.MINOR.PATCH' \n" "\n" " --info\n" " Print build configuration and features\n" "\n" " --init\n" " Initialise and finalise atlas library, useful for printing debug information (environment ATLAS_DEBUG=1)\n" "\n" "AUTHOR\n" " Written by Willem Deconinck.\n" "\n" "ECMWF December 2014" << std::endl; return; } else { Log::info() << "usage: atlas [--help] [--version] [--git] [--info] [--init]" << std::endl; } } } // namespace atlas //---------------------------------------------------------------------------------------------------------------------- using namespace atlas; int main(int argc, char** argv) { Version tool(argc, argv); return tool.start(); } atlas-0.46.0/src/apps/atlas-interpolations/0000775000175000017500000000000015160212070020771 5ustar alastairalastairatlas-0.46.0/src/apps/atlas-interpolations/ScripIO.h0000664000175000017500000000134515160212070022455 0ustar alastairalastair/* * (C) Copyright 2025- 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/linalg/sparse.h" #include "atlas/util/Config.h" namespace atlas { class ScripIO { public: explicit ScripIO(const util::Config& = util::NoConfig()) {} static atlas::linalg::SparseMatrixStorage read(const std::string&); static void write(const linalg::SparseMatrixStorage&, const std::string&); }; } atlas-0.46.0/src/apps/atlas-interpolations/ScripIO.cc0000664000175000017500000000771615160212070022623 0ustar alastairalastair/* * (C) Copyright 2025- 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 "ScripIO.h" #include #if ATLAS_HAVE_NETCDF #include #endif #include "atlas/linalg/sparse/SparseMatrixToTriplets.h" #include "atlas/linalg/sparse/MakeEckitSparseMatrix.h" #include "atlas/runtime/Exception.h" namespace atlas { using SparseMatrixStorage = linalg::SparseMatrixStorage; using scrip_index = int; using scrip_value = double; using scrip_size = size_t; SparseMatrixStorage ScripIO::read(const std::string& matrix_name) { #if ATLAS_HAVE_NETCDF == 0 ATLAS_THROW_EXCEPTION("Cannot read SCRIP files: Atlas not compiled with NetCDF support"); #else size_t Nr = 0; size_t Nc = 0; size_t nnz = 0; std::vector rows; std::vector cols; std::vector vals; // read SCRIP file try { netCDF::NcFile f(matrix_name, netCDF::NcFile::read); Nr = f.getDim("dst_grid_size").getSize(); Nc = f.getDim("src_grid_size").getSize(); nnz = f.getDim("num_links").getSize(); ATLAS_ASSERT(Nr > 0); ATLAS_ASSERT(Nc > 0); ATLAS_ASSERT(nnz > 0); auto var_rows = f.getVar("dst_address"); ATLAS_ASSERT(var_rows.getDimCount() == 1 && var_rows.getDim(0).getSize() == nnz); rows.resize(nnz); // NOTE: not compressed var_rows.getVar(rows.data()); auto var_cols = f.getVar("src_address"); ATLAS_ASSERT(var_cols.getDimCount() == 1 && var_cols.getDim(0).getSize() == nnz); cols.resize(nnz); var_cols.getVar(cols.data()); auto var_vals = f.getVar("remap_matrix"); ATLAS_ASSERT(var_vals.getDimCount() == 2 && var_vals.getDim(0).getSize() == nnz && var_vals.getDim(1).getSize() == 1); vals.resize(nnz); var_vals.getVar(vals.data()); } catch (netCDF::exceptions::NcException& e) { ATLAS_THROW_EXCEPTION("SCRIP reading failed : " << e.what()); } constexpr scrip_index scrip_base = 1; constexpr bool is_sorted = false; return atlas::linalg::make_sparse_matrix_storage_from_rows_columns_values(Nr, Nc, rows, cols, vals, scrip_base, is_sorted); #endif // ATLAS_HAVE_NETCDF } void ScripIO::write(const SparseMatrixStorage& matrix, const std::string& matrix_name) { #if ATLAS_HAVE_NETCDF == 0 ATLAS_THROW_EXCEPTION("Cannot write SCRIP file: Atlas not compiled with NetCDF support"); #else scrip_size Nr = matrix.rows(); scrip_size Nc = matrix.cols(); scrip_size nnz = matrix.nnz(); std::vector ia(nnz); std::vector ja(nnz); std::vector a(nnz); atlas::linalg::sparse_matrix_to_rows_columns_values(matrix, ia, ja, a); constexpr scrip_index scrip_index_base = 1; for (size_t i = 0; i < matrix.nnz(); ++i) { ja[i] += scrip_index_base; ia[i] += scrip_index_base; } try { netCDF::NcFile f(matrix_name, netCDF::NcFile::replace); f.addDim("dst_grid_size", Nr); f.addDim("src_grid_size", Nc); std::vector dims; dims.push_back(f.addDim("num_links", nnz)); netCDF::NcVar nc_dstaddr = f.addVar("dst_address", netCDF::ncInt, dims); netCDF::NcVar nc_srcaddr = f.addVar("src_address", netCDF::ncInt, dims); dims.push_back(f.addDim("num_wgts", 1)); netCDF::NcVar nc_rmatrix = f.addVar("remap_matrix", netCDF::ncDouble, dims); nc_dstaddr.putVar(ia.data()); nc_srcaddr.putVar(ja.data()); nc_rmatrix.putVar(a.data()); f.close(); } catch (netCDF::exceptions::NcException& e) { ATLAS_THROW_EXCEPTION("SCRIP writing failed : " << e.what()); } #endif } } atlas-0.46.0/src/apps/atlas-interpolations/atlas-interpolations.cc0000664000175000017500000010325715160212070025464 0ustar alastairalastair/* * (C) Copyright 2025 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/filesystem/PathName.h" #include "atlas/array.h" #include "atlas/field/Field.h" #include "atlas/field/FieldSet.h" #include "atlas/functionspace/CellColumns.h" #include "atlas/functionspace/NodeColumns.h" #include "atlas/functionspace/PointCloud.h" #include "atlas/functionspace/StructuredColumns.h" #include "atlas/grid.h" #include "atlas/interpolation.h" #include "atlas/interpolation/AssembleGlobalMatrix.h" #include "atlas/interpolation/method/MethodFactory.h" #include "atlas/linalg/sparse.h" #include "atlas/linalg/sparse/MakeEckitSparseMatrix.h" #include "atlas/mesh/Mesh.h" #include "atlas/meshgenerator.h" #include "atlas/output/Gmsh.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/runtime/AtlasTool.h" #include "atlas/util/CoordinateEnums.h" #include "atlas/util/Checksum.h" #include "atlas/util/function/VortexRollup.h" #include "ScripIO.h" using atlas::functionspace::PointCloud; using atlas::functionspace::NodeColumns; using atlas::functionspace::CellColumns; using atlas::functionspace::StructuredColumns; using atlas::util::Config; using Matrix = atlas::linalg::SparseMatrixStorage; using StopWatch = atlas::runtime::trace::StopWatch; namespace atlas { std::vector tgt_ref; // This app can: // // 1) list all available interpolators // atlas-interpolations --list // 2) compute and store the global interpolation matrix to disk: // atlas-interpolations --s.grid= --t.grid= --i.type= --output-matrix --matrix.format= // 3) read in a stored global interpolation matrix from the disk: // atlas-interpolations --s.grid= --t.grid= --i.type= --read-matrix --matrix.format= // // Formats can be 'eckit' (binary) or 'scrip' (netcdf). // // This app hides configurations of meshes, function spaces, halo size, etc. from the user. // class AtlasInterpolations : public AtlasTool { int execute(const AtlasTool::Args& args) override; std::string briefDescription() override { return "Atlas intepolation app to perform MPI-parallel interpolations for given source " "and target grids and/or generate remapping matrices.\n"; } std::string usage() override { return name() + " [OPTION] ... [--help]"; } std::string longDescription() override { return "The 's.grid' and 't.grid' arguments are source and target grids name, " "for instance: N80, F40, O24, L64x33, CS-ED-12, etc. See './bin/atlas-grids --list' " "for the complete list of available grids. See './bin/atlas-interpolation --list' " "for a list of named interpolations. See './bin/atlas-interpolation --s.grid " "--t.grid --list' for the list of available interpolations for that grid pair.\n" "\n"; } int numberOfPositionalArguments() override { return -1; } int minimumPositionalArguments() override { return 0; } Matrix read_matrix(std::string matrix_name, std::string matrix_format); void write_matrix(const Matrix& mat, std::string matrix_name, std::string matrix_format); void print_matrix(const Matrix&, std::ostream&); struct Timers { StopWatch functionspace_setup; StopWatch interpolation_setup; StopWatch interpolation_exe; StopWatch global_matrix_read; StopWatch global_matrix_setup; StopWatch global_matrix_exe; } timers; public: AtlasInterpolations(int argc, char* argv[]): AtlasTool(argc, argv) { // Commands add_option(new eckit::option::Separator("Commands")); add_option(new SimpleOption("list", "List available interpolation types")); add_option(new SimpleOption("interpolate", "Test an interpolation type")); // Configuration add_option(new eckit::option::Separator("Interpolation options")); add_option(new SimpleOption("i.knn", "number of nearest neighbours in case i.type=knn or k-nearest-neighbours. (default=1)")); add_option(new SimpleOption("i.normalise", "Normalise weights")); add_option(new SimpleOption("s.grid", "source grid")); add_option(new SimpleOption("t.grid", "target grid")); add_option(new SimpleOption("i.type", "interpolation type")); add_option(new eckit::option::Separator("Advanced configuration")); add_option(new SimpleOption("s.fs", "source function space [structured, nodes, cells, points]")); add_option(new SimpleOption("t.fs", "target function space [structured, nodes, cells, points]")); add_option(new SimpleOption("s.halo", "override default source halo")); add_option(new SimpleOption("t.halo", "override default target halo")); add_option(new SimpleOption("p.type", "partitioner type")); add_option(new eckit::option::Separator("Advanced commands")); add_option(new SimpleOption("read-matrix", "Read matrix to speed up interpolator")); add_option(new SimpleOption("test-matrix", "Test matrix in serial")); add_option(new SimpleOption("test-interpolator", "Test interpolator in parallel")); add_option(new eckit::option::Separator("Output options")); add_option(new SimpleOption("output-matrix", "Write interpolation matrix")); add_option(new SimpleOption("matrix.name", "Name of the remapping matrix. If not provided a default unique name will be chosen")); add_option(new SimpleOption("matrix.format", "Format of the remapping matrix: eckit, SCRIP")); add_option(new SimpleOption("output-gmsh", "Set to enable gmsh output for tests")); add_option(new SimpleOption("gmsh.coordinates", "Choose the coordinates in gmsh output: {lonlat, xyz}")); add_option(new SimpleOption("output-checksum", "Compute checksums")); add_option(new SimpleOption("checksum.file", "Path of files for checksums [default='checksum']")); } }; double elapsed_ms(const StopWatch& timer, bool single_rank = false) { double timer_elapsed = timer.elapsed() * 1000.; if (single_rank) { return timer_elapsed; } mpi::comm().allReduceInPlace(&timer_elapsed, 1, eckit::mpi::sum()); return timer_elapsed / mpi::size(); } std::string get_interpolation_method(const AtlasTool::Args& args) { std::string method = args.getString("i.type", "finite-element"); auto method_contains = [method](std::string_view substr) { return method.find(substr) != std::string_view::npos; }; if (method_contains("cons")) { return method_contains("2") ? "cons2" : "cons"; } return method; } std::string get_extension(const std::string& str) { std::size_t i = str.rfind('.', str.length()); if (i != std::string::npos) { return(str.substr(i+1, str.length() - i)); } return(""); } std::string get_basename(const std::string& str) { std::string basename = str; std::string ext = get_extension(str); if (ext == "eckit" || ext == "nc") { std::size_t lastindex = str.find_last_of("."); basename = str.substr(0, lastindex); } return basename; } bool string_contains(std::string_view str, std::string_view substr) { return str.find(substr) != std::string_view::npos; }; bool string_starts_with(std::string_view str, std::string_view substr) { return str.rfind(substr, 0) == 0; }; std::string get_matrix_format(const AtlasTool::Args& args) { if (args.has("matrix.format")) { return args.getString("matrix.format"); } if (args.has("matrix.name")) { auto ext = get_extension(args.getString("matrix.name")); if (ext == "eckit") { return "eckit"; } if (ext == "nc") { return "scrip"; } } return "eckit"; } std::string get_matrix_name(const AtlasTool::Args& args) { if (args.has("matrix.name")) { auto matrix = args.getString("matrix.name"); return get_basename(matrix); } auto sgrid = args.getString("s.grid"); auto tgrid = args.getString("t.grid"); auto interpolation_name = get_interpolation_method(args); return "remap_" + sgrid + "_" + tgrid + "_" + interpolation_name; } Grid get_sgrid(const AtlasTool::Args& args) { return Grid(args.getString("s.grid", "O8")); } Grid get_tgrid(const AtlasTool::Args& args) { return Grid(args.getString("t.grid", "O32")); } bool gridpoints_are_cells(const Grid& g) { if (g.type() == "healpix") { return true; } return false; } Config get_interpolation_config(const Grid& sgrid, const Grid& tgrid, const AtlasTool::Args& args) { Config c; std::string type = get_interpolation_method(args); if (type == "cons") { c.set("type", "conservative-spherical-polygon"); c.set("order", 1); c.set("src_cell_data", gridpoints_are_cells(sgrid)); c.set("tgt_cell_data", gridpoints_are_cells(tgrid)); bool normalise = true; args.get("i.normalise", normalise); c.set("normalise", normalise); } else if (type == "cons2") { c.set("type", "conservative-spherical-polygon"); c.set("order", 2); c.set("src_cell_data", gridpoints_are_cells(sgrid)); c.set("tgt_cell_data", gridpoints_are_cells(tgrid)); bool normalise = true; args.get("i.normalise", normalise); c.set("normalise", normalise); } else { c.set("type", type); } if (type == "k-nearest-neighbours" || type == "knn") { c.set("type", "k-nearest-neighbours"); c.set("k-nearest-neighbours", 1); if (args.has("i.knn")) { c.set("k-nearest-neighbours", args.getLong("i.knn")); } if (args.has("i.k-nearest-neighbours")) { c.set("k-nearest-neighbours", args.getLong("i.k-nearest-neighbours")); } } return c; } Mesh create_mesh_with_gridpoints_as_nodes(const Grid& grid, const grid::Distribution& dist, int halo) { if (gridpoints_are_cells(grid)) { if (StructuredGrid(grid)) { return MeshGenerator("structured", option::halo(halo)).generate(grid, dist); } else { ATLAS_THROW_EXCEPTION("Could not detect the mesh generator for grid \"" << grid.name() << "\" that treats grid points as nodes"); } } return Mesh(grid, dist, option::halo(halo)); } std::pair get_fs(const Grid& sgrid, const Grid& tgrid, const AtlasTool::Args& args) { enum class fs_type { unset, structured, cells, nodes, points }; auto to_fs_type = [](const std::string& str) -> fs_type { if (str == "structured") { return fs_type::structured; } if (str == "cells") { return fs_type::cells; } if (str == "nodes") { return fs_type::nodes; } if (str == "points") { return fs_type::points; } return fs_type::unset; }; FunctionSpace sfs, tfs; std::string interpolation_method = get_interpolation_method(args); fs_type sfs_type = fs_type::unset; fs_type tfs_type = fs_type::unset; bool target_follows_source = true; int shalo = 0; int thalo = 0; if (string_starts_with(interpolation_method, "cons")) { int order = string_starts_with(interpolation_method, "cons2") ? 2 : 1; target_follows_source = false; sfs_type = gridpoints_are_cells(sgrid) ? fs_type::cells : fs_type::nodes; tfs_type = gridpoints_are_cells(tgrid) ? fs_type::cells : fs_type::nodes; if (tfs_type == fs_type::nodes) { thalo = 1; } if (mpi::size() == 1 && (order == 2 || sfs_type == fs_type::nodes)) { shalo = 1; } if (mpi::size() == 1 && tgrid.type() == "ORCA") { thalo = 0; } if (mpi::size() == 1 && sgrid.type() == "ORCA") { shalo = 0; } } else if (string_starts_with(interpolation_method, "structured-")) { if (not StructuredGrid(sgrid)) { ATLAS_THROW_EXCEPTION("Interpolation method " << interpolation_method << " requires that the source grid is a StructuredGrid"); } sfs_type = fs_type::structured; tfs_type = fs_type::points; if (args.has("t.fs")) { tfs_type = to_fs_type(args.getString("t.fs")); } shalo = 1; if (string_contains(interpolation_method, "cubic")) { shalo = 2; } } else if (string_starts_with(interpolation_method, "grid-box")) { if (not StructuredGrid(sgrid)) { ATLAS_THROW_EXCEPTION("Interpolation method " << interpolation_method << " requires that the source grid is a StructuredGrid"); } if (not StructuredGrid(tgrid)) { ATLAS_THROW_EXCEPTION("Interpolation method " << interpolation_method << " requires that the target grid is a StructuredGrid"); } sfs_type = fs_type::structured; tfs_type = fs_type::structured; } else if (string_contains(interpolation_method, "nearest")) { if (StructuredGrid(sgrid)) { sfs_type = fs_type::structured; } else { sfs_type = fs_type::points; } tfs_type = fs_type::points; if (args.has("t.fs")) { tfs_type = to_fs_type(args.getString("t.fs")); } } else { sfs_type = fs_type::nodes; tfs_type = fs_type::points; if (args.has("t.fs")) { tfs_type = to_fs_type(args.getString("t.fs")); } } if (target_follows_source && sfs_type == fs_type::points && mpi::size() > 1) { if (StructuredGrid(sgrid)) { sfs_type = fs_type::structured; } else { Log::warning() << "WARNING: The source will be meshed for reason of creating the distribution of the target grid" << std::endl; sfs_type = fs_type::nodes; } } args.get("s.halo", shalo); args.get("t.halo", thalo); if (target_follows_source && sgrid.type() == "ORCA" && mpi::size() > 1) { ATLAS_THROW_EXCEPTION("ORCA grid is not yet supported as source in parallel for interpolation method \"" << interpolation_method << "\""); } if (not target_follows_source && tgrid.type() == "ORCA" && mpi::size() > 1) { ATLAS_THROW_EXCEPTION("ORCA grid is not yet supported as target in parallel for interpolation method \"" << interpolation_method << "\""); } if (string_starts_with(interpolation_method,"grid-box") && shalo > 0) { ATLAS_THROW_EXCEPTION( "Interpolation method \"" << interpolation_method << "\" only supported with --s.halo=0 (default)"); } auto create_grid_dist = [&](const Grid& grid) { grid::Distribution dist; if (mpi::size() == 1) { dist = grid::Distribution(grid, grid::Partitioner{"serial"}); } else { if (args.has("p")) { dist = grid::Distribution(grid, grid::Partitioner{args.getString("p.type")}); } else { dist = grid::Distribution(grid, grid.partitioner()); } } return dist; }; auto create_matching_grid_dist = [&](const Grid& grid, const FunctionSpace& fs_to_match) { grid::Distribution dist; if (mpi::size() == 1) { dist = grid::Distribution(grid, grid::Partitioner{"serial"}); } else { if (auto fs = functionspace::NodeColumns(fs_to_match)) { dist = grid::Distribution{grid, grid::MatchingPartitioner{fs.mesh()}}; } else if (auto fs = functionspace::CellColumns(fs_to_match)) { dist = grid::Distribution{grid, grid::MatchingPartitioner{fs.mesh()}}; } else { dist = grid::Distribution{grid, grid::MatchingPartitioner{fs_to_match}}; } } return dist; }; auto create_fs = [&](fs_type type, const Grid& grid, const grid::Distribution& dist, int halo) { FunctionSpace fs; if (type == fs_type::cells) { auto mesh = Mesh(grid, dist, option::halo(halo)); fs = CellColumns(mesh, option::halo(halo)); } else if (type == fs_type::nodes) { auto mesh = create_mesh_with_gridpoints_as_nodes(grid, dist, halo); fs = NodeColumns(mesh, option::halo(halo)); } else if (type == fs_type::structured) { fs = StructuredColumns(grid, dist, option::halo(halo)); } else if (type == fs_type::points) { fs = PointCloud(grid, dist, option::halo(halo)); } else { ATLAS_NOTIMPLEMENTED; } return fs; }; if (target_follows_source) { // This is the usual case for most interpolation methods grid::Distribution sdist, tdist; ATLAS_TRACE_SCOPE("Partition source grid") { sdist = create_grid_dist(sgrid); } ATLAS_TRACE_SCOPE("Create source function space") { sfs = create_fs(sfs_type, sgrid, sdist, shalo); } ATLAS_TRACE_SCOPE("Partition target grid") { tdist = create_matching_grid_dist(tgrid, sfs); } ATLAS_TRACE_SCOPE("Create target function space") { tfs = create_fs(tfs_type, tgrid, tdist, thalo); } } else { // This is only needed for conservative interpolation ATLAS_ASSERT(string_starts_with(interpolation_method, "cons")); grid::Distribution sdist, tdist; ATLAS_TRACE_SCOPE("Partition target grid") { tdist = create_grid_dist(tgrid); } ATLAS_TRACE_SCOPE("Create target function space") { tfs = create_fs(tfs_type, tgrid, tdist, thalo); } ATLAS_TRACE_SCOPE("Partition source grid") { sdist = create_matching_grid_dist(sgrid, tfs); } ATLAS_TRACE_SCOPE("Create source function space") { sfs = create_fs(sfs_type, sgrid, sdist, shalo); } } return std::make_pair(sfs,tfs); } template void gmsh_output(const std::string& name, const Grid& grid, const View& field, const AtlasTool::Args& args) { mpi::Scope mpi_scope("self"); Log::info() << "Storing field '" << name << ".msh'" << std::endl; ATLAS_ASSERT(field.size() <= grid.size()); std::string coords = args.getString("gmsh.coordinates", "lonlat"); output::Gmsh gmsh(name + ".msh", Config("coordinates", coords) | Config("ghost", "true")); auto mesh = Mesh(grid); gmsh.write(mesh); auto fs = gridpoints_are_cells(grid) ? FunctionSpace(CellColumns(mesh)) : FunctionSpace(NodeColumns(mesh)); auto f = fs.createField(option::name(name)); auto v = array::make_view(f); auto g = array::make_view(fs.global_index()); size_t min_size = std::min(f.size(),field.size()); for(size_t i=0; i sdata(sgrid.size()); std::vector tdata(tgrid.size()); ATLAS_TRACE_SCOPE("initialize source") { idx_t n{0}; for (auto p : sgrid.lonlat()) { sdata[n++] = util::function::vortex_rollup(p.lon(), p.lat(), 1.); } } StopWatch timer_serial_sparse_matrix_multiply; timer_serial_sparse_matrix_multiply.start(); atlas::linalg::sparse_matrix_multiply( atlas::linalg::make_host_view(matrix), atlas::array::make_view(sdata.data(), matrix.cols()), atlas::array::make_view(tdata.data(), matrix.rows())); timer_serial_sparse_matrix_multiply.stop(); Log::info() << "Serial sparse-matrix-multiply timer \t: " << elapsed_ms(timer_serial_sparse_matrix_multiply.elapsed(),true) << " [ms]" << std::endl; Log::info() << "Serial sparse-matrix non-zero entries\t: " << matrix.nnz() << std::endl; if (args.getBool("output-checksum",false)) { size_t min_size = std::min(tgt_ref.size(), tdata.size()); for (size_t i=0; i(tdata.data(), matrix.rows()).dump(Log::info()); // Log::info() << std::endl; if (args.getBool("output-gmsh",false)) { ATLAS_TRACE_SCOPE("Gmsh serial output") { std::string sname = "test_matrix_source_" + get_matrix_name(args); gmsh_output(sname, sgrid, atlas::array::make_view(sdata.data(), matrix.cols()), args); std::string tname = "test_matrix_target_" + get_matrix_name(args); gmsh_output(tname, tgrid, atlas::array::make_view(tdata.data(), matrix.rows()), args); } } } int AtlasInterpolations::execute(const AtlasTool::Args& args) { ATLAS_TRACE("main"); if (args.has("list")) { Log::info() << "Usage: " << usage() << std::endl; Log::info() << "Available interpolators:" << std::endl; std::set interpolation_types; for (const auto& key : interpolation::MethodFactory::keys()) { interpolation_types.insert(key); } for (const auto& b : interpolation_types) { if (b == "conservative-spherical-polygon") { Log::info() << "\t" << "cons" << '\n'; Log::info() << "\t" << "cons2" << '\n'; } else { Log::info() << "\t" << b << '\n'; } } return success(); } bool output_checksum = args.getBool("output-checksum",false); std::string checksum_file_path = args.getString("checksum.file","checksum"); if (output_checksum) { eckit::PathName checksum_file(checksum_file_path); if (checksum_file.exists()) { checksum_file.unlink(); } } auto sgrid = get_sgrid(args); auto tgrid = get_tgrid(args); std::string sdata_name = gridpoints_are_cells(sgrid) ? "cell data" : "node data"; std::string tdata_name = gridpoints_are_cells(tgrid) ? "cell data" : "node data"; std::string interpolation_method = get_interpolation_method(args); Log::info() << "\tsource grid\t:\t" << sgrid.name() << " (" << sdata_name <<")\n\ttarget grid\t:\t" << tgrid.name() << " (" << tdata_name <<")\n\tinterpolation\t:\t" << interpolation_method << std::endl; std::string matrix_format = get_matrix_format(args); std::string matrix_name = get_matrix_name(args); bool matrix_tested = false; Matrix matrix; if (args.getBool("read-matrix",false)) { if (mpi::comm().rank() == 0) { ATLAS_TRACE_SCOPE("Read matrix on rank 0") { timers.global_matrix_read.start(); matrix = read_matrix(matrix_name, matrix_format); timers.global_matrix_read.stop(); Log::info() << "Global matrix read timer\t: " << elapsed_ms(timers.global_matrix_read,true) << " [ms]" << std::endl; print_matrix(matrix, Log::info()); Log::info() << std::endl; if (output_checksum) { std::ofstream checksum_file(checksum_file_path ,std::ios::app); auto matrix_view = linalg::make_host_view(matrix); auto outer_checksum = util::checksum(matrix_view.inner(), matrix_view.inner_size()); auto inner_checksum = util::checksum(matrix_view.outer(), matrix_view.outer_size()); auto value_checksum = util::checksum(matrix_view.value(), matrix_view.value_size()); checksum_file << std::setw(8) << outer_checksum << " [read-matrix] checksum of matrix.outer()" << std::endl; checksum_file << std::setw(8) << inner_checksum << " [read-matrix] checksum of matrix.inner()" << std::endl; checksum_file << std::setw(8) << value_checksum << " [read-matrix] checksum of matrix.value()" << std::endl; } } } if (args.getBool("test-matrix",false)) { if (mpi::comm().rank() == 0) { test_matrix(sgrid, tgrid, matrix, args); } matrix_tested = true; } } FunctionSpace src_fs; FunctionSpace tgt_fs; Interpolation interpolator; ATLAS_TRACE_SCOPE("Setup source and target") { timers.functionspace_setup.start(); // create_fspaces(config, sgrid, tgrid, src_fs, tgt_fs); std::tie(src_fs, tgt_fs) = get_fs(sgrid, tgrid, args); timers.functionspace_setup.stop(); } interpolation::Cache cache; if (args.getBool("read-matrix", false)) { mpi::comm().barrier(); timers.global_matrix_setup.start(); auto distributed_matrix = interpolation::distribute_global_matrix(src_fs, tgt_fs, matrix); timers.global_matrix_setup.stop(); cache = interpolation::MatrixCache{std::move(distributed_matrix)}; Log::info() << "Global matrix setup timer\t: " << elapsed_ms(timers.global_matrix_setup) << " [ms]" << std::endl; } matrix.clear(); if (args.getBool("interpolate",false) || args.getBool("output-matrix",false) || args.getBool("test-interpolator",false) || (args.getBool("test-matrix",false) && !matrix_tested)) { ATLAS_TRACE_SCOPE("Setup source and target") { timers.functionspace_setup.start(); // create_fspaces(config, sgrid, tgrid, src_fs, tgt_fs); std::tie(src_fs, tgt_fs) = get_fs(sgrid, tgrid, args); timers.functionspace_setup.stop(); } ATLAS_TRACE_SCOPE("Setup interpolator") { timers.interpolation_setup.start(); auto config = get_interpolation_config(sgrid, tgrid, args); if (string_starts_with(interpolation_method,"grid-box")) { interpolator = Interpolation(config, sgrid, tgrid); } else { interpolator = Interpolation(config, src_fs, tgt_fs, cache); } timers.interpolation_setup.stop(); } Log::info() << "Grid + FunctionSpace timer\t: " << elapsed_ms(timers.functionspace_setup) << " [ms]" << std::endl; Log::info() << "Interpolation setup timer\t: " << elapsed_ms(timers.interpolation_setup) << " [ms]" << std::endl; if (args.getBool("test-interpolator", false)) { auto src_field = interpolator.source().createField(); auto tgt_field = interpolator.target().createField(); auto src_lonlat = array::make_view(interpolator.source().lonlat()); ATLAS_TRACE_SCOPE("initialize source") { auto src_field_v = array::make_view(src_field); for (idx_t i = 0; i < src_fs.size(); ++i) { src_field_v[i] = util::function::vortex_rollup(src_lonlat(i, 0), src_lonlat(i, 1), 1.); } } src_field.set_dirty(false); // all values are up to date timers.interpolation_exe.start(); interpolator.execute(src_field, tgt_field); timers.interpolation_exe.stop(); Log::info() << "Interpolation execute timer : " << elapsed_ms(timers.interpolation_exe) << " [ms]" << std::endl; Field tgt_field_global; if (output_checksum) { tgt_field_global = tgt_fs.createField(tgt_field, option::global()); auto tgt_field_global_v = array::make_view(tgt_field_global); tgt_fs.gather(tgt_field, tgt_field_global); if (mpi::rank() == 0) { auto target_checksum = util::checksum(tgt_field_global_v.data(), tgt_field_global_v.size()); std::ofstream checksum_file(checksum_file_path, std::ios::app); checksum_file << std::setw(8) << target_checksum << " [test-interp] checksum of target field" << std::endl; // for further comparison with test-matrix tgt_ref.resize(tgt_field_global_v.size()); for( idx_t i=0; i(matrix); auto outer_checksum = util::checksum(matrix_view.inner(), matrix_view.inner_size()); auto inner_checksum = util::checksum(matrix_view.outer(), matrix_view.outer_size()); auto value_checksum = util::checksum(matrix_view.value(), matrix_view.value_size()); checksum_file << std::setw(8) << outer_checksum << " [output-matrix] checksum of matrix.outer()" << std::endl; checksum_file << std::setw(8) << inner_checksum << " [output-matrix] checksum of matrix.inner()" << std::endl; checksum_file << std::setw(8) << value_checksum << " [output-matrix] checksum of matrix.value()" << std::endl; } } if (args.getBool("test-matrix",false) && !matrix_tested) { test_matrix(sgrid, tgrid, matrix, args); } } } mpi::comm().barrier(); if (mpi::rank() == 0) { if (output_checksum) { auto checksum_file = eckit::PathName(checksum_file_path); if (checksum_file.exists()) { std::ifstream f(checksum_file); if (f.is_open()) { Log::info() << "\nContent of checksum file " << checksum_file << std::endl; Log::info() << f.rdbuf() << std::endl; } } } } return success(); } //----------------------------------------------------------------------------- Matrix AtlasInterpolations::read_matrix(std::string matrix_name, std::string format) { if (format == "eckit") { Log::info() << "Reading matrix from file '" << matrix_name << ".eckit'" << std::endl; eckit::linalg::SparseMatrix eckit_matrix; eckit_matrix.load(matrix_name + ".eckit"); return atlas::linalg::make_sparse_matrix_storage(std::move(eckit_matrix)); } else if (format == "scrip") { Log::info() << "Reading matrix from file '" << matrix_name << ".nc'" << std::endl; return ScripIO::read(matrix_name + ".nc"); } else { ATLAS_THROW_EXCEPTION("Matrix format " << format << " is not recognised. Recognized are {eckit,scrip}"); } return Matrix{}; } void AtlasInterpolations::write_matrix(const Matrix& matrix, std::string matrix_name, std::string format) { ATLAS_TRACE(); if (format == "eckit") { Log::info() << "Writing matrix in eckit format to file '" << matrix_name << ".eckit'" << std::endl; auto eckit_matrix = linalg::make_non_owning_eckit_sparse_matrix(matrix); eckit_matrix.save(matrix_name+".eckit"); } else if (format == "scrip") { Log::info() << "Writing matrix in scrip format to file '" << matrix_name << ".nc'" << std::endl; ScripIO::write(matrix, matrix_name+".nc"); } else { ATLAS_NOTIMPLEMENTED; } } void AtlasInterpolations::print_matrix(const Matrix& matrix, std::ostream& out) { out << " matrix.rows : " << matrix.rows() << '\n'; out << " matrix.cols : " << matrix.cols() << '\n'; out << " matrix.nnz : " << matrix.nnz() << '\n'; } } // namespace int main(int argc, char** argv) { atlas::AtlasInterpolations tool(argc, argv); return tool.start(); } atlas-0.46.0/src/apps/atlas-interpolations/CMakeLists.txt0000664000175000017500000001266315160212070023541 0ustar alastairalastair# (C) Copyright 2025 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. ecbuild_add_executable( TARGET atlas-interpolations SOURCES atlas-interpolations.cc ScripIO.h ScripIO.cc LIBS atlas ) # Overcome Cray linking problem where the C++ library is not linked with the C library list( APPEND NetCDF_CXX_EXTRA_LIBRARIES NetCDF::NetCDF_C ) ecbuild_add_option( FEATURE NETCDF DESCRIPTION "Compile support the SCRIP format" REQUIRED_PACKAGES "NetCDF COMPONENTS C CXX" ) target_compile_definitions( atlas-interpolations PRIVATE ATLAS_HAVE_NETCDF=${HAVE_NETCDF} ) if( HAVE_NETCDF ) target_link_libraries( atlas-interpolations NetCDF::NetCDF_CXX ) endif() function( atlas_test_app_atlas_interpolations case nprocs ) string(REPLACE "/" ";" case ${case} ) list( GET case 0 sgrid ) list( GET case 1 tgrid ) list( GET case 2 itype ) unset( checksums ) unset( case_tests ) if( NOT DEFINED MPI_SLOTS ) set( MPI_SLOTS 9999 ) endif() foreach( n ${nprocs} ) if ( (n EQUAL 0) OR eckit_HAVE_MPI ) if( MPI_SLOTS GREATER_EQUAL ${n} ) foreach( t 1 ) set( case_name atlas_test_app_atlas_interpolations_${sgrid}_to_${tgrid}_with_${itype}_n${n}_t${t} ) set( test_name ${case_name}_write ) ecbuild_add_test( TARGET ${test_name} COMMAND atlas-interpolations ARGS --display-name=${test_name} --interpolate --s.grid=${sgrid} --t.grid=${tgrid} --i.type=${itype} --test-matrix --output-matrix --matrix.name=${case_name} --matrix.format=eckit --output-gmsh --gmsh.coordinates=lonlat --test-interpolator --i.knn=4 # just in case itype==knn --output-checksum --checksum.file=${test_name}.checksum MPI ${n} OMP ${t} ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) set( test_name ${case_name}_read ) ecbuild_add_test( TARGET ${test_name} COMMAND atlas-interpolations ARGS --display-name=${test_name} --interpolate --s.grid=${sgrid} --t.grid=${tgrid} --i.type=${itype} --test-matrix --read-matrix --matrix.name=${case_name} --matrix.format=eckit --test-interpolator --output-gmsh --i.knn=4 # just in case itype==knn --output-checksum --checksum.file=${test_name}.checksum MPI ${n} OMP ${t} ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) set_tests_properties( ${case_name}_read PROPERTIES DEPENDS ${case_name}_write ) # list( APPEND checksums ${test_name}.checksum ) list( APPEND case_tests ${test_name} ) endforeach() endif() endif() endforeach() # This kind of checksum test can be added, inspired from in tests/acceptance_tests/atlas_atest_mgrids # add_test( # NAME atlas_test_app_atlas_interpolations_${sgrid}_to_${tgrid}_with_${itype}_BitReproducible # COMMAND ${CMAKE_COMMAND} # "-DFILES=${checksums}" -P ${CMAKE_CURRENT_SOURCE_DIR}/compare.cmake # ) # foreach( dep ${case_tests} ) # set_tests_properties( atlas_test_app_atlas_interpolations_${sgrid}_to_${tgrid}__with_${itype}_BitReproducible PROPERTIES DEPENDS ${dep} ) # endforeach() endfunction() if (HAVE_TESTS) unset(cases) list( APPEND cases O16/O32/finite-element O16/O32/structured-bilinear O16/O32/structured-bicubic O16/O32/knn O16/H32/finite-element O16/H32/structured-bilinear O16/H32/structured-bicubic O16/H32/knn H16/O32/finite-element H16/O32/structured-bilinear H16/O32/structured-bicubic H16/O32/knn H16/H32/finite-element H16/H32/structured-bilinear H16/H32/structured-bicubic H16/H32/knn ) unset(nprocs) list( APPEND nprocs 0 8 ) foreach( case ${cases} ) atlas_test_app_atlas_interpolations( ${case} "${nprocs}" ) endforeach() # Due to currently unknown halo requirements for conservative interpolation, we only do this in serial here unset(cases) list( APPEND cases O16/O32/cons O16/O32/cons2 O16/H32/cons O16/H32/cons2 H16/O32/cons H16/O32/cons2 H16/H32/cons H16/H32/cons2 ) unset(nprocs) list( APPEND nprocs 0 ) foreach( case ${cases} ) atlas_test_app_atlas_interpolations( ${case} "${nprocs}" ) endforeach() endif()atlas-0.46.0/src/atlas/0000775000175000017500000000000015160212070014756 5ustar alastairalastairatlas-0.46.0/src/atlas/redistribution/0000775000175000017500000000000015160212070020024 5ustar alastairalastairatlas-0.46.0/src/atlas/redistribution/Redistribution.h0000664000175000017500000000471715160212070023214 0ustar alastairalastair/* * (C) Crown Copyright 2021 Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ #pragma once #include "atlas/redistribution/detail/RedistributionImpl.h" #include "atlas/util/ObjectHandle.h" namespace atlas { class Field; class FieldSet; class FunctionSpace; } // namespace atlas namespace atlas { /// \brief Base redistributer class. /// /// \details Class to map two function spaces with the same grid but /// different partitioners. class Redistribution : public util::ObjectHandle { public: using Handle::Handle; /// \brief Empty default constructor. Redistribution(); /// \brief Constructs and initialises the redistributor. /// /// \details Initialises class to copy fields from a source function space /// to fields from a target functionspace. The grids of source and /// target function space must match. /// /// \param[in] source Function space of source fields. /// \param[in] target Function space of target fields. /// \param[in] config Config to specify "type" of redistribution. Redistribution(const FunctionSpace& source, const FunctionSpace& target, const util::Config& config = util::Config()); /// \brief Maps source field to target field. /// /// \details Transfers data from source field to target field. Function /// space of source field must match sourceFunctionSpace. Same /// applies to target field. /// /// \param[in] source input field matching sourceFunctionSpace. /// \param[out] target output field matching targetFunctionSpace. void execute(const Field& source, Field& target) const; /// \brief Redistributes source field set to target fields set. /// /// \details Transfers source field set to target field set via multiple /// invocations of execute(sourceField, targetField). /// /// \param[in] source input field set. /// \param[out] target output field set. void execute(const FieldSet& sourceFieldSet, FieldSet& targetFieldSet) const; /// \brief Get const reference to source function space. const FunctionSpace& source() const; /// \brief Get const reference to target function space. const FunctionSpace& target() const; }; } // namespace atlas atlas-0.46.0/src/atlas/redistribution/Redistribution.cc0000664000175000017500000000315515160212070023345 0ustar alastairalastair/* * (C) Crown Copyright 2021 Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ #include "atlas/redistribution/Redistribution.h" #include "atlas/field/Field.h" #include "atlas/field/FieldSet.h" #include "atlas/functionspace/FunctionSpace.h" #include "atlas/redistribution/detail/RedistributeGeneric.h" #include "atlas/redistribution/detail/RedistributionImplFactory.h" #include "atlas/runtime/Exception.h" namespace atlas { // Use redistribution implementation factory to make object. Redistribution::Redistribution(): Handle(){}; Redistribution::Redistribution(const FunctionSpace& sourceFunctionSpace, const FunctionSpace& targetFunctionSpace, const util::Config& config): Handle([&]() -> redistribution::detail::RedistributionImpl* { std::string type = redistribution::detail::RedistributeGeneric::static_type(); config.get("type", type); auto impl = redistribution::detail::RedistributionImplFactory::build(type); impl->setup(sourceFunctionSpace, targetFunctionSpace); return impl; }()) {} void Redistribution::execute(const Field& source, Field& target) const { get()->execute(source, target); return; } void Redistribution::execute(const FieldSet& source, FieldSet& target) const { get()->execute(source, target); return; } const FunctionSpace& Redistribution::source() const { return get()->source(); } const FunctionSpace& Redistribution::target() const { return get()->target(); } } // namespace atlas atlas-0.46.0/src/atlas/redistribution/detail/0000775000175000017500000000000015160212070021266 5ustar alastairalastairatlas-0.46.0/src/atlas/redistribution/detail/RedistributionInterface.cc0000664000175000017500000000403615160212070026427 0ustar alastairalastair/* * (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 "RedistributionInterface.h" #include "RedistributeGeneric.h" #include "RedistributionImpl.h" #include "atlas/functionspace/FunctionSpace.h" #include "atlas/redistribution/detail/RedistributionImplFactory.h" namespace atlas { namespace redistribution { // ---------------------------------------------------------------------------- // Fortran interfaces // ---------------------------------------------------------------------------- extern "C" { detail::RedistributionImpl* atlas__Redistribution__new__config( const functionspace::FunctionSpaceImpl* fspace1, const functionspace::FunctionSpaceImpl* fspace2, const eckit::Configuration* config) { ATLAS_ASSERT(config != nullptr); std::string type = detail::RedistributeGeneric::static_type(); config->get("type", type); auto redist = redistribution::detail::RedistributionImplFactory::build(type); FunctionSpace fs1(fspace1); FunctionSpace fs2(fspace2); redist->setup(fs1, fs2); return redist; } void atlas__Redistribution__execute( const detail::RedistributionImpl* This, const field::FieldImpl* field_1, field::FieldImpl* field_2) { Field f1(field_1); Field f2(field_2); This->execute(f1, f2); } const functionspace::FunctionSpaceImpl* atlas__Redistribution__source( const detail::RedistributionImpl* This) { return This->source().get(); } const functionspace::FunctionSpaceImpl* atlas__Redistribution__target( const detail::RedistributionImpl* This) { return This->target().get(); } } // ---------------------------------------------------------------------------- } // namespace redistribution } // namespace atlas atlas-0.46.0/src/atlas/redistribution/detail/RedistributionInterface.h0000664000175000017500000000262315160212070026271 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once namespace eckit { class Configuration; } namespace atlas { namespace functionspace { class FunctionSpaceImpl; } namespace field { class FieldImpl; } } // namespace atlas namespace atlas { namespace redistribution { namespace detail { class RedistributionImpl; // ------------------------------------------------------------------- // C wrapper interfaces to C++ routines extern "C" { RedistributionImpl* atlas__Redistribution__new__config( const functionspace::FunctionSpaceImpl* fspace1, const functionspace::FunctionSpaceImpl* fspace2, const eckit::Configuration* config); void atlas__Redistribution__execute( const RedistributionImpl* This, const field::FieldImpl* field_1, field::FieldImpl* field_2); const functionspace::FunctionSpaceImpl* atlas__Redistribution__source( const RedistributionImpl* This); const functionspace::FunctionSpaceImpl* atlas__Redistribution__target( const RedistributionImpl* This); } } // namespace detail } // namespace redistribution } // namespace atlas atlas-0.46.0/src/atlas/redistribution/detail/RedistributeGeneric.h0000664000175000017500000000304015160212070025376 0ustar alastairalastair/* * (C) Crown Copyright 2021 Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ #pragma once #include #include "atlas/redistribution/detail/RedistributionImpl.h" namespace atlas { namespace redistribution { namespace detail { class RedistributeGeneric : public RedistributionImpl { public: static std::string static_type() { return "RedistributeGeneric"; } std::string type() const override { return static_type(); } void do_setup() override; void execute(const Field& source, Field& target) const override; void execute(const FieldSet& source, FieldSet& target) const override; private: // Determine datatype. void do_execute(const Field& source, Field& target) const; // Determine rank. template void do_execute(const Field& source, Field& target) const; // Perform redistribution. template void do_execute(const Field& source, Field& target) const; // Local indices to send to each PE std::vector sourceLocalIdx_{}; // Local indices to receive from each PE. std::vector targetLocalIdx_{}; // Partial sum of number of columns to send to each PE. std::vector sourceDisps_{}; // Partial sum of number of columns to receive from each PE. std::vector targetDisps_{}; std::string mpi_comm_; }; } // namespace detail } // namespace redistribution } // namespace atlas atlas-0.46.0/src/atlas/redistribution/detail/RedistributeStructuredColumns.cc0000664000175000017500000003174715160212070027704 0ustar alastairalastair/* * (C) Crown Copyright 2021 Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ #include "RedistributeStructuredColumns.h" #include #include #include "atlas/array/MakeView.h" #include "atlas/field.h" #include "atlas/field/FieldSet.h" #include "atlas/functionspace/StructuredColumns.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/runtime/Exception.h" namespace atlas { namespace redistribution { namespace detail { // Anonymous name space for helper functions. namespace { // Transform a vector element-by-element with a functor. template std::vector transformVector(const std::vector& inVector, const functorType& functor) { // Declare result. auto outVector = std::vector{}; outVector.reserve(inVector.size()); // Transform vector. std::transform(inVector.begin(), inVector.end(), std::back_inserter(outVector), functor); return outVector; } // Visit each index in a StructuredIndexRangeVector. template void forEachIndex(const StructuredIndexRangeVector& ranges, const functorType& functor) { // Loop over all ranges. std::for_each(ranges.cbegin(), ranges.cend(), [&](const StructuredIndexRange& range) { range.forEach(functor); return; }); return; } } // namespace //======================================================================== // Class public methods implementation. //======================================================================== // Constructor. void RedistributeStructuredColumns::do_setup() { source_ = source(); target_ = target(); // Assert that functionspaces are StructuredColumns. ATLAS_ASSERT(source_, source().type() + " must be StructuredColumns"); ATLAS_ASSERT(target_, target().type() + " must be StructuredColumns"); // Check that grids match. ATLAS_ASSERT(source_.grid().name() == target_.grid().name()); // Check levels match. ATLAS_ASSERT(source_.levels() == target_.levels()); // Check that communicators match. ATLAS_ASSERT(source_.mpi_comm() == target_.mpi_comm()); mpi_comm_ = source_.mpi_comm(); // Get source and target range of this function space. const auto sourceRange = StructuredIndexRange(source_); const auto targetRange = StructuredIndexRange(target_); // Get source and target ranges over all PEs. const auto sourceRanges = sourceRange.getStructuredIndexRanges(); const auto targetRanges = targetRange.getStructuredIndexRanges(); // Get intersections between sourceRange and targetRanges. auto getIntersections = [](const StructuredIndexRange& range, const StructuredIndexRangeVector& ranges) { return transformVector( ranges, [&](const StructuredIndexRange& rangesElem) { return range & rangesElem; }); }; sendIntersections_ = getIntersections(sourceRange, targetRanges); recvIntersections_ = getIntersections(targetRange, sourceRanges); // Get counts and displacements for MPI communication. auto getCountsDisplacements = [](const StructuredIndexRangeVector& intersections, const idx_t levels) { const auto counts = transformVector(intersections, [&](const StructuredIndexRange& intersection) { return static_cast(intersection.getElemCount() * levels); }); auto displacements = std::vector{0}; std::partial_sum(counts.cbegin(), counts.cend() - 1, std::back_inserter(displacements)); return std::make_pair(counts, displacements); }; std::tie(sendCounts_, sendDisplacements_) = getCountsDisplacements(sendIntersections_, source_.levels()); std::tie(recvCounts_, recvDisplacements_) = getCountsDisplacements(recvIntersections_, target_.levels()); // Trim off invalid intersections. auto trimIntersections = [](StructuredIndexRangeVector& intersections) { intersections.erase( std::remove_if(intersections.begin(), intersections.end(), [](const StructuredIndexRange& intersection) { return !(intersection.getElemCount()); }), intersections.end()); return; }; trimIntersections(sendIntersections_); trimIntersections(recvIntersections_); return; } void RedistributeStructuredColumns::execute(const Field& sourceField, Field& targetField) const { // Assert that fields are defined on StructuredColumns. ATLAS_ASSERT(functionspace::StructuredColumns(sourceField.functionspace())); ATLAS_ASSERT(functionspace::StructuredColumns(targetField.functionspace())); // Check that grids match. ATLAS_ASSERT(functionspace::StructuredColumns(sourceField.functionspace()).grid().name() == source_.grid().name()); ATLAS_ASSERT(functionspace::StructuredColumns(targetField.functionspace()).grid().name() == target_.grid().name()); // Check levels match. ATLAS_ASSERT(sourceField.levels() == source_.levels()); ATLAS_ASSERT(targetField.levels() == target_.levels()); // Determine data type of field and execute. switch (sourceField.datatype().kind()) { case array::DataType::KIND_REAL64: do_execute(sourceField, targetField); break; case array::DataType::KIND_REAL32: do_execute(sourceField, targetField); break; case array::DataType::KIND_INT32: do_execute(sourceField, targetField); break; case array::DataType::KIND_INT64: do_execute(sourceField, targetField); break; default: throw_NotImplemented("No implementation for data type " + sourceField.datatype().str(), Here()); } return; } void RedistributeStructuredColumns::execute(const FieldSet& sourceFieldSet, FieldSet& targetFieldSet) const { // Check that both FieldSets are the same size. ATLAS_ASSERT(sourceFieldSet.size() == targetFieldSet.size()); auto targetFieldSetIt = targetFieldSet.begin(); std::for_each(sourceFieldSet.cbegin(), sourceFieldSet.cend(), [&](const Field& sourceField) { execute(sourceField, *targetFieldSetIt++); return; }); return; } //======================================================================== // Class private methods implementation. //======================================================================== template void RedistributeStructuredColumns::do_execute(const Field& sourceField, Field& targetField) const { // Make Atlas view objects. const auto sourceView = array::make_view(sourceField); auto targetView = array::make_view(targetField); // Get buffer sizes. const auto nSend = sendCounts_.back() + sendDisplacements_.back(); const auto nRecv = recvCounts_.back() + recvDisplacements_.back(); // Allocate send and receive buffers. auto sendBuffer = std::vector(static_cast(nSend)); auto recvBuffer = std::vector(static_cast(nRecv)); // Set send functor. auto sendBufferIt = sendBuffer.begin(); auto sendFunctor = [&](const idx_t i, const idx_t j) { // Loop over levels const auto iNode = source_.index(i, j); const auto kEnd = source_.levels(); for (idx_t k = 0; k < kEnd; ++k) { *sendBufferIt++ = sourceView(iNode, k); } return; }; // Set receive functor. auto recvBufferIt = recvBuffer.cbegin(); auto recvFunctor = [&](const idx_t i, const idx_t j) { // Loop over levels const auto iNode = target_.index(i, j); const auto kEnd = target_.levels(); for (idx_t k = 0; k < kEnd; ++k) { targetView(iNode, k) = *recvBufferIt++; } return; }; // Write data to buffer. forEachIndex(sendIntersections_, sendFunctor); // Communicate. mpi::comm(mpi_comm_).allToAllv(sendBuffer.data(), sendCounts_.data(), sendDisplacements_.data(), recvBuffer.data(), recvCounts_.data(), recvDisplacements_.data()); // Read data from buffer. forEachIndex(recvIntersections_, recvFunctor); return; } //======================================================================== // Index range methods. //======================================================================== // Constructor. StructuredIndexRange::StructuredIndexRange(const functionspace::StructuredColumns& structuredColumns) { jBeginEnd_ = std::make_pair(structuredColumns.j_begin(), structuredColumns.j_end()); for (auto j = jBeginEnd_.first; j < jBeginEnd_.second; ++j) { iBeginEnd_.emplace_back(structuredColumns.i_begin(j), structuredColumns.i_end(j)); } mpi_comm_ = structuredColumns.mpi_comm(); return; } // Get index ranges from all PEs. StructuredIndexRangeVector StructuredIndexRange::getStructuredIndexRanges() const { auto& comm = mpi::comm(mpi_comm()); // Get MPI communicator size. const auto mpiSize = static_cast(comm.size()); // Set recv buffer for j range. auto jRecvBuffer = idxPairVector(mpiSize); // Perform all gather. comm.allGather(jBeginEnd_, jRecvBuffer.begin(), jRecvBuffer.end()); // Set i receive counts. auto iRecvCounts = transformVector( jRecvBuffer, [](const idxPair& jElem) { return static_cast(jElem.second - jElem.first); }); // Set recv displacements for i range. auto iRecvDisplacements = std::vector{0}; std::partial_sum(iRecvCounts.cbegin(), iRecvCounts.cend() - 1, std::back_inserter(iRecvDisplacements)); // Set recv buffer for i range. auto irecvBuffer = idxPairVector(static_cast(iRecvDisplacements.back() + iRecvCounts.back())); // Perform all gather. comm.allGatherv(iBeginEnd_.cbegin(), iBeginEnd_.cend(), irecvBuffer.begin(), iRecvCounts.data(), iRecvDisplacements.data()); // Make vector of indexRange structs. auto indexRanges = StructuredIndexRangeVector{}; for (size_t i = 0; i < mpiSize; ++i) { auto indexRange = StructuredIndexRange{}; indexRange.jBeginEnd_ = jRecvBuffer[i]; const auto iBegin = irecvBuffer.cbegin() + iRecvDisplacements[i]; const auto iEnd = iBegin + iRecvCounts[i]; std::copy(iBegin, iEnd, std::back_inserter(indexRange.iBeginEnd_)); indexRanges.push_back(indexRange); } return indexRanges; } // Count number of elements in index range. idx_t StructuredIndexRange::getElemCount() const { // Accumulate size of positive i range. const auto count = std::accumulate(iBeginEnd_.cbegin(), iBeginEnd_.cend(), 0, [](const idx_t cumulant, const idxPair iElem) { // Only count positive differences. return cumulant + static_cast(std::max(iElem.second - iElem.first, idx_t(0))); }); return count; } // Return the intersection between two index ranges. StructuredIndexRange StructuredIndexRange::operator&(const StructuredIndexRange& indexRange) const { // Declare result. auto intersection = StructuredIndexRange{}; // get j intersection range. intersection.jBeginEnd_ = std::make_pair(std::max(jBeginEnd_.first, indexRange.jBeginEnd_.first), std::min(jBeginEnd_.second, indexRange.jBeginEnd_.second)); // get i intersection range. if (intersection.jBeginEnd_.first < intersection.jBeginEnd_.second) { // get iterators. const auto iBeginA = iBeginEnd_.cbegin() + intersection.jBeginEnd_.first - jBeginEnd_.first; const auto iBeginB = indexRange.iBeginEnd_.cbegin() + intersection.jBeginEnd_.first - indexRange.jBeginEnd_.first; const auto iEndA = iBeginEnd_.cend() + intersection.jBeginEnd_.second - jBeginEnd_.second; std::transform(iBeginA, iEndA, iBeginB, std::back_inserter(intersection.iBeginEnd_), [](const idxPair iElemA, const idxPair iElemB) { return std::make_pair(std::max(iElemA.first, iElemB.first), std::min(iElemA.second, iElemB.second)); }); } return intersection; } // Loop over all indices. Functor should have signature // functor(const idx_t i, const idx_t j). template void StructuredIndexRange::forEach(const functorType& functor) const { auto iBeginEndIt = iBeginEnd_.begin(); // Loop over j. for (auto j = jBeginEnd_.first; j < jBeginEnd_.second; ++j) { const auto iBeginEnd = *iBeginEndIt++; // Loop over i. for (auto i = iBeginEnd.first; i < iBeginEnd.second; ++i) { // Call functor. functor(i, j); } } return; } namespace { static RedistributionImplBuilder register_builder( RedistributeStructuredColumns::static_type()); } } // namespace detail } // namespace redistribution } // namespace atlas atlas-0.46.0/src/atlas/redistribution/detail/RedistributionImpl.cc0000664000175000017500000000131715160212070025427 0ustar alastairalastair/* * (C) Crown Copyright 2021 Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ #include "atlas/redistribution/detail/RedistributionImpl.h" namespace atlas { namespace redistribution { namespace detail { void RedistributionImpl::setup(const FunctionSpace& source, const FunctionSpace& target) { source_ = source; target_ = target; do_setup(); } const FunctionSpace& RedistributionImpl::source() const { return source_; } const FunctionSpace& RedistributionImpl::target() const { return target_; } } // namespace detail } // namespace redistribution } // namespace atlas atlas-0.46.0/src/atlas/redistribution/detail/RedistributeStructuredColumns.h0000664000175000017500000001014715160212070027535 0ustar alastairalastair/* * (C) Crown Copyright 2021 Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ #pragma once #include #include "atlas/redistribution/detail/RedistributionImpl.h" #include "atlas/redistribution/detail/RedistributionImplFactory.h" namespace atlas { class Field; class FieldSet; class FunctionSpace; namespace functionspace { class StructuredColumns; } // namespace functionspace } // namespace atlas namespace atlas { namespace redistribution { namespace detail { // Forward declarations. class RedistributeStructuredColumns; class StructuredIndexRange; // Type aliases. class StructuredIndexRange; using idxPair = std::pair; using idxPairVector = std::vector; using StructuredIndexRangeVector = std::vector; /// \brief Concrete redistributor class for StructuredColumns to /// StructuredColumns. /// /// \details Class to map two function spaces with the same grid but /// different partitioners. class RedistributeStructuredColumns : public RedistributionImpl { public: RedistributeStructuredColumns() = default; /// \brief Initialises the redistributor. void do_setup() override; static std::string static_type() { return "RedistributeStructuredColumns"; } std::string type() const override { return static_type(); } /// \brief Redistributes source field to target field. /// /// \details Transfers source field to target field via an /// MPI_Alltoallv. Function space of source field must match /// sourceFunctionSpace supplied to the constructor. Same /// applies to target field. /// /// \param[in] source input field matching sourceFunctionSpace. /// \param[out] target output field matching targetFunctionSpace. void execute(const Field& source, Field& target) const override; /// \brief Redistributes source field set to target fields set. /// /// \details Transfers source field set to target field set via /// multiple invocations of execute(sourceField, targetField). /// /// \param[in] source input field set. /// \param[out] target output field set. void execute(const FieldSet& source, FieldSet& target) const override; private: // Generic execute call to handle different field types. template void do_execute(const Field& source, Field& target) const; // FunctionSpaces recast to StructuredColumns. functionspace::StructuredColumns source_; functionspace::StructuredColumns target_; // Vectors of index range intersection objects. StructuredIndexRangeVector sendIntersections_{}; StructuredIndexRangeVector recvIntersections_{}; // Counts and displacements for MPI communications. std::vector sendCounts_{}; std::vector sendDisplacements_{}; std::vector recvCounts_{}; std::vector recvDisplacements_{}; std::string mpi_comm_; }; /// \brief Helper class for function space intersections. class StructuredIndexRange { public: /// \brief Default Constructor. StructuredIndexRange() = default; /// \brief Constructor. StructuredIndexRange(const functionspace::StructuredColumns&); /// \brief Get index ranges from all PEs. StructuredIndexRangeVector getStructuredIndexRanges() const; /// \brief Count number of elements. idx_t getElemCount() const; /// \brief Intersection operator. StructuredIndexRange operator&(const StructuredIndexRange&) const; /// \brief Iterate over all indices and do something with functor. template void forEach(const functorType&) const; const std::string& mpi_comm() const { return mpi_comm_; } private: // Begin and end of j range. idxPair jBeginEnd_{}; // Begin and end of i range for each j. idxPairVector iBeginEnd_{}; std::string mpi_comm_; }; } // namespace detail } // namespace redistribution } // namespace atlas atlas-0.46.0/src/atlas/redistribution/detail/RedistributionImplFactory.cc0000664000175000017500000000264515160212070026764 0ustar alastairalastair/* * (C) Crown Copyright 2021 Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ #include #include "atlas/redistribution/detail/RedistributeGeneric.h" #include "atlas/redistribution/detail/RedistributeStructuredColumns.h" #include "atlas/redistribution/detail/RedistributionImpl.h" #include "atlas/redistribution/detail/RedistributionImplFactory.h" namespace atlas { namespace redistribution { namespace detail { //---------------------------------------------------------------------------------------------------------------------- void force_link() { static struct Link { Link() { RedistributionImplBuilder(); RedistributionImplBuilder(); } } link; } //---------------------------------------------------------------------------------------------------------------------- redistribution::detail::RedistributionImpl* RedistributionImplFactory::build(const std::string& builder) { force_link(); auto factory = get(builder); return factory->make(); } //---------------------------------------------------------------------------------------------------------------------- } // namespace detail } // namespace redistribution } // namespace atlas atlas-0.46.0/src/atlas/redistribution/detail/RedistributionImpl.h0000664000175000017500000000343715160212070025276 0ustar alastairalastair/* * (C) Crown Copyright 2021 Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ #pragma once #include "atlas/functionspace.h" #include "atlas/util/Object.h" namespace atlas { class Field; class FieldSet; class FunctionSpace; } // namespace atlas namespace atlas { namespace redistribution { namespace detail { /// \brief Abstract base class for redistributor implementation. class RedistributionImpl : public util::Object { public: RedistributionImpl() = default; /// \brief Initialises the redistributor. /// /// \details Performs MPI_Allgatherv to determine the (i, j, k) ranges /// of each source and target function space on each PE. /// The grids of source and target function space must match. /// /// \param[in] source Function space of source fields. /// \param[in] target Function space of target fields. void setup(const FunctionSpace& source, const FunctionSpace& target); /// \Setup class. virtual void do_setup() = 0; /// \brief Concrete type. virtual std::string type() const = 0; /// \brief Maps source field to target field. virtual void execute(const Field& source, Field& target) const = 0; /// \brief Maps source field set to target field set. virtual void execute(const FieldSet& source, FieldSet& target) const = 0; /// \brief Get const reference to source function space. const FunctionSpace& source() const; /// \brief Get const reference to target function space. const FunctionSpace& target() const; private: FunctionSpace source_; FunctionSpace target_; }; } // namespace detail } // namespace redistribution } // namespace atlas atlas-0.46.0/src/atlas/redistribution/detail/RedistributeGeneric.cc0000664000175000017500000004056015160212070025544 0ustar alastairalastair/* * (C) Crown Copyright 2021 Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ #include #include #include "atlas/field/Field.h" #include "atlas/functionspace/CellColumns.h" #include "atlas/functionspace/EdgeColumns.h" #include "atlas/functionspace/NodeColumns.h" #include "atlas/functionspace/PointCloud.h" #include "atlas/functionspace/StructuredColumns.h" #include "atlas/mesh/Mesh.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/redistribution/detail/RedistributeGeneric.h" #include "atlas/redistribution/detail/RedistributionImplFactory.h" #include "atlas/util/Unique.h" namespace atlas { namespace redistribution { namespace detail { using mesh::HybridElements; using mesh::Nodes; // Helper type definitions and functions for redistribution. namespace { // Define index-UID struct. (Needed to overload "<"). struct IdxUid : public std::pair { using std::pair::pair; }; Field getGhostField(const FunctionSpace& functionspace) { if (functionspace::NodeColumns(functionspace) || functionspace::StructuredColumns(functionspace) || functionspace::PointCloud(functionspace)) { return functionspace.ghost(); } if (functionspace::EdgeColumns(functionspace) || functionspace::CellColumns(functionspace)) { // TODO: Move something like this into the functionspace::EdgeColumns and functionspace::CellColumns auto& comm = mpi::comm(functionspace.mpi_comm()); // Get mesh elements. Mesh mesh = functionspace::EdgeColumns(functionspace) ? functionspace::EdgeColumns(functionspace).mesh() : functionspace::CellColumns(functionspace).mesh(); const auto& elems = functionspace::EdgeColumns(functionspace) ? mesh.edges() : mesh.cells(); // Make ghost field. auto ghost_field = Field("ghost", array::make_datatype(), array::make_shape(functionspace.size())); // Make views. auto ghost = array::make_view(ghost_field); auto remote_index = array::make_indexview(elems.remote_index()); auto partition = array::make_view(elems.partition()); // Set ghost field. const auto thisPart = static_cast(comm.rank()); for (idx_t i = 0; i < ghost.shape(0); ++i) { ghost(i) = partition(i) != thisPart || remote_index(i) != i; } return ghost_field; } return functionspace.ghost(); } // Create UID field. Field getUidField(const FunctionSpace& functionspace) { // Note there is a difference between uid_field and global_index in the type: uidx_t vs gidx_t // Currently this aliases the same type though... if (not functionspace::PointCloud(functionspace)) { return functionspace.global_index(); } else { // PointCloud has no global_index()... TODO, put something in place there. // Make UID field. Field uid_field = Field("Unique ID", array::make_datatype(), array::make_shape(functionspace.size())); // Make views. auto uid = array::make_view(uid_field); const auto lonlat = array::make_view(functionspace.lonlat()); // Set UIDs for (idx_t i = 0; i < uid_field.shape(0); ++i) { uid(i) = util::unique_lonlat(lonlat(i, LON), lonlat(i, LAT)); } return uid_field; } } // Resolve functionspace implementation type and get unique ID vector. std::vector getUidVec(const FunctionSpace& functionspace) { // Get ghost and unique ID fields from functionspace. const auto ghost_field = getGhostField(functionspace); const auto uid = getUidField(functionspace); // Make views to fields. const auto ghost = array::make_view(ghost_field); const auto uidView = array::make_view(uid); auto uidVec = std::vector{}; uidVec.reserve(functionspace.size()); // Get UIDs for non ghost elems. for (idx_t i = 0; i < functionspace.size(); ++i) { if (not ghost(i)) { uidVec.emplace_back(i, uidView(i)); } } // Sort by UID value. std::sort(uidVec.begin(), uidVec.end(), [](const IdxUid& a, const IdxUid& b) { return a.second < b.second; }); // Check UIDs are unique. if (ATLAS_BUILD_TYPE_DEBUG) { auto first_duplicate = std::adjacent_find( uidVec.begin(), uidVec.end(), [](const IdxUid& a, const IdxUid& b) { return a.second == b.second; }); ATLAS_ASSERT(uidVec.end() == first_duplicate, "Unique ID set has duplicate members"); } return uidVec; } // Copy UID index std::vector getUidIdx(const std::vector& uidVec) { auto idxVec = std::vector{}; idxVec.reserve(uidVec.size()); std::transform(uidVec.begin(), uidVec.end(), std::back_inserter(idxVec), [](const IdxUid& uid) { return uid.first; }); return idxVec; } // Copy UID value std::vector getUidVal(const std::vector& uidVec) { auto valVec = std::vector{}; valVec.reserve(uidVec.size()); std::transform(uidVec.begin(), uidVec.end(), std::back_inserter(valVec), [](const IdxUid& uid) { return uid.second; }); return valVec; } // Communicate UID values, return receive buffer and displacements. std::pair, std::vector> communicateUid(const std::string& mpi_comm, const std::vector& sendBuffer) { auto& comm = mpi::comm(mpi_comm); auto counts = std::vector(comm.size()); comm.allGather(static_cast(sendBuffer.size()), counts.begin(), counts.end()); auto disps = std::vector{}; disps.reserve(comm.size() + 1); disps.push_back(0); std::partial_sum(counts.begin(), counts.end(), std::back_inserter(disps)); auto recvBuffer = std::vector(static_cast(disps.back())); comm.allGatherv(sendBuffer.begin(), sendBuffer.end(), recvBuffer.begin(), counts.data(), disps.data()); return std::make_pair(recvBuffer, disps); } // Need to overload "<" operator for comparison of asymmetric types in // std::set_intersection. bool operator<(const IdxUid& lhs, const uidx_t& rhs) { return lhs.second < rhs; } bool operator<(const uidx_t& lhs, const IdxUid& rhs) { return lhs < rhs.second; } // Find the intersection between local and global UIDs, then return local // indices of incections and PE dispacements in vector. std::pair, std::vector> getUidIntersection(const std::string& mpi_comm, const std::vector& localUids, const std::vector& globalUids, const std::vector& globalDisps) { auto uidIntersection = std::vector{}; uidIntersection.reserve(localUids.size()); auto& comm = mpi::comm(mpi_comm); auto mpi_size = comm.size(); auto disps = std::vector{}; disps.reserve(mpi_size + 1); disps.push_back(0); // Loop over all PE and find UID intersection. for (size_t i = 0; i < mpi_size; ++i) { // Get displaced iterators. auto globalUidsBegin = globalUids.begin() + globalDisps[i]; auto globalUidsEnd = globalUids.begin() + globalDisps[i + 1]; // Get intersection. std::set_intersection(localUids.begin(), localUids.end(), globalUidsBegin, globalUidsEnd, std::back_inserter(uidIntersection)); // Set intersection displacement. disps.push_back(static_cast(uidIntersection.size())); } // Check that the set of all intersections matches UIDs on local PE. if (ATLAS_BUILD_TYPE_DEBUG) { auto tempUids = uidIntersection; std::sort(tempUids.begin(), tempUids.end(), [](const IdxUid& a, const IdxUid& b) { return a.second < b.second; }); ATLAS_ASSERT(tempUids == localUids, "Set of all UID intersections does not match local UIDs."); } // Return local indices of intersection and displacements. return make_pair(getUidIdx(uidIntersection), disps); } // Iterate over a field, in the order of an index list, and apply a functor to // each element. // Recursive ForEach to visit all elements of field. template struct ForEach { template static void apply(const std::vector& idxList, array::ArrayView& fieldView, const Functor& f, Idxs... idxs) { // Iterate over dimension Dim of array. for (idx_t idx = 0; idx < fieldView.shape(Dim); ++idx) { ForEach::apply(idxList, fieldView, f, idxs..., idx); } } }; // Beginning of recursion when Dim == 0. template struct ForEach { template static void apply(const std::vector& idxList, array::ArrayView& fieldView, const Functor& f, Idxs... idxs) { // Iterate over dimension 0 of array in order defined by idxList. for (idx_t idx : idxList) { ForEach::apply(idxList, fieldView, f, idxs..., idx); } } }; // End of recursion when Dim == Rank. template struct ForEach { template static void apply(const std::vector& idxList, array::ArrayView& fieldView, const Functor& f, Idxs... idxs) { // Apply functor. f(fieldView(idxs...)); } }; } // namespace void RedistributeGeneric::do_setup() { ATLAS_ASSERT( source().mpi_comm() == target().mpi_comm() ); mpi_comm_ = source().mpi_comm(); // get a unique ID (UID) for each owned member of functionspace. const auto sourceUidVec = getUidVec(source()); const auto targetUidVec = getUidVec(target()); // Communicate UID vectors to all PEs. auto sourceGlobalUids = std::vector{}; auto sourceGlobalDisps = std::vector{}; std::tie(sourceGlobalUids, sourceGlobalDisps) = communicateUid(mpi_comm_, getUidVal(sourceUidVec)); auto targetGlobalUids = std::vector{}; auto targetGlobalDisps = std::vector{}; std::tie(targetGlobalUids, targetGlobalDisps) = communicateUid(mpi_comm_, getUidVal(targetUidVec)); // Get intersection of local UIDs and Global UIDs. std::tie(sourceLocalIdx_, sourceDisps_) = getUidIntersection(mpi_comm_, sourceUidVec, targetGlobalUids, targetGlobalDisps); std::tie(targetLocalIdx_, targetDisps_) = getUidIntersection(mpi_comm_, targetUidVec, sourceGlobalUids, sourceGlobalDisps); } void RedistributeGeneric::execute(const Field& sourceField, Field& targetField) const { //Check functionspaces match. ATLAS_ASSERT(sourceField.functionspace().type() == source().type()); ATLAS_ASSERT(targetField.functionspace().type() == target().type()); // Check Field datatypes match. ATLAS_ASSERT(sourceField.datatype() == targetField.datatype()); // Check Field ranks match. ATLAS_ASSERT(sourceField.rank() == targetField.rank()); // Check number of levels and variables match. for (idx_t i = 1; i < sourceField.rank(); ++i) { ATLAS_ASSERT(sourceField.shape(i) == targetField.shape(i)); } // Perform redistribution. do_execute(sourceField, targetField); } void RedistributeGeneric::execute(const FieldSet& sourceFieldSet, FieldSet& targetFieldSet) const { // Check field set sizes match. ATLAS_ASSERT(sourceFieldSet.size() == targetFieldSet.size()); // Redistribute fields. for (idx_t i = 0; i < sourceFieldSet.size(); ++i) { execute(sourceFieldSet[i], targetFieldSet[i]); } } // Determine datatype. void RedistributeGeneric::do_execute(const Field& sourceField, Field& targetField) const { // Available datatypes defined in array/LocalView.cc switch (sourceField.datatype().kind()) { case array::DataType::KIND_REAL64: { return do_execute(sourceField, targetField); } case array::DataType::KIND_REAL32: { return do_execute(sourceField, targetField); } case array::DataType::KIND_INT64: { return do_execute(sourceField, targetField); } case array::DataType::KIND_INT32: { return do_execute(sourceField, targetField); } default: { ATLAS_THROW_EXCEPTION("No implementation for data type " + sourceField.datatype().str()); } } } // Determine rank. template void RedistributeGeneric::do_execute(const Field& sourceField, Field& targetField) const { // Available ranks defined in array/LocalView.cc switch (sourceField.rank()) { case 1: { return do_execute(sourceField, targetField); } case 2: { return do_execute(sourceField, targetField); } case 3: { return do_execute(sourceField, targetField); } case 4: { return do_execute(sourceField, targetField); } case 5: { return do_execute(sourceField, targetField); } case 6: { return do_execute(sourceField, targetField); } case 7: { return do_execute(sourceField, targetField); } case 8: { return do_execute(sourceField, targetField); } case 9: { return do_execute(sourceField, targetField); } default: { ATLAS_THROW_EXCEPTION("No implementation for rank " + std::to_string(sourceField.rank())); } } } // Perform redistribution. template void RedistributeGeneric::do_execute(const Field& sourceField, Field& targetField) const { // Get array views. auto sourceView = array::make_view(sourceField); auto targetView = array::make_view(targetField); const auto& comm = mpi::comm(mpi_comm_); auto mpi_size = comm.size(); // Get number of elems per column. int elemsPerCol = 1; for (int i = 1; i < Rank; ++i) { elemsPerCol *= sourceView.shape(i); } // Set send displacement and counts vectors. auto sendDisps = std::vector{}; sendDisps.reserve(mpi_size + 1); auto sendCounts = std::vector{}; sendCounts.reserve(mpi_size); std::transform(sourceDisps_.begin(), sourceDisps_.end(), std::back_inserter(sendDisps), [&](const int& disp) { return disp * elemsPerCol; }); std::adjacent_difference(sendDisps.begin() + 1, sendDisps.end(), std::back_inserter(sendCounts)); // Set recv displacement and counts vectors. auto recvDisps = std::vector{}; recvDisps.reserve(mpi_size + 1); auto recvCounts = std::vector{}; recvCounts.reserve(mpi_size); std::transform(targetDisps_.begin(), targetDisps_.end(), std::back_inserter(recvDisps), [&](const int& disp) { return disp * elemsPerCol; }); std::adjacent_difference(recvDisps.begin() + 1, recvDisps.end(), std::back_inserter(recvCounts)); // Allocate send and recv buffers. auto sendBuffer = std::vector(static_cast(sendDisps.back())); auto recvBuffer = std::vector(static_cast(recvDisps.back())); auto sendBufferIt = sendBuffer.begin(); auto recvBufferIt = recvBuffer.cbegin(); // Copy sourceField to sendBuffer. ForEach::apply(sourceLocalIdx_, sourceView, [&](const Value& elem) { *sendBufferIt++ = elem; }); // Perform MPI communication. comm.allToAllv(sendBuffer.data(), sendCounts.data(), sendDisps.data(), recvBuffer.data(), recvCounts.data(), recvDisps.data()); // Copy recvBuffer to targetField. ForEach::apply(targetLocalIdx_, targetView, [&](Value& elem) { elem = *recvBufferIt++; }); } namespace { static RedistributionImplBuilder register_builder(RedistributeGeneric::static_type()); } } // namespace detail } // namespace redistribution } // namespace atlas atlas-0.46.0/src/atlas/redistribution/detail/RedistributionImplFactory.h0000664000175000017500000000255215160212070026623 0ustar alastairalastair/* * (C) Crown Copyright 2021 Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ #pragma once #pragma once #include #include "atlas/util/Config.h" #include "atlas/util/Factory.h" namespace atlas { namespace redistribution { namespace detail { class RedistributionImpl; //---------------------------------------------------------------------------------------------------------------------- class RedistributionImplFactory : public util::Factory { public: static std::string className() { return "RedistributionFactory"; } static RedistributionImpl* build(const std::string&); using Factory::Factory; private: virtual RedistributionImpl* make() = 0; }; //---------------------------------------------------------------------------------------------------------------------- template class RedistributionImplBuilder : public RedistributionImplFactory { private: virtual RedistributionImpl* make() { return new T(); } public: using RedistributionImplFactory::RedistributionImplFactory; }; //---------------------------------------------------------------------------------------------------------------------- } // namespace detail } // namespace redistribution } // namespace atlas atlas-0.46.0/src/atlas/field.h0000664000175000017500000000101715160212070016211 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ /// @author Willem Deconinck #pragma once #include "atlas/field/Field.h" #include "atlas/field/FieldSet.h" #include "atlas/option.h" atlas-0.46.0/src/atlas/numerics/0000775000175000017500000000000015160212070016603 5ustar alastairalastairatlas-0.46.0/src/atlas/numerics/Method.h0000664000175000017500000000165015160212070020176 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include "atlas/util/Object.h" namespace atlas { namespace numerics { /// @brief Method class /// @note Abstract base class class Method : public util::Object { public: Method() {} virtual ~Method() = 0; virtual const std::string& name() const = 0; }; inline Method::~Method() {} #ifndef DOXYGEN_SHOULD_SKIP_THIS extern "C" { void atlas__Method__delete(Method* This); const char* atlas__Method__name(Method* This); } #endif } // namespace numerics } // namespace atlas atlas-0.46.0/src/atlas/numerics/fvm/0000775000175000017500000000000015160212070017373 5ustar alastairalastairatlas-0.46.0/src/atlas/numerics/fvm/Method.h0000664000175000017500000000466115160212070020773 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include "atlas/functionspace/EdgeColumns.h" #include "atlas/functionspace/NodeColumns.h" #include "atlas/numerics/Method.h" namespace eckit { class Parametrisation; } namespace atlas { class Mesh; } namespace atlas { namespace mesh { class HybridElements; } } // namespace atlas namespace atlas { namespace mesh { class Nodes; } } // namespace atlas namespace atlas { namespace numerics { namespace fvm { class Method : public numerics::Method { public: Method(Mesh&, const eckit::Configuration&); Method(Mesh&, const mesh::Halo&); Method(Mesh&); virtual std::string& name() const override { static std::string _name{"fvm"}; return _name; } const atlas::Mesh& mesh() const { return mesh_; } atlas::Mesh& mesh() { return mesh_; } size_t levels() const { return levels_; } const functionspace::NodeColumns& node_columns() const { return node_columns_; } functionspace::NodeColumns& node_columns() { return node_columns_; } const functionspace::EdgeColumns& edge_columns() const { return edge_columns_; } functionspace::EdgeColumns& edge_columns() { return edge_columns_; } const double& radius() const { return radius_; } private: void setup(); private: // data Mesh mesh_; // non-const because functionspace may modify mesh size_t levels_; mesh::Halo halo_; mesh::Nodes& nodes_; mesh::HybridElements& edges_; functionspace::NodeColumns node_columns_; functionspace::EdgeColumns edge_columns_; double radius_; }; // ------------------------------------------------------------------- // C wrapper interfaces to C++ routines extern "C" { Method* atlas__numerics__fvm__Method__new(Mesh::Implementation* mesh, const eckit::Configuration* params); const functionspace::detail::NodeColumns* atlas__numerics__fvm__Method__functionspace_nodes(Method* This); const functionspace::detail::EdgeColumns* atlas__numerics__fvm__Method__functionspace_edges(Method* This); } } // namespace fvm } // namespace numerics } // namespace atlas atlas-0.46.0/src/atlas/numerics/fvm/Nabla.h0000664000175000017500000000323415160212070020563 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include "atlas/library/config.h" #include "atlas/numerics/Nabla.h" namespace atlas { namespace numerics { namespace fvm { class Method; } } // namespace numerics } // namespace atlas namespace atlas { class Field; } namespace atlas { namespace numerics { namespace fvm { #ifndef DOXYGEN_SHOULD_SKIP_THIS class Nabla : public atlas::numerics::NablaImpl { public: Nabla(const atlas::numerics::Method&, const eckit::Parametrisation&); virtual ~Nabla() override; virtual void gradient(const Field& scalar, Field& grad) const override; virtual void divergence(const Field& vector, Field& div) const override; virtual void curl(const Field& vector, Field& curl) const override; virtual void laplacian(const Field& scalar, Field& laplacian) const override; virtual const FunctionSpace& functionspace() const override; private: void setup(); void gradient_of_scalar(const Field& scalar, Field& grad) const; void gradient_of_vector(const Field& vector, Field& grad) const; private: fvm::Method const* fvm_; std::vector pole_edges_; int metric_approach_{0}; }; #endif // ------------------------------------------------------------------ } // namespace fvm } // namespace numerics } // namespace atlas atlas-0.46.0/src/atlas/numerics/fvm/Nabla.cc0000664000175000017500000006011515160212070020722 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "eckit/config/Parametrisation.h" #include "atlas/array/ArrayView.h" #include "atlas/array/MakeView.h" #include "atlas/field/Field.h" #include "atlas/mesh/HybridElements.h" #include "atlas/mesh/Mesh.h" #include "atlas/mesh/Nodes.h" #include "atlas/numerics/fvm/Method.h" #include "atlas/numerics/fvm/Nabla.h" #include "atlas/parallel/omp/omp.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Log.h" #include "atlas/util/CoordinateEnums.h" // ======================================================= using Topology = atlas::mesh::Nodes::Topology; using Range = atlas::array::Range; namespace atlas { namespace numerics { namespace fvm { namespace { static NablaBuilder __fvm_nabla("fvm"); } Nabla::Nabla(const numerics::Method& method, const eckit::Parametrisation& p): atlas::numerics::NablaImpl(method, p) { fvm_ = dynamic_cast(&method); if (!fvm_) { throw_Exception("atlas::numerics::fvm::Nabla needs a atlas::numerics::fvm::Method", Here()); } Log::debug() << "Nabla constructed for method " << fvm_->name() << " with " << fvm_->node_columns().nb_nodes_global() << " nodes total" << std::endl; fvm_->attach(); p.get("metric_approach", metric_approach_); // Experimental, only affects divergence and curl !!! // metric_approach = 0 ORIGINAL, DEFAULT // metric_term cos(y) is multiplied with each wind component // --> cell-interface: avg = 0.5*( cos(y1)*u1 + cos(y2)*u2 ) // metric_approach = 1: // metric_term cos(0.5*(y1+y2)) is used at cell interface // --> cell-interface: avg = 0.5*(u1+u2)*cos(0.5*(y1+y2)) // Results seem to indicate that approach=0 is overall better, although approach=1 // seems to handle pole slightly better (error factor 2 to 4 times lower) setup(); } Nabla::~Nabla() = default; void Nabla::setup() { const mesh::Edges& edges = fvm_->mesh().edges(); const idx_t nedges = fvm_->edge_columns().nb_edges(); const auto edge_flags = array::make_view(edges.flags()); auto is_pole_edge = [&](idx_t e) { return Topology::check(edge_flags(e), Topology::POLE); }; // Filter pole_edges out of all edges std::vector tmp(nedges); idx_t c(0); for (idx_t jedge = 0; jedge < nedges; ++jedge) { if (is_pole_edge(jedge)) { tmp[c++] = jedge; } } pole_edges_.clear(); pole_edges_.reserve(c); for (idx_t jedge = 0; jedge < c; ++jedge) { pole_edges_.push_back(tmp[jedge]); } } void Nabla::gradient(const Field& field, Field& grad_field) const { if (field.variables() > 1) { return gradient_of_vector(field, grad_field); } else { return gradient_of_scalar(field, grad_field); } } void Nabla::gradient_of_scalar(const Field& scalar_field, Field& grad_field) const { Log::debug() << "Compute gradient of scalar field " << scalar_field.name() << " with fvm method" << std::endl; auto dispatch = [&](auto value) { using Value = std::decay_t; const Value radius = fvm_->radius(); const Value deg2rad = M_PI / 180.; const mesh::Edges& edges = fvm_->mesh().edges(); const mesh::Nodes& nodes = fvm_->mesh().nodes(); const idx_t nnodes = fvm_->node_columns().nb_nodes(); const idx_t nedges = fvm_->edge_columns().nb_edges(); const auto scalar = scalar_field.levels() ? array::make_view(scalar_field).slice(Range::all(), Range::all()) : array::make_view(scalar_field).slice(Range::all(), Range::dummy()); auto grad = grad_field.levels() ? array::make_view(grad_field).slice(Range::all(), Range::all(), Range::all()) : array::make_view(grad_field).slice(Range::all(), Range::dummy(), Range::all()); const idx_t nlev = scalar.shape(1); if (grad.shape(1) != nlev) { throw_AssertionFailed("gradient field should have same number of levels", Here()); } const auto lonlat_deg = array::make_view(nodes.lonlat()); const auto dual_volumes = array::make_view(nodes.field("dual_volumes")); const auto dual_normals = array::make_view(edges.field("dual_normals")); const auto node2edge_sign = array::make_view(nodes.field("node2edge_sign")); const mesh::Connectivity& node2edge = nodes.edge_connectivity(); const mesh::MultiBlockConnectivity& edge2node = edges.node_connectivity(); array::ArrayT avgS_arr(nedges, nlev, 2ul); auto avgS = array::make_view(avgS_arr); const Value scale = deg2rad * deg2rad * radius; atlas_omp_parallel { atlas_omp_for(idx_t jedge = 0; jedge < nedges; ++jedge) { idx_t ip1 = edge2node(jedge, 0); idx_t ip2 = edge2node(jedge, 1); Value S[2] = { static_cast(dual_normals(jedge, LON)), static_cast(dual_normals(jedge, LAT))}; for (idx_t jlev = 0; jlev < nlev; ++jlev) { Value avg = (scalar(ip1, jlev) + scalar(ip2, jlev)) * Value{0.5}; avgS(jedge, jlev, LON) = S[LON] * deg2rad * avg; avgS(jedge, jlev, LAT) = S[LAT] * deg2rad * avg; } } atlas_omp_for(idx_t jnode = 0; jnode < nnodes; ++jnode) { for (idx_t jlev = 0; jlev < nlev; ++jlev) { grad(jnode, jlev, LON) = 0.; grad(jnode, jlev, LAT) = 0.; } for (idx_t jedge = 0; jedge < node2edge.cols(jnode); ++jedge) { const idx_t iedge = node2edge(jnode, jedge); if (iedge < nedges) { const Value add = node2edge_sign(jnode, jedge); for (idx_t jlev = 0; jlev < nlev; ++jlev) { grad(jnode, jlev, LON) += add * avgS(iedge, jlev, LON); grad(jnode, jlev, LAT) += add * avgS(iedge, jlev, LAT); } } } const Value y = lonlat_deg(jnode, LAT) * deg2rad; const Value metric_y = Value{1.} / (static_cast(dual_volumes(jnode)) * scale); const Value metric_x = metric_y / std::cos(y); for (idx_t jlev = 0; jlev < nlev; ++jlev) { grad(jnode, jlev, LON) *= metric_x; grad(jnode, jlev, LAT) *= metric_y; } } } }; ATLAS_ASSERT( scalar_field.datatype() == grad_field.datatype() ); switch (scalar_field.datatype().kind()) { case (DataType::KIND_REAL32): { dispatch(float{}); break; } case (DataType::KIND_REAL64): { dispatch(double{}); break; } default: ATLAS_NOTIMPLEMENTED; } } // ================================================================================ void Nabla::gradient_of_vector(const Field& vector_field, Field& grad_field) const { Log::debug() << "Compute gradient of vector field " << vector_field.name() << " with fvm method" << std::endl; auto dispatch = [&](auto value) { using Value = std::decay_t; const Value radius = fvm_->radius(); const Value deg2rad = M_PI / 180.; const mesh::Edges& edges = fvm_->mesh().edges(); const mesh::Nodes& nodes = fvm_->mesh().nodes(); const idx_t nnodes = fvm_->node_columns().nb_nodes(); const idx_t nedges = fvm_->edge_columns().nb_edges(); const auto vector = vector_field.levels() ? array::make_view(vector_field).slice(Range::all(), Range::all(), Range::all()) : array::make_view(vector_field).slice(Range::all(), Range::dummy(), Range::all()); auto grad = grad_field.levels() ? array::make_view(grad_field).slice(Range::all(), Range::all(), Range::all()) : array::make_view(grad_field).slice(Range::all(), Range::dummy(), Range::all()); const idx_t nlev = vector.shape(1); if (grad.shape(1) != nlev) { throw_AssertionFailed("gradient field should have same number of levels", Here()); } const auto lonlat_deg = array::make_view(nodes.lonlat()); const auto dual_volumes = array::make_view(nodes.field("dual_volumes")); const auto dual_normals = array::make_view(edges.field("dual_normals")); const auto node2edge_sign = array::make_view(nodes.field("node2edge_sign")); const auto edge_flags = array::make_view(edges.flags()); auto is_pole_edge = [&](idx_t e) { return Topology::check(edge_flags(e), Topology::POLE); }; const mesh::Connectivity& node2edge = nodes.edge_connectivity(); const mesh::MultiBlockConnectivity& edge2node = edges.node_connectivity(); array::ArrayT avgS_arr(nedges, nlev, 4ul); array::ArrayView avgS = array::make_view(avgS_arr); const Value scale = deg2rad * deg2rad * radius; enum { LONdLON = 0, LONdLAT = 1, LATdLON = 2, LATdLAT = 3 }; atlas_omp_parallel { atlas_omp_for(idx_t jedge = 0; jedge < nedges; ++jedge) { idx_t ip1 = edge2node(jedge, 0); idx_t ip2 = edge2node(jedge, 1); Value pbc = 1. - 2. * is_pole_edge(jedge); Value S[2] = {static_cast(dual_normals(jedge,LON)), static_cast(dual_normals(jedge,LAT))}; Value avg[2]; for (idx_t jlev = 0; jlev < nlev; ++jlev) { avg[LON] = (vector(ip1, jlev, LON) + pbc * vector(ip2, jlev, LON)) * Value{0.5}; avg[LAT] = (vector(ip1, jlev, LAT) + pbc * vector(ip2, jlev, LAT)) * Value{0.5}; avgS(jedge, jlev, LONdLON) = S[LON] * deg2rad * avg[LON]; // above = 0 at pole because of dual_normals avgS(jedge, jlev, LONdLAT) = S[LAT] * deg2rad * avg[LON]; avgS(jedge, jlev, LATdLON) = S[LON] * deg2rad * avg[LAT]; // above = 0 at pole because of dual_normals avgS(jedge, jlev, LATdLAT) = S[LAT] * deg2rad * avg[LAT]; } } atlas_omp_for(idx_t jnode = 0; jnode < nnodes; ++jnode) { for (idx_t jlev = 0; jlev < nlev; ++jlev) { grad(jnode, jlev, LONdLON) = 0.; grad(jnode, jlev, LONdLAT) = 0.; grad(jnode, jlev, LATdLON) = 0.; grad(jnode, jlev, LATdLAT) = 0.; } for (idx_t jedge = 0; jedge < node2edge.cols(jnode); ++jedge) { const idx_t iedge = node2edge(jnode, jedge); if (iedge < nedges) { Value add = node2edge_sign(jnode, jedge); for (idx_t jlev = 0; jlev < nlev; ++jlev) { grad(jnode, jlev, LONdLON) += add * avgS(iedge, jlev, LONdLON); grad(jnode, jlev, LONdLAT) += add * avgS(iedge, jlev, LONdLAT); grad(jnode, jlev, LATdLON) += add * avgS(iedge, jlev, LATdLON); grad(jnode, jlev, LATdLAT) += add * avgS(iedge, jlev, LATdLAT); } } } const Value y = lonlat_deg(jnode, LAT) * deg2rad; const Value metric_y = Value{1.} / (static_cast(dual_volumes(jnode)) * scale); const Value metric_x = metric_y / std::cos(y); for (idx_t jlev = 0; jlev < nlev; ++jlev) { grad(jnode, jlev, LONdLON) *= metric_x; grad(jnode, jlev, LATdLON) *= metric_x; grad(jnode, jlev, LONdLAT) *= metric_y; grad(jnode, jlev, LATdLAT) *= metric_y; } } } // Fix wrong node2edge_sign for vector quantities for (size_t jedge = 0; jedge < pole_edges_.size(); ++jedge) { const idx_t iedge = pole_edges_[jedge]; const idx_t jnode = edge2node(iedge, 1); const Value metric_y = Value{1.} / (static_cast(dual_volumes(jnode)) * scale); for (idx_t jlev = 0; jlev < nlev; ++jlev) { grad(jnode, jlev, LONdLAT) -= 2. * avgS(iedge, jlev, LONdLAT) * metric_y; grad(jnode, jlev, LATdLAT) -= 2. * avgS(iedge, jlev, LATdLAT) * metric_y; } } }; ATLAS_ASSERT( vector_field.datatype() == grad_field.datatype() ); switch (vector_field.datatype().kind()) { case (DataType::KIND_REAL32): { dispatch(float{}); break; } case (DataType::KIND_REAL64): { dispatch(double{}); break; } default: ATLAS_NOTIMPLEMENTED; } } // ================================================================================ void Nabla::divergence(const Field& vector_field, Field& div_field) const { auto dispatch = [&](auto value) { using Value = std::decay_t; const Value radius = fvm_->radius(); const Value deg2rad = M_PI / 180.; const mesh::Edges& edges = fvm_->mesh().edges(); const mesh::Nodes& nodes = fvm_->mesh().nodes(); const idx_t nnodes = fvm_->node_columns().nb_nodes(); const idx_t nedges = fvm_->edge_columns().nb_edges(); const auto vector = vector_field.levels() ? array::make_view(vector_field).slice(Range::all(), Range::all(), Range::all()) : array::make_view(vector_field).slice(Range::all(), Range::dummy(), Range::all()); auto div = div_field.levels() ? array::make_view(div_field).slice(Range::all(), Range::all()) : array::make_view(div_field).slice(Range::all(), Range::dummy()); const idx_t nlev = vector.shape(1); if (div.shape(1) != nlev) { throw_AssertionFailed("div_field should have same number of levels", Here()); } const auto lonlat_deg = array::make_view(nodes.lonlat()); const auto dual_volumes = array::make_view(nodes.field("dual_volumes")); const auto dual_normals = array::make_view(edges.field("dual_normals")); const auto node2edge_sign = array::make_view(nodes.field("node2edge_sign")); const auto edge_flags = array::make_view(edges.flags()); auto is_pole_edge = [&](idx_t e) { return Topology::check(edge_flags(e), Topology::POLE); }; const mesh::Connectivity& node2edge = nodes.edge_connectivity(); const mesh::MultiBlockConnectivity& edge2node = edges.node_connectivity(); array::ArrayT avgS_arr(nedges, nlev, 2ul); array::ArrayView avgS = array::make_view(avgS_arr); const Value scale = deg2rad * deg2rad * radius; enum { LONdLON = 0, LATdLAT = 1 }; atlas_omp_parallel { atlas_omp_for(idx_t jedge = 0; jedge < nedges; ++jedge) { Value pbc = 1 - is_pole_edge(jedge); idx_t ip1 = edge2node(jedge, 0); idx_t ip2 = edge2node(jedge, 1); Value y1 = lonlat_deg(ip1, LAT) * deg2rad; Value y2 = lonlat_deg(ip2, LAT) * deg2rad; Value cosy1, cosy2; if (metric_approach_ == 0) { cosy1 = std::cos(y1) * pbc; cosy2 = std::cos(y2) * pbc; } else { cosy1 = cosy2 = std::cos(0.5 * (y1 + y2)) * pbc; } Value S[2] = {static_cast(dual_normals(jedge, LON)) * deg2rad, static_cast(dual_normals(jedge, LAT)) * deg2rad}; Value avg[2]; for (idx_t jlev = 0; jlev < nlev; ++jlev) { Value u1 = vector(ip1, jlev, LON); Value u2 = vector(ip2, jlev, LON); Value v1 = vector(ip1, jlev, LAT) * cosy1; Value v2 = vector(ip2, jlev, LAT) * cosy2; avg[LON] = (u1 + u2) * 0.5; avg[LAT] = (v1 + v2) * 0.5; avgS(jedge, jlev, LONdLON) = avg[LON] * S[LON]; avgS(jedge, jlev, LATdLAT) = avg[LAT] * S[LAT]; } } atlas_omp_for(idx_t jnode = 0; jnode < nnodes; ++jnode) { for (idx_t jlev = 0; jlev < nlev; ++jlev) { div(jnode, jlev) = 0.; } for (idx_t jedge = 0; jedge < node2edge.cols(jnode); ++jedge) { idx_t iedge = node2edge(jnode, jedge); if (iedge < nedges) { Value add = node2edge_sign(jnode, jedge); for (idx_t jlev = 0; jlev < nlev; ++jlev) { div(jnode, jlev) += add * (avgS(iedge, jlev, LONdLON) + avgS(iedge, jlev, LATdLAT)); } } } const Value y = lonlat_deg(jnode, LAT) * deg2rad; Value metric = Value{1.} / (static_cast(dual_volumes(jnode)) * scale * std::cos(y)); for (idx_t jlev = 0; jlev < nlev; ++jlev) { div(jnode, jlev) *= metric; } } } }; ATLAS_ASSERT( vector_field.datatype() == div_field.datatype() ); switch (vector_field.datatype().kind()) { case (DataType::KIND_REAL32): { dispatch(float{}); break; } case (DataType::KIND_REAL64): { dispatch(double{}); break; } default: ATLAS_NOTIMPLEMENTED; } } void Nabla::curl(const Field& vector_field, Field& curl_field) const { auto dispatch = [&](auto value) { using Value = std::decay_t; const Value radius = fvm_->radius(); const Value deg2rad = M_PI / 180.; const mesh::Edges& edges = fvm_->mesh().edges(); const mesh::Nodes& nodes = fvm_->mesh().nodes(); const idx_t nnodes = fvm_->node_columns().nb_nodes(); const idx_t nedges = fvm_->edge_columns().nb_edges(); const auto vector = vector_field.levels() ? array::make_view(vector_field).slice(Range::all(), Range::all(), Range::all()) : array::make_view(vector_field).slice(Range::all(), Range::dummy(), Range::all()); auto curl = curl_field.levels() ? array::make_view(curl_field).slice(Range::all(), Range::all()) : array::make_view(curl_field).slice(Range::all(), Range::dummy()); const idx_t nlev = vector.shape(1); if (curl.shape(1) != nlev) { throw_AssertionFailed("curl field should have same number of levels", Here()); } const auto lonlat_deg = array::make_view(nodes.lonlat()); const auto dual_volumes = array::make_view(nodes.field("dual_volumes")); const auto dual_normals = array::make_view(edges.field("dual_normals")); const auto node2edge_sign = array::make_view(nodes.field("node2edge_sign")); const auto edge_flags = array::make_view(edges.flags()); auto is_pole_edge = [&](idx_t e) { return Topology::check(edge_flags(e), Topology::POLE); }; const mesh::Connectivity& node2edge = nodes.edge_connectivity(); const mesh::MultiBlockConnectivity& edge2node = edges.node_connectivity(); array::ArrayT avgS_arr(nedges, nlev, 2ul); array::ArrayView avgS = array::make_view(avgS_arr); const Value scale = deg2rad * deg2rad * radius; enum { LONdLAT = 0, LATdLON = 1 }; atlas_omp_parallel { atlas_omp_for(idx_t jedge = 0; jedge < nedges; ++jedge) { idx_t ip1 = edge2node(jedge, 0); idx_t ip2 = edge2node(jedge, 1); Value y1 = lonlat_deg(ip1, LAT) * deg2rad; Value y2 = lonlat_deg(ip2, LAT) * deg2rad; Value pbc = 1 - is_pole_edge(jedge); Value cosy1; Value cosy2; if (metric_approach_ == 0) { cosy1 = std::cos(y1) * pbc; cosy2 = std::cos(y2) * pbc; } else { cosy1 = cosy2 = std::cos(Value{0.5} * (y1 + y2)) * pbc; } Value S[2] = {static_cast(dual_normals(jedge, LON)) * deg2rad, static_cast(dual_normals(jedge, LAT)) * deg2rad}; Value avg[2]; for (idx_t jlev = 0; jlev < nlev; ++jlev) { Value u1 = vector(ip1, jlev, LON) * cosy1; Value u2 = vector(ip2, jlev, LON) * cosy2; Value v1 = vector(ip1, jlev, LAT); Value v2 = vector(ip2, jlev, LAT); avg[LON] = (u1 + u2) * Value{0.5}; avg[LAT] = (v1 + v2) * Value{0.5}; avgS(jedge, jlev, LONdLAT) = avg[LON] * S[LAT]; avgS(jedge, jlev, LATdLON) = avg[LAT] * S[LON]; } } atlas_omp_for(idx_t jnode = 0; jnode < nnodes; ++jnode) { for (idx_t jlev = 0; jlev < nlev; ++jlev) { curl(jnode, jlev) = 0.; } for (idx_t jedge = 0; jedge < node2edge.cols(jnode); ++jedge) { idx_t iedge = node2edge(jnode, jedge); if (iedge < nedges) { double add = node2edge_sign(jnode, jedge); for (idx_t jlev = 0; jlev < nlev; ++jlev) { curl(jnode, jlev) += add * (avgS(iedge, jlev, LATdLON) - avgS(iedge, jlev, LONdLAT)); } } } Value y = static_cast(lonlat_deg(jnode, LAT)) * deg2rad; Value metric = Value{1.} / (static_cast(dual_volumes(jnode)) * scale * std::cos(y)); for (idx_t jlev = 0; jlev < nlev; ++jlev) { curl(jnode, jlev) *= metric; } } } }; ATLAS_ASSERT( vector_field.datatype() == curl_field.datatype() ); switch (vector_field.datatype().kind()) { case (DataType::KIND_REAL32): { dispatch(float{}); break; } case (DataType::KIND_REAL64): { dispatch(double{}); break; } default: ATLAS_NOTIMPLEMENTED; } } void Nabla::laplacian(const Field& scalar, Field& lapl) const { Field grad(fvm_->node_columns().createField(option::name("grad") | option::levels(scalar.levels()) | option::variables(2) | option::datatype(scalar.datatype()))); gradient(scalar, grad); if (fvm_->node_columns().halo().size() < 2) { fvm_->node_columns().haloExchange(grad); } divergence(grad, lapl); } const FunctionSpace& Nabla::functionspace() const { return fvm_->node_columns(); } } // namespace fvm } // namespace numerics } // namespace atlas atlas-0.46.0/src/atlas/numerics/fvm/Method.cc0000664000175000017500000001236615160212070021132 0ustar alastairalastair/* * (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/array/ArrayView.h" #include "atlas/array/MakeView.h" #include "atlas/functionspace/EdgeColumns.h" #include "atlas/functionspace/NodeColumns.h" #include "atlas/mesh/HybridElements.h" #include "atlas/mesh/Mesh.h" #include "atlas/mesh/Nodes.h" #include "atlas/mesh/actions/BuildDualMesh.h" #include "atlas/mesh/actions/BuildEdges.h" #include "atlas/mesh/actions/BuildParallelFields.h" #include "atlas/numerics/fvm/Method.h" #include "atlas/parallel/omp/omp.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Trace.h" #include "atlas/util/CoordinateEnums.h" #include "atlas/util/Earth.h" // ======================================================= using namespace atlas::mesh::actions; namespace atlas { namespace numerics { namespace fvm { namespace { mesh::Halo get_halo(const eckit::Parametrisation& params) { size_t halo_size(1); params.get("halo", halo_size); return mesh::Halo(halo_size); } size_t get_levels(const eckit::Parametrisation& params) { size_t levels(0); params.get("levels", levels); return levels; } double get_radius(const eckit::Parametrisation& params) { double radius = util::Earth::radius(); params.get("radius", radius); return radius; } } // namespace Method::Method(Mesh& mesh): Method::Method(mesh, util::NoConfig()) {} Method::Method(Mesh& mesh, const mesh::Halo& halo): Method::Method(mesh, util::Config("halo", halo.size())) {} Method::Method(Mesh& mesh, const eckit::Configuration& params): mesh_(mesh), levels_(get_levels(params)), halo_(get_halo(params)), nodes_(mesh.nodes()), edges_(mesh.edges()), radius_(get_radius(params)) { setup(); } void Method::setup() { ATLAS_TRACE("fvm::Method::setup "); util::Config config; config.set("halo", halo_.size()); if (levels_) { config.set("levels", levels_); } node_columns_ = functionspace::NodeColumns(mesh(), config); edge_columns_ = functionspace::EdgeColumns(mesh(), config); { ATLAS_TRACE_SCOPE("build_median_dual_mesh") build_median_dual_mesh(mesh()); ATLAS_TRACE_SCOPE("build_node_to_edge_connectivity") build_node_to_edge_connectivity(mesh()); const idx_t nnodes = nodes_.size(); auto edge_flags = array::make_view(edges_.flags()); using Topology = mesh::Nodes::Topology; auto is_pole_edge = [&](size_t e) { return Topology::check(edge_flags(e), Topology::POLE); }; // Compute sign { const mesh::Connectivity& node_edge_connectivity = nodes_.edge_connectivity(); const mesh::MultiBlockConnectivity& edge_node_connectivity = edges_.node_connectivity(); if (!nodes_.has_field("node2edge_sign")) { nodes_.add(Field("node2edge_sign", array::make_datatype(), array::make_shape(nnodes, node_edge_connectivity.maxcols()))); } array::ArrayView node2edge_sign = array::make_view(nodes_.field("node2edge_sign")); atlas_omp_parallel_for(idx_t jnode = 0; jnode < nnodes; ++jnode) { for (idx_t jedge = 0; jedge < node_edge_connectivity.cols(jnode); ++jedge) { idx_t iedge = node_edge_connectivity(jnode, jedge); idx_t ip1 = edge_node_connectivity(iedge, 0); if (jnode == ip1) { node2edge_sign(jnode, jedge) = 1.; } else { node2edge_sign(jnode, jedge) = -1.; if (is_pole_edge(iedge)) { node2edge_sign(jnode, jedge) = 1.; } } } } } } } // ------------------------------------------------------------------------------------------ extern "C" { Method* atlas__numerics__fvm__Method__new(Mesh::Implementation* mesh, const eckit::Configuration* config) { ATLAS_ASSERT(mesh != nullptr, "Cannot access uninitialised atlas_Mesh"); ATLAS_ASSERT(config != nullptr, "Cannot access uninitialised atlas_Config"); Mesh m(mesh); return new Method(m, *config); } const functionspace::detail::NodeColumns* atlas__numerics__fvm__Method__functionspace_nodes(Method* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialisd atlas_Method"); return dynamic_cast(This->node_columns().get()); } const functionspace::detail::EdgeColumns* atlas__numerics__fvm__Method__functionspace_edges(Method* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialisd atlas_Method"); return dynamic_cast(This->edge_columns().get()); } } // ------------------------------------------------------------------------------------------ } // namespace fvm } // namespace numerics } // namespace atlas atlas-0.46.0/src/atlas/numerics/Nabla.h0000664000175000017500000000732715160212070020002 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include "atlas/util/Object.h" #include "atlas/util/ObjectHandle.h" namespace eckit { class Parametrisation; } namespace atlas { namespace numerics { class Method; } } // namespace atlas namespace atlas { namespace field { class FieldImpl; } namespace functionspace { class FunctionSpaceImpl; } } // namespace atlas namespace atlas { class Field; class FunctionSpace; } // namespace atlas namespace atlas { namespace numerics { #ifndef DOXYGEN_SHOULD_SKIP_THIS class NablaImpl : public util::Object { public: NablaImpl(const Method&, const eckit::Parametrisation&); virtual ~NablaImpl(); virtual void gradient(const Field& scalar, Field& grad) const = 0; virtual void divergence(const Field& vector, Field& div) const = 0; virtual void curl(const Field& vector, Field& curl) const = 0; virtual void laplacian(const Field& scalar, Field& laplacian) const = 0; virtual const FunctionSpace& functionspace() const = 0; private: util::ObjectHandle method_; }; #endif // ------------------------------------------------------------------ class Nabla : DOXYGEN_HIDE(public util::ObjectHandle) { public: using Handle::Handle; Nabla() = default; Nabla(const Method&); Nabla(const Method&, const eckit::Parametrisation&); void gradient(const Field& scalar, Field& grad) const; void divergence(const Field& vector, Field& div) const; void curl(const Field& vector, Field& curl) const; void laplacian(const Field& scalar, Field& laplacian) const; }; // ------------------------------------------------------------------ #ifndef DOXYGEN_SHOULD_SKIP_THIS class NablaFactory { public: static const Nabla::Implementation* build(const Method&, const eckit::Parametrisation&); static void list(std::ostream&); static bool has(const std::string& name); private: virtual const Nabla::Implementation* make(const Method&, const eckit::Parametrisation&) = 0; protected: NablaFactory(const std::string&); virtual ~NablaFactory(); private: std::string name_; }; // ------------------------------------------------------------------ template class NablaBuilder : public NablaFactory { public: NablaBuilder(const std::string& name): NablaFactory(name) {} private: virtual const Nabla::Implementation* make(const Method& method, const eckit::Parametrisation& p) { return new T(method, p); } }; // ------------------------------------------------------------------ extern "C" { void atlas__Nabla__delete(Nabla::Implementation* This); const Nabla::Implementation* atlas__Nabla__create(const Method* method, const eckit::Parametrisation* params); void atlas__Nabla__gradient(const Nabla::Implementation* This, const field::FieldImpl* scalar, field::FieldImpl* grad); void atlas__Nabla__divergence(const Nabla::Implementation* This, const field::FieldImpl* vector, field::FieldImpl* div); void atlas__Nabla__curl(const Nabla::Implementation* This, const field::FieldImpl* vector, field::FieldImpl* curl); void atlas__Nabla__laplacian(const Nabla::Implementation* This, const field::FieldImpl* scalar, field::FieldImpl* laplacian); const functionspace::FunctionSpaceImpl* atlas__Nabla__functionspace(const Nabla::Implementation* This); } #endif } // namespace numerics } // namespace atlas atlas-0.46.0/src/atlas/numerics/Nabla.cc0000664000175000017500000001466215160212070020140 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ // file deepcode ignore CppMemoryLeak: static pointers for global registry are OK and will be cleaned up at end #include #include #include "eckit/thread/AutoLock.h" #include "eckit/thread/Mutex.h" #include "atlas/library/config.h" #include "atlas/numerics/Method.h" #include "atlas/numerics/Nabla.h" #include "atlas/numerics/fvm/Method.h" #include "atlas/numerics/fvm/Nabla.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Log.h" #include "atlas/util/Config.h" namespace { static eckit::Mutex* local_mutex = nullptr; static std::map* m = nullptr; static pthread_once_t once = PTHREAD_ONCE_INIT; static void init() { local_mutex = new eckit::Mutex(); m = new std::map(); } } // namespace namespace atlas { namespace numerics { NablaImpl::NablaImpl(const Method& method, const eckit::Parametrisation&): method_(&method) {} NablaImpl::~NablaImpl() = default; Nabla::Nabla(const Method& method, const eckit::Parametrisation& p): Handle(NablaFactory::build(method, p)) {} Nabla::Nabla(const Method& method): Nabla(method, util::NoConfig()) {} void Nabla::gradient(const Field& scalar, Field& grad) const { get()->gradient(scalar, grad); } void Nabla::divergence(const Field& vector, Field& div) const { get()->divergence(vector, div); } void Nabla::curl(const Field& vector, Field& curl) const { get()->curl(vector, curl); } void Nabla::laplacian(const Field& scalar, Field& laplacian) const { get()->laplacian(scalar, laplacian); } namespace { template void load_builder() { NablaBuilder("tmp"); } struct force_link { force_link() { load_builder(); } }; } // namespace NablaFactory::NablaFactory(const std::string& name): name_(name) { pthread_once(&once, init); eckit::AutoLock lock(local_mutex); ATLAS_ASSERT(m->find(name) == m->end()); (*m)[name] = this; } NablaFactory::~NablaFactory() { eckit::AutoLock lock(local_mutex); m->erase(name_); } void NablaFactory::list(std::ostream& out) { pthread_once(&once, init); eckit::AutoLock lock(local_mutex); static force_link static_linking; const char* sep = ""; for (std::map::const_iterator j = m->begin(); j != m->end(); ++j) { out << sep << (*j).first; sep = ", "; } } bool NablaFactory::has(const std::string& name) { pthread_once(&once, init); eckit::AutoLock lock(local_mutex); static force_link static_linking; return (m->find(name) != m->end()); } const NablaImpl* NablaFactory::build(const Method& method, const eckit::Parametrisation& p) { pthread_once(&once, init); eckit::AutoLock lock(local_mutex); static force_link static_linking; std::map::const_iterator j = m->find(method.name()); Log::debug() << "Looking for NablaFactory [" << method.name() << "]" << '\n'; if (j == m->end()) { Log::error() << "No NablaFactory for [" << method.name() << "]" << '\n'; Log::error() << "NablaFactories are:" << '\n'; for (j = m->begin(); j != m->end(); ++j) { Log::error() << " " << (*j).first << '\n'; } throw_Exception(std::string("No NablaFactory called ") + method.name()); } return (*j).second->make(method, p); } extern "C" { void atlas__Nabla__delete(Nabla::Implementation* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialisd atlas_numerics_Nabla"); delete This; } const Nabla::Implementation* atlas__Nabla__create(const Method* method, const eckit::Parametrisation* config) { ATLAS_ASSERT(method != nullptr, "Cannot access uninitialisd atlas_numerics_Method"); ATLAS_ASSERT(config != nullptr, "Cannot access uninitialisd atlas_Config"); const Nabla::Implementation* nabla(nullptr); { Nabla n(*method, *config); nabla = n.get(); nabla->attach(); } nabla->detach(); return nabla; } void atlas__Nabla__gradient(const Nabla::Implementation* This, const field::FieldImpl* scalar, field::FieldImpl* grad) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialisd atlas_numerics_Nabla"); ATLAS_ASSERT(scalar != nullptr, "Cannot access uninitialisd atlas_Field"); ATLAS_ASSERT(grad != nullptr, "Cannot access uninitialisd atlas_Field"); Field fgrad(grad); This->gradient(scalar, fgrad); } void atlas__Nabla__divergence(const Nabla::Implementation* This, const field::FieldImpl* vector, field::FieldImpl* div) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialisd atlas_numerics_Nabla"); ATLAS_ASSERT(vector != nullptr, "Cannot access uninitialisd atlas_Field"); ATLAS_ASSERT(div != nullptr, "Cannot access uninitialisd atlas_Field"); Field fdiv(div); This->divergence(vector, fdiv); } void atlas__Nabla__curl(const Nabla::Implementation* This, const field::FieldImpl* vector, field::FieldImpl* curl) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialisd atlas_numerics_Nabla"); ATLAS_ASSERT(vector != nullptr, "Cannot access uninitialisd atlas_Field"); ATLAS_ASSERT(curl != nullptr, "Cannot access uninitialisd atlas_Field"); Field fcurl(curl); This->curl(vector, fcurl); } void atlas__Nabla__laplacian(const Nabla::Implementation* This, const field::FieldImpl* scalar, field::FieldImpl* laplacian) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialisd atlas_numerics_Nabla"); ATLAS_ASSERT(scalar != nullptr, "Cannot access uninitialisd atlas_Field"); ATLAS_ASSERT(laplacian != nullptr, "Cannot access uninitialisd atlas_Field"); Field flaplacian(laplacian); This->laplacian(scalar, flaplacian); } const functionspace::FunctionSpaceImpl* atlas__Nabla__functionspace(const Nabla::Implementation* This) { return This->functionspace().get(); } } // extern "C" } // namespace numerics } // namespace atlas atlas-0.46.0/src/atlas/numerics/Method.cc0000664000175000017500000000205715160212070020336 0ustar alastairalastair/* * (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/numerics/Method.h" #include "atlas/library/config.h" #include "atlas/runtime/Exception.h" namespace atlas { namespace numerics { //---------------------------------------------------------------------------------------------------------------------- // C wrapper interfaces to C++ routines extern "C" { void atlas__Method__delete(Method* This) { ATLAS_ASSERT(This != nullptr); delete This; This = nullptr; } const char* atlas__Method__name(Method* This) { ATLAS_ASSERT(This != nullptr); return This->name().c_str(); } } // ------------------------------------------------------------------ } // namespace numerics } // namespace atlas atlas-0.46.0/src/atlas/mesh.h0000664000175000017500000000130115160212070016056 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ /// @author Willem Deconinck #pragma once #include "atlas/mesh/Connectivity.h" #include "atlas/mesh/ElementType.h" #include "atlas/mesh/Elements.h" #include "atlas/mesh/Halo.h" #include "atlas/mesh/HybridElements.h" #include "atlas/mesh/IsGhostNode.h" #include "atlas/mesh/Mesh.h" #include "atlas/mesh/Nodes.h" atlas-0.46.0/src/atlas/grid.h0000664000175000017500000000133015160212070016051 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ /// @author Willem Deconinck #pragma once #include "atlas/grid/CubedSphereGrid.h" #include "atlas/grid/Distribution.h" #include "atlas/grid/Grid.h" #include "atlas/grid/Iterator.h" #include "atlas/grid/Partitioner.h" #include "atlas/grid/SpecRegistry.h" #include "atlas/grid/StructuredGrid.h" #include "atlas/grid/UnstructuredGrid.h" atlas-0.46.0/src/atlas/projection.h0000664000175000017500000000073515160212070017310 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ /// @author Willem Deconinck #pragma once #include "atlas/projection/Projection.h" atlas-0.46.0/src/atlas/meshgenerator/0000775000175000017500000000000015160212070017621 5ustar alastairalastairatlas-0.46.0/src/atlas/meshgenerator/MeshGenerator.cc0000664000175000017500000000441115160212070022673 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include #include "atlas/grid/Distribution.h" #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" using atlas::Mesh; namespace atlas { //---------------------------------------------------------------------------------------------------------------------- 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 { get()->hash(h); } Mesh MeshGenerator::generate(const Grid& g, const grid::Distribution& d) const { return get()->generate(g, d); } Mesh MeshGenerator::generate(const Grid& g, const grid::Partitioner& p) const { return get()->generate(g, p); } Mesh MeshGenerator::generate(const Grid& g) const { return get()->generate(g); } Mesh MeshGenerator::operator()(const Grid& g, const grid::Distribution& d) const { return get()->operator()(g, d); } Mesh MeshGenerator::operator()(const Grid& g, const grid::Partitioner& p) const { return get()->operator()(g, p); } Mesh MeshGenerator::operator()(const Grid& g) const { return get()->operator()(g); } std::string MeshGenerator::type() const { return get()->type(); } //---------------------------------------------------------------------------------------------------------------------- } // namespace atlas atlas-0.46.0/src/atlas/meshgenerator/detail/0000775000175000017500000000000015160212070021063 5ustar alastairalastairatlas-0.46.0/src/atlas/meshgenerator/detail/RegularMeshGenerator.cc0000664000175000017500000005501115160212070025461 0ustar alastairalastair/* * (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 "eckit/utils/Hash.h" #include "atlas/array/Array.h" #include "atlas/array/ArrayView.h" #include "atlas/array/IndexView.h" #include "atlas/field/Field.h" #include "atlas/grid/Distribution.h" #include "atlas/grid/Partitioner.h" #include "atlas/grid/StructuredGrid.h" #include "atlas/library/config.h" #include "atlas/mesh/ElementType.h" #include "atlas/mesh/Elements.h" #include "atlas/mesh/HybridElements.h" #include "atlas/mesh/Mesh.h" #include "atlas/mesh/Nodes.h" #include "atlas/meshgenerator/detail/MeshGeneratorFactory.h" #include "atlas/meshgenerator/detail/RegularMeshGenerator.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Log.h" #include "atlas/util/CoordinateEnums.h" #define DEBUG_OUTPUT 0 #define DEBUG_OUTPUT_DETAIL 0 using Topology = atlas::mesh::Nodes::Topology; namespace atlas { namespace meshgenerator { RegularMeshGenerator::RegularMeshGenerator(const eckit::Parametrisation& p) { std::string mpi_comm = mpi::comm().name(); p.get("mpi_comm", mpi_comm); options.set("mpi_comm",mpi_comm); configure_defaults(); // options copied from Structured MeshGenerator size_t nb_parts; if (p.get("nb_parts", nb_parts)) { options.set("nb_parts", nb_parts); } size_t part; if (p.get("part", part)) { options.set("part", part); } std::string partitioner; if (p.get("partitioner", partitioner)) { if (not grid::Partitioner::exists(partitioner)) { Log::warning() << "Atlas does not have support for partitioner " << partitioner << ". " << "Defaulting to use partitioner EqualRegions" << std::endl; partitioner = "equal_regions"; } options.set("partitioner", partitioner); } // options specifically for this MeshGenerator bool periodic_x; if (p.get("periodic_x", periodic_x)) { options.set("periodic_x", periodic_x); } bool periodic_y; if (p.get("periodic_y", periodic_y)) { options.set("periodic_y", periodic_y); } bool biperiodic; if (p.get("biperiodic", biperiodic)) { options.set("periodic_x", biperiodic); options.set("periodic_y", biperiodic); } } void RegularMeshGenerator::configure_defaults() { std::string mpi_comm; options.get("mpi_comm",mpi_comm); auto& comm = mpi::comm(mpi_comm); // This option sets number of parts the mesh will be split in options.set("nb_parts", comm.size()); // This option sets the part that will be generated options.set("part", comm.rank()); // This options sets the default partitioner std::string partitioner; if (grid::Partitioner::exists("ectrans") && comm.size() > 1) { partitioner = "ectrans"; } else { partitioner = "checkerboard"; } options.set("partitioner", partitioner); // Options for for periodic grids options.set("periodic_x", false); options.set("periodic_y", false); } void RegularMeshGenerator::generate(const Grid& grid, Mesh& mesh) const { ATLAS_ASSERT(!mesh.generated()); const RegularGrid rg = RegularGrid(grid); if (!rg) { throw_Exception("RegularMeshGenerator can only work with a Regular grid", Here()); } size_t nb_parts = options.get("nb_parts"); std::string partitioner_type = "checkerboard"; options.get("checkerboard", partitioner_type); // if ( rg->nlat()%2 == 1 ) partitioner_factory = "equal_regions"; // Odd // number of latitudes // if ( nb_parts == 1 || eckit::mpi::size() == 1 ) partitioner_factory = // "equal_regions"; // Only one part --> Trans is slower mpi::scope::push(options.getString("mpi_comm")); grid::Partitioner partitioner(partitioner_type, nb_parts); grid::Distribution distribution(partitioner.partition(grid)); mpi::scope::pop(); generate(grid, distribution, mesh); } void RegularMeshGenerator::hash(eckit::Hash& h) const { h.add("RegularMeshGenerator"); options.hash(h); } void RegularMeshGenerator::generate(const Grid& grid, const grid::Distribution& distribution, Mesh& mesh) const { const auto rg = RegularGrid(grid); if (!rg) { throw_Exception("Grid could not be cast to a Regular", Here()); } ATLAS_ASSERT(!mesh.generated()); if (grid.size() != static_cast(distribution.size())) { std::stringstream msg; msg << "Number of points in grid (" << grid.size() << ") different from " "number of points in grid distribution (" << distribution.size() << ")"; throw_AssertionFailed(msg.str(), Here()); } // clone some grid properties setGrid(mesh, rg, distribution); generate_mesh(rg, distribution, mesh); } void RegularMeshGenerator::generate_mesh(const RegularGrid& rg, const grid::Distribution& distribution, // const Region& region, Mesh& mesh) const { mpi::Scope mpi_scope(options.getString("mpi_comm")); int mypart = options.get("part"); int nparts = options.get("nb_parts"); int nx = rg.nx(); int ny = rg.ny(); const int y_numbering = (rg.y().front() < rg.y().back()) ? +1 : -1; bool periodic_x = options.get("periodic_x") or rg.periodic(); bool periodic_y = options.get("periodic_y"); Log::debug() << Here() << " periodic_x = " << periodic_x << std::endl; Log::debug() << Here() << " periodic_y = " << periodic_y << std::endl; // for asynchronous output #if DEBUG_OUTPUT sleep(mypart); #endif // this function should do the following: // - define nodes with // mesh.nodes().resize(nnodes); // mesh::Nodes& nodes = mesh.nodes(); // following properties should be defined: // array::ArrayView xy ( nodes.xy() ); // array::ArrayView glb_idx ( nodes.global_index() ); // array::ArrayView part ( nodes.partition() ); // array::ArrayView ghost ( nodes.ghost() ); // array::ArrayView flags ( nodes.flags() ); // - define cells (only quadrilaterals for now) with // mesh.cells().add(mesh::ElementType::create("Quadrilateral"), nquads ); // further define cells with // array::ArrayView cells_glb_idx( mesh.cells().global_index() // ); // array::ArrayView cells_part( mesh.cells().partition() ); // - define connectivity with // mesh::HybridElements::Connectivity& node_connectivity = // mesh.cells().node_connectivity(); // node_connectivity.set( jcell, quad_nodes ); // where quad_nodes is a 4-element integer array containing the LOCAL // indices of the nodes // Start with calculating number of quadrilaterals // The rule do determine if a cell belongs to a proc is the following: if the // lowerleft corner of the cell belongs to that proc. // so we loop over all gridpoints, select those that belong to the proc, and // determine the number of cells int ii_glb; // global index int ncells; // vector of local indices: necessary for remote indices of ghost nodes std::vector local_idx(rg.size(), -1); std::vector current_idx(nparts, 0); // index counter for each proc // determine rectangle (ix_min:ix_max) x (iy_min:iy_max) surrounding the nodes // on this processor int ix_min, ix_max, iy_min, iy_max, ix_glb, iy_glb, ix, iy; int nnodes_nonghost, nnodes; // number of nodes: non-ghost; total; inside // surrounding rectangle int nnodes_SR, ii; // loop over all points to determine local indices and surroundig rectangle ix_min = nx + 1; ix_max = 0; iy_min = ny + 1; iy_max = 0; nnodes_nonghost = 0; ii_glb = 0; for (iy = 0; iy < ny; iy++) { for (ix = 0; ix < nx; ix++) { local_idx[ii_glb] = current_idx[distribution.partition(ii_glb)]++; // store local index on // the local proc of // this point if (distribution.partition(ii_glb) == mypart) { ++nnodes_nonghost; // non-ghost node: belongs to this part ix_min = std::min(ix_min, ix); ix_max = std::max(ix_max, ix); iy_min = std::min(iy_min, iy); iy_max = std::max(iy_max, iy); } ++ii_glb; // global index } } // add one row/column for ghost nodes (which include periodicity points) ix_max = ix_max + 1; iy_max = iy_max + 1; #if DEBUG_OUTPUT_DETAIL std::cout << "[" << mypart << "] : " << "SR = " << ix_min << ":" << ix_max << " x " << iy_min << ":" << iy_max << std::endl; #endif // dimensions of surrounding rectangle (SR) int nxl = ix_max - ix_min + 1; int nyl = iy_max - iy_min + 1; // upper estimate for number of nodes nnodes_SR = nxl * nyl; // partitions and local indices in SR std::vector parts_SR(nnodes_SR, -1); std::vector local_idx_SR(nnodes_SR, -1); std::vector is_ghost_SR(nnodes_SR, true); ii = 0; // index inside SR for (iy = 0; iy < nyl; iy++) { iy_glb = (iy_min + iy); // global y-index for (ix = 0; ix < nxl; ix++) { ix_glb = (ix_min + ix); // global x-index is_ghost_SR[ii] = !((parts_SR[ii] == mypart) && ix < nxl - 1 && iy < nyl - 1); if (ix_glb < nx && iy_glb < ny) { ii_glb = (iy_glb)*nx + ix_glb; // global index parts_SR[ii] = distribution.partition(ii_glb); local_idx_SR[ii] = local_idx[ii_glb]; is_ghost_SR[ii] = !((parts_SR[ii] == mypart) && ix < nxl - 1 && iy < nyl - 1); } else if (ix_glb == nx && iy_glb < ny) { // take properties from the point to the left parts_SR[ii] = distribution.partition(iy_glb * nx + ix_glb - 1); local_idx_SR[ii] = -1; is_ghost_SR[ii] = true; } else if (iy_glb == ny && ix_glb < nx) { // take properties from the point below parts_SR[ii] = distribution.partition((iy_glb - 1) * nx + ix_glb); local_idx_SR[ii] = -1; is_ghost_SR[ii] = true; } else { // take properties from the point belowleft parts_SR[ii] = distribution.partition((iy_glb - 1) * nx + ix_glb - 1); local_idx_SR[ii] = -1; is_ghost_SR[ii] = true; } ++ii; } } #if DEBUG_OUTPUT_DETAIL std::cout << "[" << mypart << "] : " << "parts_SR = "; for (ii = 0; ii < nnodes_SR; ii++) std::cout << parts_SR[ii] << ","; std::cout << std::endl; std::cout << "[" << mypart << "] : " << "local_idx_SR = "; for (ii = 0; ii < nnodes_SR; ii++) std::cout << local_idx_SR[ii] << ","; std::cout << std::endl; std::cout << "[" << mypart << "] : " << "is_ghost_SR = "; for (ii = 0; ii < nnodes_SR; ii++) std::cout << is_ghost_SR[ii] << ","; std::cout << std::endl; #endif // vectors marking nodes that are necessary for this proc's cells std::vector is_node_SR(nnodes_SR, false); // determine number of cells and number of nodes nnodes = 0; ncells = 0; for (iy = 0; iy < nyl - 1; iy++) { // don't loop into ghost/periodicity row for (ix = 0; ix < nxl - 1; ix++) { // don't loop into ghost/periodicity column ii = iy * nxl + ix; if (!is_ghost_SR[ii]) { // mark this node as being used if (!is_node_SR[ii]) { ++nnodes; is_node_SR[ii] = true; } // check if this node is the lowerleft corner of a new cell if ((ix_min + ix < nx - 1 || periodic_x) && (iy_min + iy < ny - 1 || periodic_y)) { ++ncells; // mark lowerright corner ii = iy * nxl + ix + 1; if (!is_node_SR[ii]) { ++nnodes; is_node_SR[ii] = true; } // mark upperleft corner ii = (iy + 1) * nxl + ix; if (!is_node_SR[ii]) { ++nnodes; is_node_SR[ii] = true; } // mark upperright corner ii = (iy + 1) * nxl + ix + 1; if (!is_node_SR[ii]) { ++nnodes; is_node_SR[ii] = true; } } // periodic points are always needed, even if they don't belong to a // cell ii = iy * nxl + ix + 1; if (periodic_x && ix_min + ix == nx - 1 && !is_node_SR[ii]) { ++nnodes; is_node_SR[ii] = true; } ii = (iy + 1) * nxl + ix; if (periodic_y && iy_min + iy == ny - 1 && !is_node_SR[ii]) { ++nnodes; is_node_SR[ii] = true; } ii = (iy + 1) * nxl + ix + 1; if (periodic_x && periodic_y && ix_min + ix == nx - 1 && iy_min + iy == ny - 1 && !is_node_SR[ii]) { ++nnodes; is_node_SR[ii] = true; } } } } #if DEBUG_OUTPUT_DETAIL std::cout << "[" << mypart << "] : " << "nnodes = " << nnodes << std::endl; std::cout << "[" << mypart << "] : " << "is_node_SR = "; for (ii = 0; ii < nnodes_SR; ii++) std::cout << is_node_SR[ii] << ","; std::cout << std::endl; #endif // define nodes and associated properties mesh.nodes().resize(nnodes); mesh::Nodes& nodes = mesh.nodes(); auto xy = array::make_view(nodes.xy()); auto lonlat = array::make_view(nodes.lonlat()); auto glb_idx = array::make_view(nodes.global_index()); auto remote_idx = array::make_indexview(nodes.remote_index()); auto part = array::make_view(nodes.partition()); auto ghost = array::make_view(nodes.ghost()); auto flags = array::make_view(nodes.flags()); // define cells and associated properties mesh.cells().add(mesh::ElementType::create("Quadrilateral"), ncells); int quad_begin = mesh.cells().elements(0).begin(); auto cells_part = array::make_view(mesh.cells().partition()); mesh::HybridElements::Connectivity& node_connectivity = mesh.cells().node_connectivity(); idx_t quad_nodes[4]; int jcell = quad_begin; int inode, inode_nonghost, inode_ghost; // global indices for periodicity points inode = nx * ny; std::vector glb_idx_px(ny + 1, -1); std::vector glb_idx_py(nx + 1, -1); if (periodic_x) { for (iy = 0; iy < ny + (periodic_y ? 1 : 0); iy++) { glb_idx_px[iy] = inode++; } } if (periodic_y) { for (ix = 0; ix < nx; ix++) { glb_idx_py[ix] = inode++; } } // loop over nodes and set properties ii = 0; inode_nonghost = 0; inode_ghost = nnodes_nonghost; // ghost nodes start counting after nonghost nodes for (iy = 0; iy < nyl; iy++) { for (ix = 0; ix < nxl; ix++) { // node properties if (is_node_SR[ii]) { // set node counter if (is_ghost_SR[ii]) { inode = inode_ghost++; } else { inode = inode_nonghost++; } // global index ix_glb = (ix_min + ix); // don't take modulus here: periodicity points // have their own global index. iy_glb = (iy_min + iy); if (ix_glb < nx && iy_glb < ny) { ii_glb = iy_glb * nx + ix_glb; // no periodic point } else { if (ix_glb == nx) { // periodicity point in x-direction ii_glb = glb_idx_px[iy_glb]; } else { // periodicity point in x-direction ii_glb = glb_idx_py[ix_glb]; } } glb_idx(inode) = ii_glb + 1; // starting from 1 // grid coordinates double _xy[2]; if (iy_glb < ny) { // normal calculation rg.xy(ix_glb, iy_glb, _xy); } else { // for periodic_y grids, iy_glb==ny lies outside the range of // latitudes in the Structured grid... // so we extrapolate from two other points -- this is okay for regular // grids with uniform spacing. double xy1[2], xy2[2]; rg.xy(ix_glb, iy_glb - 1, xy1); rg.xy(ix_glb, iy_glb - 2, xy2); _xy[0] = 2 * xy1[0] - xy2[0]; _xy[1] = 2 * xy1[1] - xy2[1]; } xy(inode, LON) = _xy[LON]; xy(inode, LAT) = _xy[LAT]; // geographic coordinates by using projection rg.projection().xy2lonlat(_xy); lonlat(inode, LON) = _xy[LON]; lonlat(inode, LAT) = _xy[LAT]; // part part(inode) = parts_SR[ii]; // ghost nodes ghost(inode) = is_ghost_SR[ii]; // flags Topology::reset(flags(inode)); if (ghost(inode)) { Topology::set(flags(inode), Topology::GHOST); remote_idx(inode) = local_idx_SR[ii]; // change local index -- required for cells local_idx_SR[ii] = inode; } else { remote_idx(inode) = -1; } #if DEBUG_OUTPUT_DETAIL std::cout << "[" << mypart << "] : " << "New node " << "\n\t"; std::cout << "[" << mypart << "] : " << "\tinode=" << inode << "; ix_glb=" << ix_glb << "; iy_glb=" << iy_glb << "; glb_idx=" << ii_glb << std::endl; std::cout << "[" << mypart << "] : " << "\tglon=" << lonlat(inode, 0) << "; glat=" << lonlat(inode, 1) << "; glb_idx=" << glb_idx(inode) << std::endl; #endif } ++ii; } } // loop over nodes and define cells for (iy = 0; iy < nyl - 1; iy++) { // don't loop into ghost/periodicity row for (ix = 0; ix < nxl - 1; ix++) { // don't loop into ghost/periodicity column ii = iy * nxl + ix; if (!is_ghost_SR[ii]) { if ((ix_min + ix < nx - 1 || periodic_x) && (iy_min + iy < ny - 1 || periodic_y)) { // define cell corners (local indices) if (y_numbering < 0) { quad_nodes[0] = local_idx_SR[ii]; quad_nodes[3] = local_idx_SR[iy * nxl + ix + 1]; // point to the right quad_nodes[2] = local_idx_SR[(iy + 1) * nxl + ix + 1]; // point above right quad_nodes[1] = local_idx_SR[(iy + 1) * nxl + ix]; // point above } else { quad_nodes[0] = local_idx_SR[ii]; quad_nodes[1] = local_idx_SR[iy * nxl + ix + 1]; // point to the right quad_nodes[2] = local_idx_SR[(iy + 1) * nxl + ix + 1]; // point above right quad_nodes[3] = local_idx_SR[(iy + 1) * nxl + ix]; // point above } node_connectivity.set(jcell, quad_nodes); cells_part(jcell) = mypart; #if DEBUG_OUTPUT_DETAIL std::cout << "[" << mypart << "] : " << "New quad " << jcell << "\n\t"; std::cout << "[" << mypart << "] : " << quad_nodes[0] << "," << quad_nodes[1] << "," << quad_nodes[2] << "," << quad_nodes[3] << std::endl; #endif ++jcell; } } } } #if DEBUG_OUTPUT // list nodes for (inode = 0; inode < nnodes; inode++) { std::cout << "[" << mypart << "] : " << " node " << inode << ": ghost = " << ghost(inode) << ", glb_idx = " << glb_idx(inode) - 1 << ", part = " << part(inode) << ", lon = " << lonlat(inode, 0) << ", lat = " << lonlat(inode, 1) << ", remote_idx = " << remote_idx(inode) << std::endl; } int* cell_nodes; for (jcell = 0; jcell < ncells; jcell++) { std::cout << "[" << mypart << "] : " << " cell " << jcell << ": " << node_connectivity(jcell, 0) << "," << node_connectivity(jcell, 1) << "," << node_connectivity(jcell, 2) << "," << node_connectivity(jcell, 3) << std::endl; } #endif mesh.metadata().set("nb_parts",options.getInt("nb_parts")); mesh.metadata().set("part",options.getInt("part")); mesh.metadata().set("mpi_comm",options.getString("mpi_comm")); generateGlobalElementNumbering(mesh); nodes.metadata().set("parallel", true); } namespace { static MeshGeneratorBuilder __RegularMeshGenerator("regular"); } } // namespace meshgenerator } // namespace atlas atlas-0.46.0/src/atlas/meshgenerator/detail/StructuredMeshGenerator.h0000664000175000017500000000430115160212070026062 0ustar alastairalastair/* * (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/meshgenerator/MeshGenerator.h" #include "atlas/meshgenerator/detail/MeshGeneratorImpl.h" #include "atlas/util/Config.h" #include "atlas/util/Metadata.h" #ifndef DOXYGEN_SHOULD_SKIP_THIS namespace eckit { class Parametrisation; } namespace atlas { template class vector; class Mesh; } // namespace atlas namespace atlas { class StructuredGrid; namespace grid { class Distribution; } // namespace grid } // namespace atlas #endif namespace atlas { namespace meshgenerator { struct Region; //---------------------------------------------------------------------------------------------------------------------- class StructuredMeshGenerator : public MeshGenerator::Implementation { public: StructuredMeshGenerator(const eckit::Parametrisation& = util::NoConfig()); virtual void generate(const Grid&, const grid::Distribution&, Mesh&) const override; virtual void generate(const Grid&, Mesh&) const override; using MeshGenerator::Implementation::generate; std::string type() const override { return "structured"; } private: virtual void hash(eckit::Hash&) const override; void configure_defaults(); void generate_region(const StructuredGrid&, const grid::Distribution& distribution, int mypart, Region& region) const; void generate_mesh_new(const StructuredGrid&, const grid::Distribution& distribution, const Region& region, Mesh& m) const; void generate_mesh(const StructuredGrid&, const grid::Distribution& distribution, const Region& region, Mesh& m) const; private: util::Config options; }; //---------------------------------------------------------------------------------------------------------------------- } // namespace meshgenerator } // namespace atlas atlas-0.46.0/src/atlas/meshgenerator/detail/MeshGeneratorImpl.h0000664000175000017500000000431715160212070024626 0ustar alastairalastair/* * (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/util/Object.h" namespace eckit { class Hash; class Parametrisation; } // namespace eckit namespace atlas { class Mesh; class Grid; class Projection; } // namespace atlas namespace atlas { namespace grid { class Distribution; class Partitioner; } // namespace grid } // namespace atlas namespace atlas { namespace meshgenerator { //---------------------------------------------------------------------------------------------------------------------- class MeshGeneratorImpl : public util::Object { public: MeshGeneratorImpl(); virtual ~MeshGeneratorImpl(); virtual void hash(eckit::Hash&) const = 0; virtual void generate(const Grid&, const grid::Partitioner&, Mesh&) const; virtual void generate(const Grid&, const grid::Distribution&, Mesh&) const = 0; virtual void generate(const Grid&, Mesh&) const = 0; Mesh generate(const Grid&, const grid::Partitioner&) const; Mesh generate(const Grid&, const grid::Distribution&) const; Mesh generate(const Grid&) const; Mesh operator()(const Grid&, const grid::Distribution&) const; Mesh operator()(const Grid&, const grid::Partitioner&) const; Mesh operator()(const Grid&) const; virtual std::string type() const = 0; protected: void generateGlobalElementNumbering(Mesh& mesh) const; void setProjection(Mesh&, const Projection&) const; void setGrid(Mesh&, const Grid&, const grid::Distribution&) const; void setGrid(Mesh&, const Grid&, const std::string& distribution) const; }; //---------------------------------------------------------------------------------------------------------------------- } // namespace meshgenerator //---------------------------------------------------------------------------------------------------------------------- } // namespace atlas atlas-0.46.0/src/atlas/meshgenerator/detail/DelaunayMeshGenerator.cc0000664000175000017500000003606715160212070025634 0ustar alastairalastair/* * (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 "eckit/utils/Hash.h" #include "atlas/array/ArrayView.h" #include "atlas/array/MakeView.h" #include "atlas/field/Field.h" #include "atlas/grid/Distribution.h" #include "atlas/grid/Grid.h" #include "atlas/grid/Iterator.h" #include "atlas/mesh/ElementType.h" #include "atlas/mesh/HybridElements.h" #include "atlas/mesh/Mesh.h" #include "atlas/mesh/Nodes.h" #include "atlas/mesh/actions/BuildConvexHull3D.h" #include "atlas/mesh/actions/BuildXYZField.h" #include "atlas/mesh/actions/ExtendNodesGlobal.h" #include "atlas/meshgenerator/detail/DelaunayMeshGenerator.h" #include "atlas/meshgenerator/detail/MeshGeneratorFactory.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/projection/Projection.h" #include "atlas/runtime/Log.h" #include "atlas/util/Config.h" #include "atlas/util/CoordinateEnums.h" using atlas::Mesh; namespace atlas { namespace meshgenerator { //---------------------------------------------------------------------------------------------------------------------- DelaunayMeshGenerator::DelaunayMeshGenerator(const eckit::Parametrisation& p) { p.get("mpi_comm",mpi_comm_=mpi::comm().name()); p.get("part",part_=mpi::comm(mpi_comm_).rank()); p.get("reshuffle",reshuffle_=true); p.get("remove_duplicate_points",remove_duplicate_points_=true); p.get("extension_grid", extension_grid_); } DelaunayMeshGenerator::~DelaunayMeshGenerator() = default; void DelaunayMeshGenerator::hash(eckit::Hash& h) const { h.add("Delaunay"); // no other settings } void DelaunayMeshGenerator::generate(const Grid& grid, const grid::Distribution& dist, Mesh& mesh) const { mpi::Scope mpi_scope(mpi_comm_); auto build_global_mesh = [&](Mesh& mesh) { idx_t nb_nodes = grid.size(); mesh.nodes().resize(nb_nodes); auto xy = array::make_view(mesh.nodes().xy()); auto lonlat = array::make_view(mesh.nodes().lonlat()); auto ghost = array::make_view(mesh.nodes().ghost()); auto gidx = array::make_view(mesh.nodes().global_index()); auto part = array::make_view(mesh.nodes().partition()); auto flags = array::make_view(mesh.nodes().flags()); size_t jnode{0}; Projection projection = grid.projection(); PointLonLat Pll; for (PointXY Pxy : grid.xy()) { xy(jnode, size_t(XX)) = Pxy.x(); xy(jnode, size_t(YY)) = Pxy.y(); Pll = projection.lonlat(Pxy); lonlat(jnode, size_t(LON)) = Pll.lon(); lonlat(jnode, size_t(LAT)) = Pll.lat(); part(jnode) = dist.partition(jnode); ghost(jnode) = part(jnode) != part_; gidx(jnode) = jnode + 1; if (ghost(jnode)) { util::Topology::view(flags(jnode)).set(util::Topology::GHOST); } ++jnode; } mesh::actions::BuildXYZField()(mesh); mesh::actions::ExtendNodesGlobal{extension_grid_}(grid, mesh); ///< does nothing if global domain mesh::actions::BuildConvexHull3D()(mesh); auto cells_gidx = array::make_view(mesh.cells().global_index()); for (idx_t jelem=0; jelem(global_mesh.nodes().xy()); auto g_lonlat = array::make_view(global_mesh.nodes().lonlat()); auto g_ghost = array::make_view(global_mesh.nodes().ghost()); auto g_gidx = array::make_view(global_mesh.nodes().global_index()); auto g_part = array::make_view(global_mesh.nodes().partition()); size_t owned_nodes_count = dist.nb_pts()[part_]; std::vector owned_nodes; owned_nodes.reserve(1.4*owned_nodes_count); for (size_t jnode=0; jnode < global_mesh.nodes().size(); ++ jnode) { if (g_ghost(jnode) == 0) { owned_nodes.emplace_back(jnode); } } auto& g_node_connectivity = global_mesh.cells().node_connectivity(); std::set ghost_nodes; std::vector owned_elements; owned_elements.reserve(1.4*owned_nodes_count); std::set element_nodes_uncertainty; std::set element_uncertainty; constexpr idx_t OWNED = -1; constexpr idx_t GHOST = -2; constexpr idx_t UNCERTAIN = -3; constexpr idx_t CERTAIN = -4; auto elem_node_partition = [&](idx_t jelem, idx_t jnode) -> int { return g_part(g_node_connectivity(jelem,jnode)); }; auto get_elem_ownership = [&](idx_t jelem) -> int { int p0 = elem_node_partition(jelem,0); int p1 = elem_node_partition(jelem,1); int p2 = elem_node_partition(jelem,2); if (p0 != part_ && p1 != part_ && p2 != part_) { return GHOST; } if ((p0 == p1 || p0 == p2) && p0 == part_) { return OWNED; } else if (p1 == p2 && p1 == part_) { return OWNED; } else if ( p0 == p1 || p0 == p2 || p1 == p2 ) { return CERTAIN; } return UNCERTAIN; }; auto get_elem_part = [&](idx_t jelem) -> int { int p0 = elem_node_partition(jelem,0); int p1 = elem_node_partition(jelem,1); int p2 = elem_node_partition(jelem,2); if (p0 == p1 || p0 == p2) { return p0; } else if (p1 == p2) { return p1; } return UNCERTAIN; }; auto collect_element = [&](idx_t jelem){ owned_elements.emplace_back(jelem); for (idx_t j=0; j<3; ++j) { if (elem_node_partition(jelem,j) != part_) { ghost_nodes.insert(g_node_connectivity(jelem,j)); } } }; for (idx_t jelem=0; jelem> node2element; for (idx_t jelem=0; jelemsecond.emplace_back(jelem); } } } } // Log::info() << "node2element" << std::endl; // for( auto& pair: node2element ) { // idx_t jnode = pair.first; // auto& elems = pair.second; // // Log::info() << jnode << " : " << elems << std::endl; // } auto get_elem_edge = [&](idx_t jelem, idx_t jedge) { if (jedge == 0) { return std::array{ g_node_connectivity(jelem,0), g_node_connectivity(jelem,1), }; } else if(jedge == 1) { return std::array{ g_node_connectivity(jelem,1), g_node_connectivity(jelem,2), }; } else if(jedge == 2) { return std::array{ g_node_connectivity(jelem,2), g_node_connectivity(jelem,0), }; } return std::array{-1,-1}; }; auto get_elem_neighbours = [&](idx_t jelem) -> std::array { std::array elem_neighbours{-1,-1,-1}; idx_t jneighbour=0; for (idx_t jedge=0; jedge<3; ++jedge) { auto edge = get_elem_edge(jelem,jedge); // Log::info() << "jelem,jedge " << jelem << "," << jedge << " : " << edge << " p: " << g_part(edge[0]) << " " << g_part(edge[1]) << std::endl; auto& elem_candidates = node2element.at(edge[0]); for (auto& ielem : elem_candidates) { for (idx_t iedge=0; iedge<3; ++iedge) { auto candidate_edge = get_elem_edge(ielem,iedge); if ( edge[0] == candidate_edge[1] && edge[1] == candidate_edge[0] ) { elem_neighbours[jneighbour++] = ielem; goto next_neighbour; } } } next_neighbour:; } return elem_neighbours; }; for( idx_t jelem : element_uncertainty ) { auto elem_neighbours = get_elem_neighbours(jelem); idx_t e0 = elem_neighbours[0] >= 0 ? get_elem_part(elem_neighbours[0]) : UNCERTAIN; idx_t e1 = elem_neighbours[1] >= 0 ? get_elem_part(elem_neighbours[1]) : UNCERTAIN; idx_t e2 = elem_neighbours[2] >= 0 ? get_elem_part(elem_neighbours[2]) : UNCERTAIN; idx_t elem_part = UNCERTAIN; if (e0 == e1 || e0 == e2) { elem_part = e0; } else if (e1 == e2) { elem_part = e1; } else if (e0 != UNCERTAIN) { elem_part = e0; } else if (e1 != UNCERTAIN) { elem_part = e1; } else if (e2 != UNCERTAIN) { elem_part = e2; } if (elem_part == UNCERTAIN) { elem_part = elem_node_partition(jelem,0); } if (elem_part == part_) { collect_element(jelem); } } } size_t nb_nodes = owned_nodes.size() + ghost_nodes.size(); mesh.nodes().resize(nb_nodes); auto xy = array::make_view(mesh.nodes().xy()); auto lonlat = array::make_view(mesh.nodes().lonlat()); auto ghost = array::make_view(mesh.nodes().ghost()); auto gidx = array::make_view(mesh.nodes().global_index()); auto part = array::make_view(mesh.nodes().partition()); auto halo = array::make_view(mesh.nodes().halo()); auto flags = array::make_view(mesh.nodes().flags()); halo.assign(0.); std::unordered_map from_gnode; for (idx_t jnode=0; jnode(mesh.cells().global_index()); auto cell_part = array::make_view(mesh.cells().partition()); for (idx_t jelem=0; jelem triag_nodes { from_gnode[g_node_connectivity(gelem,0)], from_gnode[g_node_connectivity(gelem,1)], from_gnode[g_node_connectivity(gelem,2)] }; node_connectivity.set(jelem, triag_nodes.data()); cell_gidx(jelem) = gelem+1; cell_part(jelem) = part_; } }; extract_mesh_partition(global_mesh, mesh); setGrid(mesh, grid, dist.type()); mesh.metadata().set("mpi_comm",mpi_comm_); } void DelaunayMeshGenerator::generate(const Grid& g, Mesh& mesh) const { mpi::Scope mpi_scope(mpi_comm_); generate( g, grid::Distribution{g,g.partitioner()}, mesh); } namespace { static MeshGeneratorBuilder __delaunay("delaunay"); } //---------------------------------------------------------------------------------------------------------------------- } // namespace meshgenerator } // namespace atlas atlas-0.46.0/src/atlas/meshgenerator/detail/StructuredMeshGenerator.cc0000664000175000017500000017135215160212070026233 0ustar alastairalastair/* * (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 #include "eckit/log/ProgressTimer.h" #include "eckit/types/FloatCompare.h" #include "eckit/utils/Hash.h" #include "atlas/array.h" #include "atlas/array/ArrayView.h" #include "atlas/array/MakeView.h" #include "atlas/field/Field.h" #include "atlas/grid/Distribution.h" #include "atlas/grid/Partitioner.h" #include "atlas/grid/StructuredGrid.h" #include "atlas/library/config.h" #include "atlas/mesh/ElementType.h" #include "atlas/mesh/Elements.h" #include "atlas/mesh/HybridElements.h" #include "atlas/mesh/Mesh.h" #include "atlas/mesh/Nodes.h" #include "atlas/meshgenerator/detail/MeshGeneratorFactory.h" #include "atlas/meshgenerator/detail/StructuredMeshGenerator.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/runtime/Log.h" #include "atlas/runtime/Trace.h" #include "atlas/util/CoordinateEnums.h" #define DEBUG_OUTPUT 0 using namespace atlas::array; using atlas::Mesh; using Topology = atlas::mesh::Nodes::Topology; namespace atlas { namespace meshgenerator { namespace { static double to_rad = M_PI / 180.; static double to_deg = 180. * M_1_PI; } // namespace struct Region { int north {-1}; int south {-1}; std::unique_ptr elems; int ntriags {0}; int nquads {0}; int nnodes {0}; std::vector lat_begin; std::vector lat_end; std::vector nb_lat_elems; }; StructuredMeshGenerator::StructuredMeshGenerator(const eckit::Parametrisation& p) { configure_defaults(); bool include_pole; if (p.get("include_pole", include_pole)) { options.set("include_pole", include_pole); } bool patch_pole; if (p.get("patch_pole", patch_pole)) { options.set("patch_pole", patch_pole); } bool patch_quads; if (p.get("patch_quads", patch_quads)) { options.set("patch_quads", patch_quads); } bool unique_pole; if (p.get("unique_pole", unique_pole)) { options.set("unique_pole", unique_pole); } bool force_include_pole; if (p.get("force_include_north_pole", force_include_pole)) { options.set("force_include_north_pole", force_include_pole); } if (p.get("force_include_south_pole", force_include_pole)) { options.set("force_include_south_pole", force_include_pole); } bool three_dimensional; if (p.get("three_dimensional", three_dimensional) || p.get("3d", three_dimensional)) { options.set("three_dimensional", three_dimensional); } std::string mpi_comm_name = mpi::comm().name(); p.get("mpi_comm", mpi_comm_name); options.set("mpi_comm",mpi_comm_name); auto& comm = mpi::comm(mpi_comm_name); // This option sets number of parts the mesh will be split in options.set("nb_parts", comm.size()); // This option sets the part that will be generated options.set("part", comm.rank()); size_t nb_parts; if (p.get("nb_parts", nb_parts)) { options.set("nb_parts", nb_parts); } size_t part; if (p.get("part", part)) { options.set("part", part); } double angle; if (p.get("angle", angle)) { options.set("angle", angle); } bool triangulate; if (p.get("triangulate", triangulate)) { options.set("triangulate", triangulate); } bool ghost_at_end; if (p.get("ghost_at_end", ghost_at_end)) { options.set("ghost_at_end", ghost_at_end); } std::string partitioner; if (p.get("partitioner", partitioner)) { if (not grid::Partitioner::exists(partitioner)) { Log::warning() << "Atlas does not have support for partitioner " << partitioner << ". " << "Defaulting to use partitioner EqualRegions" << std::endl; partitioner = "equal_regions"; } } else { partitioner = "equal_regions"; } options.set("partitioner", partitioner); } void StructuredMeshGenerator::configure_defaults() { // This option creates a point at the pole when true options.set("include_pole", false); // This option sets the part that will be generated options.set("patch_pole", true); // This option disregards multiple poles in grid (e.g. lonlat up to poles) and // connects elements // to the first node only. Note this option will only be looked at in case // other option // "three_dimensional"==true options.set("unique_pole", true); // This option creates elements that connect east to west at greenwich // meridian // when true, instead of creating periodic ghost-points at east boundary when // false options.set("three_dimensional", false); // Experimental option. This is what makes tripolar grid patched with quads options.set("patch_quads", false); // This option sets the maximum angle deviation for a quadrilateral element // angle = 30 --> minimises number of triangles // angle = 0 --> maximises number of triangles options.set("angle", 0.); options.set("triangulate", false); options.set("ghost_at_end", true); } void StructuredMeshGenerator::generate(const Grid& grid, Mesh& mesh) const { ATLAS_TRACE(); ATLAS_ASSERT(!mesh.generated()); const StructuredGrid rg = StructuredGrid(grid); if (!rg) { throw_Exception("Structured can only work with a Structured grid", Here()); } idx_t nb_parts = options.getInt("nb_parts"); std::string partitioner_type = "equal_regions"; options.get("partitioner", partitioner_type); if (partitioner_type == "ectrans") { if (rg.ny() % 2 == 1) { partitioner_type = "equal_regions"; // Odd number of latitudes } if (nb_parts != mpi::size()) { partitioner_type = "equal_regions"; } } if (nb_parts == 1) { partitioner_type = "serial"; } mpi::scope::push(options.getString("mpi_comm")); grid::Partitioner partitioner(partitioner_type, nb_parts); grid::Distribution distribution(partitioner.partition(grid)); mpi::scope::pop(); generate(grid, distribution, mesh); } void StructuredMeshGenerator::hash(eckit::Hash& h) const { h.add("Structured"); options.hash(h); } void StructuredMeshGenerator::generate(const Grid& grid, const grid::Distribution& distribution, Mesh& mesh) const { ATLAS_TRACE("structuredmeshgenerator(grid,dist,mesh)"); Log::debug() << "StructuredMeshGenerator generating mesh from " << grid.name() << std::endl; const StructuredGrid rg = StructuredGrid(grid); if (!rg) { throw_Exception("Grid could not be cast to a Structured", Here()); } ATLAS_ASSERT(!mesh.generated()); if (grid.size() != idx_t(distribution.size())) { std::stringstream msg; msg << "Number of points in grid (" << grid.size() << ") different from " "number of points in grid distribution (" << distribution.size() << ")"; throw_AssertionFailed(msg.str(), Here()); } idx_t mypart = options.getInt("part"); // show distribution #if DEBUG_OUTPUT int inode = 0; Log::info() << "Partition : " << std::endl; for (size_t ilat = 0; ilat < rg.ny(); ilat++) { for (size_t ilon = 0; ilon < rg.nx(ilat); ilon++) { Log::info() << std::setw(3) << distribution.partition(inode); inode++; } Log::info() << std::endl; } #endif // clone some grid properties setGrid(mesh, rg, distribution); Region region; generate_region(rg, distribution, mypart, region); if (not rg.projection() && rg.domain().global()) { double max_dy = 0; for (size_t ilat = 0; ilat < rg.ny()-1; ilat++) { max_dy = std::max(max_dy, std::abs(rg.y(ilat)-rg.y(ilat+1))); } double cell_maximum_diagonal_on_unit_sphere = std::sqrt(max_dy*max_dy + max_dy*max_dy) * M_PI / 180.; mesh.metadata().set("cell_maximum_diagonal_on_unit_sphere",cell_maximum_diagonal_on_unit_sphere); } mesh.metadata().set("nb_parts",options.getInt("nb_parts")); mesh.metadata().set("part",options.getInt("part")); mesh.metadata().set("mpi_comm",options.getString("mpi_comm")); generate_mesh(rg, distribution, region, mesh); } void StructuredMeshGenerator::generate_region(const StructuredGrid& rg, const grid::Distribution& distribution, int mypart, Region& region) const { ATLAS_TRACE(); double max_angle = options.getDouble("angle"); bool triangulate_quads = options.getBool("triangulate"); bool three_dimensional = options.getBool("three_dimensional"); bool has_north_pole = eckit::types::is_approximately_equal(rg.y().front(), 90.); bool has_south_pole = eckit::types::is_approximately_equal(rg.y().back(), -90.); bool unique_pole = options.getBool("unique_pole") && three_dimensional && has_north_pole && has_south_pole; bool periodic_east_west = rg.periodic(); region.lat_begin.resize(rg.ny(), -1); region.lat_end.resize(rg.ny(), -1); region.nb_lat_elems.resize(rg.ny(), 0); idx_t lat_north = -1; idx_t lat_south = -1; if (distribution.nb_partitions() == 1) { lat_north = 0; lat_south = rg.ny() - 1; } else { ATLAS_TRACE_SCOPE("compute bounds") { // This part does not scale /* Find min and max latitudes used by this part. */ size_t num_threads = atlas_omp_get_max_threads(); if (num_threads == 1) { int n = 0; for (idx_t jlat = 0; jlat < rg.ny(); ++jlat) { for (idx_t jlon = 0; jlon < rg.nx(jlat); ++jlon) { if (distribution.partition(n) == mypart) { lat_north = jlat; goto end_north; } ++n; } } end_north:; n = rg.size() - 1; for (idx_t jlat = rg.ny() - 1; jlat >= 0; --jlat) { for (idx_t jlon = rg.nx(jlat) - 1; jlon >= 0; --jlon) { if (distribution.partition(n) == mypart) { lat_south = jlat; goto end_south; } --n; } } end_south:; } else { std::vector thread_reduce_lat_north(num_threads, std::numeric_limits::max()); std::vector thread_reduce_lat_south(num_threads, std::numeric_limits::min()); atlas_omp_parallel { const idx_t thread_num = atlas_omp_get_thread_num(); const idx_t jbegin = static_cast(size_t(thread_num) * size_t(rg.ny()) / size_t(num_threads)); const idx_t jend = static_cast(size_t(thread_num + 1) * size_t(rg.ny()) / size_t(num_threads)); auto& thread_lat_north = thread_reduce_lat_north[thread_num]; auto& thread_lat_south = thread_reduce_lat_south[thread_num]; for (idx_t jlat = jbegin; jlat < jend && thread_lat_north > jend; ++jlat) { int n = rg.index(0, jlat); for (idx_t jlon = 0; jlon < rg.nx(jlat); ++jlon, ++n) { if (distribution.partition(n) == mypart) { thread_lat_north = jlat; break; } } } for (idx_t jlat = jend - 1; jlat >= jbegin && thread_lat_south < jbegin; --jlat) { int n = rg.index(0, jlat); for (idx_t jlon = 0; jlon < rg.nx(jlat); ++jlon, ++n) { if (distribution.partition(n) == mypart) { thread_lat_south = jlat; break; } } } } lat_north = *std::min_element(thread_reduce_lat_north.begin(), thread_reduce_lat_north.end()); lat_south = *std::max_element(thread_reduce_lat_south.begin(), thread_reduce_lat_south.end()); if (lat_north == std::numeric_limits::max()) { lat_north = -1; } if (lat_south == std::numeric_limits::min()) { lat_south = -1; } } } } if (lat_north == -1 && lat_south == -1 ) { return; } ATLAS_ASSERT(lat_north >= 0); ATLAS_ASSERT(lat_south < rg.ny() ); std::vector offset(rg.ny(), 0); int n = 0; for (idx_t jlat = 0; jlat < rg.ny(); ++jlat) { offset.at(jlat) = n; n += rg.nx(jlat); } /* We need to connect to next region */ if (lat_north - 1 >= 0 && rg.nx(lat_north - 1) > 0) { --lat_north; } if (idx_t(lat_south + 1) <= rg.ny() - 1 && rg.nx(lat_south + 1) > 0) { ++lat_south; } region.north = lat_north; region.south = lat_south; array::ArrayShape shape = array::make_shape(region.south - region.north, 4 * rg.nxmax(), 4); region.elems.reset(array::Array::create(shape)); [[maybe_unused]] int nelems = 0; region.nquads = 0; region.ntriags = 0; array::ArrayView elemview = array::make_view(*region.elems); elemview.assign(-1); ATLAS_TRACE_SCOPE("generate elements") { eckit::Channel blackhole; eckit::ProgressTimer progress("Generating elements for " + std::to_string(lat_south - lat_north) + " rows", lat_south - lat_north, " row", 5., lat_south - lat_north > 1280 ? Log::trace() : blackhole); for (idx_t jlat = lat_north; jlat < lat_south; ++jlat, ++progress) { // std::stringstream filename; filename << "/tmp/debug/"< dS1N2) { if (ipN1 != ipN2) { try_make_triangle_down = true; } else { try_make_triangle_up = true; } } else { throw_Exception("Should not be here", Here()); } } } else { if (ipN1 == ipN2) { try_make_triangle_up = true; } else if (ipS1 == ipS2) { try_make_triangle_down = true; } else { try_make_quad = true; } // try_make_quad = true; } } else { dN1S2 = std::abs(xN1 - xS2); dS1N2 = std::abs(xS1 - xN2); // dN2S2 = std::abs(xN2-xS2); // Log::info() << " dN1S2 " << dN1S2 << " dS1N2 " << dS1N2 << " // dN2S2 " << dN2S2 << std::endl; if ((dN1S2 <= dS1N2) && (ipS1 != ipS2)) { try_make_triangle_up = true; } else if ((dN1S2 >= dS1N2) && (ipN1 != ipN2)) { try_make_triangle_down = true; } else { if (ipN1 == ipN2) { try_make_triangle_up = true; } else if (ipS1 == ipS2) { try_make_triangle_down = true; } else { ATLAS_DEBUG_VAR(dN1S2); ATLAS_DEBUG_VAR(dS1N2); ATLAS_DEBUG_VAR(jlat); Log::info() << ipN1 << "(" << xN1 << ") " << ipN2 << "(" << xN2 << ") " << std::endl; Log::info() << ipS1 << "(" << xS1 << ") " << ipS2 << "(" << xS2 << ") " << std::endl; throw_Exception("Should not try to make a quadrilateral!", Here()); } } } ATLAS_ASSERT( (ipN1!=ipN2) || (ipS1 != ipS2) ); if( ipN1 == ipN2 ) { try_make_triangle_up = true; try_make_triangle_down = false; try_make_quad = false; } if( ipS1 == ipS2 ) { try_make_triangle_up = false; try_make_triangle_down = true; try_make_quad = false; } // ------------------------------------------------ // END RULES // ------------------------------------------------ #if DEBUG_OUTPUT ATLAS_DEBUG_VAR(jelem); #endif auto elem = lat_elems_view.slice(jelem, Range::all()); if (try_make_quad) { // add quadrilateral #if DEBUG_OUTPUT Log::info() << " " << ipN1 << " " << ipN2 << '\n'; Log::info() << " " << ipS1 << " " << ipS2 << '\n'; #endif elem(0) = ipN1; elem(1) = ipS1; elem(2) = ipS2; elem(3) = ipN2; std::array np{pN1, pN2, pS1, pS2}; std::array pcnts; for (int j = 0; j < 4; ++j) { pcnts[j] = static_cast(std::count(np.begin(), np.end(), np[j])); } if (pcnts[0] > 2) { // 3 or more of pN1 pE = pN1; if (latS == rg.ny() - 1) { pE = pS1; } } else if (pcnts[2] > 2) { // 3 or more of pS1 pE = pS1; if (latN == 0) { pE = pN1; } } else { std::array::iterator p_max = std::max_element(pcnts.begin(), pcnts.end()); if (*p_max > 2) { // 3 or 4 points belong to same part pE = np[std::distance(np.begin(), p_max)]; } else { // 3 or 4 points don't belong to mypart pE = pN1; if (latS == rg.ny() - 1) { pE = pS1; } } } add_quad = (pE == mypart); if (add_quad) { ++region.nquads; ++jelem; ++nelems; if (region.lat_begin.at(latN) == -1) { region.lat_begin.at(latN) = ipN1; } if (region.lat_begin.at(latS) == -1) { region.lat_begin.at(latS) = ipS1; } region.lat_begin.at(latN) = std::min(region.lat_begin.at(latN), ipN1); region.lat_begin.at(latS) = std::min(region.lat_begin.at(latS), ipS1); region.lat_end.at(latN) = std::max(region.lat_end.at(latN), ipN2); region.lat_end.at(latS) = std::max(region.lat_end.at(latS), ipS2); } else { #if DEBUG_OUTPUT Log::info() << "Quad belongs to other partition" << std::endl; #endif } ipN1 = ipN2; ipS1 = ipS2; } else if (try_make_triangle_down) // make triangle down { // triangle without ip3 #if DEBUG_OUTPUT Log::info() << "v " << ipN1 << " " << ipN2 << '\n'; Log::info() << " " << ipS1 << '\n'; #endif elem(0) = ipN1; elem(1) = ipS1; elem(2) = -1; elem(3) = ipN2; pE = pN1; if (latS == rg.ny() - 1) { pE = pS1; } add_triag = (mypart == pE); if (add_triag) { ATLAS_ASSERT(ipN1 != ipN2, "Faulty triangle with latN = "+std::to_string(latN)+"("+std::to_string(rg.y(latN))+")"); ++region.ntriags; ++jelem; ++nelems; if (region.lat_begin.at(latN) == -1) { region.lat_begin.at(latN) = ipN1; } if (region.lat_begin.at(latS) == -1) { region.lat_begin.at(latS) = ipS1; } region.lat_begin.at(latN) = std::min(region.lat_begin.at(latN), ipN1); region.lat_begin.at(latS) = std::min(region.lat_begin.at(latS), ipS1); region.lat_end.at(latN) = std::max(region.lat_end.at(latN), ipN2); region.lat_end.at(latS) = std::max(region.lat_end.at(latS), ipS1); } else { #if DEBUG_OUTPUT Log::info() << "Downward Triag belongs to other partition" << std::endl; #endif } ipN1 = ipN2; // and ipS1=ipS1; } else if (try_make_triangle_up) // make triangle up { // triangle without ip4 #if DEBUG_OUTPUT Log::info() << "^ " << ipN1 << " (" << pN1 << ")" << '\n'; Log::info() << " " << ipS1 << " (" << pS1 << ")" << " " << ipS2 << " (" << pS2 << ")" << '\n'; #endif elem(0) = ipN1; elem(1) = ipS1; elem(2) = ipS2; elem(3) = -1; if (pS1 == pE && pN1 != pE) { if (xN1 < 0.5 * (xS1 + xS2)) { pE = pN1; } // else pE of previous element } else { pE = pN1; } if (ipN1 == rg.nx(latN)) { pE = pS1; } if (latS == rg.ny() - 1) { pE = pS1; } add_triag = (mypart == pE); if (add_triag) { ++region.ntriags; ++jelem; ++nelems; if (region.lat_begin.at(latN) == -1) { region.lat_begin.at(latN) = ipN1; } if (region.lat_begin.at(latS) == -1) { region.lat_begin.at(latS) = ipS1; } region.lat_begin.at(latN) = std::min(region.lat_begin.at(latN), ipN1); region.lat_begin.at(latS) = std::min(region.lat_begin.at(latS), ipS1); region.lat_end.at(latN) = std::max(region.lat_end.at(latN), ipN1); region.lat_end.at(latS) = std::max(region.lat_end.at(latS), ipS2); } else { #if DEBUG_OUTPUT Log::info() << "Upward Triag belongs to other partition" << std::endl; #endif } ipS1 = ipS2; // and ipN1=ipN1; } else { throw_Exception("Could not detect which element to create", Here()); } ipN2 = std::min(endN, ipN1 + 1); ipS2 = std::min(endS, ipS1 + 1); } region.nb_lat_elems.at(jlat) = jelem; #if DEBUG_OUTPUT ATLAS_DEBUG_VAR(region.nb_lat_elems.at(jlat)); #endif if (region.nb_lat_elems.at(jlat) == 0 && latN == region.north) { ++region.north; } if (region.nb_lat_elems.at(jlat) == 0 && latS == region.south) { --region.south; } // region.lat_end.at(latN) = std::min(region.lat_end.at(latN), // int(rg.nx(latN)-1)); // region.lat_end.at(latS) = std::min(region.lat_end.at(latS), // int(rg.nx(latS)-1)); if (yN == 90 && unique_pole) { region.lat_end.at(latN) = rg.nx(latN) - 1; } if (yS == -90 && unique_pole) { region.lat_end.at(latS) = rg.nx(latS) - 1; } if (region.nb_lat_elems.at(jlat) > 0) { region.lat_end.at(latN) = std::max(region.lat_end.at(latN), region.lat_begin.at(latN)); region.lat_end.at(latS) = std::max(region.lat_end.at(latS), region.lat_begin.at(latS)); } } // for jlat } // Log::info() << "nb_triags = " << region.ntriags << std::endl; // Log::info() << "nb_quads = " << region.nquads << std::endl; // Log::info() << "nb_elems = " << nelems << std::endl; int nb_region_nodes = 0; for (int jlat = region.north; jlat <= region.south; ++jlat) { n = offset.at(jlat); region.lat_begin.at(jlat) = std::max(0, region.lat_begin.at(jlat)); for (idx_t jlon = 0; jlon < rg.nx(jlat); ++jlon) { if (distribution.partition(n) == mypart) { region.lat_begin.at(jlat) = std::min(region.lat_begin.at(jlat), jlon); region.lat_end.at(jlat) = std::max(region.lat_end.at(jlat), jlon); } ++n; } nb_region_nodes += region.lat_end.at(jlat) - region.lat_begin.at(jlat) + 1; // Count extra periodic node // if( periodic_east_west && size_t(region.lat_end.at(jlat)) == rg.nx(jlat) // - 1) ++nb_region_nodes; } region.nnodes = nb_region_nodes; if (region.nnodes == 0) { throw_Exception( "Trying to generate mesh with too many partitions. Reduce " "the number of partitions.", Here()); } #if DEBUG_OUTPUT ATLAS_DEBUG("End of generate_region()"); #endif } namespace { struct GhostNode { GhostNode(int _jlat, int _jlon, int _jnode) { jlat = _jlat; jlon = _jlon; jnode = _jnode; } int jlat; int jlon; int jnode; }; } // namespace void StructuredMeshGenerator::generate_mesh(const StructuredGrid& rg, const grid::Distribution& distribution, const Region& region, Mesh& mesh) const { ATLAS_TRACE(); ATLAS_ASSERT(!mesh.generated()); int mypart = options.getInt("part"); int nparts = options.getInt("nb_parts"); int n, l; const int y_numbering = (rg.y().front() < rg.y().back()) ? +1 : -1; double y_north = y_numbering < 0 ? rg.y().front() : rg.y().back(); double y_south = y_numbering < 0 ? rg.y().back() : rg.y().front(); idx_t nx_north = y_numbering < 0 ? rg.nx().front() : rg.nx().back(); idx_t nx_south = y_numbering < 0 ? rg.nx().back() : rg.nx().front(); std::vector part_north(nx_north); std::vector part_south(nx_south); if (y_numbering < 0) { distribution.partition(0, nx_north, part_north); distribution.partition(rg.size() - nx_south, rg.size(), part_south); } else { distribution.partition(0, nx_south, part_south); distribution.partition(rg.size() - nx_north, rg.size(), part_north); } bool mypart_at_north = std::any_of(part_north.begin(), part_north.end(), [&](int p) { return p == mypart; }); bool mypart_at_south = std::any_of(part_south.begin(), part_south.end(), [&](int p) { return p == mypart; }); bool three_dimensional = options.getBool("three_dimensional"); bool periodic_east_west = rg.periodic(); bool include_periodic_ghost_points = periodic_east_west && !three_dimensional; bool remove_periodic_ghost_points = periodic_east_west && three_dimensional; bool has_point_at_north_pole = y_north == 90 && nx_north > 0; bool has_point_at_south_pole = y_south == -90 && nx_south > 0; bool possible_north_pole = !has_point_at_north_pole && rg.domain().containsNorthPole() && mypart_at_north; bool possible_south_pole = !has_point_at_south_pole && rg.domain().containsSouthPole() && mypart_at_south; bool force_include_north_pole(options.has("force_include_north_pole") && options.getBool("force_include_north_pole")); bool force_include_south_pole(options.has("force_include_south_pole") && options.getBool("force_include_south_pole")); bool include_north_pole = (possible_north_pole && options.getBool("include_pole")) || force_include_north_pole; bool include_south_pole = (possible_south_pole && options.getBool("include_pole")) || force_include_south_pole; bool patch_north_pole = possible_north_pole && options.getBool("patch_pole") && nx_north > 0; bool patch_south_pole = possible_south_pole && options.getBool("patch_pole") && nx_south > 0; bool patch_quads = options.getBool("patch_quads"); int nnewnodes = (!has_point_at_north_pole && include_north_pole ? 1 : 0) + (!has_point_at_south_pole && include_south_pole ? 1 : 0); if (three_dimensional && nparts != 1) { throw_Exception("Cannot generate three_dimensional mesh in parallel", Here()); } int nnodes = region.nnodes; int ntriags = region.ntriags; int nquads = region.nquads; if (include_north_pole) { ++nnodes; ntriags += nx_north - (periodic_east_west ? 0 : 1); } else if (patch_north_pole) { if (patch_quads) { nquads += (nx_north + 1) / 2; } else { ntriags += nx_north - 2; } } if (include_south_pole) { ++nnodes; ntriags += nx_south - (periodic_east_west ? 0 : 1); } else if (patch_south_pole) { if (patch_quads) { nquads += (nx_south + 1) / 2; } else { ntriags += nx_south - 2; } } size_t node_numbering_size = nnodes; if (remove_periodic_ghost_points) { for (idx_t jlat = 0; jlat < rg.ny(); ++jlat) { if (region.lat_end[jlat] >= rg.nx(jlat)) { --nnodes; } } } ATLAS_ASSERT(nnodes >= nnewnodes); #if DEBUG_OUTPUT ATLAS_DEBUG_VAR(include_periodic_ghost_points); ATLAS_DEBUG_VAR(include_north_pole); ATLAS_DEBUG_VAR(include_south_pole); ATLAS_DEBUG_VAR(three_dimensional); ATLAS_DEBUG_VAR(patch_north_pole); ATLAS_DEBUG_VAR(patch_south_pole); ATLAS_DEBUG_VAR(rg.size()); ATLAS_DEBUG_VAR(nnodes); ATLAS_DEBUG_VAR(ntriags); ATLAS_DEBUG_VAR(nquads); ATLAS_DEBUG_VAR(options.getBool("ghost_at_end")); #endif std::vector offset_glb(rg.ny()); std::vector offset_loc(region.south - region.north + 1, 0); n = 0; for (idx_t jlat = 0; jlat < rg.ny(); ++jlat) { offset_glb.at(jlat) = n; n += rg.nx(jlat); } std::vector periodic_glb(rg.ny()); if (include_periodic_ghost_points) { for (idx_t jlat = 0; jlat < rg.ny(); ++jlat) { if (rg.nx(jlat) > 0) { periodic_glb.at(jlat) = n; ++n; } } } else { for (idx_t jlat = 0; jlat < rg.ny(); ++jlat) { if (rg.nx(jlat) > 0) { periodic_glb.at(jlat) = offset_glb.at(jlat) + rg.nx(jlat) - 1; } } } int max_glb_idx = n; mesh.nodes().resize(nnodes); mesh::Nodes& nodes = mesh.nodes(); auto xy = array::make_view(nodes.xy()); auto lonlat = array::make_view(nodes.lonlat()); auto glb_idx = array::make_view(nodes.global_index()); auto part = array::make_view(nodes.partition()); auto ghost = array::make_view(nodes.ghost()); auto flags = array::make_view(nodes.flags()); auto halo = array::make_view(nodes.halo()); idx_t jnorth = -1; idx_t jsouth = -1; std::vector node_numbering(node_numbering_size, -1); if (nnodes > 0) { if (options.getBool("ghost_at_end")) { std::vector ghost_nodes; ghost_nodes.reserve(nnodes); idx_t node_number = 0; idx_t jnode = 0; l = 0; ATLAS_ASSERT(region.south >= region.north); for (int jlat = region.north; jlat <= region.south; ++jlat) { int ilat = jlat - region.north; offset_loc.at(ilat) = l; l += region.lat_end.at(jlat) - region.lat_begin.at(jlat) + 1; if (region.lat_end.at(jlat) < region.lat_begin.at(jlat)) { ATLAS_DEBUG_VAR(jlat); ATLAS_DEBUG_VAR(region.lat_begin[jlat]); ATLAS_DEBUG_VAR(region.lat_end[jlat]); } for (idx_t jlon = region.lat_begin.at(jlat); jlon <= region.lat_end.at(jlat); ++jlon) { if (jlon < rg.nx(jlat)) { n = offset_glb.at(jlat) + jlon; if (distribution.partition(n) == mypart) { node_numbering.at(jnode) = node_number; ++node_number; } else { ghost_nodes.emplace_back(jlat, jlon, jnode); } ++jnode; } else if (include_periodic_ghost_points) // add periodic point { //#warning TODO: use commented approach part(jnode) = mypart; // part(jnode) = parts.at( offset_glb.at(jlat) ); ghost(jnode) = 1; halo(jnode) = 0; ghost_nodes.emplace_back(jlat, rg.nx(jlat), jnode); ++jnode; } else { --l; } } } for (size_t jghost = 0; jghost < ghost_nodes.size(); ++jghost) { node_numbering.at(ghost_nodes.at(jghost).jnode) = node_number; ++node_number; } if (include_north_pole) { node_numbering.at(jnode) = jnode; ++jnode; } if (include_south_pole) { node_numbering.at(jnode) = jnode; ++jnode; } ATLAS_ASSERT(jnode == nnodes); } else // No renumbering { for (idx_t jnode = 0; jnode < nnodes; ++jnode) { node_numbering.at(jnode) = jnode; } } idx_t jnode = 0; l = 0; for (idx_t jlat = region.north; jlat <= region.south; ++jlat) { idx_t ilat = jlat - region.north; offset_loc.at(ilat) = l; l += region.lat_end.at(jlat) - region.lat_begin.at(jlat) + 1; double y = rg.y(jlat); for (idx_t jlon = region.lat_begin.at(jlat); jlon <= region.lat_end.at(jlat); ++jlon) { if (jlon < rg.nx(jlat)) { idx_t inode = node_numbering.at(jnode); n = offset_glb.at(jlat) + jlon; double x = rg.x(jlon, jlat); // std::cout << "jlat = " << jlat << "; jlon = " << jlon << "; x = " << // x << std::endl; xy(inode, XX) = x; xy(inode, YY) = y; // geographic coordinates by using projection double crd[] = {x, y}; rg.projection().xy2lonlat(crd); lonlat(inode, LON) = crd[LON]; lonlat(inode, LAT) = crd[LAT]; glb_idx(inode) = n + 1; part(inode) = distribution.partition(n); ghost(inode) = 0; halo(inode) = 0; Topology::reset(flags(inode)); Topology::set(flags(inode), Topology::BC); if (jlat == 0) { if (y_numbering < 0 && !include_north_pole) { Topology::set(flags(inode), Topology::NORTH); } if (y_numbering > 0 && !include_south_pole) { Topology::set(flags(inode), Topology::SOUTH); } } if (jlat == rg.ny() - 1) { if (y_numbering < 0 && !include_south_pole) { Topology::set(flags(inode), Topology::SOUTH); } if (y_numbering > 0 && !include_north_pole) { Topology::set(flags(inode), Topology::NORTH); } } if (jlon == 0 && include_periodic_ghost_points) { Topology::set(flags(inode), Topology::WEST); } if (part(inode) != mypart) { Topology::set(flags(inode), Topology::GHOST); ghost(inode) = 1; } ++jnode; } else if (include_periodic_ghost_points) // add periodic point { idx_t inode = node_numbering.at(jnode); // int inode_left = node_numbering.at(jnode-1); double x = rg.x(rg.nx(jlat), jlat); xy(inode, XX) = x; xy(inode, YY) = y; // geographic coordinates by using projection double crd[] = {x, y}; rg.projection().xy2lonlat(crd); lonlat(inode, LON) = crd[LON]; lonlat(inode, LAT) = crd[LAT]; glb_idx(inode) = periodic_glb.at(jlat) + 1; //#warning TODO: use commented approach // part(inode) = parts.at( offset_glb.at(jlat) ); part(inode) = mypart; // The actual part will be fixed later ghost(inode) = 1; halo(inode) = 0; Topology::reset(flags(inode)); Topology::set(flags(inode), Topology::BC | Topology::EAST); Topology::set(flags(inode), Topology::GHOST); if (jlat == 0) { if (y_numbering < 0 && !include_north_pole) { Topology::set(flags(inode), Topology::NORTH); } if (y_numbering > 0 && !include_south_pole) { Topology::set(flags(inode), Topology::SOUTH); } } if (jlat == rg.ny() - 1) { if (y_numbering < 0 && !include_south_pole) { Topology::set(flags(inode), Topology::SOUTH); } if (y_numbering > 0 && !include_north_pole) { Topology::set(flags(inode), Topology::NORTH); } } ++jnode; } else { --l; } } } if (include_north_pole) { idx_t inode = node_numbering.at(jnode); jnorth = jnode; double y = 90.; double x = 180.; xy(inode, XX) = x; xy(inode, YY) = y; // geographic coordinates by using projection double crd[] = {x, y}; rg.projection().xy2lonlat(crd); lonlat(inode, LON) = crd[LON]; lonlat(inode, LAT) = crd[LAT]; glb_idx(inode) = periodic_glb.at(rg.ny() - 1) + 2; part(inode) = mypart; ghost(inode) = 0; halo(inode) = 0; Topology::reset(flags(inode)); Topology::set(flags(inode), Topology::NORTH); ++jnode; } if (include_south_pole) { idx_t inode = node_numbering.at(jnode); jsouth = jnode; double y = -90.; double x = 180.; xy(inode, XX) = x; xy(inode, YY) = y; // geographic coordinates by using projection double crd[] = {x, y}; rg.projection().xy2lonlat(crd); lonlat(inode, LON) = crd[LON]; lonlat(inode, LAT) = crd[LAT]; glb_idx(inode) = periodic_glb.at(rg.ny() - 1) + 3; part(inode) = mypart; ghost(inode) = 0; halo(inode) = 0; Topology::reset(flags(inode)); Topology::set(flags(inode), Topology::SOUTH); ++jnode; } } mesh.metadata().set("nb_nodes_including_halo[0]", nodes.size()); nodes.metadata().set("NbRealPts", size_t(nnodes - nnewnodes)); nodes.metadata().set("NbVirtualPts", size_t(nnewnodes)); nodes.global_index().metadata().set("human_readable", true); nodes.global_index().metadata().set("min", 1); nodes.global_index().metadata().set("max", max_glb_idx); // Now handle elements // ------------------- mesh.cells().add(mesh::ElementType::create("Quadrilateral"), nquads); mesh.cells().add(mesh::ElementType::create("Triangle"), ntriags); mesh::HybridElements::Connectivity& node_connectivity = mesh.cells().node_connectivity(); array::ArrayView cells_glb_idx = array::make_view(mesh.cells().global_index()); array::ArrayView cells_part = array::make_view(mesh.cells().partition()); array::ArrayView cells_flags = array::make_view(mesh.cells().flags()); /* * label all patch cells a non-patch */ cells_flags.assign(0); /* * Fill in connectivity tables with global node indices first */ idx_t jcell; idx_t jquad = 0; idx_t jtriag = 0; idx_t quad_begin = mesh.cells().elements(0).begin(); idx_t triag_begin = mesh.cells().elements(1).begin(); idx_t quad_nodes[4]; idx_t triag_nodes[3]; auto fix_quad_orientation = [](idx_t nodes[]) { idx_t tmp; tmp = nodes[0]; nodes[0] = nodes[1]; nodes[1] = tmp; tmp = nodes[2]; nodes[2] = nodes[3]; nodes[3] = tmp; }; auto fix_triag_orientation = [](idx_t nodes[]) { idx_t tmp; tmp = nodes[0]; nodes[0] = nodes[1]; nodes[1] = tmp; }; bool regular_cells_glb_idx = rg.regular(); if (options.getBool("triangulate") || y_numbering > 0 || rg.y().front() != 90. || rg.y().back() != -90.) { regular_cells_glb_idx = false; } if ((region.nquads + region.ntriags) > 0) { for (idx_t jlat = region.north; jlat < region.south; ++jlat) { idx_t ilat = jlat - region.north; idx_t jlatN = jlat; idx_t jlatS = jlat + 1; idx_t ilatN = ilat; idx_t ilatS = ilat + 1; for (idx_t jelem = 0; jelem < region.nb_lat_elems.at(jlat); ++jelem) { const auto elem = array::make_view(*region.elems).slice(ilat, jelem, Range::all()); if (elem(2) >= 0 && elem(3) >= 0) // This is a quad { quad_nodes[0] = node_numbering.at(offset_loc.at(ilatN) + elem(0) - region.lat_begin.at(jlatN)); quad_nodes[1] = node_numbering.at(offset_loc.at(ilatS) + elem(1) - region.lat_begin.at(jlatS)); quad_nodes[2] = node_numbering.at(offset_loc.at(ilatS) + elem(2) - region.lat_begin.at(jlatS)); quad_nodes[3] = node_numbering.at(offset_loc.at(ilatN) + elem(3) - region.lat_begin.at(jlatN)); if (three_dimensional && periodic_east_west) { if (elem(2) == rg.nx(jlatS)) { quad_nodes[2] = node_numbering.at(offset_loc.at(ilatS)); } if (elem(3) == rg.nx(jlatN)) { quad_nodes[3] = node_numbering.at(offset_loc.at(ilatN)); } } if (y_numbering > 0) { fix_quad_orientation(quad_nodes); } jcell = quad_begin + jquad++; node_connectivity.set(jcell, quad_nodes); cells_glb_idx(jcell) = jcell + 1; cells_part(jcell) = mypart; if( regular_cells_glb_idx ) { cells_glb_idx(jcell) = glb_idx( quad_nodes[0] ); } } else // This is a triag { if (elem(3) < 0) // This is a triangle pointing up { triag_nodes[0] = node_numbering.at(offset_loc.at(ilatN) + elem(0) - region.lat_begin.at(jlatN)); triag_nodes[1] = node_numbering.at(offset_loc.at(ilatS) + elem(1) - region.lat_begin.at(jlatS)); triag_nodes[2] = node_numbering.at(offset_loc.at(ilatS) + elem(2) - region.lat_begin.at(jlatS)); if (three_dimensional && periodic_east_west) { if (elem(0) == rg.nx(jlatN)) { triag_nodes[0] = node_numbering.at(offset_loc.at(ilatN)); } if (elem(2) == rg.nx(jlatS)) { triag_nodes[2] = node_numbering.at(offset_loc.at(ilatS)); } } if (y_numbering > 0) { fix_triag_orientation(triag_nodes); } } else // This is a triangle pointing down { triag_nodes[0] = node_numbering.at(offset_loc.at(ilatN) + elem(0) - region.lat_begin.at(jlatN)); triag_nodes[1] = node_numbering.at(offset_loc.at(ilatS) + elem(1) - region.lat_begin.at(jlatS)); triag_nodes[2] = node_numbering.at(offset_loc.at(ilatN) + elem(3) - region.lat_begin.at(jlatN)); if (three_dimensional && periodic_east_west) { if (elem(1) == rg.nx(jlatS)) { triag_nodes[1] = node_numbering.at(offset_loc.at(ilatS)); } if (elem(3) == rg.nx(jlatN)) { triag_nodes[2] = node_numbering.at(offset_loc.at(ilatN)); } } if (y_numbering > 0) { fix_triag_orientation(triag_nodes); } } jcell = triag_begin + jtriag++; node_connectivity.set(jcell, triag_nodes); cells_glb_idx(jcell) = jcell + 1; cells_part(jcell) = mypart; } } } if (include_north_pole) { idx_t ilat = 0; idx_t ip1 = 0; idx_t nlon = rg.nx(0) - (periodic_east_west ? 0 : 1); for (idx_t ip2 = 0; ip2 < nlon; ++ip2) { jcell = triag_begin + jtriag++; idx_t ip3 = ip2 + 1; if (three_dimensional && ip3 == rg.nx(0)) { ip3 = 0; } triag_nodes[0] = node_numbering.at(jnorth + ip1); triag_nodes[1] = node_numbering.at(offset_loc.at(ilat) + ip2); triag_nodes[2] = node_numbering.at(offset_loc.at(ilat) + ip3); if (y_numbering > 0) { fix_triag_orientation(triag_nodes); } node_connectivity.set(jcell, triag_nodes); cells_glb_idx(jcell) = jcell + 1; cells_part(jcell) = mypart; } } else if (patch_north_pole) { idx_t jlat = y_numbering < 0 ? 0 : rg.ny() - 1; idx_t ilat = y_numbering < 0 ? 0 : std::abs(region.south - region.north); if (patch_quads) { idx_t ip1, ip2, ip3, ip4; idx_t jforward = 0; idx_t jbackward = rg.nx(jlat) - 1; while (true) { ip1 = jforward; ip2 = jforward + 1; ip3 = jbackward - 1; ip4 = jbackward; quad_nodes[0] = node_numbering.at(offset_loc.at(ilat) + ip1); quad_nodes[1] = node_numbering.at(offset_loc.at(ilat) + ip2); quad_nodes[2] = node_numbering.at(offset_loc.at(ilat) + ip3); quad_nodes[3] = node_numbering.at(offset_loc.at(ilat) + ip4); jcell = quad_begin + jquad++; node_connectivity.set(jcell, quad_nodes); cells_glb_idx(jcell) = jcell + 1; cells_part(jcell) = mypart; Topology::set(cells_flags(jcell), Topology::PATCH); if (jcell == cells_glb_idx.size() - 1) { } if (jforward + 1 == jbackward) { break; } ++jforward; --jbackward; } } else { idx_t ip1, ip2, ip3; idx_t jforward = 0; idx_t jbackward = rg.nx(jlat) - 1; bool forward = true; while (true) { if (forward) { ip1 = jforward; ip2 = jforward + 1; ip3 = jbackward; } else { ip1 = jforward; ip2 = jbackward - 1; ip3 = jbackward; } triag_nodes[0] = node_numbering.at(offset_loc.at(ilat) + ip1); triag_nodes[1] = node_numbering.at(offset_loc.at(ilat) + ip2); triag_nodes[2] = node_numbering.at(offset_loc.at(ilat) + ip3); // if ( y_numbering > 0 ) { // fix_triag_orientation( triag_nodes ); // } jcell = triag_begin + jtriag++; node_connectivity.set(jcell, triag_nodes); cells_glb_idx(jcell) = jcell + 1; cells_part(jcell) = mypart; Topology::set(cells_flags(jcell), Topology::PATCH); if (jbackward == jforward + 2) { break; } if (forward) { ++jforward; forward = false; } else { --jbackward; forward = true; } } } } if (include_south_pole) { idx_t jlat = y_numbering > 0 ? 0 : rg.ny() - 1; idx_t ilat = y_numbering > 0 ? 0 : std::abs(region.south - region.north); idx_t ip1 = 0; idx_t nlon = rg.nx(jlat) + 1 - (periodic_east_west ? 0 : 1); for (idx_t ip2 = 1; ip2 < nlon; ++ip2) { jcell = triag_begin + jtriag++; int ip3 = ip2 - 1; triag_nodes[0] = node_numbering.at(jsouth + ip1); triag_nodes[1] = node_numbering.at(offset_loc.at(ilat) + ip2); triag_nodes[2] = node_numbering.at(offset_loc.at(ilat) + ip3); if (three_dimensional && ip2 == rg.nx(jlat)) { triag_nodes[1] = node_numbering.at(offset_loc.at(ilat) + 0); } if (y_numbering > 0) { fix_triag_orientation(triag_nodes); } node_connectivity.set(jcell, triag_nodes); cells_glb_idx(jcell) = jcell + 1; cells_part(jcell) = mypart; } } else if (patch_south_pole) { idx_t jlat = y_numbering > 0 ? 0 : rg.ny() - 1; idx_t ilat = y_numbering > 0 ? 0 : std::abs(region.south - region.north); if (patch_quads) { idx_t ip1, ip2, ip3, ip4; idx_t jforward = 0; idx_t jbackward = rg.nx(jlat) - 1; while (true) { ip1 = jbackward; ip2 = jbackward - 1; ip3 = jforward + 1; ip4 = jforward; quad_nodes[0] = node_numbering.at(offset_loc.at(ilat) + ip1); quad_nodes[1] = node_numbering.at(offset_loc.at(ilat) + ip2); quad_nodes[2] = node_numbering.at(offset_loc.at(ilat) + ip3); quad_nodes[3] = node_numbering.at(offset_loc.at(ilat) + ip4); jcell = quad_begin + jquad++; node_connectivity.set(jcell, quad_nodes); cells_glb_idx(jcell) = jcell + 1; cells_part(jcell) = mypart; Topology::set(cells_flags(jcell), Topology::PATCH); if (jcell == cells_glb_idx.size() - 1) { } if (jforward + 1 == jbackward) { break; } ++jforward; --jbackward; } } else { idx_t ip1, ip2, ip3; idx_t jforward = 0; idx_t jbackward = rg.nx(jlat) - 1; bool forward = true; while (true) { if (forward) { ip1 = jbackward; ip2 = jforward + 1; ip3 = jforward; } else { ip1 = jbackward; ip2 = jbackward - 1; ip3 = jforward; } triag_nodes[0] = node_numbering.at(offset_loc.at(ilat) + ip1); triag_nodes[1] = node_numbering.at(offset_loc.at(ilat) + ip2); triag_nodes[2] = node_numbering.at(offset_loc.at(ilat) + ip3); // if ( y_numbering > 0 ) { // fix_triag_orientation( triag_nodes ); // } jcell = triag_begin + jtriag++; node_connectivity.set(jcell, triag_nodes); cells_glb_idx(jcell) = jcell + 1; cells_part(jcell) = mypart; Topology::set(cells_flags(jcell), Topology::PATCH); if (jbackward == jforward + 2) { break; } if (forward) { ++jforward; forward = false; } else { --jbackward; forward = true; } } } } } if (regular_cells_glb_idx) { mesh.cells().global_index().metadata().set("min", 1); mesh.cells().global_index().metadata().set("max", rg.nx(0) * (rg.ny()-1) ); } else { generateGlobalElementNumbering(mesh); } } namespace { static MeshGeneratorBuilder __Structured("structured"); } // namespace } // namespace meshgenerator } // namespace atlas atlas-0.46.0/src/atlas/meshgenerator/detail/MeshGeneratorFactory.cc0000664000175000017500000000442415160212070025471 0ustar alastairalastair/* * (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/meshgenerator/detail/CubedSphereDualMeshGenerator.h" #include "atlas/meshgenerator/detail/CubedSphereMeshGenerator.h" #include "atlas/meshgenerator/detail/DelaunayMeshGenerator.h" #include "atlas/meshgenerator/detail/HealpixMeshGenerator.h" #include "atlas/meshgenerator/detail/MeshGeneratorFactory.h" #include "atlas/meshgenerator/detail/NodalCubedSphereMeshGenerator.h" #include "atlas/meshgenerator/detail/StructuredMeshGenerator.h" using atlas::Mesh; namespace atlas { namespace meshgenerator { //---------------------------------------------------------------------------------------------------------------------- void force_link() { static struct Link { Link() { MeshGeneratorBuilder(); MeshGeneratorBuilder(); MeshGeneratorBuilder(); MeshGeneratorBuilder(); MeshGeneratorBuilder(); MeshGeneratorBuilder(); } } link; } //---------------------------------------------------------------------------------------------------------------------- const MeshGenerator::Implementation* MeshGeneratorFactory::build(const std::string& builder) { return build(builder, util::NoConfig()); } const MeshGenerator::Implementation* MeshGeneratorFactory::build(const std::string& builder, const eckit::Parametrisation& param) { force_link(); auto factory = get(builder); return factory->make(param); } //---------------------------------------------------------------------------------------------------------------------- } // namespace meshgenerator } // namespace atlas atlas-0.46.0/src/atlas/meshgenerator/detail/cubedsphere/0000775000175000017500000000000015160212070023354 5ustar alastairalastairatlas-0.46.0/src/atlas/meshgenerator/detail/cubedsphere/CubedSphereUtility.cc0000664000175000017500000002370015160212070027442 0ustar alastairalastair/* * (C) Crown Copyright 2021 Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ #include "atlas/meshgenerator/detail/cubedsphere/CubedSphereUtility.h" #include "atlas/grid/CubedSphereGrid.h" #include "atlas/grid/Iterator.h" #include "atlas/projection/Jacobian.h" #include "atlas/projection/detail/CubedSphereProjectionBase.h" namespace atlas { namespace meshgenerator { namespace detail { namespace cubedsphere { // ----------------------------------------------------------------------------- // NeighbourJacobian class // ----------------------------------------------------------------------------- NeighbourJacobian::NeighbourJacobian(const CubedSphereGrid& csGrid) { // N must be greater than 2. if (csGrid.N() < 2) { throw_Exception("Jacobians can only be calculated for N > 1 .", Here()); } // Assumes cell centre staggering. if (csGrid.stagger() != "C") { throw_Exception("NeighbourJacobian class only works for cell-centre grid", Here()); } // Get projection. csProjection_ = &csGrid.cubedSphereProjection(); // Get tiles. const auto& csTiles = csProjection_->getCubedSphereTiles(); // Get grid size. N_ = csGrid.N(); // Get cell width. const double cellWidth = 90. / N_; for (size_t t = 0; t < 6; ++t) { // Calculate tile Jacobians. dxy_by_dij_[t] = csTiles.tileJacobian(t) * cellWidth; // Get inverse. dij_by_dxy_[t] = dxy_by_dij_[t].inverse(); // Set xy00. Grid point needs moving to (i = 0, j = 0). xy00_[t] = csTiles.tileCentre(t) + dxy_by_dij_[t] * PointIJ(-0.5 * N_, -0.5 * N_); // Get xy min/max. xyMin_[t].x() = csTiles.tileCentre(t).x() + 45.; xyMax_[t].x() = csTiles.tileCentre(t).x() - 45.; xyMin_[t].y() = csTiles.tileCentre(t).y() + 45.; xyMax_[t].y() = csTiles.tileCentre(t).y() - 45.; // Neighbour assignment lambda. const auto neighbourAssignment = [&](TileEdge::k k) -> void { // Shift points in to neighbouring tiles. PointIJ Dij; switch (k) { case TileEdge::LEFT: { Dij = PointIJ(-2, 0); break; } case TileEdge::BOTTOM: { Dij = PointIJ(0, -2); break; } case TileEdge::RIGHT: { Dij = PointIJ(N_, 0); break; } case TileEdge::TOP: { Dij = PointIJ(0, N_); break; } case TileEdge::UNDEFINED: { throw_Exception("Undefined tile edge.", Here()); } } // half-index displacements. const auto dij00 = PointIJ(0.5, 0.5); const auto dij10 = PointIJ(1.5, 0.5); const auto dij01 = PointIJ(0.5, 1.5); // Get neighbour xy points in xy space local to tile. const PointXY xy00Local = xy00_[t] + dxy_by_dij_[t] * (Dij + dij00); const PointXY xy10Local = xy00_[t] + dxy_by_dij_[t] * (Dij + dij10); const PointXY xy01Local = xy00_[t] + dxy_by_dij_[t] * (Dij + dij01); // Convert from local xy to global xy. const PointXY xy00Global = csTiles.tileCubePeriodicity(xy00Local, static_cast(t)); const PointXY xy10Global = csTiles.tileCubePeriodicity(xy10Local, static_cast(t)); const PointXY xy01Global = csTiles.tileCubePeriodicity(xy01Local, static_cast(t)); // Get neighbour tile ID. neighbours_[t].t_[k] = csTiles.indexFromXY(xy00Global.data()); // Set Jacobian of global xy with respect to local ij. auto dxyGlobal_by_dij = Jacobian{{xy10Global[0] - xy00Global[0], xy01Global[0] - xy00Global[0]}, {xy10Global[1] - xy00Global[1], xy01Global[1] - xy00Global[1]}}; // Rescale by cell width (gains an extra couple of decimal places of precision). const auto sign = [](double num, double tol = 360. * std::numeric_limits::epsilon()) { return std::abs(num) < tol ? 0 : num > 0 ? 1. : -1.; }; dxyGlobal_by_dij = Jacobian{sign(dxyGlobal_by_dij[0][0]), sign(dxyGlobal_by_dij[0][1]), sign(dxyGlobal_by_dij[1][0]), sign(dxyGlobal_by_dij[1][1])} * cellWidth; // Chain rule to get Jacobian with respect to local xy. neighbours_[t].dxyGlobal_by_dxyLocal_[k] = dxyGlobal_by_dij * dij_by_dxy_[t]; // Set local xy00 neighbours_[t].xy00Local_[k] = xy00Local; // Set global xy00 neighbours_[t].xy00Global_[k] = xy00Global; }; // Assign neighbours (good job we put it all in a lambda!). neighbourAssignment(TileEdge::LEFT); neighbourAssignment(TileEdge::BOTTOM); neighbourAssignment(TileEdge::RIGHT); neighbourAssignment(TileEdge::TOP); } } PointXY NeighbourJacobian::xy(const PointIJ& ij, idx_t t) const { // Get jacobian. const Jacobian& jac = dxy_by_dij_[static_cast(t)]; const PointXY& xy00 = xy00_[static_cast(t)]; // Return ij return xy00 + jac * ij; } PointIJ NeighbourJacobian::ij(const PointXY& xy, idx_t t) const { // Get jacobian. const Jacobian& jac = dij_by_dxy_[static_cast(t)]; const PointXY& xy00 = xy00_[static_cast(t)]; // Return ij return jac * (xy - xy00); } PointTXY NeighbourJacobian::xyLocalToGlobal(const PointXY& xyLocal, idx_t tLocal) const { // The tileCubePeriodicity method fails when extrapolating along an unowned // tile edge. This method explicitly places an xy point on to a neighbouring // tile to avoid this. Once the correct xy position has been found, // tileCubePeriodicity will correcty find the "owned" xy position of a point // on an unowned tile edge. // Declare result. PointXY xyGlobal; idx_t tGlobal; // Get ij. const PointIJ ijLocal = ij(xyLocal, tLocal); // Get tiles. const auto& csTiles = csProjection_->getCubedSphereTiles(); if (ijInterior(ijLocal)) { // We're within the tile boundary (possibly on an edge). // Return local values if not on edge. if (!ijEdge(ijLocal)) { return PointTXY(tLocal, xyLocal); } // We're on an edge. Will need to check with Tiles class. xyGlobal = xyLocal; tGlobal = tLocal; } else { // We're outside the tile boundary. // Figure out which tile xy is on. TileEdge::k k; if (ijLocal.iNode() < 0) { k = TileEdge::LEFT; } else if (ijLocal.jNode() < 0) { k = TileEdge::BOTTOM; } else if (ijLocal.iNode() > N_) { k = TileEdge::RIGHT; } else if (ijLocal.jNode() > N_) { k = TileEdge::TOP; } else { throw_Exception("Cannot determine neighbour tile.", Here()); } // Get reference points and jacobian. const PointXY& xy00Local_ = neighbours_[static_cast(tLocal)].xy00Local_[k]; const PointXY& xy00Global_ = neighbours_[static_cast(tLocal)].xy00Global_[k]; const Jacobian& jac = neighbours_[static_cast(tLocal)].dxyGlobal_by_dxyLocal_[k]; // Get t. tGlobal = neighbours_[static_cast(tLocal)].t_[k]; // Calculate global xy. xyGlobal = xy00Global_ + jac * (xyLocal - xy00Local_); } // Need to be very careful with floating point comparisons used in projection // class. Move point on to edge if it is very close. xyGlobal = snapToEdge(xyGlobal, tGlobal); // Correct for edge-ownership rules. xyGlobal = csTiles.tileCubePeriodicity(xyGlobal, tGlobal); tGlobal = csTiles.indexFromXY(xyGlobal.data()); return PointTXY(tGlobal, xyGlobal); } PointTIJ NeighbourJacobian::ijLocalToGlobal(const PointIJ& ijLocal, idx_t tLocal) const { // Use xyLocalToGlobal method to take care of this. // Get global xyt. PointTXY txyGlobal = xyLocalToGlobal(xy(ijLocal, tLocal), tLocal); // convert to ijt return PointTIJ(txyGlobal.t(), ij(txyGlobal.xy(), txyGlobal.t())); } bool NeighbourJacobian::ijInterior(const PointIJ& ij) const { return ij.iNode() >= 0 && ij.iNode() <= N_ && ij.jNode() >= 0 && ij.jNode() <= N_; } bool NeighbourJacobian::ijEdge(const PointIJ& ij) const { return ijInterior(ij) && (ij.iNode() == 0 || ij.iNode() == N_ || ij.jNode() == 0 || ij.jNode() == N_); } bool NeighbourJacobian::ijCross(const PointIJ& ij) const { const bool inCorner = (ij.iNode() < 0 && ij.jNode() < 0) || // bottom-left corner. (ij.iNode() > N_ && ij.jNode() < 0) || // bottom-right corner. (ij.iNode() > N_ && ij.jNode() > N_) || // top-right corner. (ij.iNode() < 0 && ij.jNode() > N_); // top-left corner. return !inCorner; } PointXY NeighbourJacobian::snapToEdge(const PointXY& xy, idx_t t) const { const auto nudgeValue = [](double a, double b) -> double { // Set tolerance to machine epsilon * 360 degrees. constexpr double tol = 360. * std::numeric_limits::epsilon(); // If a is nearly equal to b, return b. Otherwise return a. return std::abs(a - b) <= tol ? b : a; }; // If point is near edge, place it exactly on edge. const PointXY& xyMin = xyMin_[static_cast(t)]; const PointXY& xyMax = xyMax_[static_cast(t)]; return PointXY(nudgeValue(nudgeValue(xy.x(), xyMin.x()), xyMax.x()), nudgeValue(nudgeValue(xy.y(), xyMin.y()), xyMax.y())); } } // namespace cubedsphere } // namespace detail } // namespace meshgenerator } // namespace atlas atlas-0.46.0/src/atlas/meshgenerator/detail/cubedsphere/CubedSphereUtility.h0000664000175000017500000001237415160212070027311 0ustar alastairalastair/* * (C) Crown Copyright 2021 Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ #pragma once #include "atlas/library/config.h" #include "atlas/projection/detail/ProjectionImpl.h" #include "atlas/runtime/Exception.h" #include "atlas/util/Point.h" namespace atlas { class CubedSphereGrid; } namespace atlas {} namespace atlas { namespace projection { namespace detail { class CubedSphereProjectionBase; } // namespace detail } // namespace projection } // namespace atlas namespace atlas { namespace meshgenerator { namespace detail { namespace cubedsphere { /// Enum for (i, j, t) coordinate fields. struct Coordinates { enum k : idx_t { T, I, J }; }; /// Enum for tile edges. struct TileEdge { enum k : size_t { LEFT, BOTTOM, RIGHT, TOP, UNDEFINED }; }; /// Class to store (i, j) indices as a Point2 coordinate. class PointIJ : public Point2 { public: using Point2::Point2; PointIJ(): Point2() {} /// Index constructor. template inline PointIJ(IndexI i, IndexJ j): Point2(static_cast(i), static_cast(j)) {} /// @{ /// Return i or j by value. inline double i() const { return x_[0]; } inline double j() const { return x_[1]; } /// @} /// @{ /// Return i or j by reference inline double& i() { return x_[0]; } inline double& j() { return x_[1]; } /// @} /// @{ /// Round i or j to node index. inline idx_t iNode() const { return static_cast(std::round(i())); } inline idx_t jNode() const { return static_cast(std::round(j())); } /// @} /// @{ /// Round i or j to cell index. inline idx_t iCell() const { return static_cast(std::floor(i())); } inline idx_t jCell() const { return static_cast(std::floor(j())); } /// @} }; /// (t, PointXY) tuple. class PointTXY : public std::pair { using std::pair::pair; public: idx_t& t() { return first; } PointXY& xy() { return second; } const idx_t& t() const { return first; } const PointXY& xy() const { return second; } }; /// (t, PointIJ) tuple. class PointTIJ : public std::pair { using std::pair::pair; public: idx_t& t() { return first; } PointIJ& ij() { return second; } const idx_t& t() const { return first; } const PointIJ& ij() const { return second; } }; /// \brief Class to convert between ij and xy on a tile and its four /// surrounding neighbours. /// /// Helper class to deal with the coordinate system roations and /// displacements between a tile and its neighbours. This class /// is specifcially written to comupute the (x, y) and (t, i, j) /// coordinates of halos that extend across tile boundaries. class NeighbourJacobian { private: using Jacobian = projection::Jacobian; public: /// Default constructor. NeighbourJacobian() = default; /// Grid-data constructor. NeighbourJacobian(const CubedSphereGrid& csGrid); /// Convert ij on local tile t to xy. PointXY xy(const PointIJ& ij, idx_t t) const; /// Convert xy on local tile t to ij. PointIJ ij(const PointXY& xy, idx_t t) const; /// Convert extrapolated xy on tile t to global xy and t (needed for halos). PointTXY xyLocalToGlobal(const PointXY& xyLocal, idx_t tLocal) const; /// Convert extrapolated ij on tile t to global ij and t (needed for halos). PointTIJ ijLocalToGlobal(const PointIJ& ijLocal, idx_t tLocal) const; /// Return true if ij is interior or on the edge of a tile. bool ijInterior(const PointIJ& ij) const; /// Return true if ij is on the edge of a tile. bool ijEdge(const PointIJ& ij) const; /// Return true if ij is in the valid "+" halo extension of at tile. bool ijCross(const PointIJ& ij) const; /// Makes sure points near tile edges are *exactly* on the edge. PointXY snapToEdge(const PointXY& xy, idx_t t) const; private: // Pointer to grid projection. const projection::detail::CubedSphereProjectionBase* csProjection_{}; // Grid size. idx_t N_{}; // Jacobian of xy with respect to ij for each tile. std::array dxy_by_dij_{}; // Jacobian of ij with respect to xy for each tile. std::array dij_by_dxy_{}; // Lower-left xy position on each tile. std::array xy00_{}; // Min xy on each tile. std::array xyMin_{}; // Max xy on each tile. std::array xyMax_{}; // Properties of four neighbours of a tile. struct Neighbours { // Tile ID. std::array t_{}; // Jacobian of remote xy with respect to local xy. std::array dxyGlobal_by_dxyLocal_{}; // Lower left most local xy position on neighbour tiles. std::array xy00Local_; std::array xy00Global_; }; // Set of neighbours for each tile. std::array neighbours_{}; }; } // namespace cubedsphere } // namespace detail } // namespace meshgenerator } // namespace atlas atlas-0.46.0/src/atlas/meshgenerator/detail/MeshGeneratorInterface.cc0000664000175000017500000001003115160212070025751 0ustar alastairalastair/* * (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/meshgenerator/detail/MeshGeneratorInterface.h" #include "atlas/grid/Distribution.h" #include "atlas/grid/Grid.h" #include "atlas/grid/Partitioner.h" #include "atlas/mesh/Mesh.h" #include "atlas/meshgenerator.h" #include "atlas/meshgenerator/detail/MeshGeneratorImpl.h" #include "atlas/runtime/Exception.h" using atlas::Mesh; namespace atlas { namespace meshgenerator { //---------------------------------------------------------------------------------------------------------------------- extern "C" { void atlas__MeshGenerator__delete(MeshGenerator::Implementation* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialisd atlas_MeshGenerator"); delete This; } const MeshGenerator::Implementation* atlas__MeshGenerator__create_noconfig(const char* name) { const MeshGenerator::Implementation* meshgenerator(nullptr); { MeshGenerator m(std::string{name}); meshgenerator = m.get(); meshgenerator->attach(); } meshgenerator->detach(); return meshgenerator; } const MeshGenerator::Implementation* atlas__MeshGenerator__create(const char* name, const eckit::Parametrisation* config) { const MeshGenerator::Implementation* meshgenerator(nullptr); ATLAS_ASSERT(config); { MeshGenerator m(std::string(name), *config); meshgenerator = m.get(); meshgenerator->attach(); } meshgenerator->detach(); return meshgenerator; } Mesh::Implementation* atlas__MeshGenerator__generate__grid_griddist( const MeshGenerator::Implementation* This, const Grid::Implementation* grid, const grid::Distribution::Implementation* distribution) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialisd atlas_MeshGenerator"); ATLAS_ASSERT(grid != nullptr, "Cannot access uninitialisd atlas_Grid"); ATLAS_ASSERT(distribution != nullptr, "Cannot access uninitialisd atlas_GridDistribution"); Mesh::Implementation* m; { Mesh mesh = This->generate(Grid(grid), grid::Distribution(distribution)); mesh.get()->attach(); m = mesh.get(); } m->detach(); return m; } Mesh::Implementation* atlas__MeshGenerator__generate__grid(const MeshGenerator::Implementation* This, const Grid::Implementation* grid) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialisd atlas_MeshGenerator"); ATLAS_ASSERT(grid != nullptr, "Cannot access uninitialisd atlas_Grid"); Mesh::Implementation* m; { Mesh mesh = This->generate(Grid(grid)); ; mesh.get()->attach(); m = mesh.get(); } m->detach(); return m; } Mesh::Implementation* atlas__MeshGenerator__generate__grid_partitioner( const MeshGenerator::Implementation* This, const Grid::Implementation* grid, const grid::Partitioner::Implementation* partitioner) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_MeshGenerator"); ATLAS_ASSERT(grid != nullptr, "Cannot access uninitialised atlas_Grid"); ATLAS_ASSERT(partitioner != nullptr, "Cannot access uninitialised atlas_Partitioner"); Mesh::Implementation* m; { Mesh mesh = This->generate(Grid(grid), grid::Partitioner(partitioner)); mesh.get()->attach(); m = mesh.get(); } m->detach(); return m; } } // extern "C" //---------------------------------------------------------------------------------------------------------------------- } // namespace meshgenerator //---------------------------------------------------------------------------------------------------------------------- } // namespace atlas atlas-0.46.0/src/atlas/meshgenerator/detail/HealpixMeshGenerator.cc0000664000175000017500000012465615160212070025466 0ustar alastairalastair/* * (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 "eckit/utils/Hash.h" #include "atlas/array/Array.h" #include "atlas/array/ArrayView.h" #include "atlas/array/IndexView.h" #include "atlas/field/Field.h" #include "atlas/grid/Distribution.h" #include "atlas/grid/Partitioner.h" #include "atlas/grid/StructuredGrid.h" #include "atlas/library/config.h" #include "atlas/mesh/ElementType.h" #include "atlas/mesh/Elements.h" #include "atlas/mesh/HybridElements.h" #include "atlas/mesh/Mesh.h" #include "atlas/mesh/Nodes.h" #include "atlas/meshgenerator/detail/HealpixMeshGenerator.h" #include "atlas/meshgenerator/detail/MeshGeneratorFactory.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Log.h" #include "atlas/util/CoordinateEnums.h" #include "atlas/util/Topology.h" #define DEBUG_OUTPUT 0 #define DEBUG_OUTPUT_DETAIL 0 using atlas::util::Topology; namespace atlas { namespace meshgenerator { HealpixMeshGenerator::HealpixMeshGenerator(const eckit::Parametrisation& p) { std::string mpi_comm = mpi::comm().name(); p.get("mpi_comm", mpi_comm); options.set("mpi_comm",mpi_comm); configure_defaults(); size_t nb_parts; if (p.get("nb_parts", nb_parts)) { options.set("nb_parts", nb_parts); } size_t part; if (p.get("part", part)) { options.set("part", part); } bool three_dimensional; if (p.get("3d", three_dimensional)) { options.set("3d", three_dimensional); } options.get("3d",three_dimensional); std::string pole_elements{ three_dimensional ? "quads" : options.getString("pole_elements") }; if (p.get("pole_elements", pole_elements)) { if (pole_elements != "pentagons" and pole_elements != "quads") { Log::warning() << "Atlas::HealpixMeshGenerator accepts \"pentagons\" or \"quads\" for \"pole_elements\"." << "Defaulting to pole_elements = quads" << std::endl; } } options.set("pole_elements", pole_elements); std::string partitioner; if (p.get("partitioner", partitioner)) { if (not grid::Partitioner::exists(partitioner)) { Log::warning() << "Atlas does not have support for partitioner " << partitioner << ". " << "Defaulting to use partitioner EqualRegions" << std::endl; partitioner = "equal_regions"; } options.set("partitioner", partitioner); } } void HealpixMeshGenerator::configure_defaults() { std::string mpi_comm; options.get("mpi_comm",mpi_comm); auto& comm = mpi::comm(mpi_comm); // This option sets number of parts the mesh will be split in options.set("nb_parts", comm.size()); // This option sets the part that will be generated options.set("part", comm.rank()); // This option switches between original HEALPix with 1 point at the pole (3d -> true) // or HEALPix with 8 or 4 points at the pole (3d -> false) options.set("3d", false); // This options switches between pentagons and quads as the pole elements for (3d -> false) // choose: "quads" or "pentagons" options.set("pole_elements", "pentagons"); // This options sets the default partitioner std::string partitioner; if (grid::Partitioner::exists("equal_regions") && comm.size() > 1) { partitioner = "equal_regions"; } else { partitioner = "serial"; } options.set("partitioner", partitioner); } // match glb_idx of node in (nb_pole_nodes==8 and ==4)-meshes to glb_idx of nodes in (nb_pole_nodes==1)-mesh gidx_t HealpixMeshGenerator::match_node_idx(const gidx_t& gidx, const int ns) const { const gidx_t nb_nodes_orig = 12 * ns * ns; if (gidx > nb_nodes_ - 1) { // no change in index for the periodic nodes return gidx; } if (nb_pole_nodes_ > 1) { if (gidx == nb_pole_nodes_ / 2) { return 0; } if (gidx == nb_nodes_ - nb_pole_nodes_ / 2) { return nb_nodes_orig + 1; } bool at_north_pole = (gidx < nb_pole_nodes_); bool at_south_pole = (gidx < nb_nodes_ and gidx >= nb_nodes_orig + nb_pole_nodes_); if (at_north_pole) { return nb_nodes_orig + 2 + gidx - (gidx > nb_pole_nodes_ / 2 ? 1 : 0); } if (at_south_pole) { return gidx - nb_pole_nodes_ + nb_pole_nodes_ + 1 - (gidx > nb_nodes_orig + nb_pole_nodes_ * 3 / 2 ? 1 : 0); } return gidx - nb_pole_nodes_ + 1; } // no change for 3d healpix mesh with one node per pole, i.e. nb_pole_nodes = 1 return gidx; } // return "global_id - 1" gidx_t HealpixMeshGenerator::idx_xy_to_x(const int xidx, const int yidx, const int ns) const { ATLAS_ASSERT(yidx < 4 * ns + 1 && yidx >= 0); ATLAS_ASSERT(xidx >= 0); const gidx_t nb_nodes_orig = 12 * ns * ns; auto ghostIdx = [this](int latid) { return this->nb_nodes_ + latid; }; gidx_t ret; if (yidx == 0) { ATLAS_ASSERT(xidx <= nb_pole_nodes_ && xidx >= 0); ret = (xidx != nb_pole_nodes_ ? xidx : ghostIdx(yidx)); } else if (yidx < ns) { ATLAS_ASSERT(xidx < 4 * yidx + 1 && xidx >= 0); ret = (xidx != 4 * yidx ? 2 * yidx * (yidx - 1) + nb_pole_nodes_ + xidx : ghostIdx(yidx)); } else if (yidx <= 2 * ns) { ATLAS_ASSERT(xidx < 4 * ns + 1 && xidx >= 0); ret = (xidx != 4 * ns ? 2 * ns * (ns - 1) + 4 * ns * (yidx - ns) + nb_pole_nodes_ + xidx : ghostIdx(yidx)); } else if (yidx <= 3 * ns) { ATLAS_ASSERT(xidx < 4 * ns + 1 && xidx >= 0); ret = (xidx != 4 * ns ? 2 * ns * (3 * ns + 1) + 4 * ns * (yidx - 2 * ns - 1) + nb_pole_nodes_ + xidx : ghostIdx(yidx)); } else if (yidx == 3 * ns + 1 && ns > 1) { ATLAS_ASSERT(xidx < 4 * (ns - 1) + 1 && xidx >= 0); ret = (xidx != 4 * (ns - 1) ? 2 * ns * (5 * ns + 1) + 4 * ns * (yidx - 3 * ns - 1) + nb_pole_nodes_ + xidx : ghostIdx(yidx)); } else if (yidx < 4 * ns) { ATLAS_ASSERT(xidx < 4 * (ns - (yidx - 3 * ns)) + 1 && xidx >= 0); ret = (xidx != 4 * (ns - (yidx - 3 * ns)) ? 2 * ns * (5 * ns + 1) + 4 * ns * (yidx - 3 * ns - 1) - 2 * (yidx - 3 * ns) * (yidx - 3 * ns - 1) + nb_pole_nodes_ + xidx : ghostIdx(yidx)); } else { ATLAS_ASSERT(xidx <= nb_pole_nodes_ && xidx >= 0); ret = (xidx != nb_pole_nodes_ ? nb_nodes_orig + nb_pole_nodes_ + xidx : ghostIdx(yidx)); } return ret; } // return global_id of the node "above" (xidx,yidx) node gidx_t HealpixMeshGenerator::up_idx(const int xidx, const int yidx, const int ns) const { ATLAS_ASSERT(yidx <= 4 * ns && yidx >= 0); const gidx_t nb_nodes_orig = 12 * ns * ns; auto ghostIdx = [this](int latid) { return this->nb_nodes_ + latid; }; int ret; if (yidx == 0) { ATLAS_ASSERT(xidx < nb_pole_nodes_); ret = (xidx != nb_pole_nodes_ - 1 ? xidx + 1 : ghostIdx(0)); } else if (yidx == 1) { ATLAS_ASSERT(xidx < 4); ret = (nb_pole_nodes_ == 8 ? 2 * xidx : (nb_pole_nodes_ == 4 ? xidx : 0)); } else if (yidx < ns) { ATLAS_ASSERT(xidx < 4 * yidx); if (xidx != 4 * yidx - 1) { ret = 2 * (yidx - 2) * (yidx - 1) + nb_pole_nodes_ + xidx - std::floor(xidx / (double)yidx); } else { ret = ghostIdx(yidx - 1); } } else if (yidx == ns && ns < 3) { ATLAS_ASSERT(xidx < 4 * ns); if (xidx != 4 * ns - 1) { ret = 2 * ns * (ns - 1) + nb_pole_nodes_ - 4 * (ns - 1) + (xidx + 1) / 2; } else { ret = ghostIdx(yidx - 1); } } else if (yidx == ns) { ATLAS_ASSERT(xidx < 4 * ns); if (xidx != 4 * ns - 1) { ret = 2 * (ns - 2) * (ns - 1) + nb_pole_nodes_ + xidx - std::floor(xidx / (double)yidx); } else { ret = ghostIdx(yidx - 1); } } else if (yidx <= 3 * ns) { ATLAS_ASSERT(xidx < 4 * ns); int staggering = (yidx - ns) % 2; if (xidx != 4 * ns - 1 || (xidx == 4 * ns - 1 && staggering)) { ret = 2 * ns * (ns - 1) + nb_pole_nodes_ + 4 * ns * (yidx - ns - 1) + xidx + 1 - staggering; } else { ret = ghostIdx(yidx - 1); } } else if (yidx < 4 * ns - 1) { int yidxl = 4 * ns - yidx; ATLAS_ASSERT(xidx < 4 * yidxl); ret = nb_nodes_orig + nb_pole_nodes_ + 1 - 2 * (yidxl + 2) * (yidxl + 1) + xidx + std::floor(xidx / (double)yidxl); } else if (yidx == 4 * ns - 1) { ATLAS_ASSERT(xidx < 4); ret = nb_nodes_orig + nb_pole_nodes_ - 3 - (ns == 1 ? 4 : 8) + 2 * xidx; } else { ATLAS_ASSERT(xidx < nb_pole_nodes_); if (ns == 1) { if (xidx != nb_pole_nodes_ - 1) { ret = nb_nodes_orig + nb_pole_nodes_ - 4 + (nb_pole_nodes_ != 4 ? (xidx % 2 ? -4 + (xidx + 1) / 2 : xidx / 2) : xidx); } else { ret = (nb_pole_nodes_ == 4 ? nb_nodes_orig + xidx : (nb_pole_nodes_ == 8 ? ghostIdx(4 * ns - 2) : ghostIdx(4 * ns))); } } else { ret = nb_nodes_orig + nb_pole_nodes_ + (xidx % 2 ? xidx - 12 : xidx - 4 - xidx / 2); } } return ret; } // return global_id of the node "below" (xidx,yidx) node gidx_t HealpixMeshGenerator::down_idx(const int xidx, const int yidx, const int ns) const { ATLAS_ASSERT(yidx <= 4 * ns); const gidx_t nb_nodes_orig = 12 * ns * ns; auto ghostIdx = [this](int latid) { return this->nb_nodes_ + latid; }; int ret; if (yidx == 0) { ATLAS_ASSERT(xidx < nb_pole_nodes_); if (ns == 1) { if (xidx != nb_pole_nodes_ - 1) { if (nb_pole_nodes_ == 8) { ret = 8 + (xidx % 2 ? 4 + (xidx + 1) / 2 : xidx / 2); } else { ret = 1 + xidx; } } else { ret = ghostIdx(2); } } else { ret = nb_pole_nodes_ + ((xidx + 1) % 2 ? xidx / 2 : 4 + xidx); } } else if (yidx < ns) { ATLAS_ASSERT(xidx < 4 * yidx); ret = 2 * yidx * (yidx + 1) + nb_pole_nodes_ + 1 + xidx + std::floor(xidx / (double)yidx); } else if (yidx == ns && ns == 1) { ATLAS_ASSERT(xidx < 4); ret = (xidx != 3 ? nb_pole_nodes_ + 5 + xidx : ghostIdx(2)); } else if (yidx == 2 * ns && ns == 1) { ATLAS_ASSERT(xidx < 4); ret = (nb_pole_nodes_ == 8 ? 16 + xidx : (nb_pole_nodes_ == 4 ? 12 + xidx : 9 + xidx)); } else if (yidx < 3 * ns && ns > 1) { ATLAS_ASSERT(xidx < 4 * ns); int staggering = (yidx - ns) % 2; if (xidx != 4 * ns - 1 || (xidx == 4 * ns - 1 && staggering)) { ret = 2 * ns * (ns - 1) + nb_pole_nodes_ + 4 * ns * (yidx - ns + 1) + xidx + (yidx != 3 * ns ? 1 - staggering : 0); } else { ret = ghostIdx(yidx + 1); } } else if (yidx == 4 * ns - 2) { ATLAS_ASSERT(xidx < 8); if (nb_pole_nodes_ == 8) { ret = (xidx != 7 ? nb_nodes_orig + 4 + (xidx + 1) / 2 : ghostIdx(4 * ns - 1)); } else if (nb_pole_nodes_ == 4) { ret = (xidx != 7 ? nb_nodes_orig + (xidx + 1) / 2 : ghostIdx(4 * ns - 1)); } else { ret = (xidx != 7 ? nb_nodes_orig - 3 + (xidx + 1) / 2 : ghostIdx(4 * ns - 1)); } } else if (yidx == 4 * ns - 1) { ATLAS_ASSERT(xidx < 4); if (nb_pole_nodes_ == 8) { ret = nb_nodes_orig + nb_pole_nodes_ + 2 * xidx; } else if (nb_pole_nodes_ == 4) { ret = nb_nodes_orig + nb_pole_nodes_ + xidx; } else { ret = nb_nodes_orig + 1; } } else if (yidx < 4 * ns - 1) { int yidxl = yidx - 3 * ns; ATLAS_ASSERT(xidx < 4 * (ns - yidxl)); if (xidx != 4 * (ns - yidxl) - 1) { ret = 2 * ns * (5 * ns + 1) + nb_pole_nodes_ + 4 * ns * yidxl - 2 * (yidxl + 1) * yidxl + xidx - std::floor(xidx / (double)(ns - yidxl)); } else { ret = ghostIdx(yidx + 1); } } else if (yidx == 4 * ns) { ATLAS_ASSERT(xidx < nb_pole_nodes_); ret = (xidx != nb_pole_nodes_ - 1 ? nb_nodes_orig + nb_pole_nodes_ + xidx + 1 : ghostIdx(yidx)); } else { throw_AssertionFailed("Invalid value of yidx", Here()); } return ret; } // return global_id of the node "to the right of" (xidx,yidx) node gidx_t HealpixMeshGenerator::right_idx(const int xidx, const int yidx, const int ns) const { ATLAS_ASSERT(yidx <= 4 * ns); const gidx_t nb_nodes_orig = 12 * ns * ns; auto ghostIdx = [this](int latid) { return this->nb_nodes_ + latid; }; int ret = -1; if (yidx == 0) { ATLAS_ASSERT(xidx < nb_pole_nodes_); if (ns == 1) { ret = (xidx != nb_pole_nodes_ - 1 ? (xidx % 2 ? nb_pole_nodes_ + (xidx + 1) / 2 : nb_pole_nodes_ + 5 + xidx / 2) : ghostIdx(1)); } else { ret = (xidx != nb_pole_nodes_ - 1 ? (xidx % 2 ? nb_pole_nodes_ + (xidx + 1) / 2 : nb_pole_nodes_ + 5 + xidx) : ghostIdx(1)); } } else if (yidx == 1) { ATLAS_ASSERT(xidx < 4); if (nb_pole_nodes_ == 8) { ret = 1 + 2 * xidx; } else if (nb_pole_nodes_ == 4) { ret = (xidx != 3 ? xidx + 1 : ghostIdx(0)); } else { ret = (xidx != 3 ? xidx + 2 : ghostIdx(1)); } } else if (yidx < ns) { ATLAS_ASSERT(xidx < 4 * yidx); ret = (xidx != 4 * yidx - 1 ? 2 * yidx * (yidx - 1) + nb_pole_nodes_ + 1 + xidx : ghostIdx(yidx)); } else if (yidx == 3 && ns == 1) { ATLAS_ASSERT(xidx < 4); if (nb_pole_nodes_ == 8) { ret = 21 + 2 * xidx; } else if (nb_pole_nodes_ == 4) { ret = (xidx != 3 ? 17 + xidx : ghostIdx(yidx + 1)); } else { ret = (xidx != 3 ? 10 + xidx : ghostIdx(yidx)); } } else if (yidx <= 3 * ns) { ATLAS_ASSERT(xidx < 4 * ns + 1); ret = (xidx != 4 * ns - 1 ? 2 * ns * (ns - 1) + 4 * ns * (yidx - ns) + nb_pole_nodes_ + 1 + xidx : ghostIdx(yidx)); } else if (yidx < 4 * ns - 1 && ns > 1) { int yidxl = yidx - 3 * ns; ATLAS_ASSERT(xidx < 4 * (ns - yidxl)); if (xidx != 4 * (ns - yidxl) - 1) { ret = 2 * ns * (5 * ns + 1) + 4 * ns * (yidx - 3 * ns - 1) - 2 * (yidx - 3 * ns) * (yidx - 3 * ns - 1) + nb_pole_nodes_ + 1 + xidx; } else { ret = ghostIdx(yidx); } } else if (yidx == 4 * ns - 1) { ATLAS_ASSERT(xidx < 4); if (nb_pole_nodes_ == 8) { ret = nb_nodes_orig + nb_pole_nodes_ + 1 + 2 * xidx; } else if (nb_pole_nodes_ == 4) { ret = (xidx != 3 ? nb_nodes_orig + nb_pole_nodes_ + 1 + xidx : ghostIdx(yidx + 1)); } else { ret = (xidx != 3 ? nb_nodes_orig - 2 + xidx : ghostIdx(yidx)); } } else if (yidx == 4 * ns) { ATLAS_ASSERT(xidx < nb_pole_nodes_); if (xidx != nb_pole_nodes_ - 1) { ret = (xidx % 2 ? nb_nodes_orig + 4 + (xidx + 1) / 2 : nb_nodes_orig + 4 - (ns == 1 ? 3 : 7) + xidx); } else { ret = ghostIdx(yidx - 1); } } return ret; } // return global_id - 1 of the pentagon node "to the right of" (xidx,yidx) node // pentagon points are only needed for yidx == 1 and yidx == 4 * ns - 1 gidx_t HealpixMeshGenerator::pentagon_right_idx(const int xidx, const int yidx, const int ns) const { auto ghostIdx = [this](int latid) { return this->nb_nodes_ + latid; }; if (yidx == 1) { return (xidx != 3 ? nb_pole_nodes_ + xidx + 1 : ghostIdx(1)); } else if (yidx == 4 * ns - 1) { return (xidx != 3 ? (12 * ns * ns + xidx + 1) : ghostIdx(yidx)); } else { return -2; } } void HealpixMeshGenerator::generate(const Grid& grid, Mesh& mesh) const { ATLAS_ASSERT(HealpixGrid(grid), "Grid could not be cast to a HealpixGrid"); ATLAS_ASSERT(!mesh.generated()); const StructuredGrid rg = StructuredGrid(grid); if (!rg) { throw_Exception("HealpixMeshGenerator can only work with a Healpix grid", Here()); } size_t nb_parts = options.get("nb_parts"); std::string partitioner_type = "equal_regions"; options.get("partitioner", partitioner_type); mpi::scope::push(options.getString("mpi_comm")); grid::Partitioner partitioner(partitioner_type, nb_parts); grid::Distribution distribution(partitioner.partition(grid)); mpi::scope::pop(); generate(grid, distribution, mesh); } void HealpixMeshGenerator::hash(eckit::Hash& h) const { h.add("HealpixMeshGenerator"); options.hash(h); } void HealpixMeshGenerator::generate(const Grid& grid, const grid::Distribution& distribution, Mesh& mesh) const { ATLAS_TRACE(); Log::debug() << "HealpixMeshGenerator generating mesh from " << grid.name() << std::endl; ATLAS_ASSERT(HealpixGrid(grid), "Grid could not be cast to a HealpixGrid"); ATLAS_ASSERT(!mesh.generated()); if (grid.size() != static_cast(distribution.size())) { std::stringstream msg; msg << "Number of points in grid (" << grid.size() << ") different from " "number of points in grid distribution (" << distribution.size() << ")"; throw_AssertionFailed(msg.str(), Here()); } // clone some grid properties setGrid(mesh, grid, distribution); generate_mesh(grid, distribution, mesh); } void HealpixMeshGenerator::generate_mesh(const StructuredGrid& grid, const grid::Distribution& distribution, Mesh& mesh) const { // This function should do the following: // - define nodes with // mesh.nodes().resize(nnodes); // mesh::Nodes& nodes = mesh.nodes(); // following properties should be defined: // array::ArrayView xy ( nodes.xy() ); // array::ArrayView glb_idx ( nodes.global_index() ); // array::ArrayView part ( nodes.partition() ); // array::ArrayView ghost ( nodes.ghost() ); // array::ArrayView flags ( nodes.flags() ); // - define cells (only quadrilaterals for now) with // mesh.cells().add( mesh::ElementType::create("Quadrilateral"), nquads ); // mesh.cells().add( mesh::ElementType::create("Pentagon"), npents ); // further define cells with // array::ArrayView cells_glb_idx( mesh.cells().global_index() // ); // array::ArrayView cells_part( mesh.cells().partition() ); // - define connectivity with // mesh::HybridElements::Connectivity& node_connectivity = // mesh.cells().node_connectivity(); // node_connectivity.set( jcell, cell_nodes ); // where cell_nodes is a 4-element or 5-element integer array containing the LOCAL // indices of the nodes // // The rule do determine if a cell belongs to a proc is the following with some // exceptions near poles: cell belongs to a proc if the left corner of the cell belongs to that proc. ATLAS_TRACE(); ATLAS_ASSERT(HealpixGrid(grid)); const int mypart = options.get("part"); const int nparts = options.get("nb_parts"); const bool three_dimensional = options.get("3d"); const std::string pole_elements = options.get("pole_elements"); const int nb_pole_nodes = (pole_elements == "pentagons") ? 4 : (three_dimensional ? 1 : 8); const int ny = grid.ny() + 2; const int ns = (ny - 1) / 4; const int nvertices = 12 * ns * ns + 2 * nb_pole_nodes; nb_pole_nodes_ = nb_pole_nodes; nb_points_ = 12 * ns * ns + (nb_pole_nodes == 8 ? 8 : 0); nb_nodes_ = nvertices; int inode; auto nb_lat_nodes = [ny, nb_pole_nodes, &grid](int latid) { return ((latid == 0) or (latid == ny - 1) ? nb_pole_nodes : grid.nx()[latid - 1]); }; int ii, ix, iy, ii_ghost, ii_glb; int iy_min, iy_max; // a belt (iy_min:iy_max) surrounding the nodes on this processor int nnodes_nonghost; // non-ghost node: belongs to this part // vector of local indices: necessary for remote indices of ghost nodes std::vector local_idx(nvertices, -1); std::vector current_idx(nparts, 0); // index counter for each proc // ANSATZ: requirement on the partitioner auto compute_part = [&](int iy, gidx_t ii_glb) -> int { // nodes at the pole belong to proc_0 (north) and proc_maxRank (south) // a node gets its proc rank from the element for which this node would be its west vertex return (iy == 0 ? 0 : (iy == ny - 1 ? nparts - 1 : distribution.partition(ii_glb - nb_pole_nodes))); }; #if DEBUG_OUTPUT_DETAIL for (iy = 0; iy < ny; iy++) { int nx = nb_lat_nodes(iy); for (ix = 0; ix < nx; ix++) { Log::info() << "iy, ix, glb_idx, up_idx, down_idx, right_idx, pent_right_idx : " << iy << ", " << ix << ", " << idx_xy_to_x(ix, iy, ns) + 1 << ", " << up_idx(ix, iy, ns) + 1 << ", " << down_idx(ix, iy, ns) + 1 << ", " << right_idx(ix, iy, ns) + 1 << ", " << pentagon_right_idx(ix, iy, ns) + 1 << std::endl; } Log::info() << std::endl; } #endif // loop over all points to determine local indices and surrounding rectangle ii_glb = 0; // global index starting from 0 iy_min = ny + 1; iy_max = 0; nnodes_nonghost = 0; for (iy = 0; iy < ny; iy++) { int nx = nb_lat_nodes(iy); for (ix = 0; ix < nx; ix++) { int proc_id = compute_part(iy, ii_glb); local_idx[ii_glb] = current_idx[proc_id]++; if (proc_id == mypart) { ++nnodes_nonghost; iy_min = std::min(iy_min, iy); iy_max = std::max(iy_max, iy); } ++ii_glb; } } #if DEBUG_OUTPUT_DETAIL inode = 0; Log::info() << "local_idx : " << std::endl; for (size_t ilat = 0; ilat < ny; ilat++) { for (size_t ilon = 0; ilon < nb_lat_nodes(ilat); ilon++) { Log::info() << std::setw(4) << local_idx[inode]; inode++; } Log::info() << std::endl; } inode = 0; Log::info() << "global_idx : " << std::endl; for (size_t ilat = 0; ilat < ny; ilat++) { for (size_t ilon = 0; ilon < nb_lat_nodes(ilat); ilon++) { Log::info() << std::setw(4) << inode; inode++; } Log::info() << std::endl; } #endif // dimensions of surrounding belt (SB) int nnodes_SB = 0; if (iy_min <= 2) { iy_min = 0; } else { --iy_min; } if (iy_max >= ny - 3) { iy_max = ny - 1; } else { ++iy_max; } for (int iy = iy_min; iy <= iy_max; iy++) { // (east) periodic point adds +1 here nnodes_SB += nb_lat_nodes(iy) + 1; } #if DEBUG_OUTPUT Log::info() << "[" << mypart << "] : nnodes_SB = " << nnodes_SB << "\n"; #endif // partitions and local indices in SB std::vector parts_SB(nnodes_SB, -1); std::vector local_idx_SB(nnodes_SB, -1); std::vector is_ghost_SB(nnodes_SB, true); // starting from index 0, first global node-index for this partition int parts_sidx = idx_xy_to_x(0, iy_min, ns); ii = 0; // index inside SB ii_ghost = nnodes_SB - (iy_max - iy_min + 1); // first local ghost idx for (iy = iy_min; iy <= iy_max; iy++) { int nx = nb_lat_nodes(iy) + 1; for (ix = 0; ix < nx; ix++) { if (ix != nx - 1) { ii_glb = ii + parts_sidx; parts_SB[ii] = compute_part(iy, ii_glb); local_idx_SB[ii] = ii; is_ghost_SB[ii] = !(parts_SB[ii] == mypart); ++ii; } else { parts_SB[ii_ghost] = compute_part(iy, ii_glb); local_idx_SB[ii_ghost] = ii_ghost; is_ghost_SB[ii_ghost] = true; ++ii_ghost; } } } #if DEBUG_OUTPUT_DETAIL Log::info() << "[" << mypart << "] : " << "parts_SB = "; for (ii = 0; ii < nnodes_SB; ii++) { Log::info() << parts_SB[ii] << ","; } Log::info() << std::endl; Log::info() << "[" << mypart << "] : " << "local_idx_SB = "; for (ii = 0; ii < nnodes_SB; ii++) { Log::info() << local_idx_SB[ii] << ","; } Log::info() << std::endl; Log::info() << "[" << mypart << "] : " << "is_ghost_SB = "; for (ii = 0; ii < nnodes_SB; ii++) { Log::info() << is_ghost_SB[ii] << ","; } Log::info() << std::endl; #endif // vectors marking nodes that are necessary for this proc's cells std::vector is_node_SB(nnodes_SB, false); // determine number of cells and number of nodes int nnodes = 0; int nquads = 0; int npents = 0; ii = 0; int glb2loc_ghost_offset = -nnodes_SB + iy_max + nb_nodes_ + 1; auto get_local_id = [this, &parts_sidx, &glb2loc_ghost_offset](gidx_t gidx) { return gidx - (gidx < nb_nodes_ ? parts_sidx : glb2loc_ghost_offset); }; for (iy = iy_min; iy <= iy_max; iy++) { int nx = nb_lat_nodes(iy); for (ix = 0; ix < nx; ix++) { if (not is_ghost_SB[ii]) { const bool at_pole = (iy == 0 or iy == ny - 1); bool not_duplicate_cell = (at_pole ? (ix % 2) : 1); if (at_pole and nb_pole_nodes < 8) { // nodes at the poles do not own any pentagons // if nb_pole_node=1, the node at the poles does not own any quads not_duplicate_cell = false; } if (not at_pole or not_duplicate_cell) { if (not is_node_SB[ii]) { ++nnodes; is_node_SB[ii] = true; } bool is_pentagon = (pole_elements == "pentagons" and (iy == 1 or iy == ny - 2)); if (is_pentagon) { ++npents; // mark the pentagon right node idx_t iil = get_local_id(pentagon_right_idx(ix, iy, ns)); if (not is_node_SB[iil]) { ++nnodes; is_node_SB[iil] = true; } } else { ++nquads; } // mark upper corner idx_t iil = get_local_id(up_idx(ix, iy, ns)); if (not is_node_SB[iil]) { ++nnodes; is_node_SB[iil] = true; } // mark lower corner iil = get_local_id(down_idx(ix, iy, ns)); if (not is_node_SB[iil]) { ++nnodes; is_node_SB[iil] = true; } // mark right corner iil = get_local_id(right_idx(ix, iy, ns)); if (not is_node_SB[iil]) { ++nnodes; is_node_SB[iil] = true; } } } ++ii; } } int ncells = nquads + npents; ATLAS_ASSERT(ncells > 0); #if DEBUG_OUTPUT Log::info() << "[" << mypart << "] : " << "nnodes = " << nnodes << ", nquads = " << nquads << ", npents = " << npents << ", parts_sidx = " << parts_sidx << std::endl; Log::info() << "[" << mypart << "] : " << "iy_min = " << iy_min << ", iy_max = " << iy_max << std::endl; #endif #if DEBUG_OUTPUT_DETAIL Log::info() << "[" << mypart << "] : " << "is_node_SB = "; for (int ii = 0; ii < nnodes_SB; ii++) { Log::info() << is_node_SB[ii] << ","; } Log::info() << std::endl; #endif // define nodes and associated properties mesh.nodes().resize(nnodes); mesh::Nodes& nodes = mesh.nodes(); auto xy = array::make_view(nodes.xy()); auto lonlat = array::make_view(nodes.lonlat()); auto glb_idx = array::make_view(nodes.global_index()); auto remote_idx = array::make_indexview(nodes.remote_index()); auto part = array::make_view(nodes.partition()); auto ghost = array::make_view(nodes.ghost()); auto halo = array::make_view(nodes.halo()); auto flags = array::make_view(nodes.flags()); int quad_begin; int pent_begin; // define cells and associated properties mesh.cells().add(mesh::ElementType::create("Quadrilateral"), nquads); quad_begin = mesh.cells().elements(0).begin(); pent_begin = quad_begin + mesh.cells().elements(0).size(); if (pole_elements == "pentagons") { mesh.cells().add(mesh::ElementType::create("Pentagon"), npents); pent_begin = mesh.cells().elements(1).begin(); } auto cells_part = array::make_view(mesh.cells().partition()); auto cells_glb_idx = array::make_view(mesh.cells().global_index()); auto& node_connectivity = mesh.cells().node_connectivity(); std::array cell_nodes; int jquadcell = quad_begin; int jpentcell = pent_begin; int inode_nonghost, inode_ghost; // loop over nodes and set properties inode_nonghost = 0; inode_ghost = nnodes_nonghost; // ghost nodes start counting after nonghost nodes ii = 0; // index inside SB for (iy = iy_min; iy <= iy_max; iy++) { int nx = nb_lat_nodes(iy) + 1; for (ix = 0; ix < nx; ix++) { int iil = idx_xy_to_x(ix, iy, ns); iil -= (iil < nb_nodes_ ? parts_sidx : -nnodes_SB + iy_max + nb_nodes_ + 1); if (is_node_SB[iil]) { // set node counter if (is_ghost_SB[iil]) { inode = inode_ghost++; } else { inode = inode_nonghost++; } // flags Topology::reset(flags(inode)); glb_idx(inode) = 1 + match_node_idx(idx_xy_to_x(ix, iy, ns), ns); // grid coordinates double _xy[2]; double xy1[2], xy2[2]; if (iy == 0) { _xy[0] = (nb_pole_nodes == 8 ? 45. * ix : (nb_pole_nodes == 4 ? 90. * ix : 180.)); _xy[1] = 90.; Topology::set(flags(inode), Topology::BC); } else if (iy == ny - 1) { _xy[0] = (nb_pole_nodes == 8 ? 45. * ix : (nb_pole_nodes == 4 ? 90. * ix : 180.)); _xy[1] = -90.; Topology::set(flags(inode), Topology::BC); } else if (ix == nx - 1) { grid.xy(ix - 1, iy - 1, xy1); grid.xy(ix - 2, iy - 1, xy2); _xy[0] = 1.5 * xy1[0] - 0.5 * xy2[0]; _xy[1] = xy1[1]; Topology::set(flags(inode), Topology::BC); } else if (ix == 0) { grid.xy(ix + 1, iy - 1, xy1); grid.xy(ix, iy - 1, xy2); _xy[0] = 1.5 * xy2[0] - 0.5 * xy1[0]; _xy[1] = xy1[1]; Topology::set(flags(inode), Topology::BC); } else { grid.xy(ix, iy - 1, xy1); grid.xy(ix - 1, iy - 1, xy2); _xy[0] = 0.5 * (xy1[0] + xy2[0]); _xy[1] = xy1[1]; } if (Topology::check(flags(inode), Topology::BC)) { if (iy == 0) { Topology::set(flags(inode), Topology::NORTH); } else if (iy == ny - 1) { Topology::set(flags(inode), Topology::SOUTH); } if (ix == 0) { Topology::set(flags(inode), Topology::WEST); } else if (ix == nx - 1) { Topology::set(flags(inode), Topology::EAST | Topology::GHOST); ATLAS_ASSERT(is_ghost_SB[iil]); } } xy(inode, LON) = _xy[LON]; xy(inode, LAT) = _xy[LAT]; // geographic coordinates by using projection grid.projection().xy2lonlat(_xy); lonlat(inode, LON) = _xy[LON]; lonlat(inode, LAT) = _xy[LAT]; part(inode) = parts_SB[iil]; ghost(inode) = is_ghost_SB[iil]; halo(inode) = 0; if (ghost(inode)) { Topology::set(flags(inode), Topology::GHOST); remote_idx(inode) = local_idx_SB[iil]; } else { remote_idx(inode) = -1; } if (Topology::check(flags(inode), Topology::BC | Topology::EAST)) { part(inode) = mypart; // To be fixed later } local_idx_SB[iil] = inode; #if DEBUG_OUTPUT_DETAIL Log::info() << "[" << mypart << "] : " << "New node \tinode=" << inode << "; iil= " << iil << "; ix=" << ix << "; iy=" << iy << "; glon=" << lonlat(inode, 0) << "; glat=" << lonlat(inode, 1) << "; glb_idx=" << glb_idx(inode) << "; loc_idx=" << local_idx_SB[iil] << std::endl; #endif } ii += (ix != nx - 1 ? 1 : 0); } } auto get_local_idx_SB = [&local_idx_SB, &get_local_id](gidx_t gidx) { return local_idx_SB[get_local_id(gidx)]; }; ii = 0; // index inside SB (surrounding belt) int jcell_offset = 0; // global index offset due to extra points at the north pole gidx_t jcell = 0; // global cell counter gidx_t points_in_partition = 0; for (iy = iy_min; iy <= iy_max; iy++) { int nx = nb_lat_nodes(iy) + 1; points_in_partition += (nx - 1); for (ix = 0; ix < nx; ix++) { const bool at_pole = (iy == 0 or iy == ny - 1); int not_duplicate_cell = (at_pole ? ix % 2 : 1); if (at_pole and (not not_duplicate_cell or nb_pole_nodes < 8)) { // if nb_pole_nodes = 4 : the pole nodes do not own any pentagons // if nb_pole_nodes = 1 : the pole nodes do not own any quads continue; } bool pentagon = (iy == 1 or iy == 4 * ns - 1) and pole_elements == "pentagons"; int iil = idx_xy_to_x(ix, iy, ns); iil -= (iil < nb_nodes_ ? parts_sidx : -nnodes_SB + iy_max + nb_nodes_ + 1); if (not is_ghost_SB[iil]) { jcell++; // a cell will be added // define cell vertices (in local indices) in cell_nodes int node_id = 0; cell_nodes[node_id++] = get_local_idx_SB(idx_xy_to_x(ix, iy, ns)); cell_nodes[node_id++] = get_local_idx_SB(down_idx(ix, iy, ns)); bool south_hemisphere = (iy > 2 * ns); if (pentagon && not south_hemisphere) { cell_nodes[node_id++] = get_local_idx_SB(pentagon_right_idx(ix, iy, ns)); } cell_nodes[node_id++] = get_local_idx_SB(right_idx(ix, iy, ns)); if (pentagon && south_hemisphere) { // in the south hemisphere the pentagon point comes in a different clock-wise ordering cell_nodes[node_id++] = get_local_idx_SB(pentagon_right_idx(ix, iy, ns)); } cell_nodes[node_id++] = get_local_idx_SB(up_idx(ix, iy, ns)); // match global cell indexing for the three healpix versions if (nb_pole_nodes == 1) { cells_glb_idx(jquadcell) = parts_sidx + points_in_partition - nx + ix + 1; } else if (nb_pole_nodes == 8) { if (iy == 0) { cells_glb_idx(jquadcell) = 12 * ns * ns + 1 + ix / 2; jcell_offset++; } else if (iy == ny - 1) { cells_glb_idx(jquadcell) = 12 * ns * ns + 5 + ix / 2; jcell_offset++; } else { cells_glb_idx(jquadcell) = parts_sidx + iil - 3 - (mypart != 0 ? 4 : jcell_offset); } } else if (nb_pole_nodes == 4) { if (iy == 0 or iy == ny - 1) { continue; } if (pentagon) { cells_glb_idx(jpentcell) = (mypart != 0 ? 12 * ns * ns - 3 + ix : jcell); jcell_offset++; } else { cells_glb_idx(jquadcell) = parts_sidx + points_in_partition - nx - 6 + ix + (mypart != 0 ? 4 : jcell_offset); } } #if DEBUG_OUTPUT_DETAIL std::cout << "[" << mypart << "] : "; if (pentagon) { std::cout << "New pent: loc-idx " << jpentcell << ", glb-idx " << cells_glb_idx(jpentcell) << ": "; } else { std::cout << "New quad: loc-idx " << jquadcell << ", glb-idx " << cells_glb_idx(jquadcell) << ": "; } std::cout << glb_idx(cell_nodes[0]) << "," << glb_idx(cell_nodes[1]) << "," << glb_idx(cell_nodes[2]) << "," << glb_idx(cell_nodes[3]); if (pentagon) { std::cout << "," << glb_idx(cell_nodes[4]); } std::cout << std::endl; #endif // add cell to the node connectivity table if (pentagon) { if (south_hemisphere) { // Rotate by 2 so that output in 3d shows healpix quad when taking the first 4 nodes // // 5 3 // .´ `. .´ `. // 1 4 ----rotate-by-2---> 4 2 // | | | | // 2------3 --- south pole --- 5------1 // std::rotate(cell_nodes.begin(),cell_nodes.begin()+2,cell_nodes.end()); } cells_part(jpentcell) = mypart; node_connectivity.set(jpentcell, cell_nodes.data()); ++jpentcell; } else { cells_part(jquadcell) = mypart; node_connectivity.set(jquadcell, cell_nodes.data()); ++jquadcell; } } ii += (ix != nx - 1 ? 1 : 0); } } #if DEBUG_OUTPUT_DETAIL // list nodes Log::info() << "Listing nodes ..."; for (inode = 0; inode < nnodes; inode++) { std::cout << "[" << mypart << "] : " << " node " << inode << ": ghost = " << ghost(inode) << ", glb_idx = " << glb_idx(inode) << ", part = " << part(inode) << ", lon = " << lonlat(inode, 0) << ", lat = " << lonlat(inode, 1) << ", remote_idx = " << remote_idx(inode) << std::endl; } for (gidx_t jcell = quad_begin; jcell < nquads; jcell++) { std::cout << "[" << mypart << "] : " << " cell " << jcell << ", glb-idx " << cells_glb_idx(jcell) << ": " << glb_idx(node_connectivity(jcell, 0)) << "," << glb_idx(node_connectivity(jcell, 1)) << "," << glb_idx(node_connectivity(jcell, 2)) << "," << glb_idx(node_connectivity(jcell, 3)) << std::endl; } for (gidx_t jcell = pent_begin; jcell < nquads; jcell++) { std::cout << "[" << mypart << "] : " << " cell " << jcell << ", glb-idx " << cells_glb_idx(jcell) << ": " << glb_idx(node_connectivity(jcell, 0)) << "," << glb_idx(node_connectivity(jcell, 1)) << "," << glb_idx(node_connectivity(jcell, 2)) << "," << glb_idx(node_connectivity(jcell, 3)) << std::endl; } #endif mesh.metadata().set("nb_parts",options.getInt("nb_parts")); mesh.metadata().set("part",options.getInt("part")); mesh.metadata().set("mpi_comm",options.getString("mpi_comm")); mesh.metadata().set("nb_nodes_including_halo[0]", nodes.size()); nodes.metadata().set("NbRealPts", size_t(nnodes)); nodes.metadata().set("NbVirtualPts", size_t(0)); nodes.global_index().metadata().set("human_readable", true); nodes.global_index().metadata().set("min", 1); nodes.global_index().metadata().set("max", nb_nodes_ + grid.ny() + 2); mesh.cells().global_index().metadata().set("human_readable", true); mesh.cells().global_index().metadata().set("min", 1); mesh.cells().global_index().metadata().set("max", nb_points_); //generateGlobalElementNumbering(mesh); } // generate_mesh namespace { static MeshGeneratorBuilder __HealpixMeshGenerator(HealpixMeshGenerator::static_type()); } } // namespace meshgenerator } // namespace atlas atlas-0.46.0/src/atlas/meshgenerator/detail/CubedSphereDualMeshGenerator.h0000664000175000017500000000335515160212070026725 0ustar alastairalastair/* * (C) Crown Copyright 2021 Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ #pragma once #include "atlas/array.h" #include "atlas/meshgenerator/MeshGenerator.h" #include "atlas/meshgenerator/detail/MeshGeneratorImpl.h" #include "atlas/util/Config.h" #include "atlas/util/Metadata.h" #ifndef DOXYGEN_SHOULD_SKIP_THIS namespace eckit { class Parametrisation; } namespace atlas { class CubedSphereGrid; class Mesh; template class vector; } // namespace atlas namespace atlas { namespace grid { class Distribution; } // namespace grid } // namespace atlas #endif namespace atlas { namespace meshgenerator { //-------------------------------------------------------------------------------------------------- class CubedSphereDualMeshGenerator : public MeshGenerator::Implementation { public: CubedSphereDualMeshGenerator(const eckit::Parametrisation& = util::NoConfig()); virtual void generate(const Grid&, const grid::Distribution&, Mesh&) const override; virtual void generate(const Grid&, Mesh&) const override; using MeshGenerator::Implementation::generate; static std::string static_type() { return "cubedsphere_dual"; } std::string type() const override { return static_type(); } private: virtual void hash(eckit::Hash&) const override; void configure_defaults(); void generate_mesh(const CubedSphereGrid&, const grid::Distribution&, Mesh&) const; void set_metadata(Mesh&) const; private: util::Metadata options; }; //-------------------------------------------------------------------------------------------------- } // namespace meshgenerator } // namespace atlas atlas-0.46.0/src/atlas/meshgenerator/detail/NodalCubedSphereMeshGenerator.cc0000664000175000017500000003320215160212070027225 0ustar alastairalastair/* * (C) Copyright 2020 UCAR * * 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. */ #include #include #include #include #include #include "eckit/utils/Hash.h" #include "atlas/field/Field.h" #include "atlas/grid/CubedSphereGrid.h" #include "atlas/grid/Distribution.h" #include "atlas/grid/Partitioner.h" #include "atlas/library/config.h" #include "atlas/mesh/Connectivity.h" #include "atlas/mesh/ElementType.h" #include "atlas/mesh/Elements.h" #include "atlas/mesh/HybridElements.h" #include "atlas/mesh/Mesh.h" #include "atlas/mesh/Nodes.h" #include "atlas/meshgenerator/detail/MeshGeneratorFactory.h" #include "atlas/meshgenerator/detail/NodalCubedSphereMeshGenerator.h" #include "atlas/meshgenerator/detail/cubedsphere/CubedSphereUtility.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Log.h" #include "atlas/util/Constants.h" #include "atlas/util/CoordinateEnums.h" #define DEBUG_OUTPUT 0 #define DEBUG_OUTPUT_DETAIL 0 using Topology = atlas::mesh::Nodes::Topology; namespace atlas { namespace meshgenerator { // ------------------------------------------------------------------------------------------------- NodalCubedSphereMeshGenerator::NodalCubedSphereMeshGenerator(const eckit::Parametrisation& p) {} // ------------------------------------------------------------------------------------------------- void NodalCubedSphereMeshGenerator::configure_defaults() {} // ------------------------------------------------------------------------------------------------- void NodalCubedSphereMeshGenerator::generate(const Grid& grid, Mesh& mesh) const { // Check for proper grid and need for mesh ATLAS_ASSERT(!mesh.generated()); const CubedSphereGrid csg = CubedSphereGrid(grid); if (!csg) { throw_Exception("NodalCubedSphereMeshGenerator can only work with a cubedsphere grid", Here()); } // Number of processors //size_t nb_parts = options.get( "nb_parts" ); // Decomposition type std::string partitioner_type = "checkerboard"; options.get("checkerboard", partitioner_type); // Partitioner grid::Partitioner partitioner(partitioner_type, 1); grid::Distribution distribution(partitioner.partition(grid)); generate(grid, distribution, mesh); } // ------------------------------------------------------------------------------------------------- void NodalCubedSphereMeshGenerator::generate(const Grid& grid, const grid::Distribution& distribution, Mesh& mesh) const { const auto csgrid = CubedSphereGrid(grid); using namespace detail::cubedsphere; const int N = csgrid.N(); const int nTiles = csgrid.tiles().size(); // N must be greater than 1. if (N < 2) { throw_Exception("N must be greater than 1 for NodalCubedSphereMeshGenerator", Here()); } // grid must have node staggering. if (csgrid.stagger() != "L") { throw_Exception( "NodalCubedSphereMeshGenerator will only work with a" "nodal grid. Try CubedSphereMeshGenerator instead.", Here()); } // Get tiles const auto& csprojection = csgrid.cubedSphereProjection(); // grid must use FV3Tiles class. if (csprojection.getCubedSphereTiles().type() != "cubedsphere_fv3") { throw_Exception("NodalCubedSphereMeshGenerator only works with FV3 tiles", Here()); } // Make a list linking ghost (t, i, j) values to known (t, i, j) // This will be different for each cube-sphere once MeshGenerator is generalised. using Tij = typename std::array; // first: ghost (t, i, j); second owned (t, i, j). using TijPair = typename std::pair; auto ghostToOwnedTij = std::vector{}; // ------------------------------------------------------------------------- // BEGIN FV3 SPECIFIC MAP // ------------------------------------------------------------------------- // Special points. ghostToOwnedTij.push_back(TijPair{{0, N, N}, {2, 0, 0}}); ghostToOwnedTij.push_back(TijPair{{1, N, N}, {3, 0, 0}}); ghostToOwnedTij.push_back(TijPair{{2, N, N}, {4, 0, 0}}); ghostToOwnedTij.push_back(TijPair{{3, N, N}, {5, 0, 0}}); ghostToOwnedTij.push_back(TijPair{{4, N, N}, {0, 0, 0}}); ghostToOwnedTij.push_back(TijPair{{5, N, N}, {1, 0, 0}}); ghostToOwnedTij.push_back(TijPair{{2, 0, N}, {0, 0, N}}); ghostToOwnedTij.push_back(TijPair{{4, 0, N}, {0, 0, N}}); ghostToOwnedTij.push_back(TijPair{{3, N, 0}, {1, N, 0}}); ghostToOwnedTij.push_back(TijPair{{5, N, 0}, {1, N, 0}}); // Tile 1 for (idx_t ix = 1; ix < N; ix++) { ghostToOwnedTij.push_back(TijPair{{0, ix, N}, {2, 0, N - ix}}); } for (idx_t iy = 0; iy < N; iy++) { ghostToOwnedTij.push_back(TijPair{{0, N, iy}, {1, 0, iy}}); } // Tile 2 for (idx_t ix = 0; ix < N; ix++) { ghostToOwnedTij.push_back(TijPair{{1, ix, N}, {2, ix, 0}}); } for (idx_t iy = 1; iy < N; iy++) { ghostToOwnedTij.push_back(TijPair{{1, N, iy}, {3, N - iy, 0}}); } // Tile 3 for (idx_t ix = 1; ix < N; ix++) { ghostToOwnedTij.push_back(TijPair{{2, ix, N}, {4, 0, N - ix}}); } for (idx_t iy = 0; iy < N; iy++) { ghostToOwnedTij.push_back(TijPair{{2, N, iy}, {3, 0, iy}}); } // Tile 4 for (idx_t ix = 0; ix < N; ix++) { ghostToOwnedTij.push_back(TijPair{{3, ix, N}, {4, ix, 0}}); } for (idx_t iy = 1; iy < N; iy++) { ghostToOwnedTij.push_back(TijPair{{3, N, iy}, {5, N - iy, 0}}); } // Tile 5 for (idx_t ix = 1; ix < N; ix++) { ghostToOwnedTij.push_back(TijPair{{4, ix, N}, {0, 0, N - ix}}); } for (idx_t iy = 0; iy < N; iy++) { ghostToOwnedTij.push_back(TijPair{{4, N, iy}, {5, 0, iy}}); } // Tile 6 for (idx_t ix = 0; ix < N; ix++) { ghostToOwnedTij.push_back(TijPair{{5, ix, N}, {0, ix, 0}}); } for (idx_t iy = 1; iy < N; iy++) { ghostToOwnedTij.push_back(TijPair{{5, N, iy}, {1, N - iy, 0}}); } // ------------------------------------------------------------------------- // END FV3 SPECIFIC MAP // ------------------------------------------------------------------------- ATLAS_TRACE("NodalCubedSphereMeshGenerator::generate"); Log::debug() << "Number of faces per tile edge = " << std::to_string(N) << std::endl; // Number of nodes int nnodes = nTiles * N * N + 2; // Number of unique grid nodes int nnodes_all = nTiles * (N + 1) * (N + 1); // Number of grid nodes including edge and corner duplicates int ncells = nTiles * N * N; // Number of unique grid cells // Construct mesh nodes // -------------------- mesh.nodes().resize(nnodes_all); mesh::Nodes& nodes = mesh.nodes(); auto xy = array::make_view(nodes.xy()); auto lonlat = array::make_view(nodes.lonlat()); auto glb_idx = array::make_view(nodes.global_index()); auto remote_idx = array::make_indexview(nodes.remote_index()); auto part = array::make_view(nodes.partition()); auto ghost = array::make_view(nodes.ghost()); auto flags = array::make_view(nodes.flags()); // Loop over entire grid // --------------------- // Node array will include duplicates of the grid points to make it easier to fill up the // neighbours. However the mesh will not contain these points so no Ghost nodes are required. // We could include the duplicate points in the array if we make them Ghost nodes but it is not // clear if this provides any benefit. array::ArrayT NodeArrayT(nTiles, N + 1, N + 1); // All grid points including duplicates auto NodeArray = array::make_view(NodeArrayT); // Add owned nodes to node array idx_t nOwned = 0; auto addOwnedNode = [&](Tij tijOwned) { // Set node array NodeArray(tijOwned[0], tijOwned[1], tijOwned[2]) = nOwned; // Get xy from global xy grid array double xy_[3]; csgrid.xy(tijOwned[1], tijOwned[2], tijOwned[0], xy_); xy(nOwned, XX) = xy_[XX]; xy(nOwned, YY) = xy_[YY]; // Get lonlat from global lonlat array double lonlat_[2]; csgrid.lonlat(tijOwned[1], tijOwned[2], tijOwned[0], lonlat_); lonlat(nOwned, LON) = lonlat_[LON]; lonlat(nOwned, LAT) = lonlat_[LAT]; // Is not ghost node mesh::Nodes::Topology::reset(flags(nOwned)); ghost(nOwned) = 0; glb_idx(nOwned) = nOwned + 1; remote_idx(nOwned) = nOwned; part(nOwned) = distribution.partition(nOwned); ++nOwned; return; }; // Loop over owned (t, i, j) for (auto& p : csgrid.tij()) { addOwnedNode(Tij{p.t(), p.i(), p.j()}); } // Assert that the correct number of nodes have been set ATLAS_ASSERT(nnodes == nOwned, "Insufficient nodes"); // Vector of ghost global index of each ghost point auto ghostGblIdx = std::vector(); auto ownedGblIdx = std::vector(); // Add ghost nodes to node array // (nGhost started after nOwned) auto nGhost = nOwned; auto addGhostNode = [&](Tij tijGhost, Tij tijOwned) { // Get concrete node id auto nOwned = NodeArray(tijOwned[0], tijOwned[1], tijOwned[2]); // Add ghost node to NodeArray NodeArray(tijGhost[0], tijGhost[1], tijGhost[2]) = nGhost; // "Create" ghost xy coordinate. // Get Jacobian of coords rtw indices auto x0 = xy(NodeArray(tijGhost[0], 0, 0), XX); auto y0 = xy(NodeArray(tijGhost[0], 0, 0), YY); auto dx_di = xy(NodeArray(tijGhost[0], 1, 0), XX) - x0; auto dx_dj = xy(NodeArray(tijGhost[0], 0, 1), XX) - x0; auto dy_di = xy(NodeArray(tijGhost[0], 1, 0), YY) - y0; auto dy_dj = xy(NodeArray(tijGhost[0], 0, 1), YY) - y0; // Set xy coordinates xy(nGhost, XX) = x0 + tijGhost[1] * dx_di + tijGhost[2] * dx_dj; xy(nGhost, YY) = y0 + tijGhost[1] * dy_di + tijGhost[2] * dy_dj; // Same lonlat as concrete points lonlat(nGhost, LON) = lonlat(nOwned, LON); lonlat(nGhost, LAT) = lonlat(nOwned, LAT); // Is ghost node mesh::Nodes::Topology::set(flags(nGhost), mesh::Nodes::Topology::GHOST); ghost(nGhost) = 1; // Partitioning logic to be added in future PR glb_idx(nGhost) = nGhost + 1; remote_idx(nGhost) = nGhost; // not sure (below - for multiple PEs) part(nGhost) = distribution.partition(nOwned); // Append metadata // Global indicies of ghost node and owned node ghostGblIdx.push_back(nGhost + 1); ownedGblIdx.push_back(nOwned + 1); ++nGhost; }; // Loop over ghost (t, i, j) for (auto& pPair : ghostToOwnedTij) { addGhostNode(pPair.first, pPair.second); } // Assert that the correct number of nodes have been set when duplicates are added ATLAS_ASSERT(nnodes_all == nGhost, "Insufficient nodes"); for (idx_t it = 0; it < nTiles; it++) { for (idx_t ix = 0; ix < N + 1; ix++) { for (idx_t iy = 0; iy < N + 1; iy++) { ATLAS_ASSERT(NodeArray(it, ix, iy) != -9999, "Node Array Not Set Properly"); } } } // Cells in mesh mesh.cells().add(mesh::ElementType::create("Quadrilateral"), nTiles * N * N); //int quad_begin = mesh.cells().elements( 0 ).begin(); auto cells_part = array::make_view(mesh.cells().partition()); auto cells_gidx = array::make_view(mesh.cells().global_index()); auto cells_ridx = array::make_indexview(mesh.cells().remote_index()); atlas::mesh::HybridElements::Connectivity& node_connectivity = mesh.cells().node_connectivity(); int icell = 0; idx_t quad_nodes[4]; for (int it = 0; it < nTiles; it++) { for (int ix = 0; ix < N; ix++) { for (int iy = 0; iy < N; iy++) { quad_nodes[0] = NodeArray(it, ix, iy); quad_nodes[1] = NodeArray(it, ix + 1, iy); quad_nodes[2] = NodeArray(it, ix + 1, iy + 1); quad_nodes[3] = NodeArray(it, ix, iy + 1); node_connectivity.set(icell, quad_nodes); cells_part(icell) = part(quad_nodes[0]); cells_gidx(icell) = icell + 1; cells_ridx(icell) = icell; ++icell; } } } // Assertion that correct number of cells are set ATLAS_ASSERT(ncells == icell, "Insufficient cells have been set"); // Parallel generateGlobalElementNumbering(mesh); nodes.metadata().set("parallel", true); // Global indices of ghost nodes. nodes.metadata().set("ghost-global-idx", ghostGblIdx); // Global indices of owned nodes for each ghost node (same order as above) nodes.metadata().set("owned-global-idx", ownedGblIdx); } // ------------------------------------------------------------------------------------------------- void NodalCubedSphereMeshGenerator::hash(eckit::Hash& h) const { h.add("NodalCubedSphereMeshGenerator"); options.hash(h); } // ------------------------------------------------------------------------------------------------- namespace { static MeshGeneratorBuilder NodalCubedSphereMeshGenerator( NodalCubedSphereMeshGenerator::static_type()); } // ------------------------------------------------------------------------------------------------- } // namespace meshgenerator } // namespace atlas atlas-0.46.0/src/atlas/meshgenerator/detail/MeshGeneratorInterface.h0000664000175000017500000000515615160212070025627 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once namespace eckit { class Parametrisation; } // namespace eckit namespace atlas { namespace grid { class DistributionImpl; } // namespace grid } // namespace atlas namespace atlas { namespace grid { namespace detail { namespace grid { class Grid; } // namespace grid } // namespace detail } // namespace grid using GridImpl = grid::detail::grid::Grid; } // namespace atlas namespace atlas { namespace mesh { namespace detail { class MeshImpl; } // namespace detail } // namespace mesh } // namespace atlas namespace atlas { namespace grid { namespace detail { namespace partitioner { class Partitioner; } // namespace partitioner } // namespace detail } // namespace grid using PartitionerImpl = grid::detail::partitioner::Partitioner; } // namespace atlas namespace atlas { namespace meshgenerator { class MeshGeneratorImpl; //---------------------------------------------------------------------------------------------------------------------- extern "C" { void atlas__MeshGenerator__delete(MeshGeneratorImpl* This); const MeshGeneratorImpl* atlas__MeshGenerator__create_noconfig(const char* name); const MeshGeneratorImpl* atlas__MeshGenerator__create(const char* name, const eckit::Parametrisation* params); mesh::detail::MeshImpl* atlas__MeshGenerator__generate__grid_griddist(const MeshGeneratorImpl* This, const GridImpl* grid, const grid::DistributionImpl* distribution); mesh::detail::MeshImpl* atlas__MeshGenerator__generate__grid(const MeshGeneratorImpl* This, const GridImpl* grid); mesh::detail::MeshImpl* atlas__MeshGenerator__generate__grid_partitioner(const MeshGeneratorImpl* This, const GridImpl* grid, const PartitionerImpl* partitioner); } //---------------------------------------------------------------------------------------------------------------------- } // namespace meshgenerator //---------------------------------------------------------------------------------------------------------------------- } // namespace atlas atlas-0.46.0/src/atlas/meshgenerator/detail/MeshGeneratorImpl.cc0000664000175000017500000001015015160212070024754 0ustar alastairalastair/* * (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 "eckit/utils/Hash.h" #include "atlas/array/ArrayView.h" #include "atlas/field/Field.h" #include "atlas/grid/Distribution.h" #include "atlas/grid/Grid.h" #include "atlas/mesh/HybridElements.h" #include "atlas/mesh/Mesh.h" #include "atlas/meshgenerator/MeshGenerator.h" #include "atlas/meshgenerator/detail/MeshGeneratorImpl.h" #include "atlas/parallel/mpi/mpi.h" using atlas::Mesh; namespace atlas { namespace meshgenerator { //---------------------------------------------------------------------------------------------------------------------- MeshGeneratorImpl::MeshGeneratorImpl() = default; MeshGeneratorImpl::~MeshGeneratorImpl() = default; Mesh MeshGeneratorImpl::operator()(const Grid& grid) const { Mesh mesh; generate(grid, mesh); return mesh; } Mesh MeshGeneratorImpl::operator()(const Grid& grid, const grid::Partitioner& partitioner) const { Mesh mesh; generate(grid, partitioner, mesh); return mesh; } Mesh MeshGeneratorImpl::operator()(const Grid& grid, const grid::Distribution& distribution) const { Mesh mesh; generate(grid, distribution, mesh); return mesh; } Mesh MeshGeneratorImpl::generate(const Grid& grid) const { Mesh mesh; generate(grid, mesh); return mesh; } void MeshGeneratorImpl::generate(const Grid& grid, const grid::Partitioner& partitioner, Mesh& mesh) const { generate(grid, grid::Distribution(grid, partitioner), mesh); } Mesh MeshGeneratorImpl::generate(const Grid& grid, const grid::Partitioner& partitioner) const { Mesh mesh; generate(grid, partitioner, mesh); return mesh; } Mesh MeshGeneratorImpl::generate(const Grid& grid, const grid::Distribution& distribution) const { Mesh mesh; generate(grid, distribution, mesh); return mesh; } //---------------------------------------------------------------------------------------------------------------------- void MeshGeneratorImpl::generateGlobalElementNumbering(Mesh& mesh) const { const auto& comm = mpi::comm(mesh.mpi_comm()); idx_t mpi_size = static_cast(comm.size()); gidx_t loc_nb_elems = mesh.cells().size(); std::vector elem_counts(mpi_size); std::vector elem_displs(mpi_size); ATLAS_TRACE_MPI(ALLGATHER) { comm.allGather(loc_nb_elems, elem_counts.begin(), elem_counts.end()); } elem_displs.at(0) = 0; for (idx_t jpart = 1; jpart < mpi_size; ++jpart) { elem_displs.at(jpart) = elem_displs.at(jpart - 1) + elem_counts.at(jpart - 1); } gidx_t gid = 1 + elem_displs.at(comm.rank()); array::ArrayView glb_idx = array::make_view(mesh.cells().global_index()); for (idx_t jelem = 0; jelem < mesh.cells().size(); ++jelem) { glb_idx(jelem) = gid++; } gidx_t max_glb_idx = std::accumulate(elem_counts.begin(), elem_counts.end(), gidx_t(0)); mesh.cells().global_index().metadata().set("human_readable", true); mesh.cells().global_index().metadata().set("min", 1); mesh.cells().global_index().metadata().set("max", max_glb_idx); } void MeshGeneratorImpl::setProjection(Mesh& mesh, const Projection& p) const { mesh.setProjection(p); } void MeshGeneratorImpl::setGrid(Mesh& mesh, const Grid& g, const grid::Distribution& d) const { mesh.setGrid(g); mesh.metadata().set("distribution", d.type()); } void MeshGeneratorImpl::setGrid(Mesh& mesh, const Grid& g, const std::string& d) const { mesh.setGrid(g); mesh.metadata().set("distribution", d); } //---------------------------------------------------------------------------------------------------------------------- } // namespace meshgenerator //---------------------------------------------------------------------------------------------------------------------- } // namespace atlas atlas-0.46.0/src/atlas/meshgenerator/detail/CubedSphereDualMeshGenerator.cc0000664000175000017500000004621215160212070027062 0ustar alastairalastair/* * (C) Crown Copyright 2021 Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ #include #include #include #include #include #include #include "eckit/utils/Hash.h" #include "atlas/functionspace/CubedSphereColumns.h" #include "atlas/grid/CubedSphereGrid.h" #include "atlas/grid/Distribution.h" #include "atlas/grid/Partitioner.h" #include "atlas/library/config.h" #include "atlas/mesh/Mesh.h" #include "atlas/mesh/Nodes.h" #include "atlas/meshgenerator/MeshGenerator.h" #include "atlas/meshgenerator/detail/CubedSphereDualMeshGenerator.h" #include "atlas/meshgenerator/detail/MeshGeneratorFactory.h" #include "atlas/meshgenerator/detail/cubedsphere/CubedSphereUtility.h" #include "atlas/parallel/mpi/mpi.h" #define DEBUG_OUTPUT 0 #define DEBUG_OUTPUT_DETAIL 0 namespace atlas { namespace meshgenerator { // ----------------------------------------------------------------------------- CubedSphereDualMeshGenerator::CubedSphereDualMeshGenerator(const eckit::Parametrisation& p) { // Get mpi_comm std::string mpi_comm = mpi::comm().name(); p.get("mpi_comm", mpi_comm); options.set("mpi_comm", mpi_comm); configure_defaults(); // Get number of partitions. size_t nb_parts; if (p.get("nb_parts", nb_parts)) { options.set("nb_parts", nb_parts); } // Get this partition. int part; if (p.get("part", part)) { options.set("part", part); } // Get halo size. int halo; if (p.get("halo", halo)) { options.set("halo", halo); } // Get partitioner. std::string partitioner; if (p.get("partitioner", partitioner) && partitioner.size()) { options.set("partitioner", partitioner); } } // ----------------------------------------------------------------------------- void CubedSphereDualMeshGenerator::configure_defaults() { auto& comm = mpi::comm(options.getString("mpi_comm")); // This option sets number of partitions. options.set("nb_parts", comm.size()); // This option sets the part that will be generated. options.set("part", comm.rank()); // This options sets the number of halo elements around each partition. options.set("halo", 0); // This options sets the default partitioner. options.set("partitioner", "cubedsphere"); } // ----------------------------------------------------------------------------- void CubedSphereDualMeshGenerator::generate(const Grid& grid, Mesh& mesh) const { // Get partitioner type and number of partitions from config. const idx_t nParts = static_cast(options.get("nb_parts")); const std::string partType = options.get("partitioner"); auto partConfig = util::Config{}; partConfig.set("type", partType); partConfig.set("partitions", nParts); partConfig.set("mpi_comm",options.getString("mpi_comm")); // Use lonlat instead of xy for non cubedsphere partitioner. if (partType != "cubedsphere") { partConfig.set("coordinates", "lonlat"); } // Set distribution. mpi::Scope mpi_scope(options.getString("mpi_comm")); const auto partitioner = grid::Partitioner(partConfig); const auto distribution = grid::Distribution(grid, partitioner); generate(grid, distribution, mesh); } // ----------------------------------------------------------------------------- void CubedSphereDualMeshGenerator::generate(const Grid& grid, const grid::Distribution& distribution, Mesh& mesh) const { // Check for correct grid and need for mesh ATLAS_ASSERT(!mesh.generated()); // Cast grid to cubed sphere grid. const auto csGrid = CubedSphereGrid(grid); // Check for successful cast. if (!csGrid) { throw_Exception("CubedSphereDualMeshGenerator can only work with a cubedsphere grid.", Here()); } // Check for correct grid stagger. if (csGrid.stagger() != "C") { throw_Exception("CubedSphereDualMeshGenerator will only work with a cell-centroid grid.", Here()); } // Clone some grid properties. setGrid(mesh, csGrid, distribution); mpi::Scope mpi_scope(options.getString("mpi_comm")); generate_mesh(csGrid, distribution, mesh); } // ----------------------------------------------------------------------------- namespace { // (i, j) pair class IJ { public: IJ(idx_t i, idx_t j): i_(i), j_(j) {} idx_t i() const { return i_; } idx_t j() const { return j_; } IJ operator+(const IJ& ij) const { return IJ{i() + ij.i(), j() + ij.j()}; } IJ operator-(const IJ& ij) const { return IJ{i() - ij.i(), j() - ij.j()}; } private: idx_t i_{}; idx_t j_{}; }; // Helper function to copy fields. template void copyField(const Field& sourceField, Field& targetField) { // Assign source field values to target field. array::make_view(targetField).assign(array::make_view(sourceField)); } // Get the surrounding node (i, j) pairs from a cell (i, j) pair. std::vector getIjNodes(const IJ& ijCell, idx_t N) { // Rotate ij 90 degrees anitclockwise about ijPivot. auto rotateAnticlockwise = [&](const IJ& ij, const IJ& ijPivot) { const auto ijTemp = ij - ijPivot; return IJ{-ijTemp.j(), ijTemp.i()} + ijPivot; }; // Rotate ij 90 degrees clockwise about ijPivot. auto rotateClockwise = [&](const IJ& ij, const IJ& ijPivot) { const auto ijTemp = ij - ijPivot; return IJ{ijTemp.j(), -ijTemp.i()} + ijPivot; }; // Set standard surrounding nodes. auto ijNodes = std::vector{{ijCell.i() - 1, ijCell.j() - 1}, {ijCell.i(), ijCell.j() - 1}, {ijCell.i(), ijCell.j()}, {ijCell.i() - 1, ijCell.j()}}; // Modify nodes that lie in invalid corners of ij space. // Either remove a node to make cell triangular, or rotate two of the // nodes out of the forbidden space. // Bottom-left corner. if (ijCell.i() <= 0 && ijCell.j() <= 0) { // Triangle. if (ijCell.i() == 0 && ijCell.j() == 0) { ijNodes.erase(ijNodes.begin()); } // Quad (i) else if (ijCell.i() == 0) { ijNodes[0] = rotateClockwise(ijNodes[1], IJ{0, 0}); ijNodes[3] = rotateClockwise(ijNodes[2], IJ{0, 0}); } else { // Quad (ii) ijNodes[0] = rotateAnticlockwise(ijNodes[3], IJ{0, 0}); ijNodes[1] = rotateAnticlockwise(ijNodes[2], IJ{0, 0}); } } // Bottom-right corner. else if (ijCell.i() >= N && ijCell.j() <= 0) { // Triangle. if (ijCell.i() == N && ijCell.j() == 0) { ijNodes.erase(ijNodes.begin() + 1); } // Quad (i) else if (ijCell.j() == 0) { ijNodes[0] = rotateClockwise(ijNodes[3], IJ{N - 1, 0}); ijNodes[1] = rotateClockwise(ijNodes[2], IJ{N - 1, 0}); } // Quad (ii) else { ijNodes[1] = rotateAnticlockwise(ijNodes[0], IJ{N - 1, 0}); ijNodes[2] = rotateAnticlockwise(ijNodes[3], IJ{N - 1, 0}); } } // Top-right corner. else if (ijCell.i() >= N && ijCell.j() >= N) { // Triangle. if (ijCell.i() == N && ijCell.j() == N) { ijNodes.erase(ijNodes.begin() + 2); } // Quad (i) else if (ijCell.i() == N) { ijNodes[1] = rotateClockwise(ijNodes[0], IJ{N - 1, N - 1}); ijNodes[2] = rotateClockwise(ijNodes[3], IJ{N - 1, N - 1}); } // Quad (ii) else { ijNodes[2] = rotateAnticlockwise(ijNodes[1], IJ{N - 1, N - 1}); ijNodes[3] = rotateAnticlockwise(ijNodes[0], IJ{N - 1, N - 1}); } } // Top-left corner. else if (ijCell.i() <= 0 && ijCell.j() >= N) { // Triangle. if (ijCell.i() == 0 && ijCell.j() == N) { std::vector triangle {ijNodes[0], ijNodes[1], ijNodes[2]}; ijNodes.swap(triangle); } // Quad (i) else if (ijCell.j() == N) { ijNodes[2] = rotateClockwise(ijNodes[1], IJ{0, N - 1}); ijNodes[3] = rotateClockwise(ijNodes[0], IJ{0, N - 1}); } // Quad (ii) else { ijNodes[0] = rotateAnticlockwise(ijNodes[1], IJ{0, N - 1}); ijNodes[3] = rotateAnticlockwise(ijNodes[2], IJ{0, N - 1}); } } return ijNodes; } } // namespace void CubedSphereDualMeshGenerator::generate_mesh(const CubedSphereGrid& csGrid, const grid::Distribution& distribution, Mesh& mesh) const { ATLAS_TRACE("CubedSphereDualMeshGenerator::generate"); using Topology = atlas::mesh::Nodes::Topology; using namespace detail::cubedsphere; const idx_t N = csGrid.N(); const idx_t nHalo = options.get("halo"); //-------------------------------------------------------------------------- // Create a cubed-sphere mesh. //-------------------------------------------------------------------------- // Generate cubed sphere primal mesh. auto primalOptions = options; primalOptions.set("halo", nHalo + 1); const auto primalMesh = MeshGenerator("cubedsphere", primalOptions).generate(csGrid, distribution); // Generate fucntionspaces for cubed sphere primal mesh. const auto primalCellsFunctionSpace = functionspace::CubedSphereCellColumns(primalMesh); const auto primalNodesFunctionSpace = functionspace::CubedSphereNodeColumns(primalMesh); const auto& primalCells = primalCellsFunctionSpace.cells(); const auto& primalNodes = primalNodesFunctionSpace.nodes(); //-------------------------------------------------------------------------- // Set dual mesh nodes (easy part). //-------------------------------------------------------------------------- auto& nodes = mesh.nodes(); nodes.resize(primalCellsFunctionSpace.size()); nodes.add(Field("tij", array::make_datatype(), array::make_shape(nodes.size(), 3))); // Copy mesh fields to dual mesh. copyField(primalCells.global_index(), nodes.global_index()); copyField(primalCells.remote_index(), nodes.remote_index()); copyField(primalCells.partition(), nodes.partition()); copyField(primalCells.halo(), nodes.halo()); copyField(primalCells.flags(), nodes.flags()); copyField(primalCells.field("ghost"), nodes.ghost()); copyField(primalCells.field("xy"), nodes.xy()); copyField(primalCells.field("lonlat"), nodes.lonlat()); copyField(primalCells.field("tij"), nodes.field("tij")); // Need to decrement halo by one. auto nodesHalo = array::make_view(nodes.halo()); for (idx_t idx = 0; idx < nodes.size(); ++idx) { nodesHalo(idx) = std::max(0, nodesHalo(idx) - 1); } //-------------------------------------------------------------------------- // Set dual mesh cells (not so easy part). //-------------------------------------------------------------------------- // Make views to cubed sphere nodes. const auto primalNodesHalo = array::make_view(primalNodes.halo()); const auto primalNodesTij = array::make_view(primalNodes.field("tij")); // Loop over all nodes of primal mesh, excluding outermost halo. // Find dual mesh nodes around primal mesh nodes. // Note: some halo cells near corners may be incomplete. // Set of nodes around a cell. struct NodeList { std::vector nodes{}; // Node indices. bool incomplete{}; // True if nodes are missing. }; auto nodeLists = std::vector{}; for (idx_t idx = 0; idx < primalNodes.size(); ++idx) { // Exclude outer ring of cubed sphere mesh halo. if (primalNodesHalo(idx) == nHalo + 1) { break; } nodeLists.emplace_back(); auto& nodeList = nodeLists.back(); // Get tij of cell. const auto tCell = primalNodesTij(idx, Coordinates::T); const auto ijCell = IJ{primalNodesTij(idx, Coordinates::I), primalNodesTij(idx, Coordinates::J)}; // Get ij of surrounding nodes. auto ijNodes = getIjNodes(ijCell, N); // Add indices to nodes vector. for (const auto& ijNode : ijNodes) { if (primalCellsFunctionSpace.is_valid_index(tCell, ijNode.i(), ijNode.j())) { nodeList.nodes.push_back(primalCellsFunctionSpace.index(tCell, ijNode.i(), ijNode.j())); } else { nodeList.incomplete = true; } } } // Figure out element types. // Set first element type. ( first = type, second = count ) enum struct ElemType : size_t { LINE = 2, TRIANGLE = 3, QUADRILATERAL = 4 }; auto typeCounts = std::vector>{std::make_pair(static_cast(nodeLists[0].nodes.size()), 1)}; // Count the number of consecutive lines, triangles or quadtrilaterals in dual mesh. // This is an attempt to keep dual mesh cells in the same order as mesh nodes. // Otherwise, the halo exchange bookkeeping is invalidated. for (size_t idx = 1; idx < nodeLists.size(); ++idx) { // Get the element type. const auto elemType = static_cast(nodeLists[idx].nodes.size()); // Increment counter if this elemType is the same as last one if (elemType == typeCounts.back().first) { ++typeCounts.back().second; } // Otherwise add a new counter. else { typeCounts.emplace_back(elemType, 1); } } // Add cells to mesh. auto& cells = mesh.cells(); idx_t nCells{}; // Loop through type counters. for (const auto& typeCount : typeCounts) { // Select element type. switch (typeCount.first) { // Add a block of lines. case ElemType::LINE: { cells.add(mesh::ElementType::create("Line"), typeCount.second); break; } // Add a block of triangles. case ElemType::TRIANGLE: { cells.add(mesh::ElementType::create("Triangle"), typeCount.second); break; } // Add a block of quadrilaterals. case ElemType::QUADRILATERAL: { cells.add(mesh::ElementType::create("Quadrilateral"), typeCount.second); break; } default: { ATLAS_THROW_EXCEPTION("Unknown element type with " + std::to_string(static_cast(typeCount.first)) + " nodes."); break; } } // Increment the total number of cells. nCells += typeCount.second; } // Add extra fields to cells. cells.add(Field("tij", array::make_datatype(), array::make_shape(cells.size(), 3))); cells.add(Field("xy", array::make_datatype(), array::make_shape(cells.size(), 2))); cells.add(Field("lonlat", array::make_datatype(), array::make_shape(cells.size(), 2))); cells.add(Field("ghost", array::make_datatype(), array::make_shape(cells.size()))); // Copy dual cells fields from nodes. copyField(primalNodes.global_index(), cells.global_index()); copyField(primalNodes.remote_index(), cells.remote_index()); copyField(primalNodes.partition(), cells.partition()); copyField(primalNodes.ghost(), cells.field("ghost")); copyField(primalNodes.halo(), cells.halo()); copyField(primalNodes.flags(), cells.flags()); copyField(primalNodes.field("tij"), cells.field("tij")); copyField(primalNodes.xy(), cells.field("xy")); copyField(primalNodes.lonlat(), cells.field("lonlat")); // Get view of flags field. auto dualCellsFlags = array::make_view(cells.flags()); // Get node connectivity. auto& nodeConnectivity = cells.node_connectivity(); // Loop over dual mesh cells and set connectivity. for (idx_t idx = 0; idx < nCells; ++idx) { // Set connectivity. nodeConnectivity.set(idx, nodeLists[idx].nodes.data()); // Set invalid flag if cell is incomplete. if (nodeLists[idx].incomplete) { Topology::set(dualCellsFlags(idx), Topology::INVALID); } } // Set metadata. set_metadata(mesh); return; } // ----------------------------------------------------------------------------- void CubedSphereDualMeshGenerator::set_metadata(Mesh& mesh) const { const auto nHalo = options.get("halo"); // Set basic halo metadata. mesh.metadata().set("halo", nHalo); mesh.metadata().set("halo_locked", true); mesh.nodes().metadata().set("parallel", true); mesh.cells().metadata().set("parallel", true); mesh.metadata().set("mpi_comm",options.getString("mpi_comm")); // Loop over nodes and count number of halo elements. auto nNodes = std::vector(nHalo + 2, 0); const auto nodeHalo = array::make_view(mesh.nodes().halo()); for (idx_t i = 0; i < mesh.nodes().size(); ++i) { ++nNodes[static_cast(nodeHalo(i))]; } std::partial_sum(nNodes.begin(), nNodes.end(), nNodes.begin()); // Set node halo metadata. for (size_t i = 0; i < nNodes.size(); ++i) { const auto str = "nb_nodes_including_halo[" + std::to_string(i) + "]"; mesh.metadata().set(str, nNodes[i]); } // Loop over cells and count number of halo elements. auto nCells = std::vector>(mesh.cells().nb_types(), std::vector(nHalo + 1, 0)); const auto cellHalo = array::make_view(mesh.cells().halo()); for (idx_t i = 0; i < mesh.cells().nb_types(); ++i) { const auto& elems = mesh.cells().elements(i); for (idx_t j = elems.begin(); j < elems.end(); ++j) { ++nCells[static_cast(i)][static_cast(cellHalo(j))]; } std::partial_sum(nCells[static_cast(i)].begin(), nCells[static_cast(i)].end(), nCells[static_cast(i)].begin()); } // Set cell halo metadata. for (size_t i = 0; i < nCells.size(); ++i) { for (size_t j = 0; j < nCells[i].size(); ++j) { const auto str = "nb_cells_including_halo[" + std::to_string(i) + "][" + std::to_string(j) + "]"; mesh.metadata().set(str, nCells[i][j]); } } } // ----------------------------------------------------------------------------- void CubedSphereDualMeshGenerator::hash(eckit::Hash& h) const { h.add("CubedSphereDualMeshGenerator"); options.hash(h); } // ----------------------------------------------------------------------------- namespace { static MeshGeneratorBuilder CubedSphereDualMeshGenerator( CubedSphereDualMeshGenerator::static_type()); } // ----------------------------------------------------------------------------- } // namespace meshgenerator } // namespace atlas atlas-0.46.0/src/atlas/meshgenerator/detail/HealpixMeshGenerator.h0000664000175000017500000000463715160212070025324 0ustar alastairalastair/* * (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/meshgenerator/MeshGenerator.h" #include "atlas/meshgenerator/detail/MeshGeneratorImpl.h" #include "atlas/util/Config.h" #include "atlas/util/Metadata.h" #ifndef DOXYGEN_SHOULD_SKIP_THIS namespace eckit { class Parametrisation; } namespace atlas { class StructuredGrid; class Mesh; template class vector; } // namespace atlas namespace atlas { namespace grid { class Distribution; } // namespace grid } // namespace atlas #endif namespace atlas { namespace meshgenerator { //---------------------------------------------------------------------------------------------------------------------- class HealpixMeshGenerator : public MeshGenerator::Implementation { public: HealpixMeshGenerator(const eckit::Parametrisation& = util::NoConfig()); virtual void generate(const Grid&, const grid::Distribution&, Mesh&) const override; virtual void generate(const Grid&, Mesh&) const override; using MeshGenerator::Implementation::generate; virtual void hash(eckit::Hash&) const override; virtual std::string type() const override { return static_type(); } static std::string static_type() { return "healpix"; } private: void configure_defaults(); void generate_mesh(const StructuredGrid&, const grid::Distribution&, Mesh& m) const; gidx_t match_node_idx(const gidx_t& gidx, const int ns) const; gidx_t idx_xy_to_x(const int xidx, const int yidx, const int ns) const; gidx_t up_idx(const int xidx, const int yidx, const int ns) const; gidx_t down_idx(const int xidx, const int yidx, const int ns) const; gidx_t right_idx(const int xidx, const int yidx, const int ns) const; gidx_t pentagon_right_idx(const int xidx, const int yidx, const int ns) const; private: util::Metadata options; mutable gidx_t nb_points_; mutable gidx_t nb_nodes_; mutable int nb_pole_nodes_; }; //---------------------------------------------------------------------------------------------------------------------- } // namespace meshgenerator } // namespace atlas atlas-0.46.0/src/atlas/meshgenerator/detail/RegularMeshGenerator.h0000664000175000017500000000350615160212070025325 0ustar alastairalastair/* * (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/meshgenerator/MeshGenerator.h" #include "atlas/meshgenerator/detail/MeshGeneratorImpl.h" #include "atlas/util/Config.h" #include "atlas/util/Metadata.h" #ifndef DOXYGEN_SHOULD_SKIP_THIS namespace eckit { class Parametrisation; } namespace atlas { class RegularGrid; class Mesh; template class vector; } // namespace atlas namespace atlas { namespace grid { class Distribution; } // namespace grid } // namespace atlas #endif namespace atlas { namespace meshgenerator { //---------------------------------------------------------------------------------------------------------------------- class RegularMeshGenerator : public MeshGenerator::Implementation { public: RegularMeshGenerator(const eckit::Parametrisation& = util::NoConfig()); virtual void generate(const Grid&, const grid::Distribution&, Mesh&) const override; virtual void generate(const Grid&, Mesh&) const override; using MeshGenerator::Implementation::generate; std::string type() const override { return "regular"; } private: virtual void hash(eckit::Hash&) const override; void configure_defaults(); void generate_mesh(const RegularGrid&, const grid::Distribution& distribution, Mesh& m) const; private: util::Metadata options; }; //---------------------------------------------------------------------------------------------------------------------- } // namespace meshgenerator } // namespace atlas atlas-0.46.0/src/atlas/meshgenerator/detail/NodalCubedSphereMeshGenerator.h0000664000175000017500000000311715160212070027071 0ustar alastairalastair/* * (C) Copyright 2020 UCAR * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ #pragma once #include "atlas/array.h" #include "atlas/meshgenerator/MeshGenerator.h" #include "atlas/meshgenerator/detail/MeshGeneratorImpl.h" #include "atlas/util/Config.h" #include "atlas/util/Metadata.h" #ifndef DOXYGEN_SHOULD_SKIP_THIS namespace eckit { class Parametrisation; } namespace atlas { class Mesh; template class vector; } // namespace atlas namespace atlas { namespace grid { class Distribution; } // namespace grid } // namespace atlas #endif namespace atlas { namespace meshgenerator { //-------------------------------------------------------------------------------------------------- class NodalCubedSphereMeshGenerator : public MeshGenerator::Implementation { public: NodalCubedSphereMeshGenerator(const eckit::Parametrisation& = util::NoConfig()); virtual void generate(const Grid&, const grid::Distribution&, Mesh&) const override; virtual void generate(const Grid&, Mesh&) const override; using MeshGenerator::Implementation::generate; static std::string static_type() { return "nodal-cubedsphere"; } std::string type() const override { return static_type(); } private: virtual void hash(eckit::Hash&) const override; void configure_defaults(); private: util::Metadata options; }; //-------------------------------------------------------------------------------------------------- } // namespace meshgenerator } // namespace atlas atlas-0.46.0/src/atlas/meshgenerator/detail/DelaunayMeshGenerator.h0000664000175000017500000000307315160212070025465 0ustar alastairalastair/* * (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/meshgenerator/MeshGenerator.h" #include "atlas/meshgenerator/detail/MeshGeneratorImpl.h" namespace atlas { class Mesh; class Grid; } // namespace atlas namespace atlas { namespace meshgenerator { //---------------------------------------------------------------------------------------------------------------------- class DelaunayMeshGenerator : public MeshGenerator::Implementation { public: DelaunayMeshGenerator(const eckit::Parametrisation& = util::NoConfig()); virtual ~DelaunayMeshGenerator() override; std::string type() const override { return "delaunay"; } private: // methods virtual void hash(eckit::Hash&) const override; using MeshGenerator::Implementation::generate; virtual void generate(const Grid&, const grid::Distribution&, Mesh&) const override; virtual void generate(const Grid&, Mesh&) const override; std::string mpi_comm_; int part_; bool remove_duplicate_points_; bool reshuffle_; std::string extension_grid_; }; //---------------------------------------------------------------------------------------------------------------------- } // namespace meshgenerator } // namespace atlas atlas-0.46.0/src/atlas/meshgenerator/detail/MeshGeneratorFactory.h0000664000175000017500000000320015160212070025322 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include "atlas/util/Config.h" #include "atlas/util/Factory.h" namespace atlas { namespace meshgenerator { //---------------------------------------------------------------------------------------------------------------------- class MeshGeneratorImpl; class MeshGeneratorFactory : public util::Factory { public: static std::string className() { return "MeshGeneratorFactory"; } static const MeshGeneratorImpl* build(const std::string&); static const MeshGeneratorImpl* build(const std::string&, const eckit::Parametrisation&); using Factory::Factory; private: virtual const MeshGeneratorImpl* make(const eckit::Parametrisation&) = 0; }; //---------------------------------------------------------------------------------------------------------------------- template class MeshGeneratorBuilder : public MeshGeneratorFactory { private: virtual const MeshGeneratorImpl* make(const eckit::Parametrisation& param) { return new T(param); } public: using MeshGeneratorFactory::MeshGeneratorFactory; }; //---------------------------------------------------------------------------------------------------------------------- } // namespace meshgenerator } // namespace atlas atlas-0.46.0/src/atlas/meshgenerator/detail/CubedSphereMeshGenerator.cc0000664000175000017500000011566415160212070026264 0ustar alastairalastair/* * (C) Crown Copyright 2021 Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ #include #include #include #include #include #include #include "eckit/utils/Hash.h" #include "atlas/field/Field.h" #include "atlas/grid/CubedSphereGrid.h" #include "atlas/grid/Distribution.h" #include "atlas/grid/Iterator.h" #include "atlas/grid/Partitioner.h" #include "atlas/grid/Tiles.h" #include "atlas/library/config.h" #include "atlas/mesh/Connectivity.h" #include "atlas/mesh/ElementType.h" #include "atlas/mesh/Elements.h" #include "atlas/mesh/HybridElements.h" #include "atlas/mesh/Mesh.h" #include "atlas/mesh/Nodes.h" #include "atlas/meshgenerator/detail/CubedSphereMeshGenerator.h" #include "atlas/meshgenerator/detail/MeshGeneratorFactory.h" #include "atlas/meshgenerator/detail/cubedsphere/CubedSphereUtility.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/projection/detail/CubedSphereProjectionBase.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Log.h" #include "atlas/util/Constants.h" #include "atlas/util/CoordinateEnums.h" #define DEBUG_OUTPUT 0 #define DEBUG_OUTPUT_DETAIL 0 namespace atlas { namespace meshgenerator { // ----------------------------------------------------------------------------- CubedSphereMeshGenerator::CubedSphereMeshGenerator(const eckit::Parametrisation& p) { // Get mpi_comm std::string mpi_comm = mpi::comm().name(); p.get("mpi_comm", mpi_comm); options.set("mpi_comm", mpi_comm); configure_defaults(); // Get number of partitions. int nb_parts; if (p.get("nb_parts", nb_parts)) { options.set("nb_parts", nb_parts); } // Get this partition. int part; if (p.get("part", part)) { options.set("part", part); } // Get halo size. int halo; if (p.get("halo", halo)) { options.set("halo", halo); } // Get partitioner. std::string partitioner; if (p.get("partitioner", partitioner) && partitioner.size()) { options.set("partitioner", partitioner); } } // ----------------------------------------------------------------------------- void CubedSphereMeshGenerator::configure_defaults() { auto& comm = mpi::comm(options.getString("mpi_comm")); // This option sets number of partitions. options.set("nb_parts", comm.size()); // This option sets the part that will be generated. options.set("part", comm.rank()); // This options sets the number of halo elements around each partition. options.set("halo", 0); // This options sets the default partitioner. options.set("partitioner", "cubedsphere"); } // ----------------------------------------------------------------------------- void CubedSphereMeshGenerator::generate(const Grid& grid, Mesh& mesh) const { // Get partitioner type and number of partitions from config. const idx_t nParts = static_cast(options.get("nb_parts")); const std::string partType = options.get("partitioner"); auto partConfig = util::Config{}; partConfig.set("type", partType); partConfig.set("partitions", nParts); // Use lonlat instead of xy for non cubedsphere partitioner. if (partType != "cubedsphere") { partConfig.set("coordinates", "lonlat"); } // Set distribution. mpi::Scope mpi_scope(options.getString("mpi_comm")); const auto partitioner = grid::Partitioner(partConfig); const auto distribution = grid::Distribution(grid, partitioner); generate(grid, distribution, mesh); } // ----------------------------------------------------------------------------- void CubedSphereMeshGenerator::generate(const Grid& grid, const grid::Distribution& distribution, Mesh& mesh) const { // Check for correct grid and need for mesh ATLAS_ASSERT(!mesh.generated()); // Cast grid to cubed sphere grid. const auto csGrid = CubedSphereGrid(grid); // Check for successful cast. if (!csGrid) { throw_Exception( "CubedSphereMeshGenerator can only work " "with a cubedsphere grid.", Here()); } // Check for correct grid stagger. if (csGrid.stagger() != "C") { throw_Exception( "CubedSphereMeshGenerator will only work with a " "cell-centroid grid.", Here()); } // Check for sensible halo size. if (options.get("halo") > csGrid.N()) { throw_Exception("Halo size " + std::to_string(options.get("halo")) + " " "is larger than grid size " + std::to_string(csGrid.N()) + ".", Here()); } // Clone some grid properties. setGrid(mesh, csGrid, distribution); mesh.metadata().set("mpi_comm",options.getString("mpi_comm")); generate_mesh(csGrid, distribution, mesh); } // ----------------------------------------------------------------------------- void CubedSphereMeshGenerator::generate_mesh(const CubedSphereGrid& csGrid, const grid::Distribution& distribution, Mesh& mesh) const { ATLAS_TRACE("CubedSphereMeshGenerator::generate"); // --------------------------------------------------------------------------- // CUBED SPHERE MESH GENERATOR // --------------------------------------------------------------------------- // // Mesh generator creates a cubed sphere mesh by generating individual meshes // for each cubed sphere tile and then working out the correspondence between // overlapping nodes and cells. // // Meshgenerator places cell at each grid point on this partition. Halo // cells are added to the mesh if options.get("halo") > 0. The halo cells may // either be interior to the tile, || exterior. Interior halo cells share // their xy coordinate and global ID with the corresponding cells on other // partitions. Exterior halo cells have a unique global ID and their xy // coordinates are extrapolated from the interior tile. The global IDs of non- // halo cells match the global IDs of the cell-centre grid points. // // Nodes are added around cells. The nodes around interior cells are assigned // an owner by the following rules: // * node (i > 0, j > 0) is owned by cell (i - 1, j - 1) // * node (i = 0, j > 0) is owned by cell (0 , j - 1) // * node (i > 0, j = 0) is owned by cell (i - 1, 0 ) // * node (i = 0, j = 0) is owned by cell (0 , 0 ) // // The partition of the owning cell determines the partition of the node. // Ghost nodes are added to the mesh to complete cells at partition // boundaries, cells exterior to the tiles, or on tile edges which do not // own nodes. // // There are several stages to the mesh generator: // 1. Preamble. // 2. Define global cell distribution. // 3. Define global node distribution. // 4. Locate local cells. // 5. Locate local nodes // 6. Assign nodes to mesh. // 7. Assign cells to mesh. // 8. Finalise. // --------------------------------------------------------------------------- // 1. PREAMBLE // Setup some general parameters of the mesh, as well as define useful // lambda functions and structs. // --------------------------------------------------------------------------- using Topology = atlas::mesh::Nodes::Topology; using atlas::array::make_datatype; using atlas::array::make_shape; using namespace detail::cubedsphere; struct GlobalElem; struct LocalElem; // Define bad index values. constexpr idx_t undefinedIdx = -1; constexpr idx_t undefinedGlobalIdx = -1; // Define cell/node type. enum class ElemType { UNDEFINED, // Not set. Writing this type to mesh is an error. OWNER, // Owner element on this partition. HALO // Ghost or halo element. }; // Struct to store the information of globals cells/nodes. // These must be generated for all cells/nodes on all partitions. Data is // stored in (t, j, i) row-major order. struct GlobalElem { LocalElem* localPtr{}; // Pointer to local element. gidx_t globalIdx{undefinedGlobalIdx}; // Global index. idx_t remoteIdx{undefinedIdx}; // Remote index. int part{undefinedIdx}; // Partition. }; // Struct to store additional information of local cells/nodes. // These are only generated for nodes on this partition. Data is stored as a // list which can be sorted. struct LocalElem { GlobalElem* globalPtr{}; // Pointer to global element. ElemType type{ElemType::UNDEFINED}; // Cell/node Type. int halo{undefinedIdx}; // Halo level. idx_t t{}; // t, i and j. idx_t i{}; idx_t j{}; }; // Define ij bounding box for each face (this partition only). struct BoundingBox { idx_t iBegin{std::numeric_limits::max()}; idx_t iEnd{std::numeric_limits::min()}; idx_t jBegin{std::numeric_limits::max()}; idx_t jEnd{std::numeric_limits::min()}; }; // Get dimensions of grid const idx_t N = csGrid.N(); // Get size of halo. int nHalo = 0; options.get("halo", nHalo); // Unique non-halo nodes and cells. const idx_t nNodesUnique = 6 * N * N + 2; const idx_t nCellsUnique = 6 * N * N; // Total array sizes (including invalid corner ijs). const idx_t nNodesArray = 6 * (N + 2 * nHalo + 1) * (N + 2 * nHalo + 1); const idx_t nCellsArray = 6 * (N + 2 * nHalo) * (N + 2 * nHalo); // Total number of possible cells and nodes (including halos). const idx_t nNodesTotal = nNodesArray - 6 * 4 * nHalo * nHalo; const idx_t nCellsTotal = nCellsArray - 6 * 4 * nHalo * nHalo; // Projection and jacobian. const auto& csProjection = csGrid.cubedSphereProjection(); const auto jacobian = NeighbourJacobian(csGrid); // Get partition information. const int nParts = options.get("nb_parts"); const int thisPart = options.get("part"); // Define an index counter. const auto idxSum = [](const std::vector& idxCounts) -> idx_t { return std::accumulate(idxCounts.begin(), idxCounts.end(), 0); }; // Helper functions to get node vector idx from (i, j, t). const auto getNodeIdx = [&](idx_t i, idx_t j, idx_t t) -> size_t { const idx_t rowSize = (N + 2 * nHalo + 1); const idx_t tileSize = rowSize * rowSize; return static_cast(t * tileSize + (j + nHalo) * rowSize + i + nHalo); }; // Helper functions to get cell vector idx from (i, j, t). const auto getCellIdx = [&](idx_t i, idx_t j, idx_t t) -> size_t { const idx_t rowSize = (N + 2 * nHalo); const idx_t tileSize = rowSize * rowSize; return static_cast(t * tileSize + (j + nHalo) * rowSize + i + nHalo); }; // Return true for nodes interior to tile (excluding edge). const auto interiorNode = [&](idx_t i, idx_t j) -> bool { return i > 0 && i < N && j > 0 && j < N; }; // return true for nodes exterior to tile (excluding edge). const auto exteriorNode = [&](idx_t i, idx_t j) -> bool { return i < 0 || i > N || j < 0 || j > N; }; // Return true for cells interior to tile. const auto interiorCell = [&](idx_t i, idx_t j) -> bool { return i >= 0 && i < N && j >= 0 && j < N; }; // Return true for impossible node (i, j) combinations. const auto invalidNode = [&](idx_t i, idx_t j) -> bool { const bool inCorner = (i < 0 && j < 0) || // Bottom-left corner. (i > N && j < 0) || // Bottom-right corner. (i > N && j > N) || // Top-right corner. (i < 0 && j > N); // Top-left corner. if (inCorner) { return true; } const bool outOfBounds = i < -nHalo || i > N + nHalo || j < -nHalo || j > N + nHalo; if (outOfBounds) { return true; } return false; }; // Return true for impossible cell (i, j) combinations. const auto invalidCell = [&](idx_t i, idx_t j) -> bool { const bool inCorner = (i < 0 && j < 0) || // Bottom-left corner. (i > N - 1 && j < 0) || // Bottom-right corner. (i > N - 1 && j > N - 1) || // Top-right corner. (i < 0 && j > N - 1); // Top-left corner. if (inCorner) { return true; } const bool outOfBounds = i < -nHalo || i > N + nHalo - 1 || j < -nHalo || j > N + nHalo - 1; if (outOfBounds) { return true; } return false; }; // Return the (i, j) of cell that owns node (i, j). // It's possible that this may need to be user-defined in the future. const auto nodeOwnerCell = [](idx_t iNode, idx_t jNode) -> std::pair { return std::make_pair(iNode > 0 ? iNode - 1 : iNode, jNode > 0 ? jNode - 1 : jNode); }; // --------------------------------------------------------------------------- // 2. GLOBAL CELL DISTRIBUTION // Need to construct a lightweight global mesh. This will help us pair up // local and remote indices on different partitions. // --------------------------------------------------------------------------- // Make vector of all possible cells. auto globalCells = std::vector(static_cast(nCellsArray)); // Initialise bounding box. auto cellBounds = std::vector(6); // Set tij grid iterator. auto tijIt = csGrid.tij().begin(); for (gidx_t gridIdx = 0; gridIdx < csGrid.size(); ++gridIdx) { // It's more than likely that the grid order follows the same (t, j, i) row- // major order of the mesh. However, we shouldn't assume this is true. // Get cell index. const idx_t t = (*tijIt).t(); const idx_t i = (*tijIt).i(); const idx_t j = (*tijIt).j(); const size_t cellIdx = getCellIdx(i, j, t); GlobalElem& globalCell = globalCells[cellIdx]; // Set global index. globalCell.globalIdx = gridIdx + 1; // Set partition. globalCell.part = distribution.partition(gridIdx); if (globalCell.part == static_cast(thisPart)) { // Keep track of local (t, j, i) bounds. BoundingBox& bounds = cellBounds[static_cast(t)]; bounds.iBegin = std::min(bounds.iBegin, i - nHalo); bounds.iEnd = std::max(bounds.iEnd, i + nHalo + 1); bounds.jBegin = std::min(bounds.jBegin, j - nHalo); bounds.jEnd = std::max(bounds.jEnd, j + nHalo + 1); } // Increment tij. ++tijIt; } // Set counters for cell local indices. auto cellLocalIdxCount = std::vector(nParts, 0); // Give possible edge-halo cells a unique global ID. gidx_t cellGlobalIdxCount = nCellsUnique + 1; for (idx_t t = 0; t < 6; ++t) { for (idx_t j = -nHalo; j < N + nHalo; ++j) { for (idx_t i = -nHalo; i < N + nHalo; ++i) { // Skip invalid cell. if (invalidCell(i, j)) { continue; } // Get cell. GlobalElem& globalCell = globalCells[getCellIdx(i, j, t)]; // Set cell remote index if interior cell. if (interiorCell(i, j)) { // Set remote index. globalCell.remoteIdx = cellLocalIdxCount[static_cast(globalCell.part)]++; } else { // Set global index. globalCell.globalIdx = cellGlobalIdxCount++; // Leave remote index and part undefined. } } } } ATLAS_ASSERT(idxSum(cellLocalIdxCount) == nCellsUnique); ATLAS_ASSERT(cellGlobalIdxCount == nCellsTotal + 1); // --------------------------------------------------------------------------- // 3. GLOBAL NODE DISTRIBUTION // Construct a lightweight global distribution of nodes. Again, this will // help us match up local and remote indices accross partitions. // --------------------------------------------------------------------------- // Make list of all nodes. auto globalNodes = std::vector(static_cast(nNodesArray)); // Set counters for local node indices. auto nodeLocalIdxCount = std::vector(nParts, 0); // Set counter for global indices. gidx_t nodeGlobalOwnedIdxCount = 1; idx_t nodeGlobalGhostIdxCount = nNodesUnique + 1; for (idx_t t = 0; t < 6; ++t) { for (idx_t j = -nHalo; j < N + nHalo + 1; ++j) { for (idx_t i = -nHalo; i < N + nHalo + 1; ++i) { // Skip if not a valid node. if (invalidNode(i, j)) { continue; } // Get this node. GlobalElem& globalNode = globalNodes[getNodeIdx(i, j, t)]; // Get owner cell. idx_t iCell, jCell; std::tie(iCell, jCell) = nodeOwnerCell(i, j); const GlobalElem& ownerCell = globalCells[getCellIdx(iCell, jCell, t)]; if (interiorNode(i, j)) { // Node is definitely an owner. globalNode.globalIdx = nodeGlobalOwnedIdxCount++; globalNode.part = ownerCell.part; globalNode.remoteIdx = nodeLocalIdxCount[static_cast(globalNode.part)]++; } else if (exteriorNode(i, j)) { // Node is definitely a ghost. globalNode.globalIdx = nodeGlobalGhostIdxCount++; // Leave remote index and partition undefined. } else { // We're not sure (i.e., node is on a tile edge). // Check that xy is on this tile. PointXY xy = jacobian.xy(PointIJ(i, j), t); xy = jacobian.snapToEdge(xy, t); // This will only determine if tGlobal does not match t. // This is cheaper than determining the correct tGlobal. idx_t tGlobal = csProjection.getCubedSphereTiles().indexFromXY(xy.data()); if (tGlobal == t) { // Node is an owner. globalNode.globalIdx = nodeGlobalOwnedIdxCount++; globalNode.part = ownerCell.part; globalNode.remoteIdx = nodeLocalIdxCount[static_cast(globalNode.part)]++; } else { // Node is a ghost. globalNode.globalIdx = nodeGlobalGhostIdxCount++; } } // Finished with this node. } } // Finished with all nodes on tile. } // Finished with all tiles. ATLAS_ASSERT(nodeGlobalOwnedIdxCount == nNodesUnique + 1); ATLAS_ASSERT(nodeGlobalGhostIdxCount == nNodesTotal + 1); ATLAS_ASSERT(idxSum(nodeLocalIdxCount) == nNodesUnique); // --------------------------------------------------------------------------- // 4. LOCATE LOCAL CELLS. // Now that we know where all the nodes and cells are, we can make a list // of local cells to add the the mesh. "Owner" cells correspond to grid // points on this parition. "Halo" cells are at most nHalo grid points // away from an owner cell. // --------------------------------------------------------------------------- // Make vector of local cells. auto localCells = std::vector{}; // Loop over all possible local cells. for (idx_t t = 0; t < 6; ++t) { // Limit range to bounds recorded earlier. const BoundingBox& bounds = cellBounds[static_cast(t)]; for (idx_t j = bounds.jBegin; j < bounds.jEnd; ++j) { for (idx_t i = bounds.iBegin; i < bounds.iEnd; ++i) { if (invalidCell(i, j)) { continue; } // Get cell GlobalElem& globalCell = globalCells[getCellIdx(i, j, t)]; // Check if cell is an owner. if (globalCell.part == static_cast(thisPart)) { // Cell is an owner. localCells.emplace_back(); LocalElem& localCell = localCells.back(); localCell.type = ElemType::OWNER; localCell.halo = 0; localCell.t = t; localCell.i = i; localCell.j = j; localCell.globalPtr = &globalCell; } else { // Cell is halo if there are nearby owners. bool ownerFound = false; idx_t halo = nHalo; for (idx_t jHalo = j - nHalo; jHalo < j + nHalo + 1; ++jHalo) { for (idx_t iHalo = i - nHalo; iHalo < i + nHalo + 1; ++iHalo) { if (invalidCell(iHalo, jHalo) || (iHalo == i && jHalo == j)) { continue; } // Is there a nearby owner cell? const GlobalElem& ownerCell = globalCells[getCellIdx(iHalo, jHalo, t)]; const bool isOwner = ownerCell.part == static_cast(thisPart); ownerFound = ownerFound || isOwner; if (isOwner) { // Determine halo level from cell distance (l-infinity norm). const idx_t dist = std::max(std::abs(iHalo - i), std::abs(jHalo - j)); halo = std::min(dist, halo); } } } if (ownerFound) { // Cell is a halo. localCells.emplace_back(); LocalElem& localCell = localCells.back(); localCell.type = ElemType::HALO; localCell.halo = halo; localCell.t = t; localCell.i = i; localCell.j = j; localCell.globalPtr = &globalCell; } // Finished with halo search. } // Finished with cell. } } // Finished with all cells on tile. } // Finished with all tiles. // Partition by cell type. auto haloBeginIt = std::stable_partition(localCells.begin(), localCells.end(), [](const LocalElem& localCell) -> bool { return localCell.type == ElemType::OWNER; }); // Sort by halo. std::stable_sort(haloBeginIt, localCells.end(), [](const LocalElem& cellA, const LocalElem& cellB) -> bool { return cellA.halo < cellB.halo; }); // Point global cell to local cell. This is needed to determine node halos. // Need to determine remote index and partition if we haven't done so already. for (LocalElem& localCell : localCells) { localCell.globalPtr->localPtr = &localCell; if (localCell.globalPtr->remoteIdx == undefinedIdx) { const PointTIJ tijGlobal = jacobian.ijLocalToGlobal(PointIJ(localCell.i + 0.5, localCell.j + 0.5), localCell.t); const idx_t& t = tijGlobal.t(); const idx_t& i = tijGlobal.ij().iCell(); const idx_t& j = tijGlobal.ij().jCell(); const GlobalElem& ownerCell = globalCells[getCellIdx(i, j, t)]; localCell.globalPtr->remoteIdx = ownerCell.remoteIdx; localCell.globalPtr->part = ownerCell.part; } } // --------------------------------------------------------------------------- // 5. LOCATE LOCAL NODES. // We can now locate the nodes surrounding the owner and halo cells on the // mesh. // --------------------------------------------------------------------------- // Make vector of local nodes. auto localNodes = std::vector{}; // Loop over all possible local nodes. for (idx_t t = 0; t < 6; ++t) { // Limit range to bounds recorded earlier. const BoundingBox& bounds = cellBounds[static_cast(t)]; for (idx_t j = bounds.jBegin; j < bounds.jEnd + 1; ++j) { for (idx_t i = bounds.iBegin; i < bounds.iEnd + 1; ++i) { if (invalidNode(i, j)) { continue; } // Get node. GlobalElem& globalNode = globalNodes[getNodeIdx(i, j, t)]; // Check if node is an owner. if (globalNode.part == static_cast(thisPart)) { // Node is an owner. localNodes.emplace_back(); LocalElem& localNode = localNodes.back(); localNode.type = ElemType::OWNER; localNode.halo = 0; localNode.i = i; localNode.j = j; localNode.t = t; localNode.globalPtr = &globalNode; } else { // Node is possibly a ghost or halo. // Search four neighbouring cells of node. bool isGhost = false; idx_t halo = nHalo; // A double-loop with no more than four iterations! for (idx_t iCell = i - 1; iCell < i + 1; ++iCell) { for (idx_t jCell = j - 1; jCell < j + 1; ++jCell) { if (invalidCell(iCell, jCell)) { continue; } const GlobalElem& globalCell = globalCells[getCellIdx(iCell, jCell, t)]; // Get halo information from cell. // By this point, any global cell on this PE will have a // non-null local cell pointer. if (globalCell.localPtr) { isGhost = true; halo = std::min(halo, globalCell.localPtr->halo); } } } if (isGhost) { // Node is an ghost/halo. localNodes.emplace_back(); LocalElem& localNode = localNodes.back(); localNode.type = ElemType::HALO; localNode.halo = halo; localNode.t = t; localNode.i = i; localNode.j = j; localNode.globalPtr = &globalNode; } } // Finished with this node. } } // Finished with all nodes on tile. } // Finished with all tiles. // Partition by node type. auto ghostBeginIt = std::stable_partition(localNodes.begin(), localNodes.end(), [](const LocalElem& localNode) -> bool { return localNode.type == ElemType::OWNER; }); // Sort by halo. std::stable_sort(ghostBeginIt, localNodes.end(), [](const LocalElem& nodeA, const LocalElem& nodeB) -> bool { return nodeA.halo < nodeB.halo; }); // Associate global nodes with local nodes. Allows us to determine node // local indices around a cell. // Determine partition and remote index if we haven't done so already. for (LocalElem& localNode : localNodes) { localNode.globalPtr->localPtr = &localNode; if (localNode.globalPtr->remoteIdx == undefinedIdx) { const PointTIJ tijGlobal = jacobian.ijLocalToGlobal(PointIJ(localNode.i, localNode.j), localNode.t); const idx_t& t = tijGlobal.t(); const idx_t& i = tijGlobal.ij().iNode(); const idx_t& j = tijGlobal.ij().jNode(); const GlobalElem& ownerNode = globalNodes[getNodeIdx(i, j, t)]; localNode.globalPtr->remoteIdx = ownerNode.remoteIdx; localNode.globalPtr->part = ownerNode.part; } } // --------------------------------------------------------------------------- // 6. ASSIGN NODES TO MESH // In addition to the usual fields, we will add t, i and j to mesh.nodes(). // --------------------------------------------------------------------------- // Resize nodes. auto& nodes = mesh.nodes(); nodes.resize(static_cast(localNodes.size())); // Add extra field. Field tijField = nodes.add(Field("tij", make_datatype(), make_shape(nodes.size(), 3))); tijField.set_variables(3); // Get field views auto nodesGlobalIdx = array::make_view(nodes.global_index()); auto nodesRemoteIdx = array::make_indexview(nodes.remote_index()); auto nodesXy = array::make_view(nodes.xy()); auto nodesLonLat = array::make_view(nodes.lonlat()); auto nodesPart = array::make_view(nodes.partition()); auto nodesGhost = array::make_view(nodes.ghost()); auto nodesHalo = array::make_view(nodes.halo()); auto nodesFlags = array::make_view(nodes.flags()); auto nodesTij = array::make_view(tijField); // Set fields. idx_t nodeLocalIdx = 0; for (const LocalElem& localNode : localNodes) { // Set global index. nodesGlobalIdx(nodeLocalIdx) = localNode.globalPtr->globalIdx; // Set node remote index. nodesRemoteIdx(nodeLocalIdx) = localNode.globalPtr->remoteIdx; // Set node partition. nodesPart(nodeLocalIdx) = localNode.globalPtr->part; // Set xy. const PointXY xyLocal = jacobian.xy(PointIJ(localNode.i, localNode.j), localNode.t); const PointXY xyGlobal = interiorNode(localNode.i, localNode.j) ? xyLocal : jacobian.xyLocalToGlobal(xyLocal, localNode.t).xy(); nodesXy(nodeLocalIdx, XX) = xyLocal.x(); nodesXy(nodeLocalIdx, YY) = xyLocal.y(); // Set lon-lat. const PointLonLat lonLat = csProjection.lonlat(xyGlobal); nodesLonLat(nodeLocalIdx, LON) = lonLat.lon(); nodesLonLat(nodeLocalIdx, LAT) = lonLat.lat(); // Set tij. nodesTij(nodeLocalIdx, Coordinates::T) = localNode.t; nodesTij(nodeLocalIdx, Coordinates::I) = localNode.i; nodesTij(nodeLocalIdx, Coordinates::J) = localNode.j; // Set halo. nodesHalo(nodeLocalIdx) = localNode.halo; // Set flags. Topology::reset(nodesFlags(nodeLocalIdx)); switch (localNode.type) { case ElemType::UNDEFINED: { ATLAS_ASSERT(0, "Trying to add undefined node to mesh."); break; } case ElemType::OWNER: { nodesGhost(nodeLocalIdx) = 0; // Vitally important that these two match! ATLAS_ASSERT(nodeLocalIdx == localNode.globalPtr->remoteIdx, "Owner local index and remote index do not match."); break; } case ElemType::HALO: { nodesGhost(nodeLocalIdx) = 1; Topology::set(nodesFlags(nodeLocalIdx), Topology::GHOST); break; } } ++nodeLocalIdx; } // --------------------------------------------------------------------------- // 7. ASSIGN CELLS TO MESH // Again, we'll add t, i and j to mesh.cells(). We'll also add the xy and // lonlat coordinates of the cell centres. // --------------------------------------------------------------------------- auto& cells = mesh.cells(); // Resize cells. cells.add(mesh::ElementType::create("Quadrilateral"), static_cast(localCells.size())); // Add extra fields. tijField = cells.add(Field("tij", make_datatype(), make_shape(cells.size(), 3))); tijField.set_variables(3); Field xyField = cells.add(Field("xy", make_datatype(), make_shape(cells.size(), 2))); xyField.set_variables(2); Field lonLatField = cells.add(Field("lonlat", make_datatype(), make_shape(cells.size(), 2))); lonLatField.set_variables(2); Field ghostField = cells.add(Field("ghost", make_datatype(), make_shape(cells.size()))); // Set field views. auto cellsGlobalIdx = array::make_view(cells.global_index()); auto cellsRemoteIdx = array::make_indexview(cells.remote_index()); auto cellsPart = array::make_view(cells.partition()); auto cellsGhost = array::make_view(ghostField); auto cellsHalo = array::make_view(cells.halo()); auto cellsFlags = array::make_view(cells.flags()); auto cellsTij = array::make_view(tijField); auto cellsXy = array::make_view(xyField); auto cellsLonLat = array::make_view(lonLatField); // Set local cells. auto& nodeConnectivity = cells.node_connectivity(); const idx_t cellElemIdx0 = cells.elements(0).begin(); // Set method to get node local index. const auto getNodeLocalIdx = [&](idx_t i, idx_t j, idx_t t) -> idx_t { const GlobalElem& globalNode = globalNodes[getNodeIdx(i, j, t)]; // Do some pointer arithmetic to get local index. return static_cast(globalNode.localPtr - localNodes.data()); }; idx_t cellLocalIdx = 0; for (const LocalElem& localCell : localCells) { // Get local indices four surroundings nodes. const auto quadNodeIdx = std::array{getNodeLocalIdx(localCell.i, localCell.j, localCell.t), getNodeLocalIdx(localCell.i + 1, localCell.j, localCell.t), getNodeLocalIdx(localCell.i + 1, localCell.j + 1, localCell.t), getNodeLocalIdx(localCell.i, localCell.j + 1, localCell.t)}; // Set connectivity. nodeConnectivity.set(cellLocalIdx + cellElemIdx0, quadNodeIdx.data()); // Set global index. cellsGlobalIdx(cellLocalIdx) = localCell.globalPtr->globalIdx; // Set cell remote index. cellsRemoteIdx(cellLocalIdx) = localCell.globalPtr->remoteIdx; // Set partition. cellsPart(cellLocalIdx) = localCell.globalPtr->part; // Set xy. const PointXY xyLocal = jacobian.xy(PointIJ(localCell.i + 0.5, localCell.j + 0.5), localCell.t); const PointXY xyGlobal = interiorCell(localCell.i, localCell.j) ? xyLocal : jacobian.xyLocalToGlobal(xyLocal, localCell.t).xy(); cellsXy(cellLocalIdx, XX) = xyLocal.x(); cellsXy(cellLocalIdx, YY) = xyLocal.y(); // Set lon-lat. const PointLonLat lonLat = csProjection.lonlat(xyGlobal); cellsLonLat(cellLocalIdx, LON) = lonLat.lon(); cellsLonLat(cellLocalIdx, LAT) = lonLat.lat(); // Set tij. cellsTij(cellLocalIdx, Coordinates::T) = localCell.t; cellsTij(cellLocalIdx, Coordinates::I) = localCell.i; cellsTij(cellLocalIdx, Coordinates::J) = localCell.j; // Set halo. cellsHalo(cellLocalIdx) = localCell.halo; // Set ghost. cellsGhost(cellLocalIdx) = cellsHalo(cellLocalIdx) > 0; // Set flags. Topology::reset(cellsFlags(cellLocalIdx)); switch (localCell.type) { case ElemType::UNDEFINED: { ATLAS_ASSERT(0, "Trying to add undefined cell to mesh."); break; } case ElemType::OWNER: { // Vitally important that these two match! ATLAS_ASSERT(cellLocalIdx == localCell.globalPtr->remoteIdx, "Owner local index and remote index do not match."); break; } case ElemType::HALO: { Topology::set(cellsFlags(cellLocalIdx), Topology::GHOST); break; } } ++cellLocalIdx; } // --------------------------------------------------------------------------- // 8. FINALISE // Done. That was rather a lot of bookkeeping! // --------------------------------------------------------------------------- set_metadata(mesh); return; } // ----------------------------------------------------------------------------- void CubedSphereMeshGenerator::set_metadata(Mesh& mesh) const { const auto nHalo = options.get("halo"); // Set basic halo metadata. mesh.metadata().set("halo", nHalo); mesh.metadata().set("halo_locked", true); mesh.nodes().metadata().set("parallel", true); mesh.cells().metadata().set("parallel", true); // Loop over nodes and count number of halo elements. auto nNodes = std::vector(nHalo + 1, 0); const auto nodeHalo = array::make_view(mesh.nodes().halo()); for (idx_t i = 0; i < mesh.nodes().size(); ++i) { ++nNodes[static_cast(nodeHalo(i))]; } std::partial_sum(nNodes.begin(), nNodes.end(), nNodes.begin()); // Set node halo metadata. for (size_t i = 0; i < nNodes.size(); ++i) { const auto str = "nb_nodes_including_halo[" + std::to_string(i) + "]"; mesh.metadata().set(str, nNodes[i]); } // Loop over cells and count number of halo elements. auto nCells = std::vector(nHalo + 1, 0); const auto cellHalo = array::make_view(mesh.cells().halo()); for (idx_t i = 0; i < mesh.cells().size(); ++i) { ++nCells[static_cast(cellHalo(i))]; } std::partial_sum(nCells.begin(), nCells.end(), nCells.begin()); // Set cell halo metadata. for (size_t i = 0; i < nCells.size(); ++i) { const auto str = "nb_cells_including_halo[0][" + std::to_string(i) + "]"; mesh.metadata().set(str, nCells[i]); } } // ----------------------------------------------------------------------------- void CubedSphereMeshGenerator::hash(eckit::Hash& h) const { h.add("CubedSphereMeshGenerator"); options.hash(h); } // ----------------------------------------------------------------------------- namespace { static MeshGeneratorBuilder CubedSphereMeshGenerator(CubedSphereMeshGenerator::static_type()); } // ----------------------------------------------------------------------------- } // namespace meshgenerator } // namespace atlas atlas-0.46.0/src/atlas/meshgenerator/detail/CubedSphereMeshGenerator.h0000664000175000017500000000334015160212070026111 0ustar alastairalastair/* * (C) Crown Copyright 2021 Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ #pragma once #include "atlas/array.h" #include "atlas/meshgenerator/MeshGenerator.h" #include "atlas/meshgenerator/detail/MeshGeneratorImpl.h" #include "atlas/util/Config.h" #include "atlas/util/Metadata.h" #ifndef DOXYGEN_SHOULD_SKIP_THIS namespace eckit { class Parametrisation; } namespace atlas { class CubedSphereGrid; class Mesh; template class vector; } // namespace atlas namespace atlas { namespace grid { class Distribution; } // namespace grid } // namespace atlas #endif namespace atlas { namespace meshgenerator { //-------------------------------------------------------------------------------------------------- class CubedSphereMeshGenerator : public MeshGenerator::Implementation { public: CubedSphereMeshGenerator(const eckit::Parametrisation& = util::NoConfig()); virtual void generate(const Grid&, const grid::Distribution&, Mesh&) const override; virtual void generate(const Grid&, Mesh&) const override; using MeshGenerator::Implementation::generate; static std::string static_type() { return "cubedsphere"; } std::string type() const override { return static_type(); } private: virtual void hash(eckit::Hash&) const override; void configure_defaults(); void generate_mesh(const CubedSphereGrid&, const grid::Distribution&, Mesh&) const; void set_metadata(Mesh&) const; private: util::Metadata options; }; //-------------------------------------------------------------------------------------------------- } // namespace meshgenerator } // namespace atlas atlas-0.46.0/src/atlas/meshgenerator/MeshGenerator.h0000664000175000017500000000425015160212070022536 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include "atlas/library/config.h" #include "atlas/util/Config.h" #include "atlas/util/ObjectHandle.h" namespace eckit { class Hash; class Parametrisation; } // namespace eckit namespace atlas { class Mesh; class Grid; class Projection; } // namespace atlas namespace atlas { namespace grid { class Distribution; class Partitioner; } // namespace grid } // namespace atlas namespace atlas { namespace meshgenerator { class MeshGeneratorImpl; } //---------------------------------------------------------------------------------------------------------------------- class MeshGenerator : DOXYGEN_HIDE(public util::ObjectHandle) { public: using Parameters = atlas::util::Config; public: using Handle::Handle; MeshGenerator(const std::string&, const eckit::Parametrisation& = util::NoConfig()); MeshGenerator(const eckit::Parametrisation&); void hash(eckit::Hash&) const; Mesh generate(const Grid&, const grid::Distribution&) const; Mesh generate(const Grid&, const grid::Partitioner&) const; Mesh generate(const Grid&) const; Mesh operator()(const Grid&, const grid::Distribution&) const; Mesh operator()(const Grid&, const grid::Partitioner&) const; Mesh operator()(const Grid&) const; std::string type() const; }; //---------------------------------------------------------------------------------------------------------------------- // Shorthands class StructuredMeshGenerator : public MeshGenerator { public: StructuredMeshGenerator(const eckit::Parametrisation& config = util::NoConfig()): MeshGenerator("structured", config) {} }; //---------------------------------------------------------------------------------------------------------------------- } // namespace atlas atlas-0.46.0/src/atlas/array_fwd.h0000664000175000017500000000713515160212070017113 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ /// @file array_fwd.h /// @brief Forward declarations of public API header atlas/array.h /// @author Willem Deconinck /// @file array_fwd.h /// @author Willem Deconinck #pragma once #include #include #include "atlas/array/ArrayViewDefs.h" #define ENABLE_IF_NOT_CONST typename std::enable_if::value, Value>::type* = nullptr #define ENABLE_IF_CONST typename std::enable_if::value, Value>::type* = nullptr namespace atlas { namespace array { class DataType; class ArraySpec; class ArrayShape; class ArrayStrides; class Array; template class ArrayT; template class ArrayView; template class LocalView; template class IndexView; template ArrayView make_view(Array& array); template ArrayView make_view(const Array& array); template LocalView make_view(Value data[], const ArrayShape& shape); template LocalView make_view(const Value data[], const ArrayShape& shape); template LocalView make_view(Value data[], const ArrayShape& shape); template LocalView make_view(typename std::remove_const::type data[], const ArrayShape& shape); //------------------------------------------------------------------------------------------------------ template LocalView make_view(Value data[], size_t size); template LocalView make_view(const Value data[], size_t size); template LocalView make_view(Value data[], size_t size); template LocalView make_view(typename std::remove_const::type data[], size_t size); template ArrayView make_host_view(Array& array); template ArrayView make_host_view(const Array& array); template ArrayView make_device_view(Array& array); template ArrayView make_device_view(const Array& array); template IndexView make_indexview(Array& array); template IndexView make_indexview(const Array& array); template IndexView make_host_indexview(Array& array); template IndexView make_host_indexview(const Array& array); // class Table; // template // class TableView; // template // class TableRow; // template // TableView make_table_view( const Table& table ); #undef ENABLE_IF_NOT_CONST #undef ENABLE_IF_CONST } // namespace array } // namespace atlas atlas-0.46.0/src/atlas/projection/0000775000175000017500000000000015160212070017132 5ustar alastairalastairatlas-0.46.0/src/atlas/projection/Projection.h0000664000175000017500000000452415160212070021424 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include "atlas/domain/Domain.h" #include "atlas/library/config.h" #include "atlas/projection/Jacobian.h" #include "atlas/projection/detail/ProjectionImpl.h" #include "atlas/util/ObjectHandle.h" //--------------------------------------------------------------------------------------------------------------------- // Forward declarations namespace eckit { class Parametrisation; class Hash; namespace geometry { class Point2; } } // namespace eckit //--------------------------------------------------------------------------------------------------------------------- namespace atlas { class PointLonLat; class PointXY; using Point2 = eckit::geometry::Point2; //--------------------------------------------------------------------------------------------------------------------- namespace util { class Config; } namespace projection { namespace detail { class ProjectionImpl; } } // namespace projection class Projection : DOXYGEN_HIDE(public util::ObjectHandle) { public: using Spec = util::Config; using Jacobian = projection::Jacobian; public: using Handle::Handle; Projection(); Projection(const eckit::Parametrisation&); Projection(const std::string& type, const eckit::Parametrisation&); operator bool() const; void xy2lonlat(double crd[]) const; void xy2lonlat(Point2&) const; void lonlat2xy(double crd[]) const; void lonlat2xy(Point2&) const; Jacobian jacobian(const PointLonLat&) const; PointLonLat lonlat(const PointXY&) const; PointXY xy(const PointLonLat&) const; bool strictlyRegional() const; RectangularLonLatDomain lonlatBoundingBox(const Domain&) const; Spec spec() const; std::string units() const; std::string type() const; void hash(eckit::Hash&) const; }; //--------------------------------------------------------------------------------------------------------------------- } // namespace atlas atlas-0.46.0/src/atlas/projection/Jacobian.h0000664000175000017500000001011515160212070021007 0ustar alastairalastair/* * (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 #include #include #include "eckit/geometry/Point2.h" //--------------------------------------------------------------------------------------------------------------------- namespace atlas { using Point2 = eckit::geometry::Point2; //--------------------------------------------------------------------------------------------------------------------- namespace projection { class Jacobian : public std::array, 2> { public: using std::array, 2>::array; static Jacobian identity() { return Jacobian{1., 0., 0., 1.}; } Jacobian() = default; Jacobian(double j00, double j01, double j10, double j11) { (*this)[0][0] = j00; (*this)[0][1] = j01; (*this)[1][0] = j10; (*this)[1][1] = j11; } Jacobian(std::initializer_list> list): Jacobian{*(list.begin()->begin()), *(list.begin()->begin() + 1), *((list.begin() + 1)->begin()), *((list.begin() + 1)->begin() + 1)} {} Jacobian operator-(const Jacobian& jac) const { return Jacobian{(*this)[0][0] - jac[0][0], (*this)[0][1] - jac[0][1], (*this)[1][0] - jac[1][0], (*this)[1][1] - jac[1][1]}; } Jacobian operator+(const Jacobian& jac) const { return Jacobian{(*this)[0][0] + jac[0][0], (*this)[0][1] + jac[0][1], (*this)[1][0] + jac[1][0], (*this)[1][1] + jac[1][1]}; } Jacobian operator*(double a) const { return Jacobian{(*this)[0][0] * a, (*this)[0][1] * a, (*this)[1][0] * a, (*this)[1][1] * a}; } Jacobian operator*(const Jacobian& jac) const { return Jacobian{(*this)[0][0] * jac[0][0] + (*this)[0][1] * jac[1][0], (*this)[0][0] * jac[0][1] + (*this)[0][1] * jac[1][1], (*this)[1][0] * jac[0][0] + (*this)[1][1] * jac[1][0], (*this)[1][0] * jac[0][1] + (*this)[1][1] * jac[1][1]}; } Point2 operator*(const Point2& x) const { return Point2{(*this)[0][0] * x[0] + (*this)[0][1] * x[1], (*this)[1][0] * x[0] + (*this)[1][1] * x[1]}; } double norm() const { return std::sqrt((*this)[0][0] * (*this)[0][0] + (*this)[0][1] * (*this)[0][1] + (*this)[1][0] * (*this)[1][0] + (*this)[1][1] * (*this)[1][1]); } double determinant() const { return (*this)[0][0] * (*this)[1][1] - (*this)[0][1] * (*this)[1][0]; } Jacobian inverse() const { return Jacobian{(*this)[1][1], -(*this)[0][1], -(*this)[1][0], (*this)[0][0]} * (1. / determinant()); } Jacobian transpose() const { return Jacobian{(*this)[0][0], (*this)[1][0], (*this)[0][1], (*this)[1][1]}; } double dx_dlon() const { return (*this)[JDX][JDLON]; } double dy_dlon() const { return (*this)[JDY][JDLON]; } double dx_dlat() const { return (*this)[JDX][JDLAT]; } double dy_dlat() const { return (*this)[JDY][JDLAT]; } double dlon_dx() const { return (*this)[JDLON][JDX]; } double dlon_dy() const { return (*this)[JDLON][JDY]; } double dlat_dx() const { return (*this)[JDLAT][JDX]; } double dlat_dy() const { return (*this)[JDLAT][JDY]; } friend std::ostream& operator<<(std::ostream& os, const Jacobian& jac) { os << jac[0][0] << " " << jac[0][1] << "\n" << jac[1][0] << " " << jac[1][1]; return os; } private: enum { JDX = 0, JDY = 1 }; enum { JDLON = 0, JDLAT = 1 }; }; //--------------------------------------------------------------------------------------------------------------------- } // namespace projection } // namespace atlas atlas-0.46.0/src/atlas/projection/detail/0000775000175000017500000000000015160212070020374 5ustar alastairalastairatlas-0.46.0/src/atlas/projection/detail/MercatorProjection.h0000664000175000017500000000446715160212070024371 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include "atlas/domain.h" #include "atlas/projection/detail/ProjectionImpl.h" #include "atlas/util/NormaliseLongitude.h" namespace atlas { namespace projection { namespace detail { template class MercatorProjectionT final : public ProjectionImpl { public: // constructor MercatorProjectionT(const eckit::Parametrisation&); // projection name static std::string static_type() { return Rotation::typePrefix() + "mercator"; } std::string type() const override { return static_type(); } // projection and inverse projection void xy2lonlat(double crd[]) const override; void lonlat2xy(double crd[]) const override; Jacobian jacobian(const PointLonLat&) const override; bool strictlyRegional() const override { return true; } // Mercator projection cannot be used for global grids RectangularLonLatDomain lonlatBoundingBox(const Domain& domain) const override { return ProjectionImpl::lonlatBoundingBox(domain); } // specification Spec spec() const override; std::string units() const override { return "meters"; } void hash(eckit::Hash&) const override; protected: Normalise normalise_; util::NormaliseLongitude normalise_mercator_; double lon0_; // central longitude (default = 0 ) double lat1_; // latitude where cylinder cuts sphere (default = 0 ) double radius_; // sphere radius double k_radius_; // sphere radius double inv_k_radius_; // 1/(sphere radius) double eccentricity_; double semi_major_axis_; double semi_minor_axis_; double false_easting_; double false_northing_; void setup(const eckit::Parametrisation& p); private: Rotation rotation_; }; using MercatorProjection = MercatorProjectionT; using RotatedMercatorProjection = MercatorProjectionT; } // namespace detail } // namespace projection } // namespace atlas atlas-0.46.0/src/atlas/projection/detail/CubedSphereEquiDistProjection.cc0000664000175000017500000001350115160212070026601 0ustar alastairalastair/* * (C) Copyright 2020 UCAR * * 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. */ #include "CubedSphereEquiDistProjection.h" #include #include #include "eckit/config/Parametrisation.h" #include "eckit/types/FloatCompare.h" #include "eckit/utils/Hash.h" #include "atlas/projection/detail/ProjectionFactory.h" #include "atlas/projection/detail/ProjectionUtilities.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Log.h" #include "atlas/util/Config.h" #include "atlas/util/Constants.h" #include "atlas/util/CoordinateEnums.h" namespace atlas { namespace projection { namespace detail { static constexpr bool debug = false; // constexpr so compiler can optimize `if ( debug ) { ... }` out // ------------------------------------------------------------------------------------------------- CubedSphereEquiDistProjection::CubedSphereEquiDistProjection(const eckit::Parametrisation& params): CubedSphereProjectionBase(params) {} // ------------------------------------------------------------------------------------------------- void CubedSphereEquiDistProjection::xy2alphabeta(double crd[], idx_t t) const { throw_NotImplemented("xy2alphabeta not implemented for CubedSphereEquiDistProjection", Here()); } // ------------------------------------------------------------------------------------------------- void CubedSphereEquiDistProjection::alphabeta2xy(double crd[], idx_t t) const { throw_NotImplemented("alphabeta2xy not implemented for CubedSphereEquiDistProjection", Here()); } // ------------------------------------------------------------------------------------------------- void CubedSphereEquiDistProjection::lonlat2alphabeta(double crd[], idx_t t) const { throw_NotImplemented("lonlat2alphabeta not implemented for CubedSphereEquiDistProjection", Here()); } // ------------------------------------------------------------------------------------------------- void CubedSphereEquiDistProjection::alphabeta2lonlat(double crd[], idx_t t) const { throw_NotImplemented("alphabeta2lonlat not implemented for CubedSphereEquiDistProjection", Here()); } // ------------------------------------------------------------------------------------------------- Jacobian CubedSphereEquiDistProjection::jacobian(const PointLonLat& lonlat, idx_t t) const { throw_NotImplemented("jacobian not implemented for CubedSphereEquiDistProjection", Here()); } // ------------------------------------------------------------------------------------------------- Jacobian CubedSphereEquiDistProjection::alphabetaJacobian(const PointLonLat& lonlat, idx_t t) const { throw_NotImplemented("alphabetaJacobian not implemented for CubedSphereEquiDistProjection", Here()); } // ------------------------------------------------------------------------------------------------- void CubedSphereEquiDistProjection::lonlat2xy(double crd[]) const { if (debug) { Log::info() << "equidist lonlat2xy start : lonlat = " << crd[LON] << " " << crd[LAT] << std::endl; } idx_t t; double ab[2]; // alpha-beta coordinate double xyz[3]; // on Cartesian grid CubedSphereProjectionBase::lonlat2xy_pre(crd, t, xyz); //now should be tile 0 - now calculate (alpha, beta) in radians. // should be between - 45.0 and 45.0 ab[0] = 45.0 * xyz[YY] / xyz[XX]; ab[1] = -45.0 * xyz[ZZ] / xyz[XX]; if (debug) { Log::info() << "equidist lonlat2xy xyz ab : " << xyz[0] << " " << xyz[1] << " " << xyz[2] << " " << ab[0] << " " << ab[1] << std::endl; } CubedSphereProjectionBase::alphabetat2xy(t, ab, crd); if (debug) { Log::info() << "equidist lonlat2xy end : xy = " << crd[LON] << " " << crd[LAT] << std::endl; } } // ------------------------------------------------------------------------------------------------- void CubedSphereEquiDistProjection::xy2lonlat(double crd[]) const { static const double rsq3 = 1.0 / sqrt(3.0); double xyz[3]; double ab[2]; // alpha-beta coordinate idx_t t; // tile index // calculate xy (in degrees) to alpha beta (in radians) and t - tile index. CubedSphereProjectionBase::xy2alphabetat(crd, t, ab); if (debug) { Log::info() << "equidist xy2lonlat:: crd t ab : " << crd[LON] << " " << crd[1] << " " << t << " " << ab[0] << " " << ab[1] << std::endl; } xyz[0] = -rsq3; xyz[1] = -rsq3 * ab[0] / 45.; xyz[2] = -rsq3 * ab[1] / 45.; CubedSphereProjectionBase::xy2lonlat_post(xyz, t, crd); if (debug) { Log::info() << "end of equidistant xy2lonlat lonlat = " << crd[LON] << " " << crd[LAT] << std::endl; } } // ------------------------------------------------------------------------------------------------- Jacobian CubedSphereEquiDistProjection::jacobian(const PointLonLat&) const { ATLAS_NOTIMPLEMENTED; } // ------------------------------------------------------------------------------------------------- // ------------------------------------------------------------------------------------------------- CubedSphereEquiDistProjection::Spec CubedSphereEquiDistProjection::spec() const { // Fill projection specification Spec proj; proj.set("type", static_type()); return proj; } // ------------------------------------------------------------------------------------------------- void CubedSphereEquiDistProjection::hash(eckit::Hash& h) const { // Add to hash h.add(static_type()); CubedSphereProjectionBase::hash(h); } // ------------------------------------------------------------------------------------------------- namespace { static ProjectionBuilder register_1(CubedSphereEquiDistProjection::static_type()); } } // namespace detail } // namespace projection } // namespace atlas atlas-0.46.0/src/atlas/projection/detail/ProjProjection.h0000664000175000017500000000322015160212070023511 0ustar alastairalastair/* * (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" namespace atlas { namespace projection { namespace detail { // Forward declaration of Proj, defined in ProjProjection.cc struct Proj; class ProjProjection final : public ProjectionImpl { public: ProjProjection(const eckit::Parametrisation&); 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; Jacobian jacobian(const PointLonLat&) 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; private: Normalise normalise_; std::string proj_string_; std::string source_; std::string geocentric_; bool source_encoded_; bool geocentric_encoded_; std::unique_ptr proj_; Spec extraSpec_; }; } // namespace detail } // namespace projection } // namespace atlas atlas-0.46.0/src/atlas/projection/detail/LonLatProjection.h0000664000175000017500000000373015160212070023776 0ustar alastairalastair/* * (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/projection/detail/ProjectionImpl.h" namespace atlas { class Projection; } namespace atlas { namespace projection { namespace detail { template class LonLatProjectionT final : public ProjectionImpl { private: friend class atlas::Projection; LonLatProjectionT() = default; public: // constructor LonLatProjectionT(const eckit::Parametrisation&); // destructor ~LonLatProjectionT() = default; // projection name static std::string static_type() { return Rotation::typePrefix() + "lonlat"; } std::string type() const override { return static_type(); } // projection and inverse projection void xy2lonlat(double crd[]) const override { rotation_.rotate(crd); } void lonlat2xy(double crd[]) const override { rotation_.unrotate(crd); } Jacobian jacobian(const PointLonLat&) const override; bool strictlyRegional() const override { return false; } RectangularLonLatDomain lonlatBoundingBox(const Domain&) const override; // specification Spec spec() const override; std::string units() const override { return "degrees"; } operator bool() const override { return rotation_.rotated(); } void hash(eckit::Hash&) const override; private: Rotation rotation_; }; using LonLatProjection = LonLatProjectionT; using RotatedLonLatProjection = LonLatProjectionT; // -------------------------------------------------------------------------------------------------------------------- } // namespace detail } // namespace projection } // namespace atlas atlas-0.46.0/src/atlas/projection/detail/LambertAzimuthalEqualAreaProjection.h0000664000175000017500000000353715160212070027640 0ustar alastairalastair/* * (C) Copyright 1996- ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include "atlas/domain.h" #include "atlas/projection/detail/ProjectionImpl.h" namespace atlas { namespace projection { namespace detail { class LambertAzimuthalEqualAreaProjection final : public ProjectionImpl { public: // constructor LambertAzimuthalEqualAreaProjection(const eckit::Parametrisation&); // projection name static std::string static_type() { return "lambert_azimuthal_equal_area"; } std::string type() const override { return static_type(); } // projection and inverse projection void xy2lonlat(double crd[]) const override; void lonlat2xy(double crd[]) const override; Jacobian jacobian(const PointLonLat&) const override; bool strictlyRegional() const override { return true; } RectangularLonLatDomain lonlatBoundingBox(const Domain& domain) const override { return ProjectionImpl::lonlatBoundingBox(domain); } // specification Spec spec() const override; std::string units() const override { return "meters"; } void hash(eckit::Hash&) const override; private: PointLonLat reference_; ///< central longitude/standard parallel [degree]/[degree] double radius_; ///< sphere radius double lambda0_; ///< central longitude [rad] double phi1_; ///< standard parallel [rad] double sin_phi1_; double cos_phi1_; double false_northing_{0}; double false_easting_{0}; }; } // namespace detail } // namespace projection } // namespace atlas atlas-0.46.0/src/atlas/projection/detail/ProjectionImpl.cc0000664000175000017500000003401215160212070023641 0ustar alastairalastair/* * (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/detail/ProjectionImpl.h" #include #include #include #include #include #include "eckit/types/FloatCompare.h" #include "eckit/utils/Hash.h" #include "eckit/utils/MD5.h" #include "atlas/projection/detail/ProjectionFactory.h" #include "atlas/runtime/Exception.h" #include "atlas/util/Config.h" namespace atlas::projection::detail { // -------------------------------------------------------------------------------------------------------------------- namespace { void longitude_in_range(double reference, double& lon) { // keep longitude difference (to reference) range below +-180 degree while (lon > reference + 180.) { lon -= 360.; } while (lon <= reference - 180.) { lon += 360.; } } template struct DerivateBuilder : public ProjectionImpl::DerivateFactory { using DerivateFactory::DerivateFactory; ProjectionImpl::Derivate* make(const ProjectionImpl& p, PointXY A, PointXY B, double h, double refLongitude) override { return new T(p, A, B, h, refLongitude); } }; struct DerivateForwards final : ProjectionImpl::Derivate { using Derivate::Derivate; PointLonLat d(PointXY P) const override { return (xy2lonlat(P + H_) - xy2lonlat(P)) * invnH_; } }; struct DerivateBackwards final : ProjectionImpl::Derivate { using Derivate::Derivate; PointLonLat d(PointXY P) const override { return (xy2lonlat(P) - xy2lonlat(P - H_)) * invnH_; } }; struct DerivateCentral final : ProjectionImpl::Derivate { DerivateCentral(const ProjectionImpl& p, PointXY A, PointXY B, double h, double refLongitude): Derivate(p, A, B, h, refLongitude), H2_{H_ * 0.5} {} const PointXY H2_; PointLonLat d(PointXY P) const override { return (xy2lonlat(P + H2_) - xy2lonlat(P - H2_)) * invnH_; } }; } // namespace ProjectionImpl::Derivate::Derivate(const ProjectionImpl& p, PointXY A, PointXY B, double h, double refLongitude): projection_(p), H_{PointXY::mul(PointXY::normalize(PointXY::sub(B, A)), h)}, invnH_(1. / PointXY::norm(H_)), refLongitude_(refLongitude) {} ProjectionImpl::Derivate::~Derivate() = default; ProjectionImpl::Derivate* ProjectionImpl::DerivateFactory::build(const std::string& type, const ProjectionImpl& p, PointXY A, PointXY B, double h, double refLongitude) { ATLAS_ASSERT(0. < h); // force_link static DerivateBuilder __derivate1("forwards"); static DerivateBuilder __derivate2("backwards"); static DerivateBuilder __derivate3("central"); if (A.distance2(B) < h * h) { struct DerivateDegenerate final : Derivate { using Derivate::Derivate; PointLonLat d(PointXY) const override { return {}; } }; return new DerivateDegenerate(p, A, B, h, refLongitude); } auto* factory = get(type); return factory->make(p, A, B, h); } ProjectionImpl::DerivateFactory::~DerivateFactory() = default; PointLonLat ProjectionImpl::Derivate::xy2lonlat(const PointXY& p) const { PointLonLat q(p); projection_.xy2lonlat(q.data()); longitude_in_range(refLongitude_, q.lon()); return q; } // -------------------------------------------------------------------------------------------------------------------- ProjectionImpl::BoundLonLat::operator RectangularLonLatDomain() const { return RectangularLonLatDomain({min_[0], max_[0]}, {min_[1], max_[1]}); } bool ProjectionImpl::BoundLonLat::crossesDateLine(bool yes) { if ((crossesDateLine_ = crossesDateLine_ || yes)) { max_.lon() = min_.lon() + 360.; } return crossesDateLine_; } bool ProjectionImpl::BoundLonLat::includesNorthPole(bool yes) { if ((includesNorthPole_ = includesNorthPole_ || yes)) { max_.lat() = 90.; } crossesDateLine(includesNorthPole_); return includesNorthPole_; } bool ProjectionImpl::BoundLonLat::includesSouthPole(bool yes) { if ((includesSouthPole_ = includesSouthPole_ || yes)) { min_.lat() = -90.; } crossesDateLine(includesSouthPole_); return includesSouthPole_; } void ProjectionImpl::BoundLonLat::extend(PointLonLat p, PointLonLat eps) { ATLAS_ASSERT(0. <= eps.lon() && 0. <= eps.lat()); auto sub = PointLonLat::sub(p, eps); auto add = PointLonLat::add(p, eps); min_ = first_ ? sub : PointLonLat::componentsMin(min_, sub); max_ = first_ ? add : PointLonLat::componentsMax(max_, add); first_ = false; min_.lat() = std::max(min_.lat(), -90.); max_.lat() = std::min(max_.lat(), 90.); max_.lon() = std::min(max_.lon(), min_.lon() + 360.); ATLAS_ASSERT(min_.lon() <= max_.lon() && min_.lat() <= max_.lat()); includesSouthPole(eckit::types::is_approximately_equal(min_.lat(), -90.)); includesNorthPole(eckit::types::is_approximately_equal(max_.lat(), 90.)); crossesDateLine(eckit::types::is_approximately_equal(max_.lon() - min_.lon(), 360.)); } // -------------------------------------------------------------------------------------------------------------------- ProjectionImpl::Normalise::Normalise(const eckit::Parametrisation& p) { values_.resize(2); bool provided = false; if (p.get("normalise", values_)) { provided = true; } else if (p.get("normalize", values_)) { provided = true; } else if (p.get("west", values_[0])) { values_[1] = values_[0] + 360.; provided = true; } if (provided) { normalise_ = std::make_unique(values_[0], values_[1]); } } ProjectionImpl::Normalise::Normalise(double west) { values_.resize(2); values_[0] = west; values_[1] = values_[0] + 360.; normalise_ = std::make_unique(values_[0], values_[1]); } void ProjectionImpl::Normalise::hash(eckit::Hash& hash) const { if (normalise_) { hash.add(values_[0]); hash.add(values_[1]); } } void ProjectionImpl::Normalise::spec(Spec& s) const { if (normalise_) { s.set("normalise", values_); } } // -------------------------------------------------------------------------------------------------------------------- const ProjectionImpl* ProjectionImpl::create(const eckit::Parametrisation& p) { std::string projectionType; if (p.get("type", projectionType)) { return ProjectionFactory::build(projectionType, p); } // should return error here throw_Exception("type missing in Params", Here()); } const ProjectionImpl* ProjectionImpl::create(const std::string& type, const eckit::Parametrisation& p) { return ProjectionFactory::build(type, p); } 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; // 0. setup if (domain.global()) { return domain; } RectangularDomain rect(domain); ATLAS_ASSERT(rect); // use central longitude as absolute reference (keep points within +-180 longitude range) const PointXY centre_xy{(rect.xmin() + rect.xmax()) / 2., (rect.ymin() + rect.ymax()) / 2.}; const auto centre_lon = lonlat(centre_xy).lon(); const std::string derivative = "central"; constexpr double h_deg = 0.5e-6; // precision to microdegrees constexpr double h_meters = 0.5e-1; // precision to decimeters constexpr size_t Niter = 100; const double h = units() == "degrees" ? h_deg : h_meters; // 1. determine box from projected corners const std::pair segments[] = {{{rect.xmin(), rect.ymax()}, {rect.xmax(), rect.ymax()}}, {{rect.xmax(), rect.ymax()}, {rect.xmax(), rect.ymin()}}, {{rect.xmax(), rect.ymin()}, {rect.xmin(), rect.ymin()}}, {{rect.xmin(), rect.ymin()}, {rect.xmin(), rect.ymax()}}}; BoundLonLat bounds; for (const auto& [p, dummy] : segments) { auto q = lonlat(p); longitude_in_range(centre_lon, q.lon()); bounds.extend(q, PointLonLat{h_deg, h_deg}); } // 2. locate latitude extrema by checking if poles are included (in the un-projected frame) and if not, find extrema // not at the corners by refining iteratively bounds.includesNorthPole(bounds.includesNorthPole() || rect.contains(xy(PointLonLat{0, 90 - h_deg}))); bounds.includesSouthPole(bounds.includesSouthPole() || rect.contains(xy(PointLonLat{0, -90 + h_deg}))); for (auto [A, B] : segments) { if (!bounds.includesNorthPole() || !bounds.includesSouthPole()) { std::unique_ptr derivate(DerivateFactory::build(derivative, *this, A, B, h, centre_lon)); double dAdy = derivate->d(A).lat(); double dBdy = derivate->d(B).lat(); if (!is_strictly_greater(dAdy * dBdy, 0.)) { for (size_t cnt = 0; cnt < Niter; ++cnt) { PointXY M = PointXY::middle(A, B); double dMdy = derivate->d(M).lat(); if (is_strictly_greater(dAdy * dMdy, 0.)) { A = M; dAdy = dMdy; } else if (is_strictly_greater(dBdy * dMdy, 0.)) { B = M; dBdy = dMdy; } else { break; } } // update extrema, extended by 'a small amount' (arbitrary) bounds.extend(lonlat(PointXY::middle(A, B)), PointLonLat{0, h_deg}); } } } // 3. locate longitude extrema not at the corners by refining iteratively for (auto [A, B] : segments) { if (!bounds.crossesDateLine()) { std::unique_ptr derivate(DerivateFactory::build(derivative, *this, A, B, h, centre_lon)); double dAdx = derivate->d(A).lon(); double dBdx = derivate->d(B).lon(); if (!is_strictly_greater(dAdx * dBdx, 0.)) { for (size_t cnt = 0; cnt < Niter; ++cnt) { PointXY M = PointXY::middle(A, B); double dMdx = derivate->d(M).lon(); if (is_strictly_greater(dAdx * dMdx, 0.)) { A = M; dAdx = dMdx; } else if (is_strictly_greater(dBdx * dMdx, 0.)) { B = M; dBdx = dMdx; } else { break; } } // update extrema, extended by 'a small amount' (arbitrary) bounds.extend(lonlat(PointXY::middle(A, B)), PointLonLat{h_deg, 0}); } } } // 4. return bounding box return bounds; } //--------------------------------------------------------------------------------------------------------------------- Rotated::Rotated(const PointLonLat& south_pole, double rotation_angle): util::Rotation(south_pole, rotation_angle) {} Rotated::Rotated(const eckit::Parametrisation& p): util::Rotation(p) {} void Rotated::spec(Spec& s) const { std::vector npole{northPole().lon(), northPole().lat()}; std::vector spole{southPole().lon(), southPole().lat()}; s.set("north_pole", npole); s.set("south_pole", spole); s.set("rotation_angle", rotationAngle()); } void Rotated::hash(eckit::Hash& hsh) const { hsh.add("rotated"); hsh.add(southPole().lon()); hsh.add(southPole().lat()); hsh.add(rotationAngle()); } //--------------------------------------------------------------------------------------------------------------------- extern "C" { const ProjectionImpl* atlas__Projection__ctor_config(const eckit::Parametrisation* config) { return ProjectionImpl::create(*config); } void atlas__Projection__type(const ProjectionImpl* This, char*& type, int& size) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Projection"); std::string s = This->type(); size = static_cast(s.size()); type = new char[size + 1]; std::strncpy(type, s.c_str(), size + 1); } void atlas__Projection__hash(const ProjectionImpl* This, char*& hash, int& size) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Projection"); eckit::MD5 md5; This->hash(md5); std::string s = md5.digest(); size = static_cast(s.size()); hash = new char[size + 1]; std::strncpy(hash, s.c_str(), size + 1); } ProjectionImpl::Spec* atlas__Projection__spec(const ProjectionImpl* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Projection"); return new ProjectionImpl::Spec(This->spec()); } void atlas__Projection__xy2lonlat(const ProjectionImpl* This, const double x, const double y, double& lon, double& lat) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Projection"); Point2 p(x, y); This->xy2lonlat(p); lon = p.x(); lat = p.y(); } void atlas__Projection__lonlat2xy(const ProjectionImpl* This, const double lon, const double lat, double& x, double& y) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Projection"); Point2 p(lon, lat); This->lonlat2xy(p); x = p.x(); y = p.y(); } } // extern "C" } // namespace atlas::projection::detail atlas-0.46.0/src/atlas/projection/detail/SchmidtProjection.h0000664000175000017500000000344715160212070024205 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include "atlas/domain.h" #include "atlas/projection/detail/ProjectionImpl.h" namespace atlas { namespace projection { namespace detail { template class SchmidtProjectionT final : public ProjectionImpl { public: // constructor SchmidtProjectionT(const eckit::Parametrisation& p); SchmidtProjectionT(); // projection name static std::string static_type() { return Rotation::typePrefix() + "schmidt"; } std::string type() const override { return static_type(); } // projection and inverse projection void xy2lonlat(double crd[]) const override; void lonlat2xy(double crd[]) const override; Jacobian jacobian(const PointLonLat&) const override; bool strictlyRegional() const override { return false; } // schmidt is global grid RectangularLonLatDomain lonlatBoundingBox(const Domain& domain) const override { return ProjectionImpl::lonlatBoundingBox(domain); } // specification Spec spec() const override; std::string units() const override { return "degrees"; } void hash(eckit::Hash&) const override; private: double c_; // stretching factor Rotation rotation_; PointXYZ north0_; PointXYZ north1_; }; using SchmidtProjection = SchmidtProjectionT; using RotatedSchmidtProjection = SchmidtProjectionT; } // namespace detail } // namespace projection } // namespace atlas atlas-0.46.0/src/atlas/projection/detail/LambertAzimuthalEqualAreaProjection.cc0000664000175000017500000000767415160212070030004 0ustar alastairalastair/* * (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 "LambertAzimuthalEqualAreaProjection.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 { LambertAzimuthalEqualAreaProjection::LambertAzimuthalEqualAreaProjection(const eckit::Parametrisation& params): radius_(util::Earth::radius()) { 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]; sin_phi1_ = std::sin(phi1_); cos_phi1_ = std::cos(phi1_); } void LambertAzimuthalEqualAreaProjection::lonlat2xy(double crd[]) const { double dlambda = util::Constants::degreesToRadians() * (crd[LON] - reference_.lon()); double cos_dlambda = std::cos(dlambda); double sin_dlambda = std::sin(dlambda); double phi = util::Constants::degreesToRadians() * crd[LAT]; double sin_phi = std::sin(phi); double cos_phi = std::cos(phi); double kp = radius_ * std::sqrt(2. / (1. + sin_phi1_ * sin_phi + cos_phi1_ * cos_phi * cos_dlambda)); 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] - 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) { crd[LON] = reference_[LON]; crd[LAT] = reference_[LAT]; return; } double c = 2. * std::asin(rho / (2. * radius_)); double cos_c = std::cos(c); double sin_c = std::sin(c); double lon_r = lambda0_ + std::atan2(x * sin_c, rho * cos_phi1_ * cos_c - y * sin_phi1_ * sin_c); double sin_lat_r = cos_c * sin_phi1_ + y * sin_c * cos_phi1_ / rho; crd[LON] = lon_r * util::Constants::radiansToDegrees(); crd[LAT] = sin_lat_r > 1 ? 90 : sin_lat_r < -1 ? -90 : util::Constants::radiansToDegrees() * std::asin(sin_lat_r); } ProjectionImpl::Jacobian LambertAzimuthalEqualAreaProjection::jacobian(const PointLonLat&) const { throw_NotImplemented("LambertAzimuthalEqualAreaProjection::jacobian", Here()); } LambertAzimuthalEqualAreaProjection::Spec LambertAzimuthalEqualAreaProjection::spec() const { Spec proj; proj.set("type", static_type()); 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; } void LambertAzimuthalEqualAreaProjection::hash(eckit::Hash& h) const { h.add(static_type()); h.add(radius_); h.add(reference_[LON]); h.add(reference_[LAT]); } namespace { static ProjectionBuilder register_1( LambertAzimuthalEqualAreaProjection::static_type()); } } // namespace detail } // namespace projection } // namespace atlas atlas-0.46.0/src/atlas/projection/detail/VariableResolutionProjection.cc0000664000175000017500000004734215160212070026563 0ustar alastairalastair/** * (C) Crown copyright 2021, Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ #include #include #include #include "eckit/config/Parametrisation.h" #include "eckit/utils/Hash.h" #include "atlas/functionspace/StructuredColumns.h" #include "atlas/grid.h" #include "atlas/grid/Grid.h" #include "atlas/projection/detail/ProjectionFactory.h" #include "atlas/projection/detail/VariableResolutionProjection.h" #include "atlas/runtime/Exception.h" #include "atlas/util/Config.h" #include "atlas/util/Constants.h" /** * Projection for LAM stretching * * The theory used in this code can be found under: * Lateral boundary conditions for limited area models * Terry Davies 2014 * https://doi.org/10.1002/qj.2127 * * Some equations are under: * The benefits of the Met Office variable resolution NWP model for forecasting convection * Tang et al. 2013 * https://doi.org/10.1002/met.1300 * * Generally: From a original regular grid the point in the middle of the grid are unchanged, * based on the boundary of the new internal regular grid. * The grid length is then stretched at a constant stretching (or inflation) factor r, * that is Δsi+1 = rΔsi until all the points are mapped in the new grid. * The domain is then extended to a further uniform coarse resolution region (rim) in the outer domain, * * The origin of the xy-system is at (lon,lat) = (0,0) * */ #ifdef __NVCOMPILER #define PREVENT_OPT volatile #else #define PREVENT_OPT #endif namespace atlas { namespace projection { namespace detail { static double new_ratio(int n_stretched, double var_ratio) { /** * compute ratio, * change stretching factor so that high and low grids * retain original sizes */ ///< correction used to change from double to integer constexpr float epstest = std::numeric_limits::epsilon(); ///< number of variable (stretched) grid points in one side PREVENT_OPT int var_ints = (n_stretched + epstest) / 2.; double var_ints_f = n_stretched / 2.; double logr = std::log(var_ratio); double log_ratio = (var_ints_f - 0.5) * logr; return std::exp(log_ratio / var_ints); }; ///< specification parameters template typename VariableResolutionProjectionT::Spec VariableResolutionProjectionT::spec() const { Spec proj_st; proj_st.set("type", static_type()); proj_st.set("outer.dx", delta_outer); ///< resolution of the external regular grid (rim) proj_st.set("inner.dx", delta_inner); ///< resolution of the regional model (regular grid) proj_st.set("progression", var_ratio_); ///< power used for the stretching proj_st.set("inner.xmin", x_reg_start_); ///< xstart of the internal regional grid proj_st.set("inner.ymin", y_reg_start_); ///< ystart of the internal regional grid proj_st.set("inner.xend", x_reg_end_); ///< xend of the regular part of stretched internal grid proj_st.set("inner.yend", y_reg_end_); ///< yend of the regular part of stretched internal grid proj_st.set("outer.xmin", startx_); ///< original domain startx proj_st.set("outer.xend", endx_); ///< original domain endx proj_st.set("outer.ymin", starty_); ///< original domain starty proj_st.set("outer.yend", endy_); ///< original domain endy proj_st.set("rim_widthx", rim_widthx_); ///< xsize of the rim proj_st.set("rim_widthy", rim_widthy_); ///< ysize of the rim rotation_.spec(proj_st); return proj_st; } ///< constructors template VariableResolutionProjectionT::VariableResolutionProjectionT(const eckit::Parametrisation& proj_st): ProjectionImpl(), rotation_(proj_st) { proj_st.get("outer.dx", delta_outer = 0.); ///< resolution of the external regular grid (rim) proj_st.get("inner.dx", delta_inner = 0.); ///< resolution of the regional model (regular grid) proj_st.get("progression", var_ratio_ = 0.); ///< power used for the stretching proj_st.get("inner.xmin", x_reg_start_ = 0.); ///< xstart of the internal regional grid proj_st.get("inner.ymin", y_reg_start_ = 0.); ///< ystart of the internal regional grid proj_st.get("inner.xend", x_reg_end_ = 0.); ///< xend of the regular part of stretched internal grid proj_st.get("inner.yend", y_reg_end_ = 0.); ///< yend of the regular part of stretched internal grid proj_st.get("outer.xmin", startx_ = 0.); ///< original domain startx proj_st.get("outer.xend", endx_ = 0.); ///< original domain endx proj_st.get("outer.ymin", starty_ = 0.); ///< original domain starty proj_st.get("outer.yend", endy_ = 0.); ///< original domain endy // To get the rim_widthx_ we can specify either: // - outer.nx // - outer.xwidth // - outer.width // The value for rim_widthy_ is copied from rim_widthx_ and can be overwritten with // - outer.ny // - outer.ywidth if (proj_st.has("outer.nx")) { long outer_nx; proj_st.get("outer.nx", outer_nx); rim_widthx_ = delta_outer * outer_nx; rim_widthy_ = rim_widthx_; } else if (proj_st.has("outer.width")) { proj_st.get("outer.width", rim_widthx_); rim_widthy_ = rim_widthx_; } else if (proj_st.has("outer.xwidth")) { proj_st.get("outer.xwidth", rim_widthx_); rim_widthy_ = rim_widthx_; } if (proj_st.has("outer.ny")) { long outer_ny; proj_st.get("outer.ny", outer_ny); rim_widthy_ = delta_outer * outer_ny; } else if (proj_st.has("outer.ywidth")) { proj_st.get("outer.ywidth", rim_widthy_); } constexpr float epsilon = std::numeric_limits::epsilon(); ///< value used to check if the values are equal constexpr float epstest = std::numeric_limits::epsilon(); ///< correction used to change from double to intege ///< original domain size includes the points for the rim deltax_all = (endx_ - startx_); deltay_all = (endy_ - starty_); nx_stretched = 0; ny_stretched = 0; nx_rim = 0; ny_rim = 0; if (var_ratio_ == 1) { lam_hires_size = deltax_all; phi_hires_size = deltay_all; lambda_start = x_reg_start_; phi_start = y_reg_start_; } else { lam_hires_size = x_reg_end_ - x_reg_start_; phi_hires_size = y_reg_end_ - y_reg_start_; /** * check for start of the new regular LAM grid x and y * in the middle of the previous regular grid */ ///< distance end of the grid and internal regular grid add_xf_ = (deltax_all + epstest - lam_hires_size) / 2.; add_yf_ = (deltay_all + epstest - phi_hires_size) / 2.; /** * Compute the number of points for different part of the grid * internal regular grid high resolution * stretched grid * external regular grid low resolution * +1 otherwise I have the intervals not the points */ nx_rim = rim_widthx_ / delta_outer; ny_rim = rim_widthy_ / delta_outer; nx_stretched = ((deltax_all + epstest - lam_hires_size) / delta_inner) - nx_rim; ny_stretched = ((deltay_all + epstest - phi_hires_size) / delta_inner) - ny_rim; lambda_start = x_reg_start_; phi_start = y_reg_start_; /** * check if stretched grid is in the middle * of the previous regular grid */ check_x = startx_ + add_xf_ - lambda_start; check_y = starty_ + add_yf_ - phi_start; check_st = nx_stretched - ny_stretched; checkvalue(epsilon, check_x); checkvalue(epsilon, check_y); checkvalue(epsilon, check_st); } int nx_ = (deltax_all + epstest) / delta_inner + 1; int ny_ = (deltay_all + epstest) / delta_inner + 1; int nx_inner = (lam_hires_size + epstest) / delta_inner + 1; int ny_inner = (phi_hires_size + epstest) / delta_inner + 1; ATLAS_ASSERT((nx_ - 1) - nx_rim - (nx_inner - 1) == nx_stretched); ATLAS_ASSERT((ny_ - 1) - ny_rim - (ny_inner - 1) == ny_stretched); new_ratio_[0] = var_ratio_; new_ratio_[1] = var_ratio_; if (var_ratio_ != 1) { new_ratio_[0] = new_ratio(nx_stretched, var_ratio_); new_ratio_[1] = new_ratio(ny_stretched, var_ratio_); } } template void VariableResolutionProjectionT::checkvalue(const double& epsilon, const double& value_check) const { //err_message = "USER defined limits not in the middle of the area " + str; if (value_check > epsilon || value_check < (-1. * epsilon)) { std::string err_message; std::string str = std::to_string(value_check); throw_Exception("USER defined limits not in the middle of the area " + str, Here()); } } /** * General stretch from a point in regular grid to the * a correspective point in a variable grid */ template double VariableResolutionProjectionT::general_stretch(const double crd, const bool L_long, const int n_stretched) const { constexpr float epstest = std::numeric_limits::epsilon(); ///< correction used to change from double to integer constexpr double epsrem = 0.1 * std::numeric_limits::epsilon() / std::numeric_limits::epsilon(); ///< correction used to part the find a part of an integer double inner_size; ///< number of new internal regular grid in double double inner_start; ///< start of the regular grid double inner_end; ///< end of the regular grid double point = crd; ///< starting point auto normalised = [L_long](double p) { if (L_long) { p = (p < 180) ? p + 360.0 : p; } return p; }; /* * regular grids */ if (var_ratio_ == 1) { return normalised(point); } /* * SECTION 1 * INTERNAL REGULAR GRID the point is mapped to the same point */ if (L_long) { inner_start = x_reg_start_; inner_size = lam_hires_size; } else { inner_start = y_reg_start_; inner_size = phi_hires_size; } inner_end = inner_start + inner_size; if ((point >= inner_start) && (point <= inner_end)) { return normalised(point); } /* SECTION 2 * Start variable res 'STRETCHED' * The point is mapped in a stretched grid related to * distance from the regular grid and stretch internal grid: delta_dist */ double distance_to_inner = 0.; ///< distance from point to reg. grid double delta_add = 0.; ///< additional part in stretch different from internal high resolution int n_high = 0; ///< number of points, from point to reg. grid int n_high_st = 0; ///< number of stretched points, from point to reg grid int n_high_rim = 0; ///< number of rim points, from point to reg grid double p_rem = 0.; ///< remaining part in stretch if distance_to_inner not multiple of delta_high_ double p_rem_low = 0.; ///< remaining part in rim if distance_to_inner not multiple of delta_high_ double new_ratio = new_ratio_[L_long ? 1 : 0]; if (point < inner_start) { distance_to_inner = inner_start - point; } else if (point > inner_end) { distance_to_inner = point - inner_end; } /* * number of high resolution points intervals, that are not * in the internal regular grid on one side, * this work just for the part of the stretch not the rim */ ///< always the lowest integer n_high = (distance_to_inner + epstest) / delta_inner; ///< only for the stretched part take out the rim part if (n_high > n_stretched / 2.) { n_high_st = (n_stretched / 2.); n_high_rim = n_high - n_high_st; p_rem = 0; p_rem_low = std::fmod((distance_to_inner + epsrem), delta_inner); } else { n_high_st = n_high; n_high_rim = 0; ///< part remaining, use modulo p_rem = std::fmod((distance_to_inner + epsrem), delta_inner); p_rem_low = 0.; } ///< computation of the new stretched delta integer part double delta = delta_inner; ///< initialization if is not using the cycle (first interval) double delta_last = delta; double deltacheck = 0; /* * using difference in delta for stretch * The point stretched is not in the regular grid and took out the points for the rim */ for (int i = 0; i < n_high_st; i += 1) { delta_last = delta * new_ratio; delta_add = delta_last - delta_inner; delta = delta_last; deltacheck += delta_add; } ///< recomputation of point for every interval if (point > inner_start) { /* * after the end of the internal high resolution grid * no delta_add in the last points as they are rim */ point += deltacheck; } else { /* * before the begin of the internal high resolution grid * no delta_add in the first points as they are rim */ point -= deltacheck; } ///< SECTION 3 last part of stretch adding the remaing non integer part with the same ratio as in the stretching double delta_r = p_rem * std::pow(new_ratio, (n_high_st + 1)); double delta_addr = delta_r - p_rem; if (point > inner_start) { point += delta_addr; } else { point -= delta_addr; } ///< SECTION 4 rim area if (n_high > n_stretched / 2.) { double delta_l_h_ = 0; for (int i = 0; i < n_high_rim; i += 1) { delta_l_h_ += (delta_outer - delta_inner); } if (point > inner_start) { point += (delta_l_h_ + p_rem_low * (delta_outer - delta_inner)); } else { point -= (delta_l_h_ + p_rem_low * (delta_outer - delta_inner)); } } return normalised(point); } /** * Inverse of general stretch from a point in variable grid to the * a correspective point in a regular grid */ template double VariableResolutionProjectionT::general_stretch_inv(const double crd, const bool L_long, const int n_stretched) const { constexpr float epstest = std::numeric_limits::epsilon(); ///< correction used to change from double to integer double inner_size; ///< number of new internal regular grid in double double inner_start; ///< start of the regular grid double inner_end; ///< end of the regular grid double point_st = crd; ///< input point in the variational grid double point_reg = 0.; ///< point in the regular grid auto normalised = [L_long](double p) { if (L_long) { p = (p < 180) ? p + 360.0 : p; } return p; }; point_st = normalised(point_st); /* * regular grids */ if (var_ratio_ == 1) { point_reg = point_st; return normalised(point_reg); } /* * SECTION 1 * INTERNAL REGULAR GRID the point is mapped to the same point */ if (L_long) { inner_start = x_reg_start_; inner_size = lam_hires_size; } else { inner_start = y_reg_start_; inner_size = phi_hires_size; } inner_end = inner_start + inner_size; //< points in the internal regular grid if ((point_st >= inner_start - epstest) && (point_st <= inner_end + epstest)) { point_reg = point_st; return normalised(point_reg); } /* SECTION 2 * Start variable res 'STRETCHED' * The point is mapped in a regular grid * from stretch internal grid: delta_dist * simply using delta_high */ double delta_add; ///< additional part in stretch different from internal high resolution double new_ratio = new_ratio_[L_long ? 1 : 0]; /* * number of high resolution points intervals, that are not * in the internal regular grid on one side, * this work just for the part of the stretch not the rim */ ///< computation of the new stretched delta integer part double delta = delta_inner; ///< initialization if is not using the cycle (first interval) double delta_last = delta; double deltacheck = 0; /* * using difference in delta for stretch * The point stretched is not in the regular grid and took out the points for the rim */ double point_var = 0.; for (int i = 1; i < n_stretched / 2.; i += 1) { delta_last = delta * new_ratio; delta_add = delta_last - delta_inner; delta = delta_last; deltacheck += delta_add; if (point_st > inner_start) { point_reg = inner_end + (delta_inner * i); point_var = point_reg + deltacheck; if ((point_st <= point_var + epstest) && (point_st >= point_var - epstest)) { return normalised(point_reg); } } else { point_reg = inner_start - (delta_inner * i); point_var = point_reg - deltacheck; if ((point_st <= point_var + epstest) && (point_st >= point_var - epstest)) { return normalised(point_reg); } } } ///< SECTION 3_inv rim area int n_rim; ///< number of rim points if (point_st > point_var) { n_rim = (point_st - point_var) / delta_outer; point_reg = inner_end + (delta_inner * (n_stretched / 2 + n_rim)); return normalised(point_reg); } else { if (point_st < point_var) { n_rim = (point_var - point_st) / delta_outer; point_reg = inner_start - (delta_inner * (n_stretched / 2 + n_rim)); return normalised(point_reg); } } return normalised(point_reg); } ///< xy unstretched, only unrotation template void VariableResolutionProjectionT::lonlat2xy(double crd[]) const { ///< unrotate rotation_.unrotate(crd); //< normalization crd[0] = (crd[0] < 0) ? crd[0] + 360.0 : crd[0]; ///< PUT the unstretch crd[0] = general_stretch_inv(crd[0], true, nx_stretched); crd[1] = general_stretch_inv(crd[1], false, ny_stretched); } ///< From unstretched to stretched template void VariableResolutionProjectionT::xy2lonlat(double crd[]) const { /** PUT here the stretch for a point that come input * give number of power as input,work it out using as from the start of regular grid. * atlas::PointXY unstretchedXY = crd; * atlas::PointXY stretchedXY = stretch_LAM_gen(unstretchedXY); * stretch_LAM_gen(crd[]); */ crd[0] = general_stretch(crd[0], true, nx_stretched); crd[1] = general_stretch(crd[1], false, ny_stretched); rotation_.rotate(crd); } template ProjectionImpl::Jacobian VariableResolutionProjectionT::jacobian(const PointLonLat&) const { throw_NotImplemented("VariableResolution::jacobian", Here()); } template void VariableResolutionProjectionT::hash(eckit::Hash& hsh) const { hsh.add(static_type()); rotation_.hash(hsh); //hsh.add( radius_ ); } template class VariableResolutionProjectionT; template class VariableResolutionProjectionT; namespace { static ProjectionBuilder register_1(VariableResolutionProjection::static_type()); static ProjectionBuilder register_2( RotatedVariableResolutionProjection::static_type()); } // namespace } // namespace detail } // namespace projection } // namespace atlas atlas-0.46.0/src/atlas/projection/detail/ProjectionUtilities.h0000664000175000017500000001072315160212070024560 0ustar alastairalastair/* * (C) Copyright 2020 UCAR * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ #pragma once #include #include #include "eckit/geometry/Sphere.h" #include "atlas/domain.h" #include "atlas/runtime/Log.h" #include "atlas/util/Constants.h" #include "atlas/util/CoordinateEnums.h" #include "atlas/util/Earth.h" #include "atlas/util/Point.h" namespace atlas { namespace projection { namespace detail { // ------------------------------------------------------------------------------------------------- struct ProjectionUtilities { // ----------------------------------------------------------------------------------------------- enum class CoordinateSystem { RIGHT_HAND, LEFT_HAND, }; static constexpr bool debug = false; // constexpr so compiler can optimize `if ( debug ) { ... }` out // ----------------------------------------------------------------------------------------------- static void cartesianToSpherical(const double xyz[], double lonlat[], const CoordinateSystem coordinate_system, const double& radius = 0) { using eckit::geometry::Sphere; using util::Constants; // Make point objects. const auto pointXYZ = PointXYZ(xyz); auto pointLonLat = PointLonLat(); // Transform coordinates. auto r = radius != 0. ? radius : PointXYZ::norm(pointXYZ); Sphere::convertCartesianToSpherical(r, pointXYZ, pointLonLat); // Copy to array. lonlat[LON] = pointLonLat.lon(); lonlat[LAT] = -pointLonLat.lat(); // Left or right hand system. if (coordinate_system == CoordinateSystem::RIGHT_HAND) { lonlat[LAT] += 90.; } } //------------------------------------------------------------------------------------------------ static void sphericalToCartesian(const double lonlat[], double xyz[], const CoordinateSystem coordinate_system, const double& radius = 0) { using eckit::geometry::Sphere; using util::Constants; // Make point objects. const auto pointLonLat = PointLonLat(lonlat); auto pointXYZ = PointXYZ(); // Set Radius auto r = radius != 0 ? radius : util::Earth::radius(); // Transform coordinates. Sphere::convertSphericalToCartesian(r, pointLonLat, pointXYZ, 0.0); if (debug) { Log::info() << "sphericalToCartesian:: pointLonLat pointXYZ = " << pointLonLat << " " << pointXYZ << std::endl; } // Copy to array. xyz[XX] = pointXYZ.x(); xyz[YY] = pointXYZ.y(); xyz[ZZ] = pointXYZ.z(); // Left or right hand system if (coordinate_system != CoordinateSystem::RIGHT_HAND) { xyz[ZZ] *= -1; } } //------------------------------------------------------------------------------------------------ static void rotate3dX(const double angle, double xyz[]) { const double c = std::cos(angle); const double s = std::sin(angle); double xyz_in[3]; std::copy(xyz, xyz + 3, xyz_in); xyz[YY] = c * xyz_in[YY] + s * xyz_in[ZZ]; xyz[ZZ] = -s * xyz_in[YY] + c * xyz_in[ZZ]; }; //------------------------------------------------------------------------------------------------ static void rotate3dY(const double angle, double xyz[]) { const double c = std::cos(angle); const double s = std::sin(angle); double xyz_in[3]; std::copy(xyz, xyz + 3, xyz_in); xyz[XX] = c * xyz_in[XX] - s * xyz_in[ZZ]; xyz[ZZ] = s * xyz_in[XX] + c * xyz_in[ZZ]; }; //------------------------------------------------------------------------------------------------ static void rotate3dZ(const double angle, double xyz[]) { const double c = std::cos(angle); const double s = std::sin(angle); double xyz_in[3]; std::copy(xyz, xyz + 3, xyz_in); xyz[XX] = c * xyz_in[XX] + s * xyz_in[YY]; xyz[YY] = -s * xyz_in[XX] + c * xyz_in[YY]; }; //------------------------------------------------------------------------------------------------ }; //-------------------------------------------------------------------------------------------------- } // namespace detail } // namespace projection } // namespace atlas atlas-0.46.0/src/atlas/projection/detail/SchmidtProjection.cc0000664000175000017500000001122215160212070024331 0ustar alastairalastair/* * (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 "eckit/config/Parametrisation.h" #include "eckit/utils/Hash.h" #include "atlas/projection/detail/ProjectionFactory.h" #include "atlas/projection/detail/SchmidtProjection.h" #include "atlas/runtime/Exception.h" #include "atlas/util/Config.h" #include "atlas/util/Constants.h" #include "atlas/util/UnitSphere.h" 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 { PointLonLat rotation_north_pole(const Rotated& rotation) { return rotation.northPole(); } PointLonLat rotation_north_pole(const NotRotated& rotation) { return PointLonLat{0., 90.}; } // constructor template SchmidtProjectionT::SchmidtProjectionT(const eckit::Parametrisation& params): ProjectionImpl(), rotation_(params) { if (!params.get("stretching_factor", c_)) { throw_Exception("stretching_factor missing in Params", Here()); } ATLAS_ASSERT(c_ != 0.); north0_ = {0.0, 0.0, 1.0}; atlas::util::UnitSphere::convertSphericalToCartesian(rotation_north_pole(rotation_), north1_); north1_ = PointXYZ::normalize(north1_); } // constructor template SchmidtProjectionT::SchmidtProjectionT(): ProjectionImpl(), rotation_(util::NoConfig()) {} template void SchmidtProjectionT::xy2lonlat(double crd[]) const { // stretch crd[1] = R2D(std::asin(std::cos(2. * std::atan(1 / c_ * std::tan(std::acos(std::sin(D2R(crd[1]))) * 0.5))))); // perform rotation rotation_.rotate(crd); } template void SchmidtProjectionT::lonlat2xy(double crd[]) const { // inverse rotation rotation_.unrotate(crd); // unstretch crd[1] = R2D(std::asin(std::cos(2. * std::atan(c_ * std::tan(std::acos(std::sin(D2R(crd[1]))) * 0.5))))); } template <> ProjectionImpl::Jacobian SchmidtProjectionT::jacobian(const PointLonLat&) const { throw_NotImplemented("SchmidtProjectionT::jacobian", Here()); } template ProjectionImpl::Jacobian SchmidtProjectionT::jacobian(const PointLonLat& lonlat) const { double xy[2] = {lonlat.lon(), lonlat.lat()}; lonlat2xy(xy); PointXYZ xyz; atlas::util::UnitSphere::convertSphericalToCartesian(lonlat, xyz); double zomc2 = 1.0 - 1.0 / (c_ * c_); double zopc2 = 1.0 + 1.0 / (c_ * c_); double zcosy = std::cos(D2R(xy[1])); double zsiny = std::sin(D2R(xy[1])); double zcoslat = std::cos(D2R(lonlat.lat())); double zfactor = std::sqrt((zopc2 + zsiny * zomc2) * (zopc2 + zsiny * zomc2) / (zopc2 * zopc2 - zomc2 * zomc2)); // Base vectors in unrotated frame auto u0 = PointXYZ::normalize(PointXYZ::cross(north0_, xyz)); auto v0 = PointXYZ::normalize(PointXYZ::cross(xyz, u0)); // Base vectors in rotated frame auto u1 = PointXYZ::normalize(PointXYZ::cross(north1_, xyz)); auto v1 = PointXYZ::normalize(PointXYZ::cross(xyz, u1)); double u0u1 = PointXYZ::dot(u0, u1); double v0u1 = PointXYZ::dot(v0, u1); double u0v1 = PointXYZ::dot(u0, v1); double v0v1 = PointXYZ::dot(v0, v1); Jacobian jac; jac[0] = {zcoslat * u0u1 * zfactor / zcosy, v0u1 * zfactor / zcosy}; jac[1] = {zcoslat * u0v1 * zfactor, v0v1 * zfactor}; return jac; } // specification template typename SchmidtProjectionT::Spec SchmidtProjectionT::spec() const { Spec proj_spec; proj_spec.set("type", static_type()); proj_spec.set("stretching_factor", c_); rotation_.spec(proj_spec); return proj_spec; } template void SchmidtProjectionT::hash(eckit::Hash& hsh) const { hsh.add(static_type()); rotation_.hash(hsh); hsh.add(c_); } template class SchmidtProjectionT; template class SchmidtProjectionT; namespace { static ProjectionBuilder register_1(SchmidtProjection::static_type()); static ProjectionBuilder register_2(RotatedSchmidtProjection::static_type()); } // namespace } // namespace detail } // namespace projection } // namespace atlas atlas-0.46.0/src/atlas/projection/detail/CubedSphereEquiDistProjection.h0000664000175000017500000000435515160212070026452 0ustar alastairalastair/* * (C) Copyright 2020 UCAR * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ #pragma once #include "atlas/domain.h" #include "atlas/projection/detail/CubedSphereProjectionBase.h" #include "atlas/projection/detail/ProjectionImpl.h" namespace atlas { namespace projection { namespace detail { class CubedSphereEquiDistProjection final : public CubedSphereProjectionBase { public: // constructor CubedSphereEquiDistProjection(const eckit::Parametrisation&); virtual ~CubedSphereEquiDistProjection() {} // projection name static std::string static_type() { return "cubedsphere_equidistant"; } std::string type() const override { return static_type(); } /// @brief Convert (x, y) coordinate to (alpha, beta) on tile t. void xy2alphabeta(double crd[], idx_t t) const override; /// @brief Convert (alpha, beta) coordinate to (x, y) on tile t. void alphabeta2xy(double crd[], idx_t t) const override; /// @brief Convert (lon, lat) coordinate to (alpha, beta) on tile t. void lonlat2alphabeta(double crd[], idx_t t) const override; /// @brief Convert (alpha, beta) coordinate to (lon, lat) on tile t. void alphabeta2lonlat(double crd[], idx_t t) const override; /// @brief Jacobian of (alpha, beta) with respect to (lon, lat) on tile t Jacobian alphabetaJacobian(const PointLonLat& lonlat, idx_t t) const override; /// @brief Jacobian of (x, y) with respect to (lon, lat) on tile t Jacobian jacobian(const PointLonLat& lonlat, idx_t t) const override; // projection and inverse projection void xy2lonlat(double crd[]) const override; void lonlat2xy(double crd[]) const override; Jacobian jacobian(const PointLonLat&) const override; bool strictlyRegional() const override { return false; } RectangularLonLatDomain lonlatBoundingBox(const Domain& domain) const override { return ProjectionImpl::lonlatBoundingBox(domain); } // specification Spec spec() const override; std::string units() const override { return "degrees"; } void hash(eckit::Hash&) const override; }; } // namespace detail } // namespace projection } // namespace atlas atlas-0.46.0/src/atlas/projection/detail/VariableResolutionProjection.h0000664000175000017500000000656315160212070026425 0ustar alastairalastair/** * (C) Crown copyright 2021, Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ #pragma once #include #include "atlas/domain.h" #include "atlas/projection/detail/ProjectionImpl.h" #include "atlas/util/NormaliseLongitude.h" namespace atlas { namespace projection { namespace detail { template class VariableResolutionProjectionT final : public ProjectionImpl { public: /// new_ratio_; void setup(const eckit::Parametrisation& p); private: Rotation rotation_; }; using VariableResolutionProjection = VariableResolutionProjectionT; using RotatedVariableResolutionProjection = VariableResolutionProjectionT; } // namespace detail } // namespace projection } // namespace atlas atlas-0.46.0/src/atlas/projection/detail/LonLatProjection.cc0000664000175000017500000001572115160212070024137 0ustar alastairalastair/* * (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 "eckit/geometry/GreatCircle.h" #include "eckit/types/FloatCompare.h" #include "eckit/utils/Hash.h" #include "atlas/domain.h" #include "atlas/projection/detail/LonLatProjection.h" #include "atlas/projection/detail/ProjectionFactory.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Log.h" #include "atlas/util/Config.h" namespace atlas { namespace projection { namespace detail { template LonLatProjectionT::LonLatProjectionT(const eckit::Parametrisation& config): ProjectionImpl(), rotation_(config) {} template <> void LonLatProjectionT::xy2lonlat(double[]) const {} template <> void LonLatProjectionT::lonlat2xy(double[]) const {} template <> ProjectionImpl::Jacobian LonLatProjectionT::jacobian(const PointLonLat&) const { Jacobian jac; jac[0] = {1.0, 0.0}; jac[1] = {0.0, 1.0}; return jac; } template ProjectionImpl::Jacobian LonLatProjectionT::jacobian(const PointLonLat&) const { throw_NotImplemented("LonLatProjectionT::jacobian", Here()); } template <> RectangularLonLatDomain LonLatProjectionT::lonlatBoundingBox(const Domain& domain) const { return domain; } template RectangularLonLatDomain LonLatProjectionT::lonlatBoundingBox(const Domain& domain) const { using eckit::types::is_strictly_greater; // 0. setup if (domain.global()) { return domain; } RectangularDomain rect(domain); ATLAS_ASSERT(rect); const std::string derivative = "central"; constexpr double h = 0.001; // precision to millidegrees constexpr size_t Niter = 100; // 1. determine box from projected corners const std::vector corners{ {rect.xmin(), rect.ymax()}, {rect.xmax(), rect.ymax()}, {rect.xmax(), rect.ymin()}, {rect.xmin(), rect.ymin()}}; BoundLonLat bounds; for (auto& p : corners) { bounds.extend(lonlat(p), PointLonLat{h, h}); } // 2. locate latitude extrema by checking if poles are included (in the un-projected frame) and if not, find extrema // not at the corners by refining iteratively PointXY NP{xy({0., 90.})}; PointXY SP{xy({0., -90.})}; bounds.includesNorthPole(rect.contains(NP)); bounds.includesSouthPole(rect.contains(SP)); for (size_t i = 0; i < corners.size(); ++i) { if (!bounds.includesNorthPole() || !bounds.includesSouthPole()) { PointXY A = corners[i]; PointXY B = corners[(i + 1) % corners.size()]; std::unique_ptr derivate(DerivateFactory::build(derivative, *this, A, B, h)); double dAdy = derivate->d(A).lat(); double dBdy = derivate->d(B).lat(); if (!is_strictly_greater(dAdy * dBdy, 0.)) { for (size_t cnt = 0; cnt < Niter; ++cnt) { PointXY M = PointXY::middle(A, B); double dMdy = derivate->d(M).lat(); if (is_strictly_greater(dAdy * dMdy, 0.)) { A = M; dAdy = dMdy; } else if (is_strictly_greater(dBdy * dMdy, 0.)) { B = M; dBdy = dMdy; } else { break; } } // update extrema, extended by 'a small amount' (arbitrary) bounds.extend(lonlat(PointXY::middle(A, B)), PointLonLat{0, h}); } } } // 3. locate longitude extrema by checking if date line is crossed (in the un-projected frame), in which case we // assume periodicity and if not, find extrema not at the corners by refining iteratively if (!bounds.crossesDateLine()) { PointLonLat A{xy({180., -10.})}; PointLonLat B{xy({180., 10.})}; eckit::geometry::GreatCircle DL(A, B); for (auto lon : {rect.xmin(), rect.xmax()}) { if (!bounds.crossesDateLine()) { for (auto lat : DL.latitude(lon)) { if ((bounds.crossesDateLine(domain.contains(lon, lat)))) { break; } } } } for (auto lat : {rect.ymin(), rect.ymax()}) { if (!bounds.crossesDateLine()) { for (auto lon : DL.longitude(lat)) { if ((bounds.crossesDateLine(domain.contains(lon, lat)))) { break; } } } } } for (size_t i = 0; i < corners.size(); ++i) { if (!bounds.crossesDateLine()) { PointXY A = corners[i]; PointXY B = corners[(i + 1) % corners.size()]; std::unique_ptr derivate(DerivateFactory::build(derivative, *this, A, B, h)); double dAdx = derivate->d(A).lon(); double dBdx = derivate->d(B).lon(); if (!is_strictly_greater(dAdx * dBdx, 0.)) { for (size_t cnt = 0; cnt < Niter; ++cnt) { PointXY M = PointXY::middle(A, B); double dMdx = derivate->d(M).lon(); if (is_strictly_greater(dAdx * dMdx, 0.)) { A = M; dAdx = dMdx; } else if (is_strictly_greater(dBdx * dMdx, 0.)) { B = M; dBdx = dMdx; } else { break; } } // update extrema, extended by 'a small amount' (arbitrary) bounds.extend(lonlat(PointXY::middle(A, B)), PointLonLat{h, 0}); } } } // 4. return bounding box return bounds; } template typename LonLatProjectionT::Spec LonLatProjectionT::spec() const { Spec proj_spec; proj_spec.set("type", static_type()); rotation_.spec(proj_spec); return proj_spec; } template void LonLatProjectionT::hash(eckit::Hash& hsh) const { hsh.add(static_type()); rotation_.hash(hsh); } template class LonLatProjectionT; template class LonLatProjectionT; namespace { static ProjectionBuilder register_1(LonLatProjection::static_type()); static ProjectionBuilder register_2(RotatedLonLatProjection::static_type()); } // namespace // namespace } // namespace detail } // namespace projection } // namespace atlas atlas-0.46.0/src/atlas/projection/detail/LambertConformalConicProjection.cc0000664000175000017500000001161715160212070027151 0ustar alastairalastair/* * (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("latitude0", lat0_ = 0)); if (not params.get("latitude1", lat1_)) { lat1_ = lat0_; } if (not params.get("latitude2", lat2_)) { lat2_ = lat0_; } 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.; } ProjectionImpl::Jacobian LambertConformalConicProjection::jacobian(const PointLonLat& lonlat) const { ProjectionImpl::Jacobian jac; const double deg2rad = util::Constants::degreesToRadians(); double lat = lonlat.lat(), lon = lonlat.lon(); double tanlat = tan_d(lat), coslat = cos_d(45. + lat * 0.5); double rho = radius_ * F_ * std::pow(tanlat, -n_); double theta = n_ * normalise(lon - lon0_, -180, 360); double costheta = cos_d(theta), sintheta = sin_d(theta); // double x = rho * sintheta; // double y = rho0_ - rho * costheta; auto cpj = [&](const double dlon, const double dlat, double& dx, double& dy) { double drho = deg2rad * radius_ * F_ * (-n_) * std::pow(tanlat, -n_ - 1) / (coslat * coslat) * dlat * 0.5; double dtheta = +n_ * dlon; dx = +drho * sintheta + rho * costheta * deg2rad * dtheta; dy = -drho * costheta + rho * sintheta * deg2rad * dtheta; }; const double dlat = 1.0f; const double dlon = 1.0f; cpj(dlon, 0, jac[0][0], jac[1][0]); cpj(0, dlat, jac[0][1], jac[1][1]); return jac; } 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 atlas-0.46.0/src/atlas/projection/detail/CubedSphereProjectionBase.cc0000664000175000017500000002044715160212070025733 0ustar alastairalastair/* * (C) Copyright 2020 UCAR * * 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. */ #include "CubedSphereProjectionBase.h" #include #include #include #include #include #include #include "atlas/projection/detail/ProjectionUtilities.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Log.h" #include "atlas/runtime/Trace.h" #include "atlas/util/Config.h" #include "atlas/util/Constants.h" #include "atlas/util/CoordinateEnums.h" namespace atlas { namespace projection { namespace detail { // ------------------------------------------------------------------------------------------------- // Helper functions and variables local to this translation unit namespace { static constexpr double deg2rad = util::Constants::degreesToRadians(); static void schmidtTransform(double stretchFac, double targetLon, double targetLat, double lonlat[]) { double c2p1 = 1.0 + stretchFac * stretchFac; double c2m1 = 1.0 - stretchFac * stretchFac; double sin_p = std::sin(targetLat * deg2rad); double cos_p = std::cos(targetLat * deg2rad); double sin_lat; double cos_lat; double lat_t; if (std::abs(c2m1) > 1.0e-7) { sin_lat = std::sin(lonlat[LAT] * deg2rad); lat_t = std::asin((c2m1 + c2p1 * sin_lat) / (c2p1 + c2m1 * sin_lat)); } else { // no stretching lat_t = lonlat[LAT]; } sin_lat = std::sin(lat_t); cos_lat = std::cos(lat_t); double sin_o = -(sin_p * sin_lat + cos_p * cos_lat * cos(lonlat[LON] * deg2rad)); if ((1. - std::abs(sin_o)) < 1.0e-7) { // poles lonlat[LON] = 0.0; lonlat[LAT] = std::copysign(90.0, sin_o); } else { lonlat[LAT] = std::asin(sin_o); lonlat[LON] = targetLon + atan2(-cos_lat * std::sin(lonlat[LON] * deg2rad), -sin_lat * cos_p + cos_lat * sin_p * std::cos(lonlat[LON] * deg2rad)); if (lonlat[LON] < 0.0) { lonlat[LON] += 360.; } else if (lonlat[LON] >= 360.) { lonlat[LON] -= 360.; } } } void sphericalToCartesian(const double lonlat[], double xyz[]) { auto crd_sys = ProjectionUtilities::CoordinateSystem::LEFT_HAND; constexpr double radius = 1.; ProjectionUtilities::sphericalToCartesian(lonlat, xyz, crd_sys, radius); } void cartesianToSpherical(const double xyz[], double lonlat[]) { auto crd_sys = ProjectionUtilities::CoordinateSystem::LEFT_HAND; constexpr double radius = 0.; // --> equivalent to radius = norm(xyz) ProjectionUtilities::cartesianToSpherical(xyz, lonlat, crd_sys, radius); } std::string getTileType(const eckit::Parametrisation& params) { std::string tileStr; params.get("tile.type", tileStr); return tileStr; } } // namespace // ------------------------------------------------------------------------------------------------- CubedSphereProjectionBase::CubedSphereProjectionBase(const eckit::Parametrisation& params): tiles_(getTileType(params)), tiles_offsets_ab2xy_(tiles_.ab2xyOffsets()), tiles_offsets_xy2ab_(tiles_.xy2abOffsets()) { ATLAS_TRACE("CubedSphereProjectionBase::CubedSphereProjectionBase"); // Shift projection by a longitude shiftLon_ = 0.0; if (params.has("ShiftLon")) { params.get("ShiftLon", shiftLon_); ATLAS_ASSERT(shiftLon_ <= 90.0, "ShiftLon should be <= 90.0 degrees"); ATLAS_ASSERT(shiftLon_ >= -90.0, "ShiftLon should be >= -90.0 degrees"); } // Apply a Schmidt transform doSchmidt_ = false; stretchFac_ = 0.0; targetLon_ = 0.0; targetLat_ = 0.0; if (params.has("DoSchmidt")) { params.get("DoSchmidt", doSchmidt_); if (doSchmidt_) { params.get("StretchFac", stretchFac_); params.get("TargetLon", targetLon_); params.get("TargetLat", targetLat_); } } } // ------------------------------------------------------------------------------------------------- void CubedSphereProjectionBase::hash(eckit::Hash& h) const { // Add stretching options to hash h.add(shiftLon_); h.add(doSchmidt_); h.add(stretchFac_); h.add(targetLon_); h.add(targetLat_); } // ------------------------------------------------------------------------------------------------- void CubedSphereProjectionBase::xy2lonlat_post(double xyz[], const idx_t t, double crd[]) const { cartesianToSpherical(xyz, crd); if (crd[LON] < 0.) { crd[LON] += 360.; } crd[LON] -= 180.; // Convert to cartesian sphericalToCartesian(crd, xyz); // Perform tile specific rotation tiles_.rotate(t, xyz); // Back to lonlat cartesianToSpherical(xyz, crd); // Shift longitude if (shiftLon_ != 0.0) { crd[LON] = crd[LON] + shiftLon_; if (crd[LON] < -180.) { crd[LON] = 360. + crd[LON]; } if (crd[LON] > 180.) { crd[LON] = -360. + crd[LON]; } } // To 0, 360 if (crd[LON] < 0.0) { crd[LON] += 360.; } // Schmidt transform if (doSchmidt_) { schmidtTransform(stretchFac_, targetLon_, targetLat_, crd); } // longitude does not make sense at the poles - set to 0. if (std::abs(std::abs(crd[LAT]) - 90.) < 1e-15) { crd[LON] = 0.; } } // ------------------------------------------------------------------------------------------------- void CubedSphereProjectionBase::lonlat2xy_pre(double crd[], idx_t& t, double xyz[]) const { if (std::abs(crd[LON]) < 1e-15) { crd[LON] = 0.; } if (std::abs(crd[LAT]) < 1e-15) { crd[LAT] = 0.; } // To [-45.0, 315) if (crd[LON] >= 315.0) { crd[LON] -= 360.; } // find tile which this lonlat is linked to // works [-45, 315.0) t = tiles_.indexFromLonLat(crd); sphericalToCartesian(crd, xyz); tiles_.unrotate(t, xyz); } // ------------------------------------------------------------------------------------------------- void CubedSphereProjectionBase::xy2alphabetat(const double xy[], idx_t& t, double ab[]) const { // xy is in degrees while ab is in degree // ab are the (alpha, beta) coordinates and t is the tile index. t = tiles_.indexFromXY(xy); double normalisedX = xy[XX] / 90.; double normalisedY = (xy[YY] + 135.) / 90.; ab[LON] = (normalisedX - tiles_offsets_xy2ab_[XX][size_t(t)]) * 90.0 - 45.0; ab[LAT] = (normalisedY - tiles_offsets_xy2ab_[YY][size_t(t)]) * 90.0 - 45.0; } // ------------------------------------------------------------------------------------------------- void CubedSphereProjectionBase::alphabetat2xy(const idx_t t, const double ab[], double xy[]) const { // xy and ab are in degrees // (alpha, beta) and tiles. xy[XX] = ab[LON] + 45.0 + tiles_offsets_ab2xy_[LON][size_t(t)]; xy[YY] = ab[LAT] + 45.0 + tiles_offsets_ab2xy_[LAT][size_t(t)]; tiles_.enforceXYdomain(xy); } // ------------------------------------------------------------------------------------------------- Point2 CubedSphereProjectionBase::xy2alphabeta(const Point2& xy, idx_t t) const { auto alphabeta = Point2(xy); xy2alphabeta(alphabeta.data(), t); return alphabeta; } // ------------------------------------------------------------------------------------------------- Point2 CubedSphereProjectionBase::alphabeta2xy(const Point2& alphabeta, idx_t t) const { auto xy = Point2(alphabeta); alphabeta2xy(xy.data(), t); return xy; } // ------------------------------------------------------------------------------------------------- Point2 CubedSphereProjectionBase::lonlat2alphabeta(const Point2& lonlat, idx_t t) const { auto alphabeta = Point2(lonlat); lonlat2alphabeta(alphabeta.data(), t); return alphabeta; } // ------------------------------------------------------------------------------------------------- Point2 CubedSphereProjectionBase::alphabeta2lonlat(const Point2& alphabeta, idx_t t) const { auto lonlat = Point2(alphabeta); alphabeta2lonlat(lonlat.data(), t); return lonlat; } // ------------------------------------------------------------------------------------------------- } // namespace detail } // namespace projection } // namespace atlas atlas-0.46.0/src/atlas/projection/detail/ProjectionImpl.h0000664000175000017500000001447715160212070023520 0ustar alastairalastair/* * (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 #include "atlas/projection/Jacobian.h" #include "atlas/util/Factory.h" #include "atlas/util/NormaliseLongitude.h" #include "atlas/util/Object.h" #include "atlas/util/Point.h" #include "atlas/util/Rotation.h" namespace eckit { class Parametrisation; class Hash; } // namespace eckit namespace atlas { class Domain; class RectangularLonLatDomain; namespace util { class Config; } } // namespace atlas namespace atlas::projection::detail { class ProjectionImpl : public util::Object { public: using Spec = atlas::util::Config; using Jacobian = projection::Jacobian; public: static const ProjectionImpl* create(const eckit::Parametrisation& p); static const ProjectionImpl* create(const std::string& type, const eckit::Parametrisation& p); ProjectionImpl() = default; virtual std::string type() const = 0; virtual void xy2lonlat(double crd[]) const = 0; virtual void lonlat2xy(double crd[]) const = 0; virtual Jacobian jacobian(const PointLonLat&) const = 0; void xy2lonlat(Point2&) const; void lonlat2xy(Point2&) const; 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; virtual Spec spec() const = 0; virtual std::string units() const = 0; virtual operator bool() const { return true; } virtual void hash(eckit::Hash&) const = 0; struct BoundLonLat { operator RectangularLonLatDomain() const; void extend(PointLonLat p, PointLonLat eps); bool crossesDateLine(bool); bool includesNorthPole(bool); bool includesSouthPole(bool); bool crossesDateLine() const { return crossesDateLine_; } bool includesNorthPole() const { return includesNorthPole_; } bool includesSouthPole() const { return includesSouthPole_; } private: PointLonLat min_; PointLonLat max_; bool crossesDateLine_ = false; bool includesNorthPole_ = false; bool includesSouthPole_ = false; bool first_ = true; }; struct Normalise { Normalise(const eckit::Parametrisation&); Normalise(double west); void hash(eckit::Hash&) const; void spec(Spec&) const; void operator()(double crd[]) const { if (normalise_) { crd[0] = (*normalise_)(crd[0]); } } operator bool() const { return bool(normalise_); } private: std::unique_ptr normalise_; std::vector values_; }; struct Derivate { Derivate(const ProjectionImpl& p, PointXY A, PointXY B, double h, double refLongitude = 0.); virtual ~Derivate(); virtual PointLonLat d(PointXY) const = 0; protected: const ProjectionImpl& projection_; const PointXY H_; const double invnH_; const double refLongitude_; PointLonLat xy2lonlat(const PointXY& p) const; }; struct DerivateFactory : public util::Factory { static std::string className() { return "DerivateFactory"; } static ProjectionImpl::Derivate* build(const std::string& type, const ProjectionImpl& p, PointXY A, PointXY B, double h, double refLongitude = 0.); protected: using Factory::Factory; virtual ~DerivateFactory(); virtual ProjectionImpl::Derivate* make(const ProjectionImpl& p, PointXY A, PointXY B, double h, double refLongitude = 0.) = 0; }; }; inline void ProjectionImpl::xy2lonlat(Point2& point) const { xy2lonlat(point.data()); } inline void ProjectionImpl::lonlat2xy(Point2& point) const { lonlat2xy(point.data()); } inline PointLonLat ProjectionImpl::lonlat(const PointXY& xy) const { PointLonLat lonlat(xy); xy2lonlat(lonlat.data()); return lonlat; } inline PointXY ProjectionImpl::xy(const PointLonLat& lonlat) const { PointXY xy(lonlat); lonlat2xy(xy.data()); return xy; } //--------------------------------------------------------------------------------------------------------------------- class Rotated : public util::Rotation { public: using Spec = ProjectionImpl::Spec; Rotated(const PointLonLat& south_pole, double rotation_angle = 0.); Rotated(const eckit::Parametrisation&); virtual ~Rotated() = default; static std::string classNamePrefix() { return "Rotated"; } static std::string typePrefix() { return "rotated_"; } void spec(Spec&) const; void hash(eckit::Hash&) const; }; class NotRotated { public: using Spec = ProjectionImpl::Spec; NotRotated() = default; NotRotated(const eckit::Parametrisation&) {} virtual ~NotRotated() = default; static std::string classNamePrefix() { return ""; } // deliberately empty static std::string typePrefix() { return ""; } // deliberately empty void rotate(double*) const { /* do nothing */ } void unrotate(double*) const { /* do nothing */ } bool rotated() const { return false; } void spec(Spec&) const {} void hash(eckit::Hash&) const {} }; extern "C" { const ProjectionImpl* atlas__Projection__ctor_config(const eckit::Parametrisation* config); void atlas__Projection__type(const ProjectionImpl* This, char*& type, int& size); void atlas__Projection__hash(const ProjectionImpl* This, char*& hash, int& size); ProjectionImpl::Spec* atlas__Projection__spec(const ProjectionImpl* This); void atlas__Projection__xy2lonlat(const ProjectionImpl* This, const double x, const double y, double& lon, double& lat); void atlas__Projection__lonlat2xy(const ProjectionImpl* This, const double lon, const double lat, double& x, double& y); } } // namespace atlas::projection::detail atlas-0.46.0/src/atlas/projection/detail/LambertConformalConicProjection.h0000664000175000017500000000405015160212070027004 0ustar alastairalastair/* * (C) Copyright 1996- ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include "atlas/domain.h" #include "atlas/projection/detail/ProjectionImpl.h" namespace atlas { namespace projection { namespace detail { class LambertConformalConicProjection final : public ProjectionImpl { public: // constructor LambertConformalConicProjection(const eckit::Parametrisation&); // projection name static std::string static_type() { return "lambert_conformal_conic"; } std::string type() const override { return static_type(); } // projection and inverse projection void xy2lonlat(double crd[]) const override; void lonlat2xy(double crd[]) const override; Jacobian jacobian(const PointLonLat&) const override; bool strictlyRegional() const override { return true; } RectangularLonLatDomain lonlatBoundingBox(const Domain& domain) const override { return ProjectionImpl::lonlatBoundingBox(domain); } // specification Spec spec() const override; std::string units() const override { return "meters"; } void hash(eckit::Hash&) const override; private: double 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 } // namespace projection } // namespace atlas atlas-0.46.0/src/atlas/projection/detail/MercatorProjection.cc0000664000175000017500000001556615160212070024531 0ustar alastairalastair/* * (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 "eckit/config/Parametrisation.h" #include "eckit/utils/Hash.h" #include "atlas/projection/detail/MercatorProjection.h" #include "atlas/projection/detail/ProjectionFactory.h" #include "atlas/runtime/Exception.h" #include "atlas/util/Config.h" #include "atlas/util/Constants.h" #include "atlas/util/CoordinateEnums.h" #include "atlas/util/Earth.h" #include "atlas/util/NormaliseLongitude.h" /* Projection formula's for Mercator projection from "Map Projections: A Working Manual" The origin of the xy-system is at (lon0,0) */ namespace { static constexpr double D2R(const double x) { return atlas::util::Constants::degreesToRadians() * x; } static constexpr double R2D(const double x) { return atlas::util::Constants::radiansToDegrees() * x; } } // namespace namespace atlas { namespace projection { namespace detail { // constructors template MercatorProjectionT::MercatorProjectionT(const eckit::Parametrisation& params): ProjectionImpl(), normalise_(params), rotation_(params) { bool radius_provided = params.get("radius", radius_ = util::Earth::radius()); k_radius_ = radius_; params.get("longitude0", lon0_ = 0.0); normalise_mercator_ = util::NormaliseLongitude(lon0_ - 180., lon0_ + 180.); if (params.get("latitude1", lat1_ = 0.0)) { k_radius_ *= std::cos(D2R(lat1_)); } params.get("false_northing", false_northing_ = 0.); params.get("false_easting", false_easting_ = 0.); eccentricity_ = 0.; auto squared = [](double x) { return x * x; }; if (params.get("semi_major_axis", semi_major_axis_ = radius_) && params.get("semi_minor_axis", semi_minor_axis_ = radius_)) { ATLAS_ASSERT( not radius_provided, "Ambiguous parameters provided to MercatorProjection: {radius} and {semi_major_axis,semi_minor_axis}"); eccentricity_ = std::sqrt(1. - squared(semi_minor_axis_ / semi_major_axis_)); } if (eccentricity_ != 0.) { k_radius_ /= std::sqrt(1. - squared(eccentricity_ * std::sin(D2R(lat1_)))); } inv_k_radius_ = 1. / k_radius_; } template void MercatorProjectionT::lonlat2xy(double crd[]) const { auto t = [&](double& lat) -> double { double sinlat = std::sin(D2R(lat)); double t = (1. + sinlat) / (1. - sinlat); if (eccentricity_ > 0) { // --> ellipsoidal correction double e = eccentricity_; double e_sinlat = e * sinlat; t *= std::pow((1. - e_sinlat) / (1. + e_sinlat), e); } return t; }; // first unrotate rotation_.unrotate(crd); // then project if (crd[LAT] >= 90. - 1e-3) { crd[XX] = false_easting_; crd[YY] = std::numeric_limits::infinity(); } else if (crd[LAT] <= -90. + 1e-3) { crd[XX] = false_easting_; crd[YY] = -std::numeric_limits::infinity(); } else { crd[XX] = false_easting_ + k_radius_ * (D2R(normalise_mercator_(crd[LON]) - lon0_)); crd[YY] = false_northing_ + k_radius_ * 0.5 * std::log(t(crd[LAT])); } } template void MercatorProjectionT::xy2lonlat(double crd[]) const { auto compute_lat = [&](double y) -> double { // deepcode ignore FloatingPointEquals: We want exact comparison if (eccentricity_ == 0.) { return 90. - 2. * R2D(std::atan(std::exp(-y * inv_k_radius_))); } else { // eccentricity > 0 --> ellipsoidal correction double e = eccentricity_; /* Iterative procedure to compute the latitude for the inverse projection * From the book "Map Projections-A Working Manual-John P. Snyder (1987)" * Equation (7–9) involves rapidly converging iteration: Calculate t from (15-11) * Then, assuming an initial trial phi equal to (pi/2 - 2*arctan t) in the right side of equation (7–9), * calculate phi on the left side. Substitute the calculated phi into the right side, * calculate a new phi, etc., until phi does not change significantly from the preceding trial value of phi */ constexpr double EPSILON = 1.e-12; constexpr int MAX_ITER = 15; const double t = std::exp(-y * inv_k_radius_); const double e_half = 0.5 * e; double lat = 90. - 2 * R2D(std::atan(t)); for (int i = 0; i < MAX_ITER; ++i) { double e_sinlat = e * std::sin(D2R(lat)); double dlat = 90. - 2. * R2D(std::atan(t * (std::pow(((1.0 - e_sinlat) / (1.0 + e_sinlat)), e_half)))) - lat; lat += dlat; if (std::abs(dlat) < EPSILON) { return lat; } } ATLAS_THROW_EXCEPTION("Convergence failed in computing latitude in MercatorProjection"); } }; const double x = crd[XX] - false_easting_; const double y = crd[YY] - false_northing_; // first projection crd[LON] = lon0_ + R2D(x * inv_k_radius_); crd[LAT] = compute_lat(y); // then rotate rotation_.rotate(crd); // then normalise normalise_(crd); } template ProjectionImpl::Jacobian MercatorProjectionT::jacobian(const PointLonLat&) const { throw_NotImplemented("MercatorProjectionT::jacobian", Here()); } // specification template typename MercatorProjectionT::Spec MercatorProjectionT::spec() const { Spec proj; proj.set("type", static_type()); proj.set("longitude0", lon0_); proj.set("latitude1", lat1_); proj.set("radius", radius_); proj.set("false_easting", false_easting_); proj.set("false_northing", false_northing_); normalise_.spec(proj); rotation_.spec(proj); return proj; } template void MercatorProjectionT::hash(eckit::Hash& hsh) const { hsh.add(static_type()); rotation_.hash(hsh); normalise_.hash(hsh); hsh.add(lon0_); hsh.add(lat1_); hsh.add(radius_); } template class MercatorProjectionT; template class MercatorProjectionT; namespace { static ProjectionBuilder register_1(MercatorProjection::static_type()); static ProjectionBuilder register_2(RotatedMercatorProjection::static_type()); } // namespace } // namespace detail } // namespace projection } // namespace atlas atlas-0.46.0/src/atlas/projection/detail/ProjectionFactory.cc0000664000175000017500000000541215160212070024351 0ustar alastairalastair/* * (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/projection/Projection.h" #include "atlas/projection/detail/ProjectionFactory.h" #include "atlas/util/Config.h" #include "atlas/projection/detail/CubedSphereEquiAnglProjection.h" #include "atlas/projection/detail/CubedSphereEquiDistProjection.h" #include "atlas/projection/detail/LambertAzimuthalEqualAreaProjection.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" #include "atlas/projection/detail/VariableResolutionProjection.h" namespace atlas { namespace projection { //---------------------------------------------------------------------------------------------------------------------- void force_link() { static struct Link { Link() { ProjectionBuilder(); ProjectionBuilder(); ProjectionBuilder(); ProjectionBuilder(); ProjectionBuilder(); ProjectionBuilder(); ProjectionBuilder(); ProjectionBuilder(); ProjectionBuilder(); ProjectionBuilder(); ProjectionBuilder(); ProjectionBuilder(); } } link; } //---------------------------------------------------------------------------------------------------------------------- const Projection::Implementation* ProjectionFactory::build(const std::string& builder) { return build(builder, util::NoConfig()); } const Projection::Implementation* ProjectionFactory::build(const std::string& builder, const eckit::Parametrisation& param) { force_link(); auto factory = get(builder); return factory->make(param); } //---------------------------------------------------------------------------------------------------------------------- } // namespace projection } // namespace atlas atlas-0.46.0/src/atlas/projection/detail/CubedSphereProjectionBase.h0000664000175000017500000001020515160212070025564 0ustar alastairalastair/* * (C) Copyright 2020 UCAR * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ #pragma once #include "atlas/grid/Tiles.h" #include "atlas/library/config.h" #include "atlas/projection/Jacobian.h" #include "atlas/projection/detail/ProjectionImpl.h" #include "eckit/config/Parametrisation.h" #include "eckit/utils/Hash.h" namespace atlas { class CubedSphereTiles; } namespace atlas { namespace projection { namespace detail { class CubedSphereProjectionBase : public ProjectionImpl { public: using ProjectionImpl::jacobian; // constructor CubedSphereProjectionBase(const eckit::Parametrisation&); void hash(eckit::Hash&) const; const atlas::grid::CubedSphereTiles& getCubedSphereTiles() const { return tiles_; }; /// @brief Convert (x, y) coordinate to (alpha, beta) on tile t. /// /// @details Converts the Atlas xy coordinates to the angular coordinates /// described of tile t, described by by Ronchi et al. (1996, /// Journal of Computational Physics, 124, 93). /// Note that the xy coordinate must lie within the domain /// (x <= xmin && x >= xmax) || (y <= ymin && y >= ymax) where /// xmin, xmax, ymin and ymax are the boundaries of tile t. ///@{ Point2 xy2alphabeta(const Point2& xy, idx_t t) const; virtual void xy2alphabeta(double crd[], idx_t t) const = 0; ///@} /// @brief Convert (alpha, beta) coordinate to (x, y) on tile t. /// /// @details Performs the inverse of xy2alpha beta. Note that the result is /// degenerate when abs(alpha) > 45 && abs(beta) > 45 && /// abs(alpha) == abs(beta). In these circumstances, the method /// will return the anticlockwise-most of the two possible /// values. ///@{ Point2 alphabeta2xy(const Point2& alphabeta, idx_t t) const; virtual void alphabeta2xy(double crd[], idx_t) const = 0; ///@} /// @brief Convert (lon, lat) coordinate to (alpha, beta) on tile t /// /// @details Converts the lon lat coordinates to the angular coordinates /// described of tile t, described by by Ronchi et al. (1996, /// Journal of Computational Physics, 124, 93). /// @{ Point2 lonlat2alphabeta(const Point2& lonlat, idx_t t) const; virtual void lonlat2alphabeta(double crd[], idx_t) const = 0; /// @} /// @brief Convert (lon, lat) coordinate to (alpha, beta) on tile t /// /// @details Performs the inverse of lonlat2alphabeta. /// Point2 alphabeta2lonlat(const Point2& alphabeta, idx_t t) const; virtual void alphabeta2lonlat(double crd[], idx_t) const = 0; /// @brief Jacobian of (x, y) with respect to (lon, lat) on tile t /// /// @details Returns the Jacobian /// ∂x/∂λ, ∂x/∂φ /// ∂y/∂λ, ∂y/∂φ /// for tile t. virtual Jacobian jacobian(const PointLonLat& lonlat, idx_t t) const = 0; /// @brief Jacobian of (alpha, beta) with respect to (lon, lat) on tile t /// /// @details Returns the Jacobian /// ∂α/∂λ, ∂α/∂φ /// ∂β/∂λ, ∂β/∂φ /// for tile t. virtual Jacobian alphabetaJacobian(const PointLonLat& lonlat, idx_t t) const = 0; protected: // projection and inverse projection void xy2lonlat_post(double xyz[], const idx_t t, double crd[]) const; void lonlat2xy_pre(double crd[], idx_t& t, double xyz[]) const; void xy2alphabetat(const double xy[], idx_t& t, double ab[]) const; void alphabetat2xy(const idx_t t, const double ab[], double xy[]) const; private: atlas::grid::CubedSphereTiles tiles_; // Shift entire grid double shiftLon_; // Schmidt transform bool doSchmidt_; double stretchFac_; double targetLon_; double targetLat_; std::array, 2> tiles_offsets_ab2xy_; std::array, 2> tiles_offsets_xy2ab_; }; } // namespace detail } // namespace projection } // namespace atlas atlas-0.46.0/src/atlas/projection/detail/ProjProjection.cc0000664000175000017500000001530415160212070023655 0ustar alastairalastair/* * (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 { namespace { 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(); } }; bool proj_ellipsoid_params(PJ_CONTEXT* ctxt, const std::string& proj_str, double& a, double& b) { bool success = false; pj_t identity(proj_create_crs_to_crs(ctxt, proj_str.c_str(), proj_str.c_str(), nullptr)); pj_t proj(proj_get_target_crs(ctxt, identity)); pj_t proj_ellps(proj_get_ellipsoid(ctxt, proj)); if (proj_ellps) { ATLAS_ASSERT(proj_ellipsoid_get_parameters(ctxt, proj_ellps, &a, &b, nullptr, nullptr)); ATLAS_ASSERT(0 < b && b <= a); success = true; } return success; }; std::string source_str(PJ_CONTEXT* ctxt, const std::string& proj_str) { double a, b; if (proj_ellipsoid_params(ctxt, proj_str, a, b)) { auto axes = b < a ? " +a=" + std::to_string(a) + " +b=" + std::to_string(b) : " +R=" + std::to_string(a); return "+proj=lonlat" + axes; } else { return "EPSG:4326"; // WGS84 (lat,lon) } } std::string geocentric_str(PJ_CONTEXT* ctxt, const std::string& proj_str) { double a, b; if (proj_ellipsoid_params(ctxt, proj_str, a, b)) { return b < a ? "+proj=geocent +a=" + std::to_string(a) + " +b=" + std::to_string(b) : "+proj=geocent +R=" + std::to_string(a); } else { return "EPSG:4978"; // WGS84 (x,y,z) } } } // namespace struct Proj { pj_t sourceToTarget_{nullptr}; pj_t sourceToGeocentric_{nullptr}; ctx_t context_{PJ_DEFAULT_CTX}; }; ProjProjection::ProjProjection(const eckit::Parametrisation& param): normalise_(param), proj_(new Proj()) { ATLAS_ASSERT(param.get("proj", proj_string_) && !proj_string_.empty()); source_encoded_ = param.get("proj_source", source_ = source_str(proj_->context_, proj_string_)); geocentric_encoded_ = param.get("proj_geocentric", geocentric_ = geocentric_str(proj_->context_, proj_string_)); // set x/y transformations to/from lon/lat and to/from geocentric coordinates { pj_t p1(proj_create_crs_to_crs(proj_->context_, source_.c_str(), proj_string_.c_str(), nullptr)); ATLAS_ASSERT(p1); proj_->sourceToTarget_.reset(proj_normalize_for_visualization(proj_->context_, p1)); ATLAS_ASSERT(proj_->sourceToTarget_); } { pj_t p2(proj_create_crs_to_crs(proj_->context_, source_.c_str(), geocentric_.c_str(), nullptr)); ATLAS_ASSERT(p2); proj_->sourceToGeocentric_.reset(proj_normalize_for_visualization(proj_->context_, p2)); ATLAS_ASSERT(proj_->sourceToGeocentric_); } // set semi-major/minor axis { double a, b; if (proj_ellipsoid_params(proj_->context_, proj_string_, a, b)) { 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(proj_->context_, proj_->sourceToTarget_)); ATLAS_ASSERT(target); pj_t coord(proj_crs_get_coordinate_system(proj_->context_, target)); ATLAS_ASSERT(coord); ATLAS_ASSERT(proj_cs_get_axis_count(proj_->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()) { if (units == "metre") { units = "meters"; } extraSpec_.set("units", units); } } } } // namespace detail void ProjProjection::xy2lonlat(double crd[]) const { PJ_COORD P = proj_coord(crd[XX], crd[YY], 0, 0); P = proj_trans(proj_->sourceToTarget_, PJ_INV, P); // std::memcpy(crd, &P, 2 * sizeof(double)); crd[LON] = P.enu.e; crd[LAT] = P.enu.n; normalise_(crd); } void ProjProjection::lonlat2xy(double crd[]) const { PJ_COORD P = proj_coord(crd[LON], crd[LAT], 0, 0); P = proj_trans(proj_->sourceToTarget_, PJ_FWD, P); // std::memcpy(crd, &P, 2 * sizeof(double)); crd[XX] = P.xy.x; crd[YY] = P.xy.y; } ProjectionImpl::Jacobian ProjProjection::jacobian(const PointLonLat&) const { throw_NotImplemented("ProjProjection::jacobian", Here()); } PointXYZ ProjProjection::xyz(const PointLonLat& lonlat) const { PJ_COORD P = proj_coord(lonlat.lon(), lonlat.lat(), 0, 0); P = proj_trans(proj_->sourceToGeocentric_, PJ_FWD, P); return {P.xyz.x, P.xyz.y, P.xyz.z}; } RectangularLonLatDomain ProjProjection::lonlatBoundingBox(const Domain& domain) const { return ProjectionImpl::lonlatBoundingBox(domain); } ProjProjection::Spec ProjProjection::spec() const { Spec spec; spec.set("type", type()); spec.set("proj", proj_string_); if (source_encoded_) { spec.set("proj_source", source_); } if (geocentric_encoded_) { spec.set("proj_geocentric", geocentric_); } normalise_.spec(spec); 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_string_); if (source_encoded_) { h.add(source_); } if (geocentric_encoded_) { h.add(geocentric_); } normalise_.hash(h); } namespace { static ProjectionBuilder register_1(ProjProjection::static_type()); } } // namespace detail } // namespace projection } // namespace atlas atlas-0.46.0/src/atlas/projection/detail/CubedSphereEquiAnglProjection.cc0000664000175000017500000003147515160212070026571 0ustar alastairalastair/* * (C) Copyright 2020 UCAR * * 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. */ #include "CubedSphereEquiAnglProjection.h" #include #include #include "eckit/config/Parametrisation.h" #include "eckit/types/FloatCompare.h" #include "eckit/utils/Hash.h" #include "atlas/projection/detail/ProjectionFactory.h" #include "atlas/projection/detail/ProjectionUtilities.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Log.h" #include "atlas/util/Config.h" #include "atlas/util/Constants.h" #include "atlas/util/CoordinateEnums.h" namespace atlas { namespace projection { namespace detail { namespace { static constexpr bool debug = false; // constexpr so compiler can optimize `if ( debug ) { ... }` out static constexpr double deg2rad = atlas::util::Constants::degreesToRadians(); static constexpr double rad2deg = atlas::util::Constants::radiansToDegrees(); // Define small number relative to 360. constexpr double epsilon = std::numeric_limits::epsilon() * 360.; // Define some "fuzzy" comparison operators. // a is approximately equal to b. bool equal(double a, double b) { return std::abs(a - b) <= epsilon; } // a is less than b. bool lessThan(double a, double b) { return a < b && !equal(a, b); } // a is greater than b. bool greaterThan(double a, double b) { return a > b && !equal(a, b); } // a is less than or approximately equal to b. bool lessEqual(double a, double b) { return a < b || equal(a, b); } // a is greater than or approximately equal to b. bool greaterEqual(double a, double b) { return a > b || equal(a, b); } // Sine/cosine helper struct. struct SineCosine { double sinLambda{}; double cosLambda{}; double sinPhi{}; double cosPhi{}; explicit SineCosine(const PointLonLat& lonlat) { const double lambda = lonlat.lon() * deg2rad; const double phi = lonlat.lat() * deg2rad; sinLambda = std::sin(lambda); cosLambda = std::cos(lambda); sinPhi = std::sin(phi); cosPhi = std::sqrt(1. - sinPhi * sinPhi); // Safe, as phi is between +/- 90 degrees } }; } // namespace // ------------------------------------------------------------------------------------------------- CubedSphereEquiAnglProjection::CubedSphereEquiAnglProjection(const eckit::Parametrisation& params): CubedSphereProjectionBase(params) {} // ------------------------------------------------------------------------------------------------- void CubedSphereEquiAnglProjection::xy2alphabeta(double crd[], idx_t t) const { // Get tile centre. const auto& xyCentre = getCubedSphereTiles().tileCentre(static_cast(t)); // Check that xy coordinate is within valid "+" shaped halo region. const auto inCross = [&](const double crd[]) -> bool { return (greaterEqual(crd[XX], xyCentre[XX] - 45.) && lessEqual(crd[XX], xyCentre[XX] + 45.)) || (greaterEqual(crd[YY], xyCentre[YY] - 45.) && lessEqual(crd[YY], xyCentre[YY] + 45.)); }; if (!inCross(crd)) { std::stringstream sStream; sStream << "xy coordinate (" << crd[0] << ", " << crd[1] << ") is not in range for tile " << t << "."; ATLAS_THROW_EXCEPTION(sStream.str()); } // Get alphaBeta Jacobian. const auto abJacobian = getCubedSphereTiles().tileJacobian(static_cast(t)).inverse(); // Set (alpha, beta) coord. const Point2 alphabeta = abJacobian * (Point2(crd) - xyCentre); crd[0] = alphabeta[0]; crd[1] = alphabeta[1]; // Define correction. const auto correction = [](const double crd[]) -> double { return rad2deg * std::atan(std::tan(crd[0] * deg2rad) * std::tan(crd[1] * deg2rad)); }; // Correct halo (alpha, beta) coord. if (lessThan(crd[0], -45.)) { // Left. crd[1] = -correction(crd); } else if (greaterThan(crd[0], 45.)) { // Right. crd[1] = correction(crd); } else if (lessThan(crd[1], -45.)) { // Bottom. crd[0] = -correction(crd); } else if (greaterThan(crd[1], 45.)) { // Top. crd[0] = correction(crd); } } // ------------------------------------------------------------------------------------------------- void CubedSphereEquiAnglProjection::alphabeta2xy(double crd[], idx_t t) const { // Define correction. const auto correction1 = [](const double crd[]) -> double { return rad2deg * std::atan(std::tan(crd[1] * deg2rad) / std::tan(crd[0] * deg2rad)); }; const auto correction2 = [](const double crd[]) -> double { return rad2deg * std::atan(std::tan(crd[0] * deg2rad) / std::tan(crd[1] * deg2rad)); }; // Correct halo (alpha, beta) coord. if (lessThan(crd[0], -45.) && greaterThan(crd[1], crd[0]) && lessEqual(crd[1], -crd[0])) { // Left trapezium. crd[1] = -correction1(crd); } else if (greaterThan(crd[0], 45.) && greaterEqual(crd[1], -crd[0]) && lessThan(crd[1], crd[0])) { // Right trapezium. crd[1] = correction1(crd); } else if (lessThan(crd[1], -45.) && greaterEqual(crd[0], crd[1]) && lessThan(crd[0], -crd[1])) { // Bottom trapezium. crd[0] = -correction2(crd); } else if (greaterThan(crd[1], 45.) && greaterThan(crd[0], -crd[1]) && lessEqual(crd[0], crd[1])) { // Top trapezium. crd[0] = correction2(crd); } // Get tile centre. const auto& xyCentre = getCubedSphereTiles().tileCentre(static_cast(t)); // Get xy Jacobian. const auto xyJacobian = getCubedSphereTiles().tileJacobian(static_cast(t)); // Set xy coord. const Point2 xy = xyJacobian * Point2(crd) + xyCentre; crd[XX] = xy[XX]; crd[YY] = xy[YY]; } // ------------------------------------------------------------------------------------------------- void CubedSphereEquiAnglProjection::lonlat2alphabeta(double crd[], idx_t t) const { const auto angles = SineCosine(PointLonLat(crd)); // Convert lonlat to xyz on unit sphere. auto xyz = PointXYZ{angles.cosLambda * angles.cosPhi, angles.sinLambda * angles.cosPhi, -angles.sinPhi}; const auto& tiles = getCubedSphereTiles(); tiles.unrotate(t, xyz.data()); // project into xy and rotate into alphabeta coordinates. auto alphaBeta = tiles.tileJacobian(t).inverse() * Point2{std::atan2(xyz.y(), xyz.x()), -std::atan2(xyz.z(), xyz.x())}; alphaBeta = alphaBeta * rad2deg; crd[0] = alphaBeta[0]; crd[1] = alphaBeta[1]; } // ------------------------------------------------------------------------------------------------- void CubedSphereEquiAnglProjection::alphabeta2lonlat(double crd[], idx_t t) const { // Rotate alphabeta into xy direction. const auto& tiles = getCubedSphereTiles(); auto alphaBeta = tiles.tileJacobian(t) * Point2{crd}; alphaBeta = alphaBeta * deg2rad; // project alphabeta into xyz space. auto xyz = PointXYZ{-1., -std::tan(alphaBeta[0]), std::tan(alphaBeta[1])}; tiles.rotate(t, xyz.data()); // Honestly, I figured out some of the -1 factors by trial and error. const auto r = PointXYZ::norm(xyz); auto lonlat = PointLonLat{}; if (equal(xyz.x(), 0.) && equal(xyz.y(), 0.)) { lonlat.lon() = 0.; } else { lonlat.lon() = std::atan2(-xyz.y(), -xyz.x()); } lonlat.lat() = std::asin(xyz.z() / r); lonlat = lonlat * rad2deg; lonlat.normalise(); crd[0] = lonlat.lon(); crd[1] = lonlat.lat(); } // ------------------------------------------------------------------------------------------------- Jacobian CubedSphereEquiAnglProjection::jacobian(const PointLonLat& lonlat, idx_t t) const { // Note: angular units cancel, so we leave all values in radians. const auto angles = SineCosine(lonlat); // Convert lonlat to xyz on unit sphere. auto xyz = PointXYZ{angles.cosLambda * angles.cosPhi, angles.sinLambda * angles.cosPhi, -angles.sinPhi}; // Get derivatives of xyz with respect to lambda and phi. auto dxyz_by_dlambda = PointXYZ{-angles.sinLambda * angles.cosPhi, angles.cosLambda * angles.cosPhi, 0.}; auto dxyz_by_dphi = PointXYZ{-angles.cosLambda * angles.sinPhi, -angles.sinLambda * angles.sinPhi, -angles.cosPhi}; // Rotate vectors. const auto& tiles = getCubedSphereTiles(); tiles.unrotate(t, xyz.data()); tiles.unrotate(t, dxyz_by_dlambda.data()); tiles.unrotate(t, dxyz_by_dphi.data()); // Get derivatives of a and b with respect to xyz. // Note: a and b are xy displacements from the tile centre, *not* the // (alpha, beta) coordinates defined in the tileJacobian method. const double inv_x2y2 = 1. / (xyz.x() * xyz.x() + xyz.y() * xyz.y()); const double inv_x2z2 = 1. / (xyz.x() * xyz.x() + xyz.z() * xyz.z()); const auto da_by_dxyz = PointXYZ{-xyz.y() * inv_x2y2, xyz.x() * inv_x2y2, 0.}; const auto db_by_dxyz = PointXYZ{xyz.z() * inv_x2z2, 0., -xyz.x() * inv_x2z2}; // Use chain rule to get Jacobian. const auto& dot = eckit::geometry::Point3::dot; return Jacobian{{dot(da_by_dxyz, dxyz_by_dlambda), dot(da_by_dxyz, dxyz_by_dphi)}, {dot(db_by_dxyz, dxyz_by_dlambda), dot(db_by_dxyz, dxyz_by_dphi)}}; } Jacobian CubedSphereEquiAnglProjection::alphabetaJacobian(const PointLonLat& lonlat, idx_t t) const { const auto& tiles = getCubedSphereTiles(); return tiles.tileJacobian(t).inverse() * jacobian(lonlat, t); } // ------------------------------------------------------------------------------------------------- void CubedSphereEquiAnglProjection::lonlat2xy(double crd[]) const { if (debug) { Log::info() << "equiangular lonlat2xy start : lonlat = " << crd[LON] << " " << crd[LAT] << std::endl; } idx_t t; double ab[2]; // alpha-beta coordinate double xyz[3]; // on Cartesian grid CubedSphereProjectionBase::lonlat2xy_pre(crd, t, xyz); // should be between -45.0 and 45.0 // now calculate (alpha, beta) in radians. ab[0] = std::atan2(xyz[YY], xyz[XX]) * rad2deg; ab[1] = std::atan2(-xyz[ZZ], xyz[XX]) * rad2deg; // I think the minus is here due to the // left coordinate system if (debug) { Log::debug() << "equiangular lonlat2xy xyz ab : " << xyz[XX] << " " << xyz[YY] << " " << xyz[ZZ] << " " << ab[LON] << " " << ab[LAT] << std::endl; } CubedSphereProjectionBase::alphabetat2xy(t, ab, crd); if (debug) { Log::debug() << "equiangular lonlat2xy end : xy = " << crd[LON] << " " << crd[LAT] << std::endl; } } // ------------------------------------------------------------------------------------------------- // input should be Willems xy coordinate in degrees // void CubedSphereEquiAnglProjection::xy2lonlat(double crd[]) const { if (debug) { Log::debug() << "xy2lonlat start xy = " << crd[LON] << " " << crd[LAT] << std::endl; } static const double rsq3 = 1.0 / std::sqrt(3.0); double xyz[3]; double ab[2]; // alpha-beta coordinate idx_t t; // tile index // calculate xy (in degrees) to alpha beta (in radians) and t - tile index. CubedSphereProjectionBase::xy2alphabetat(crd, t, ab); if (debug) { Log::debug() << "equiangular xy2lonlat:: crd t ab : " << crd[0] << " " << crd[1] << " " << t << " " << ab[0] << " " << ab[1] << std::endl; } xyz[0] = -rsq3; xyz[1] = -rsq3 * std::tan(ab[0] * deg2rad); xyz[2] = -rsq3 * std::tan(ab[1] * deg2rad); CubedSphereProjectionBase::xy2lonlat_post(xyz, t, crd); if (debug) { Log::debug() << "end of equiangular xy2lonlat lonlat = " << crd[LON] << " " << crd[LAT] << std::endl; } } // ------------------------------------------------------------------------------------------------- Jacobian CubedSphereEquiAnglProjection::jacobian(const PointLonLat& lonlat) const { const auto& tiles = getCubedSphereTiles(); const idx_t t = tiles.indexFromLonLat(lonlat.data()); return jacobian(lonlat, t); } // ------------------------------------------------------------------------------------------------- CubedSphereEquiAnglProjection::Spec CubedSphereEquiAnglProjection::spec() const { // Fill projection specification Spec proj; proj.set("type", static_type()); return proj; } // ------------------------------------------------------------------------------------------------- void CubedSphereEquiAnglProjection::hash(eckit::Hash& h) const { // Add to hash h.add(static_type()); CubedSphereProjectionBase::hash(h); } // ------------------------------------------------------------------------------------------------- namespace { static ProjectionBuilder register_1(CubedSphereEquiAnglProjection::static_type()); } } // namespace detail } // namespace projection } // namespace atlas atlas-0.46.0/src/atlas/projection/detail/ProjectionFactory.h0000664000175000017500000000324615160212070024216 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include "atlas/projection/Projection.h" #include "atlas/util/Factory.h" namespace eckit { class Parametrisation; } namespace atlas { namespace projection { //---------------------------------------------------------------------------------------------------------------------- class ProjectionFactory : public util::Factory { public: static std::string className() { return "ProjectionFactory"; } static const Projection::Implementation* build(const std::string&); static const Projection::Implementation* build(const std::string&, const eckit::Parametrisation&); using Factory::Factory; private: virtual const Projection::Implementation* make(const eckit::Parametrisation&) = 0; }; //---------------------------------------------------------------------------------------------------------------------- template class ProjectionBuilder : public ProjectionFactory { private: virtual const Projection::Implementation* make(const eckit::Parametrisation& param) { return new T(param); } public: using ProjectionFactory::ProjectionFactory; }; //---------------------------------------------------------------------------------------------------------------------- } // namespace projection } // namespace atlas atlas-0.46.0/src/atlas/projection/detail/CubedSphereEquiAnglProjection.h0000664000175000017500000000442415160212070026425 0ustar alastairalastair/* * (C) Copyright 2020 UCAR * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ #pragma once #include "atlas/domain.h" #include "atlas/projection/Jacobian.h" #include "atlas/projection/detail/CubedSphereProjectionBase.h" #include "atlas/projection/detail/ProjectionImpl.h" namespace atlas { namespace projection { namespace detail { class CubedSphereEquiAnglProjection final : public CubedSphereProjectionBase { public: // constructor CubedSphereEquiAnglProjection(const eckit::Parametrisation&); virtual ~CubedSphereEquiAnglProjection() {} // projection name static std::string static_type() { return "cubedsphere_equiangular"; } std::string type() const override { return static_type(); } /// @brief Convert (x, y) coordinate to (alpha, beta) on tile t. void xy2alphabeta(double crd[], idx_t t) const override; /// @brief Convert (alpha, beta) coordinate to (x, y) on tile t. void alphabeta2xy(double crd[], idx_t t) const override; /// @brief Convert (lon, lat) coordinate to (alpha, beta) on tile t. void lonlat2alphabeta(double crd[], idx_t t) const override; /// @brief Convert (alpha, beta) coordinate to (lon, lat) on tile t. void alphabeta2lonlat(double crd[], idx_t t) const override; /// @brief Jacobian of (x, y) with respect to (lon, lat) on tile t Jacobian jacobian(const PointLonLat& lonlat, idx_t t) const override; /// @brief Jacobian of (alpha, beta) with respect to (lon, lat) on tile t Jacobian alphabetaJacobian(const PointLonLat& lonlat, idx_t t) const override; // projection and inverse projection void xy2lonlat(double crd[]) const override; void lonlat2xy(double crd[]) const override; Jacobian jacobian(const PointLonLat&) const override; bool strictlyRegional() const override { return false; } RectangularLonLatDomain lonlatBoundingBox(const Domain& domain) const override { return ProjectionImpl::lonlatBoundingBox(domain); } // specification Spec spec() const override; std::string units() const override { return "degrees"; } void hash(eckit::Hash&) const override; }; } // namespace detail } // namespace projection } // namespace atlas atlas-0.46.0/src/atlas/projection/Projection.cc0000664000175000017500000000431015160212070021553 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "eckit/utils/Hash.h" #include "atlas/option/Options.h" #include "atlas/projection/Projection.h" #include "atlas/projection/detail/LonLatProjection.h" #include "atlas/projection/detail/ProjectionImpl.h" #include "atlas/util/Config.h" namespace atlas { Projection::Projection(): Handle(new projection::detail::LonLatProjection()) {} Projection::Projection(const eckit::Parametrisation& p): Handle(Implementation::create(p)) {} Projection::Projection(const std::string& type, const eckit::Parametrisation& p): Handle(Implementation::create(type, p)) {} atlas::Projection::operator bool() const { return get()->operator bool(); } void Projection::hash(eckit::Hash& h) const { get()->hash(h); } void atlas::Projection::xy2lonlat(double crd[]) const { get()->xy2lonlat(crd); } void atlas::Projection::xy2lonlat(Point2& point) const { get()->xy2lonlat(point); } void atlas::Projection::lonlat2xy(double crd[]) const { get()->lonlat2xy(crd); } void atlas::Projection::lonlat2xy(Point2& point) const { get()->lonlat2xy(point); } atlas::Projection::Jacobian atlas::Projection::jacobian(const PointLonLat& p) const { return get()->jacobian(p); } PointLonLat atlas::Projection::lonlat(const PointXY& xy) const { return get()->lonlat(xy); } PointXY atlas::Projection::xy(const PointLonLat& lonlat) const { return get()->xy(lonlat); } bool atlas::Projection::strictlyRegional() const { return get()->strictlyRegional(); } RectangularLonLatDomain Projection::lonlatBoundingBox(const Domain& domain) const { return get()->lonlatBoundingBox(domain); } Projection::Spec atlas::Projection::spec() const { return get()->spec(); } std::string atlas::Projection::units() const { return get()->units(); } std::string Projection::type() const { return get()->type(); } } // namespace atlas atlas-0.46.0/src/atlas/util/0000775000175000017500000000000015160212070015733 5ustar alastairalastairatlas-0.46.0/src/atlas/util/Polygon.h0000664000175000017500000001576315160212070017547 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ /// @file Polygon.h /// @author Pedro Maciel /// @author Willem Deconinck /// @date September 2017 #pragma once #include #include #include #include #include #include "atlas/mdspan.h" #include "atlas/library/config.h" #include "atlas/util/Config.h" #include "atlas/util/Object.h" #include "atlas/util/Point.h" #include "atlas/util/VectorOfAbstract.h" namespace eckit { class PathName; } namespace atlas { namespace util { //------------------------------------------------------------------------------------------------------ /// Polygon class Polygon : public std::vector { public: // -- Types struct edge_t : std::pair { edge_t(idx_t A, idx_t B): std::pair(A, B) {} edge_t reverse() const { return edge_t(std::pair::second, std::pair::first); } struct LessThan { bool operator()(const edge_t& e1, const edge_t& e2) const { // order ascending by 'first' return (e1.first < e2.first ? true : e1.first > e2.first ? false : e1.second < e2.second); } }; }; using edge_set_t = std::set; using container_t = std::vector; // -- Constructors Polygon(); Polygon(const edge_set_t&); // -- Operators operator bool() const; Polygon& operator+=(const Polygon&); // -- Methods void print(std::ostream&) const; // -- Friends friend std::ostream& operator<<(std::ostream& s, const Polygon& p) { p.print(s); return s; } protected: void setup(const edge_set_t&); }; //------------------------------------------------------------------------------------------------------ class PartitionPolygons; class PartitionPolygon : public Polygon, util::Object { public: using Polygon::Polygon; using PointsXY = std::vector; using PointsLonLat = std::vector; class RectangleXY { public: RectangleXY() = default; RectangleXY(const std::array& x, const std::array& y) : xmin_{x[0]}, xmax_{x[1]}, ymin_{y[0]}, ymax_{y[1]} {} double xmin() const { return xmin_; } double xmax() const { return xmax_; } double ymin() const { return ymin_; } double ymax() const { return ymax_; } private: double xmin_{std::numeric_limits::max()}; double xmax_{std::numeric_limits::max()}; double ymin_{std::numeric_limits::max()}; double ymax_{std::numeric_limits::max()}; }; /// @brief Return inscribed rectangular domain (not rotated) virtual const RectangleXY& inscribedDomain() const; /// @brief Return value of halo virtual idx_t halo() const { return 0; } /// @brief Return the memory footprint of the Polygon virtual size_t footprint() const { return 0; } /// @brief Output a python script that plots the partition virtual void outputPythonScript(const eckit::PathName&, const eckit::Configuration& = util::NoConfig()) const {} /// @brief Output a JSON file with partition polygons virtual std::string json(const eckit::Configuration& = util::NoConfig()) const; /// @brief All (x,y) coordinates defining a polygon. Last point should match first. virtual PointsXY xy() const = 0; /// @brief All (lon,lat) coordinates defining a polygon. Last point should match first. virtual PointsLonLat lonlat() const = 0; virtual void allGather(PartitionPolygons&) const = 0; }; //------------------------------------------------------------------------------------------------------ class ExplicitPartitionPolygon : public util::PartitionPolygon { public: explicit ExplicitPartitionPolygon(PointsXY&& points): ExplicitPartitionPolygon(std::move(points), RectangleXY()) {} explicit ExplicitPartitionPolygon(PointsXY&& points, const RectangleXY& inscribed): points_(std::move(points)), inscribed_(inscribed) { setup(compute_edges(points_.size())); } PointsXY xy() const override { return points_; } PointsLonLat lonlat() const override { return points_; } void allGather(util::PartitionPolygons&) const override; const RectangleXY& inscribedDomain() const override { return inscribed_; } private: static util::Polygon::edge_set_t compute_edges(idx_t points_size); private: std::vector points_; RectangleXY inscribed_; }; // namespace util //------------------------------------------------------------------------------------------------------ class PartitionPolygons : public VectorOfAbstract { public: std::string json(const eckit::Configuration& = util::NoConfig()) const; }; //------------------------------------------------------------------------------------------------------ class PolygonCoordinates { public: using Vector = VectorOfAbstract; // -- Constructors PolygonCoordinates(const Polygon&, const double x[], const double y[], size_t xstride, size_t ystride, bool removeAlignedPoints); template PolygonCoordinates(const Polygon& poly, atlas::mdspan coordinates, bool removeAlignedPoints): PolygonCoordinates(poly, &coordinates[std::array{0,0}], &coordinates[std::array{0,1}], coordinates.stride(1), coordinates.stride(1), removeAlignedPoints ) {} template PolygonCoordinates(const PointContainer& points); template PolygonCoordinates(const PointContainer& points, bool removeAlignedPoints); // -- Destructor virtual ~PolygonCoordinates(); // -- Methods /// @brief Point-in-partition test /// @param[in] P given point /// @return if point is in polygon virtual bool contains(const Point2& P) const = 0; const Point2& coordinatesMax() const; const Point2& coordinatesMin() const; const Point2& centroid() const; template const Point2& operator[](Index i) const { return coordinates_[i]; } idx_t size() const { return coordinates_.size(); } void print(std::ostream&) const; friend std::ostream& operator<<(std::ostream& out, const PolygonCoordinates& pc); protected: // -- Members Point2 coordinatesMin_; Point2 coordinatesMax_; Point2 centroid_; std::vector coordinates_; }; //------------------------------------------------------------------------------------------------------ } // namespace util } // namespace atlas atlas-0.46.0/src/atlas/util/Unique.h0000664000175000017500000002155415160212070017361 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include "atlas/array/ArrayView.h" #include "atlas/array/IndexView.h" #include "atlas/array/LocalView.h" #include "atlas/array/MakeView.h" #include "atlas/field/Field.h" #include "atlas/library/config.h" #include "atlas/mesh/Connectivity.h" #include "atlas/mesh/Mesh.h" #include "atlas/mesh/Nodes.h" #include "atlas/util/CoordinateEnums.h" #include "atlas/util/LonLatMicroDeg.h" #include "atlas/util/MicroDeg.h" #include "atlas/util/PeriodicTransform.h" namespace atlas { namespace util { class PeriodicTransform; // ---------------------------------------------------------------------------- /// @brief Compute unique positive index from lon-lat coordinates in /// microdegrees /// @return uidx_t Return type depends on ATLAS_BITS_GLOBAL [32/64] bits uidx_t unique_lonlat_microdeg(const int lon, const int lat); /// @brief Compute unique positive index from lon-lat coordinates in /// microdegrees /// @return uidx_t Return type depends on ATLAS_BITS_GLOBAL [32/64] bits uidx_t unique_lonlat_microdeg(const int lonlat[]); /// @brief Compute unique positive index from lon-lat coordinates in /// microdegrees /// @return uidx_t Return type depends on ATLAS_BITS_GLOBAL [32/64] bits uidx_t unique_lonlat(const LonLatMicroDeg&); /// @brief Compute unique positive index from lon-lat coordinates in degrees /// @return uidx_t Return type depends on ATLAS_BITS_GLOBAL [32/64] bits uidx_t unique_lonlat(const double& lon, const double& lat); uidx_t unique_lonlat(const double lonlat[]); uidx_t unique_lonlat(const array::LocalView& lonlat); uidx_t unique_lonlat(const std::array& lonlat); /// @brief Compute unique positive index from lon-lat coordinates in degrees /// @return uidx_t Return type depends on ATLAS_BITS_GLOBAL [32/64] bits uidx_t unique_lonlat(const double& lon, const double& lat, const PeriodicTransform&); /// @brief Compute unique positive index from lon-lat coordinates in degrees. /// coordinates are stored in order: /// [ x1, y1, x2, y2, ... , xn, yn ] /// @return uidx_t Return type depends on ATLAS_BITS_GLOBAL [32/64] bits uidx_t unique_lonlat(const double elem_lonlat[], size_t npts); /// @brief Compute unique positive index for a element /// This class is a functor initialised with the nodes class UniqueLonLat { public: // UniqueLonLat() {} /// @brief Constructor, needs nodes functionspace to cache the lonlat field UniqueLonLat(const mesh::Nodes&); /// @brief Constructor UniqueLonLat(const Mesh&); /// @brief Compute unique positive index of a node defined by node index. /// @return uidx_t Return type depends on ATLAS_BITS_GLOBAL [32/64] bits uidx_t operator()(int node) const; uidx_t operator()(int node, const PeriodicTransform& transform) const; /// @brief Compute unique positive index of element defined by node indices. /// The assumption is that the elements exist in a lon-lat domain and don't // degenerate to a line. /// @return uidx_t Return type depends on ATLAS_BITS_GLOBAL [32/64] bits uidx_t operator()(const mesh::Connectivity::Row& elem_nodes) const; /// @brief Compute unique positive index of element defined by node indices. /// The assumption is that the elements exist in a lon-lat domain and don't // degenerate to a line. /// @return uidx_t Return type depends on ATLAS_BITS_GLOBAL [32/64] bits uidx_t operator()(const mesh::Connectivity::Row& elem_nodes, const PeriodicTransform& transform) const; /// @brief Compute unique positive index of element defined by node indices. /// The assumption is that the elements exist in a lon-lat domain and don't // degenerate to a line. /// @return uidx_t Return type depends on ATLAS_BITS_GLOBAL [32/64] bits uidx_t operator()(const int elem_nodes[], size_t npts) const; /// @brief update the internally cached lonlat view if the field has changed void update(); private: const mesh::Nodes* nodes; array::ArrayView lonlat; }; // ---------------------------------------------------------------------------- /* Inline implementation for maximum performance */ namespace detail { /// lon and lat arguments in microdegrees template inline T uniqueT(const int lon, const int lat) { return uniqueT(lon, lat); } template <> inline int uniqueT(const int lon, const int lat); template <> inline long uniqueT(const int lon, const int lat); /// @brief unique32 computes 32bit positive unique id /// max precision is 0.02 degrees /// Numbering follows ECMWF standard grib ordering (from N-S and W-E) inline int unique32(const int lon_microdeg, const int lat_microdeg) { // Truncate microdegrees to order of degrees (16 bits), and add bits together // to 32 bit int. int iy = static_cast((180000000 - lat_microdeg) * 5e-5); // (2*microdeg(90.)-lat)*5e-5 int ix = static_cast((lon_microdeg + 720000000) * 5e-5); // (lon+2*microdeg(360.))*5e-5 iy <<= 17; int id = iy | ix; return id; } template <> inline int uniqueT(const int lon, const int lat) { return unique32(lon, lat); } /// @brief unique64 computes 64bit positive unique id /// max precision is 1 microdegree /// Numbering follows ECMWF standard grib ordering (from N-S and W-E) inline long unique64(const int lon_microdeg, const int lat_microdeg) { // Truncate microdegrees to (32 bits), and add bits together to 64 bit long. long iy = 360000000l - long(lat_microdeg); // (4*microdeg(90.)-lat) long ix = long(lon_microdeg) + 1440000000l; // (lon+4*microdeg(360.)) iy <<= 31; long id = iy | ix; return id; } template <> inline long uniqueT(const int lon, const int lat) { return unique64(lon, lat); } } // namespace detail inline uidx_t unique_lonlat_microdeg(const int lon, const int lat) { return detail::uniqueT(lon, lat); } inline uidx_t unique_lonlat_microdeg(const int lonlat[]) { return detail::uniqueT(lonlat[LON], lonlat[LAT]); } inline uidx_t unique_lonlat(const LonLatMicroDeg& p) { return unique_lonlat_microdeg(p.data()); } inline uidx_t unique_lonlat(const double& lon, const double& lat) { return detail::uniqueT(microdeg(lon), microdeg(lat)); } inline uidx_t unique_lonlat(const double lonlat[]) { return detail::uniqueT(microdeg(lonlat[LON]), microdeg(lonlat[LAT])); } inline uidx_t unique_lonlat(const std::array& lonlat) { return detail::uniqueT(microdeg(lonlat[LON]), microdeg(lonlat[LAT])); } inline uidx_t unique_lonlat(const array::LocalView& lonlat) { return unique_lonlat(lonlat.data()); } inline uidx_t unique_lonlat(const double elem_lonlat[], size_t npts) { std::array centroid{0., 0.}; for (size_t jnode = 0; jnode < npts; ++jnode) { centroid[LON] += elem_lonlat[jnode * 2 + LON]; centroid[LAT] += elem_lonlat[jnode * 2 + LAT]; } centroid[LON] /= static_cast(npts); centroid[LAT] /= static_cast(npts); return unique_lonlat(centroid); } inline UniqueLonLat::UniqueLonLat(const Mesh& mesh): nodes(&mesh.nodes()), lonlat(array::make_view(nodes->lonlat())) { update(); } inline UniqueLonLat::UniqueLonLat(const mesh::Nodes& _nodes): nodes(&_nodes), lonlat(array::make_view(nodes->lonlat())) { update(); } inline uidx_t UniqueLonLat::operator()(int node) const { return unique_lonlat(lonlat(node, LON), lonlat(node, LAT)); } inline uidx_t UniqueLonLat::operator()(const mesh::Connectivity::Row& elem_nodes) const { std::array centroid{0., 0.}; size_t npts = elem_nodes.size(); for (size_t jnode = 0; jnode < npts; ++jnode) { centroid[LON] += lonlat(elem_nodes(jnode), LON); centroid[LAT] += lonlat(elem_nodes(jnode), LAT); } centroid[LON] /= static_cast(npts); centroid[LAT] /= static_cast(npts); return unique_lonlat(centroid); } inline uidx_t UniqueLonLat::operator()(const int elem_nodes[], size_t npts) const { std::array centroid{0., 0.}; for (size_t jnode = 0; jnode < npts; ++jnode) { centroid[LON] += lonlat(elem_nodes[jnode], LON); centroid[LAT] += lonlat(elem_nodes[jnode], LAT); } centroid[LON] /= static_cast(npts); centroid[LAT] /= static_cast(npts); return unique_lonlat(centroid); } inline void UniqueLonLat::update() { lonlat = array::make_view(nodes->lonlat()); } // ---------------------------------------------------------------------------- } // namespace util } // namespace atlas atlas-0.46.0/src/atlas/util/PolygonLocator.h0000664000175000017500000001353315160212070021064 0ustar alastairalastair/* * (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 #include "atlas/library/config.h" #include "atlas/projection/Projection.h" #include "atlas/runtime/Exception.h" #include "atlas/util/KDTree.h" #include "atlas/util/Polygon.h" #ifdef POLYGONLOCATOR_DEBUGGING #include "atlas/runtime/Log.h" #endif namespace atlas { namespace util { //------------------------------------------------------------------------------------------------------ ///@brief Find polygon that contains a point /// /// Construction requires a list of polygons. /// The implementation makes use of a KDTree that holds centroids of all polygons. /// Once the nearest polygon-centroids (default=4) are found, the corresponding polygons are /// visited in order of shortest distance, to check if the point is contained within. class PolygonLocator { public: /// @brief Construct PolygonLocator from shared_ptr of polygons PolygonLocator(const std::shared_ptr polygons, const Projection& projection = Projection()): shared_polygons_(polygons), polygons_(*shared_polygons_), projection_(projection) { k_ = std::min(k_, polygons_.size()); buildKDTree(); } /// @brief Construct PolygonLocator and move polygons inside. PolygonLocator(PolygonCoordinates::Vector&& polygons, const Projection& projection = Projection()): shared_polygons_(std::make_shared(std::move(polygons))), polygons_(*shared_polygons_), projection_(projection) { k_ = std::min(k_, polygons_.size()); buildKDTree(); } /// @brief Construct PolygonLocator using reference to polygons. /// !WARNING! polygons should not go out of scope before PolygonLocator PolygonLocator(const PolygonCoordinates::Vector& polygons, const Projection& projection = Projection()): polygons_(polygons), projection_(projection) { k_ = std::min(k_, polygons_.size()); buildKDTree(); } template void operator()(const PointContainer& points, PolygonIndexContainer& index) { ATLAS_ASSERT(points.size() == index.size()); typename PointContainer::const_iterator p = points.begin(); typename PointContainer::const_iterator p_end = points.end(); typename PolygonIndexContainer::iterator i = index.begin(); for (; p != p_end; ++p, ++i) { *i = this->operator()(*p); } } /// @brief find the polygon that holds the point (lon,lat) idx_t operator()(const Point2& point) const { const auto found = kdtree_.closestPoints(point, k_); idx_t partition{-1}; for (size_t i = 0; i < found.size(); ++i) { idx_t ii = found[i].payload(); #ifdef POLYGONLOCATOR_DEBUGGING Log::info() << "Search point " << lonlat2xy(point) << " in polygon " << ii << ": "; polygons_[ii].print(Log::info()); Log::info() << " ... "; #endif if (polygons_[ii].contains(lonlat2xy(point))) { partition = ii; #ifdef POLYGONLOCATOR_DEBUGGING Log::info() << "FOUND" << std::endl; #endif break; } #ifdef POLYGONLOCATOR_DEBUGGING Log::info() << "NOT_FOUND" << std::endl; #endif } auto try_shifted_point = [&](double x) { const Point2 shifted{x, point.y()}; const auto shifted_found = kdtree_.closestPoints(shifted, k_); for (size_t i = 0; i < shifted_found.size(); ++i) { idx_t ii = shifted_found[i].payload(); if (polygons_[ii].contains(lonlat2xy(shifted))) { return ii; } } return idx_t{-1}; }; if (partition < 0) { partition = try_shifted_point(point.x() + 360.0); } if (partition < 0) { partition = try_shifted_point(point.x() - 360.0); } if (partition < 0) { std::stringstream out; out << "Could not find find point {lon,lat} = " << point << " or " << Point2{point.x() + 360.0, point.y()} << " or " << Point2{point.x() - 360.0, point.y()} << " in `k=" << k_ << "` \"nearest\" polygons ["; for (size_t i = 0; i < found.size(); ++i) { if (i > 0) { out << ", "; } out << found[i].payload(); } out << "]. Increase `k`?"; throw_AssertionFailed(out.str(), Here()); } return partition; } private: void buildKDTree() { kdtree_.reserve(polygons_.size()); for (idx_t p = 0; p < polygons_.size(); ++p) { kdtree_.insert(xy2lonlat(polygons_[p].centroid()), p); } kdtree_.build(); } Point2 lonlat2xy(const Point2& lonlat) const { Point2 xy{lonlat}; projection_.lonlat2xy(xy.data()); return xy; } Point2 xy2lonlat(const Point2& xy) const { Point2 lonlat{xy}; projection_.xy2lonlat(lonlat.data()); return lonlat; } std::shared_ptr shared_polygons_; const PolygonCoordinates::Vector& polygons_; Projection projection_; idx_t k_{4}; IndexKDTree kdtree_; }; //------------------------------------------------------------------------------------------------------ } // namespace util } // namespace atlas atlas-0.46.0/src/atlas/util/KDTree.h0000664000175000017500000002413115160212070017223 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include "eckit/config/Configuration.h" #include "atlas/util/Geometry.h" #include "atlas/util/ObjectHandle.h" #include "atlas/util/detail/KDTree.h" namespace atlas { namespace util { //------------------------------------------------------------------------------------------------------ /// @brief k-dimensional tree constructable both with 2D (lon,lat) points as with 3D (x,y,z) points /// /// The implementation is based on eckit::KDTreeMemory with 3D (x,y,z) points. /// 2D points (lon,lat) are converted when needed to 3D during insertion, and during search, so that /// a search always happens with 3D cartesian points. /// /// ### Example: /// /// Construct a KDTree, given a `list_of_lonlat_points` (e.g. `std::vector` or other container) /// A payload given is the index in this list. /// @code{.cpp} /// KDTree search; /// search.reserve( list_of_lonlat_points.size() ); /// idx_t n{0}; /// for ( auto& point : list_of_lonlat_points ) { /// search.insert( point, n++ ); /// } /// search.build(); /// @endcode /// We can now do e.g. a search for the nearest 4 neighbours (k=4) sorted by shortest distance /// @code{.cpp} /// idx_t k = 4; /// auto neighbours = search.closestPoints( PointLonLat{180., 45.}, k ).payloads(); /// @endcode /// The variable `neighbours` is now a container of indices (the payloads) of the 4 nearest points template class KDTree : public ObjectHandle> { public: using Handle = ObjectHandle>; using Implementation = typename Handle::Implementation; using Point = typename Implementation::Point; using Payload = typename Implementation::Payload; using PayloadList = typename Implementation::PayloadList; using Value = typename Implementation::Value; using ValueList = typename Implementation::ValueList; using Handle::get; using Handle::Handle; public: //-------------------------------------------------------------------------------------- // Constructors /// @brief Construct an empty kd-tree with default geometry (Earth) KDTree(): Handle(new detail::KDTreeMemory()) {} /// @brief Construct an empty kd-tree with custom geometry KDTree(const Geometry& geometry): Handle(new detail::KDTreeMemory(geometry)) {} /// @brief Construct an empty kd-tree with custom geometry KDTree(const eckit::Configuration& config): KDTree(Geometry(config.getString("geometry","Earth"))) {} /// @brief Construct a shared kd-tree with default geometry (Earth) template KDTree(const std::shared_ptr& kdtree): Handle(new detail::KDTree_eckit(kdtree)) {} /// @brief Construct a shared kd-tree with custom geometry template KDTree(const std::shared_ptr& kdtree, const Geometry& geometry): Handle(new detail::KDTree_eckit(kdtree, geometry)) {} //-------------------------------------------------------------------------------------- // Methods to build the KDTree /// @brief Reserve memory for building the kd-tree in one shot (optional, at cost of extra memory) void reserve(idx_t size) { get()->reserve(size); } /// @brief Insert spherical point (lon,lat) or 3D cartesian point (x,y,z) /// @warning If memory has been reserved with reserve(), insertion will be delayed until build() is called. template void insert(const Point& p, const Payload& payload) { get()->insert(p, payload); } /// @brief Insert kd-tree value in tree /// @warning If memory has been reserved with reserve(), insertion will be delayed until build() is called. void insert(const Value& value) { get()->insert(value); } /// @brief Build the kd-tree in one shot, if memory has been reserved. /// This will need to be called before all search functions like closestPoints(). /// @post The KDTree is ready to be used void build() { get()->build(); } /// @brief Build with spherical points (lon,lat) where longitudes, latitudes, and payloads are separate containers. /// Memory will be reserved with reserve() to match the size /// @post The KDTree is ready to be used template void build(const Longitudes& longitudes, const Latitudes& latitudes, const Payloads& payloads) { get()->build(longitudes, latitudes, payloads); } /// @brief Build with spherical points (lon,lat) given separate iterator ranges for longitudes, latitudes, and payloads. /// Memory will be reserved with reserve() to match the size /// @post The KDTree is ready to be used template void build(const LongitudesIterator& longitudes_begin, const LongitudesIterator& longitudes_end, const LatitudesIterator& latitudes_begin, const LatitudesIterator& latitudes_end, const PayloadsIterator& payloads_begin, const PayloadsIterator& payloads_end) { get()->build(longitudes_begin, longitudes_end, latitudes_begin, latitudes_end, payloads_begin, payloads_end); } /// @brief Build with templated points. Points can be either 2D (lon,lat) or 3D (x,y,z) /// Memory will be reserved with reserve() to match the size /// @post The KDTree is ready to be used template void build(const Points& points, const Payloads& payloads) { get()->build(points, payloads); } /// @brief Build with spherical points (lon,lat) given separate iterator ranges for longitudes, latitudes, and payloads. /// Memory will be reserved with reserve() to match the size /// @post The KDTree is ready to be used template void build(const PointIterator& points_begin, const PointIterator& points_end, const PayloadsIterator& payloads_begin, const PayloadsIterator& payloads_end) { get()->build(points_begin, points_end, payloads_begin, payloads_end); } /// @brief Build with vector of Value /// @post The KDTree is ready to be used void build(const std::vector& values) { get()->build(values); } //-------------------------------------------------------------------------------------- // Methods to access the KDTree bool empty() const { return get()->empty(); } size_t size() const { return get()->size(); } size_t footprint() const { return get()->footprint(); } /// @brief Find k closest points given a 3D cartesian point (x,y,z) or 2D lonlat point(lon,lat) template ValueList closestPoints(const Point& p, size_t k) const { return get()->closestPoints(p, k); } /// @brief Find closest point given a 3D cartesian point (x,y,z) or 2D lonlat point(lon,lat) template Value closestPoint(const Point& p) const { return get()->closestPoint(p); } /// @brief Find all points within a distance of given radius from a given point 3D cartesian point (x,y,z) /// or a 2D (lon,lat) point template ValueList closestPointsWithinRadius(const Point& p, double radius) const { return get()->closestPointsWithinRadius(p, radius); } /// @brief Return geometry used to convert (lon,lat) to (x,y,z) coordinates const Geometry& geometry() const { return get()->geometry(); } }; //------------------------------------------------------------------------------------------------------ using IndexKDTree2D = KDTree; // 2D search: implementation is using 2D points only using IndexKDTree3D = KDTree; // 3D search: lonlat (2D) to xyz (3D) conversion is done internally using IndexKDTree = IndexKDTree3D; // ------------------------------------------------------------------ // C wrapper interfaces to C++ routines extern "C" { IndexKDTree::Implementation* atlas__IndexKDTree__new(); IndexKDTree::Implementation* atlas__IndexKDTree__new_geometry(const Geometry::Implementation* geometry); void atlas__IndexKDTree__delete(IndexKDTree::Implementation* This); void atlas__IndexKDTree__reserve(IndexKDTree::Implementation* This, const idx_t size); void atlas__IndexKDTree__insert(IndexKDTree::Implementation* This, const double lon, const double lat, const idx_t index); void atlas__IndexKDTree__build(IndexKDTree::Implementation* This); void atlas__IndexKDTree__closestPoints(const IndexKDTree::Implementation* This, const double plon, const double plat, const size_t k, double*& lon, double*& lat, idx_t*& indices, double*& distances); void atlas__IndexKDTree__closestPoint(const IndexKDTree::Implementation* This, const double plon, const double plat, double& lon, double& lat, idx_t& index, double& distance); void atlas__IndexKDTree__closestPointsWithinRadius(const IndexKDTree::Implementation* This, const double plon, const double plat, const double radius, size_t& k, double*& lon, double*& lat, idx_t*& indices, double*& distances); const Geometry::Implementation* atlas__IndexKDTree__geometry(const IndexKDTree::Implementation* This); int atlas__IndexKDTree__empty(const IndexKDTree::Implementation* This); idx_t atlas__IndexKDTree__size(const IndexKDTree::Implementation* This); } //------------------------------------------------------------------------------------------------------ } // namespace util } // namespace atlas atlas-0.46.0/src/atlas/util/QhullSphericalTriangulation.cc0000664000175000017500000001120215160212070023717 0ustar alastairalastair#include "QhullSphericalTriangulation.h" #include "atlas/library/defines.h" #include #include #include #include #if ATLAS_HAVE_QHULL // Suppress a few warnings present in third-party libqhullcpp ATLAS_SUPPRESS_WARNINGS_PUSH ATLAS_SUPPRESS_WARNINGS_INTEGER_SIGN_CHANGE ATLAS_SUPPRESS_WARNINGS_CODE_IS_UNREACHABLE ATLAS_SUPPRESS_WARNINGS_TEMPLATE_ID_CDTOR #include #include #include #include #include ATLAS_SUPPRESS_WARNINGS_POP #endif #include "atlas/runtime/Exception.h" namespace atlas{ namespace util{ #if ATLAS_HAVE_QHULL struct QhullSphericalTriangulation::Qhull : public orgQhull::Qhull { using orgQhull::Qhull::Qhull; }; #else struct QhullSphericalTriangulation::Qhull { template Qhull(Args...) { throw_Exception("Atlas has not been compiled with Qhull",Here()); } }; #endif QhullSphericalTriangulation::~QhullSphericalTriangulation() = default; QhullSphericalTriangulation::QhullSphericalTriangulation(size_t N, const double lonlat[]) : QhullSphericalTriangulation(N, lonlat, lonlat+1, 2, 2) { } QhullSphericalTriangulation::QhullSphericalTriangulation(size_t N, const double lon[], const double lat[]) : QhullSphericalTriangulation(N, lon, lat, 1, 1) { } QhullSphericalTriangulation::QhullSphericalTriangulation(size_t N, const double lon[], const double lat[], int lon_stride, int lat_stride) { auto lonlat2xyz = [](double lon, double lat, auto& xyz) { constexpr double deg2rad = M_PI / 180.; const double lambda = deg2rad * lon; const double phi = deg2rad * lat; const double sin_phi = std::sin(phi); const double cos_phi = std::cos(phi); const double sin_lambda = std::sin(lambda); const double cos_lambda = std::cos(lambda); xyz[0] = cos_phi * cos_lambda; xyz[1] = cos_phi * sin_lambda; xyz[2] = sin_phi; }; points_xyz_.resize(N); for (size_t i = 0; i < N; ++i) { lonlat2xyz(lon[i*lon_stride], lat[i*lat_stride], points_xyz_[i]); } constexpr int dim = 3; constexpr const char* command = "Qt"; constexpr const char* comment = ""; const double* coordinates = reinterpret_cast(points_xyz_.data()); qhull_ = std::make_unique(comment, dim, N, coordinates, command); } size_t QhullSphericalTriangulation::size() const { #if ATLAS_HAVE_QHULL return qhull_->facetList().size(); #else return 0; #endif } template static inline void qhull_get_triangles(const Qhull& qhull, const Points& points_xyz_, std::array triangles[]) { #if ATLAS_HAVE_QHULL auto ensure_outward_normal = [&](auto& tri) { auto dot = [](const auto& p1, const auto& p2) { return p1[0]*p2[0] + p1[1]*p2[1] + p1[2]*p2[2]; }; auto cross = [](const auto& p1, const auto& p2) { return std::array{ p1[1] * p2[2] - p1[2] * p2[1], p1[2] * p2[0] - p1[0] * p2[2], p1[0] * p2[1] - p1[1] * p2[0] }; }; const std::array& a = points_xyz_[tri[0]]; const std::array& b = points_xyz_[tri[1]]; const std::array& c = points_xyz_[tri[2]]; std::array ba {a[0]-b[0], a[1]-b[1], a[2]-b[2]}; std::array bc {c[0]-b[0], c[1]-b[1], c[2]-b[2]}; bool outward = dot(b, cross(bc,ba)) > 0; if (not outward) { std::swap(tri[1], tri[2]); } }; size_t jtri{0}; for (const auto& facet : qhull.facetList()){ auto& tri = triangles[jtri++]; size_t jvrt{0}; for( const auto& vertex: facet.vertices()){ tri[jvrt++] = vertex.point().id(); } ensure_outward_normal(tri); } #endif } void QhullSphericalTriangulation::triangles(std::array triangles[]) const { qhull_get_triangles(*qhull_,points_xyz_,triangles); } void QhullSphericalTriangulation::triangles(std::array triangles[]) const { qhull_get_triangles(*qhull_,points_xyz_,triangles); } void QhullSphericalTriangulation::triangles(std::array triangles[]) const { qhull_get_triangles(*qhull_,points_xyz_,triangles); } void QhullSphericalTriangulation::triangles(std::array triangles[]) const { qhull_get_triangles(*qhull_,points_xyz_,triangles); } std::vector> QhullSphericalTriangulation::triangles() const { return triangles(); } } // namespace util } // namespace atlas atlas-0.46.0/src/atlas/util/NormaliseLongitude.h0000664000175000017500000000251215160212070021710 0ustar alastairalastair/* * (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 /// @file NormaliseLongitude.h namespace atlas { namespace util { class NormaliseLongitude { public: // Normalise longitude between (west - eps, east - eps ) with west = 0., east = 360. constexpr NormaliseLongitude(): west_(-eps_), east_(360. - eps_) {} // Normalise longitude between ( west-eps, east-eps ) with east = west + 360 constexpr NormaliseLongitude(double west): west_(west - eps_), east_(west + 360. - eps_) {} // Normalise longitude between ( west-eps, east+eps ) constexpr NormaliseLongitude(double west, double east): west_(west - eps_), east_(east + eps_) {} double operator()(double lon) const { while (lon < west_) { lon += 360.; } while (lon > east_) { lon -= 360.; } return lon; } private: double west_; double east_; public: static constexpr double eps_ = 1.e-11; }; } // namespace util } // namespace atlas atlas-0.46.0/src/atlas/util/Config.h0000664000175000017500000001727715160212070017327 0ustar alastairalastair/* * (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 #include "eckit/config/LocalConfiguration.h" #include "eckit/log/JSON.h" #include "atlas/library/config.h" namespace eckit { class PathName; class Hash; } // namespace eckit namespace atlas { namespace util { /// @brief Configuration class used to construct various /// atlas components class Config : public eckit::LocalConfiguration { public: // -- Constructors Config(); /// @brief Constructor starting from a path Config(const eckit::PathName&); /// @brief Constructor starting from a stream Config(std::istream&, const std::string& format = "json"); /// @brief Constructor starting from a Configuration Config(const eckit::Configuration&); /// @brief Constructor immediately setting a value. template Config(const std::string& name, const ValueT& value); Config(const std::string& name, const std::string_view value) : Config(name, std::string(value)) {} template Config(const std::string& name, std::initializer_list&& value); // -- Mutators /// @brief Operator that sets a key-value pair. template Config operator()(const std::string& name, const ValueT& value); template Config operator()(const std::string& name, std::initializer_list&& value); // Overload operators to merge two Config objects. Config operator|(const eckit::Configuration& other) const; /// @brief Set a key-value parameter using eckit::LocalConfiguration::set; Config& set(const std::string& name, const std::vector&); Config& set(const eckit::Configuration&); template Config& set(const std::string& name, std::initializer_list&& value); Config& remove(const std::string& name); // -- Accessors, overloaded from eckit::Parametrisation using eckit::LocalConfiguration::get; bool get(const std::string& name, std::vector& value) const; #if ! (ATLAS_ECKIT_VERSION_AT_LEAST(1, 26, 0) || ATLAS_ECKIT_DEVELOP) std::vector keys() const; #endif std::string json(eckit::JSON::Formatting = eckit::JSON::Formatting::indent()) const; }; // ------------------------------------------------------------------ template inline Config::Config(const std::string& name, const ValueT& value): eckit::LocalConfiguration() { set(name, value); } template inline Config::Config(const std::string& name, std::initializer_list&& value): eckit::LocalConfiguration() { set(name, value); } template inline Config Config::operator()(const std::string& name, const ValueT& value) { set(name, value); return *this; } template inline Config& Config::set(const std::string& name, std::initializer_list&& value) { set(name, std::vector(value.begin(), value.end())); return *this; } template inline Config Config::operator()(const std::string& name, std::initializer_list&& value) { set(name, std::vector(value.begin(), value.end())); return *this; } // ------------------------------------------------------------------ // C wrapper interfaces to C++ routines // clang-format off extern "C" { Config* atlas__Config__new(); Config* atlas__Config__new_from_json( const char* json ); Config* atlas__Config__new_from_file( const char* path ); void atlas__Config__delete( Config* This ); int atlas__Config__has( Config* This, const char* name ); void atlas__Config__set_config( Config* This, const char* name, const Config* value ); void atlas__Config__set_config_list( Config* This, const char* name, const Config* value[], int size ); void atlas__Config__set_int( Config* This, const char* name, int value ); void atlas__Config__set_long( Config* This, const char* name, long value ); void atlas__Config__set_float( Config* This, const char* name, float value ); void atlas__Config__set_double( Config* This, const char* name, double value ); void atlas__Config__set_string( Config* This, const char* name, const char* value ); void atlas__Config__set_array_int( Config* This, const char* name, int value[], int size ); void atlas__Config__set_array_long( Config* This, const char* name, long value[], int size ); void atlas__Config__set_array_float( Config* This, const char* name, float value[], int size ); void atlas__Config__set_array_double( Config* This, const char* name, double value[], int size ); int atlas__Config__get_config( Config* This, const char* name, Config* value ); int atlas__Config__get_config_list( Config* This, const char* name, Config** &value, int& size, int& allocated ); int atlas__Config__get_int( Config* This, const char* name, int& value ); int atlas__Config__get_long( Config* This, const char* name, long& value ); int atlas__Config__get_float( Config* This, const char* name, float& value ); int atlas__Config__get_double( Config* This, const char* name, double& value ); int atlas__Config__get_string( Config* This, const char* name, char*& value, int& size, int& allocated ); int atlas__Config__get_array_int( Config* This, const char* name, int*& value, int& size, int& allocated ); int atlas__Config__get_array_long( Config* This, const char* name, long*& value, int& size, int& allocated ); int atlas__Config__get_array_float( Config* This, const char* name, float*& value, int& size, int& allocated ); int atlas__Config__get_array_double( Config* This, const char* name, double*& value, int& size, int& allocated ); void atlas__Config__json( Config* This, char*& json, int& size, int& allocated ); } // clang-format on // ------------------------------------------------------------------ class NoConfig : public Config { public: // methods /// Destructor redundant but fixes sanity compiler warnings virtual ~NoConfig() {} virtual bool has(const std::string& name) const { return false; } virtual bool get(const std::string& name, std::string& value) const { return false; } virtual bool get(const std::string& name, bool& value) const { return false; } virtual bool get(const std::string& name, int& value) const { return false; } virtual bool get(const std::string& name, long& value) const { return false; } virtual bool get(const std::string& name, long long& value) const { return false; } virtual bool get(const std::string& name, std::size_t& value) const { return false; } virtual bool get(const std::string& name, float& value) const { return false; } virtual bool get(const std::string& name, double& value) const { return false; } virtual bool get(const std::string& name, std::vector& value) const { return false; } virtual bool get(const std::string& name, std::vector& value) const { return false; } virtual bool get(const std::string& name, std::vector& value) const { return false; } virtual bool get(const std::string& name, std::vector& value) const { return false; } virtual bool get(const std::string& name, std::vector& value) const { return false; } virtual bool get(const std::string& name, std::vector& value) const { return false; } virtual bool get(const std::string& name, std::vector& value) const { return false; } }; } // namespace util } // namespace atlas atlas-0.46.0/src/atlas/util/Topology.h0000664000175000017500000000170715160212070017725 0ustar alastairalastair/* * (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/util/Bitflags.h" namespace atlas { namespace util { class Topology : public util::Bitflags { public: enum { NONE = 0, GHOST = (1 << 1), PERIODIC = (1 << 2), BC = (1 << 3), WEST = (1 << 4), EAST = (1 << 5), NORTH = (1 << 6), SOUTH = (1 << 7), PATCH = (1 << 8), POLE = (1 << 9), LAND = (1 << 10), WATER = (1 << 11), INVALID = (1 << 12), }; }; } // namespace util } // namespace atlas atlas-0.46.0/src/atlas/util/GridPointsJSONWriter.h0000664000175000017500000000233615160212070022061 0ustar alastairalastair/* * (C) Copyright 2023 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include "eckit/config/Parametrisation.h" #include "atlas/grid.h" namespace atlas { namespace util { //------------------------------------------------------------------------------ class GridPointsJSONWriter { public: GridPointsJSONWriter(Grid grid, const eckit::Parametrisation& args); void write(std::ostream& out, eckit::Channel& info) const; void write(std::ostream& out, std::ostream* info = nullptr) const; private: Grid grid_; grid::Distribution distribution_; int precision_; int verbose_; int nb_partitions_; int partition_; bool pretty_; std::string field_; std::vector points_; long points_base_; long field_base_; }; //------------------------------------------------------------------------------ } // namespace util } // namespace atlasatlas-0.46.0/src/atlas/util/SphericalPolygon.cc0000664000175000017500000000460215160212070021526 0ustar alastairalastair/* * (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 "eckit/types/FloatCompare.h" #include "atlas/runtime/Exception.h" #include "atlas/util/CoordinateEnums.h" #include "atlas/util/SphericalPolygon.h" namespace atlas { namespace util { //------------------------------------------------------------------------------------------------------ SphericalPolygon::SphericalPolygon(const PartitionPolygon& partition_polygon): PolygonCoordinates(partition_polygon.xy(), false) {} SphericalPolygon::SphericalPolygon(const std::vector& points): PolygonCoordinates(points) {} bool SphericalPolygon::contains(const Point2& P) const { using eckit::types::is_approximately_equal; ATLAS_ASSERT(coordinates_.size() >= 2); // check first bounding box if (coordinatesMax_[LON] <= P[LON] || P[LON] < coordinatesMin_[LON]) { return false; } // winding number int wn = 0; // loop on polygon edges for (size_t i = 1; i < coordinates_.size(); ++i) { const Point2& A = coordinates_[i - 1]; const Point2& B = coordinates_[i]; // test if P is on/above/below of a great circle containing A,B const bool APB = (A[LON] <= P[LON] && P[LON] < B[LON]); const bool BPA = (B[LON] <= P[LON] && P[LON] < A[LON]); if (APB != BPA) { const double lat = is_approximately_equal(A[LAT], B[LAT]) && is_approximately_equal(std::abs(A[LAT]), 90.) ? A[LAT] : util::Earth::greatCircleLatitudeGivenLongitude(A, B, P[LON]); ATLAS_ASSERT(!std::isnan(lat)); if (is_approximately_equal(P[LAT], lat)) { return true; } wn += (P[LAT] > lat ? -1 : 1) * (APB ? -1 : 1); } } // wn == 0 only when P is outside return wn != 0; } //------------------------------------------------------------------------------------------------------ } // namespace util } // namespace atlas atlas-0.46.0/src/atlas/util/ConvexSphericalPolygon.h0000664000175000017500000001407715160212070022562 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include "eckit/deprecated.h" #include "atlas/util/Point.h" #include "atlas/util/Polygon.h" #include "atlas/runtime/Log.h" #define DEBUG_OUTPUT 0 namespace atlas { namespace util { //------------------------------------------------------------------------------------------------------ class ConvexSphericalPolygon { public: static constexpr int MAX_GRIDCELL_EDGES = 4; static constexpr int MAX_SIZE = 2 * MAX_GRIDCELL_EDGES + 1; public: class GreatCircleSegment { public: GreatCircleSegment(const PointXYZ& p1, const PointXYZ& p2): p1_(p1), p2_(p2), cross_(PointXYZ::cross(p1, p2)) {} // For a given segment, the "left" hemisphere is defined on the left of the segment when walking from first() to second() inline bool inLeftHemisphere(const PointXYZ& P, const double offset = 0., std::ostream* out = NULL) const { #if DEBUG_OUTPUT if (out) { *out << " inLeftHemi: " <= " << offset << " ? " << (PointXYZ::dot(cross(), P) >= offset) << std::endl; } #endif return (cross_[0] * P[0] + cross_[1] * P[1] + cross_[2] * P[2]) >= offset; // has to have = included } PointXYZ intersect(const GreatCircleSegment&, std::ostream* f = NULL, double pointsSameEPS = std::numeric_limits::epsilon()) const; const PointXYZ& first() const { return p1_; } const PointXYZ& second() const { return p2_; } const PointXYZ& cross() const { return cross_; } private: const PointXYZ p1_; const PointXYZ p2_; const PointXYZ cross_; }; public: ConvexSphericalPolygon() = default; template using contains_PointLonLat = std::is_same::type, PointLonLat>; template ::value, void>::type* = nullptr> ConvexSphericalPolygon(const Points& points): ConvexSphericalPolygon(points.data(), points.size()) {} ConvexSphericalPolygon(const PointLonLat points[], size_t size); ConvexSphericalPolygon(const PointXYZ& p1, const PointXYZ& p2, const PointXYZ& p3): ConvexSphericalPolygon(std::array{p1, p2, p3}.data(), 3) {} ConvexSphericalPolygon(const PointXYZ points[], size_t size); void invalidate_this_polygon() { size_ = 0; valid_ = false; area_ = 0.; } operator bool() const { return valid_; } size_t size() const { return size_; } const std::vector& edge_normals() const { if (not computed_edge_normals_) { compute_edge_normals(); } return edge_normals_; } double area() const { if (not computed_area_) { compute_centroid_and_area(); } return area_; } const PointXYZ& centroid() const { if (not computed_centroid_) { compute_centroid_and_area(); } return centroid_; } double radius() const { if (not computed_radius_) { radius_ = compute_radius(); computed_radius_ = true; } return radius_; } ConvexSphericalPolygon intersect(const ConvexSphericalPolygon& pol, std::ostream* f = nullptr, double pointsEqualEPS = std::numeric_limits::epsilon()) const; /* * @brief check if two spherical polygons area equal * @param[in] P given point in (x,y,z) coordinates * @return true if equal vertices */ bool equals(const ConvexSphericalPolygon& plg, const double deg_prec = 1e-10) const; void print(std::ostream&) const; std::string json(int precision = 0) const; friend std::ostream& operator<<(std::ostream& out, const ConvexSphericalPolygon& p) { p.print(out); return out; } const PointXYZ& operator[](idx_t n) const { return sph_coords_[n]; } int next(const int index) const { return (index == size_ - 1) ? 0 : index + 1; }; int previous(const int index) const { return (index == 0) ? size_ - 1 : index - 1; }; int compute_vertex_weights(const PointXYZ& candidatePoint, double vertex_weights[], size_t vertex_weights_size); static void fpe(bool v) { fpe_ = v; } static bool fpe() { return fpe_; } private: struct SubTriangle { PointXYZ centroid; double area; }; struct SubTriangles : public std::array { public: size_t size() const { return size_; } size_t& size() { return size_; } double area() const; std::array::const_iterator end() const { return data() + size_; } private: size_t size_{0}; }; void compute_centroid_and_area() const; double compute_radius() const; SubTriangles triangulate() const; template void clip(const GreatCircleSegment&, ClippedCoords&, const double& pointsSameEPS = std::numeric_limits::epsilon(), std::ostream* out = nullptr); // Set valid_ to true when polygon is convex void validate(); void compute_edge_normals() const; private: std::array sph_coords_; mutable PointXYZ centroid_; mutable double area_{0}; mutable double radius_{0}; size_t size_{0}; bool valid_{false}; mutable bool computed_centroid_{false}; mutable bool computed_radius_{false}; mutable bool computed_area_{false}; mutable bool computed_edge_normals_{false}; mutable std::vector edge_normals_; static bool fpe_; }; //------------------------------------------------------------------------------------------------------ } // namespace util } // namespace atlas atlas-0.46.0/src/atlas/util/Constants.h0000664000175000017500000000176615160212070020072 0ustar alastairalastair/* * (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 //------------------------------------------------------------------------------------------------------ namespace atlas { namespace util { //------------------------------------------------------------------------------------------------------ /// Some useful constants struct Constants { static constexpr double radiansToDegrees() { return 180. * M_1_PI; } static constexpr double degreesToRadians() { return M_PI / 180.; } }; //------------------------------------------------------------------------------------------------------ } // namespace util } // namespace atlas atlas-0.46.0/src/atlas/util/vector.h0000664000175000017500000001074515160212070017415 0ustar alastairalastair/* * (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 // std::swap #include "atlas/library/config.h" #include "atlas/parallel/omp/copy.h" #include "atlas/parallel/omp/fill.h" #include "atlas/runtime/Exception.h" namespace atlas { template class vector { public: using value_type = T; using iterator = T*; using const_iterator = T const*; public: vector() = default; template ::value, int>::type = 0> vector(size_t size) { resize(size); } template ::value, int>::type = 0> vector(size_t size, const value_type& value): vector(size) { assign(size, value); } vector(const vector& other) { assign(other.data_, other.data_ + other.size_); } vector(vector&& other) { std::swap(data_, other.data_); std::swap(size_, other.size_); std::swap(capacity_, other.capacity_); } vector& operator=(vector other) { std::swap(data_, other.data_); std::swap(size_, other.size_); std::swap(capacity_, other.capacity_); return *this; } template vector(const std::initializer_list& list) { assign(list.begin(), list.end()); } ~vector() { if (data_) { delete[] data_; } } template ::value, int>::type = 0> T& at(idx_t i) noexcept(false) { if (i >= size_) { throw_OutOfRange("atlas::vector", i, size_); } return data_[i]; } template ::value, int>::type = 0> T const& at(idx_t i) const noexcept(false) { if (i >= size_) { throw_OutOfRange("atlas::vector", i, size_); } return data_[i]; } template ::value, int>::type = 0> T& operator[](idx_t i) { #if ATLAS_VECTOR_BOUNDS_CHECKING return at(i); #else return data_[i]; #endif } template ::value, int>::type = 0> T const& operator[](idx_t i) const { #if ATLAS_VECTOR_BOUNDS_CHECKING return at(i); #else return data_[i]; #endif } const T* data() const { return data_; } T* data() { return data_; } idx_t size() const { return size_; } idx_t capacity() const { return capacity_; } template ::value, int>::type = 0> void assign(Size n, const value_type& value) { resize(n); omp::fill(begin(), begin() + n, value); } template ::value, int>::type = 0> void assign(const Iter& first, const Iter& last) { size_t size = std::distance(first, last); resize(size); omp::copy(first, last, begin()); } template ::value, int>::type = 0> void reserve(Size size) { if (capacity_ != 0) ATLAS_NOTIMPLEMENTED; data_ = new T[size]; capacity_ = size; } template ::value, int>::type = 0> void resize(size_t size) { if (static_cast(size) > 0) { if (capacity_ == 0) { reserve(size); } if (static_cast(size) > capacity_) { ATLAS_NOTIMPLEMENTED; } size_ = size; } } const_iterator begin() const { return data_; } const_iterator end() const { return data_ + size_; } iterator begin() { return data_; } iterator end() { return data_ + size_; } const_iterator cbegin() const { return data_; } const_iterator cend() const { return data_ + size_; } private: value_type* data_{nullptr}; idx_t size_{0}; idx_t capacity_{0}; }; } // namespace atlas atlas-0.46.0/src/atlas/util/Factory.cc0000664000175000017500000001263715160212070017662 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "atlas/util/Factory.h" #include #include #include "atlas/runtime/Exception.h" #include "atlas/runtime/Log.h" // #define DEBUG_FACTORY_REGISTRATION using lock_guard = std::lock_guard; namespace atlas { namespace util { static bool ATLAS_DEPRECATION_WARNINGS() { const char* val = std::getenv("ATLAS_DEPRECATION_WARNINGS"); if (val != nullptr) { return std::atoi(val); } return false; } static bool ATLAS_DEPRECATION_ERRORS() { const char* val = std::getenv("ATLAS_DEPRECATION_ERRORS"); if (val != nullptr) { return std::atoi(val); } return false; } bool FactoryRegistry::has(const std::string& builder) const { lock_guard lock(mutex_); return (factories_.find(builder) != factories_.end()); } FactoryBase* FactoryRegistry::get(const std::string& builder) const { lock_guard lock(mutex_); auto iterator = factories_.find(builder); if (iterator == factories_.end()) { Log::error() << "No " << factory_ << " for [" << builder << "]" << std::endl; Log::error() << "Factories are:" << std::endl; for (const auto& map_pair : factories_) { Log::error() << " " << map_pair.first << std::endl; } throw_Exception(std::string("No ") + factory_ + std::string(" called ") + builder); } else { auto* factory = iterator->second; if (factory->deprecated()) { if (ATLAS_DEPRECATION_WARNINGS()) { const std::string& message = factory->deprecated().message(); Log::warning() << "[ATLAS_DEPRECATION_WARNING] The builder " << builder << " should no longer be used. " << message << '\n'; Log::warning() << "[ATLAS_DEPRECATION_WARNING] This warning can be disabled with `export ATLAS_DEPRECATION_WARNINGS=0`" << std::endl; } if (ATLAS_DEPRECATION_ERRORS()) { const std::string& message = factory->deprecated().message(); ATLAS_THROW_EXCEPTION("[ATLAS_DEPRECATION_ERROR] The builder " << builder << " should no longer be used. " << message); } } return factory; } } void FactoryRegistry::add(const std::string& builder, FactoryBase* factory) { lock_guard lock(mutex_); ATLAS_ASSERT(factories_.find(builder) == factories_.end(), "Cannot find builder in factory"); factories_[builder] = factory; #ifdef DEBUG_FACTORY_REGISTRATION std::cout << "Registered " << builder << " (" << factory << ") in " << factory_ << std::endl << std::flush; #endif } void FactoryRegistry::remove(const std::string& builder) { lock_guard lock(mutex_); ATLAS_ASSERT(factories_.find(builder) != factories_.end()); #ifdef DEBUG_FACTORY_REGISTRATION FactoryBase* factory = factories_[builder]; std::cout << "Unregistered " << builder << " (" << factory << ") from " << factory_ << std::endl << std::flush; #endif factories_.erase(builder); } FactoryRegistry::FactoryRegistry(const std::string& factory, FactoryRegistry::Private): factory_(factory) { #ifdef DEBUG_FACTORY_REGISTRATION std::cout << "Created " << factory << std::endl; #endif } FactoryRegistry::~FactoryRegistry() { ATLAS_ASSERT(factories_.empty(), "Registry should only be destroyed once all builders have been unregistrered"); #ifdef DEBUG_FACTORY_REGISTRATION std::cout << "Destroyed " << factory_ << std::endl << std::flush; #endif } std::vector FactoryRegistry::keys() const { lock_guard lock(mutex_); std::vector _keys; _keys.reserve(factories_.size()); for (const auto& key_value : factories_) { if (not key_value.second->deprecated_) { _keys.emplace_back(key_value.first); } } return _keys; } void FactoryRegistry::list(std::ostream& out) const { lock_guard lock(mutex_); const char* sep = ""; for (const auto& key_value : factories_) { if (key_value.second->deprecated_) { out << sep << key_value.first; sep = ", "; } } } //---------------------------------------------------------------------------------------------------------------------- FactoryBase::FactoryBase(FactoryRegistry& registry, const std::string& builder, const FactoryDeprecated& deprecated): registry_(registry), builder_(builder), deprecated_(deprecated) { if (not builder_.empty()) { registry_.add(builder, this); } } FactoryBase::~FactoryBase() { if (not builder_.empty()) { registry_.remove(builder_); } } //---------------------------------------------------------------------------------------------------------------------- std::shared_ptr FactoryRegistry::instance(const std::string& factory) { static std::map> registries; if (registries.find(factory) == registries.end()) { auto [it_pair, inserted] = registries.emplace(factory, new FactoryRegistry(factory,Private())); return it_pair->second; } return registries.at(factory); } } // namespace util } // namespace atlas atlas-0.46.0/src/atlas/util/DataType.h0000664000175000017500000003134715160212070017627 0ustar alastairalastair/* * (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 //------------------------------------------------------------------------------------------------------ // For type safety we want to use std::byte for the DataType "BYTE", but it is a C++17 feature. // Backport std::byte here without any operations #if __cplusplus >= 201703L #include #else #ifndef STD_BYTE_DEFINED #define STD_BYTE_DEFINED namespace std { #ifdef _CRAYC struct byte { unsigned char byte_; }; #else enum class byte : unsigned char { }; #endif } // namespace std #endif #endif //------------------------------------------------------------------------------------------------------ namespace atlas { namespace array { class DataType { public: typedef long kind_t; static constexpr kind_t KIND_BYTE = 1; static constexpr kind_t KIND_INT32 = -4; static constexpr kind_t KIND_INT64 = -8; static constexpr kind_t KIND_REAL32 = 4; static constexpr kind_t KIND_REAL64 = 8; static constexpr kind_t KIND_UINT32 = -15; static constexpr kind_t KIND_UINT64 = -16; template static DataType create(); static DataType byte() { return DataType(KIND_BYTE); } static DataType int32() { return DataType(KIND_INT32); } static DataType int64() { return DataType(KIND_INT64); } static DataType real32() { return DataType(KIND_REAL32); } static DataType real64() { return DataType(KIND_REAL64); } static DataType uint32() { return DataType(KIND_UINT32); } static DataType uint64() { return DataType(KIND_UINT64); } template static constexpr kind_t kind(); template static constexpr kind_t kind(const DATATYPE&); template static std::string str(); template static std::string str(const DATATYPE); static kind_t str_to_kind(const std::string&); static std::string kind_to_str(kind_t); static bool kind_valid(kind_t); private: static std::string byte_str() { return "byte"; } static std::string int32_str() { return "int32"; } static std::string int64_str() { return "int64"; } static std::string real32_str() { return "real32"; } static std::string real64_str() { return "real64"; } static std::string uint32_str() { return "uint32"; } static std::string uint64_str() { return "uint64"; } [[noreturn]] static void throw_not_recognised(kind_t); [[noreturn]] static void throw_not_recognised(std::string datatype); public: DataType(const std::string&); DataType(long); DataType(const DataType&); DataType& operator=(const DataType&); std::string str() const { return kind_to_str(kind_); } kind_t kind() const { return kind_; } size_t size() const { return (kind_ == KIND_UINT32) ? 4 : (kind_ == KIND_UINT64) ? 8 : std::abs(kind_); } friend bool operator==(DataType dt1, DataType dt2); friend bool operator!=(DataType dt1, DataType dt2); friend bool operator==(DataType dt, kind_t kind); friend bool operator!=(DataType dt, kind_t kind); friend bool operator==(kind_t kind, DataType dt); friend bool operator!=(kind_t kind, DataType dt2); private: kind_t kind_; }; template <> inline std::string DataType::str() { return byte_str(); } template <> inline std::string DataType::str() { return byte_str(); } template <> inline std::string DataType::str() { static_assert(sizeof(int) == 4, ""); return int32_str(); } template <> inline std::string DataType::str() { static_assert(sizeof(int) == 4, ""); return int32_str(); } template <> inline std::string DataType::str() { static_assert(sizeof(long) == 8, ""); return int64_str(); } template <> inline std::string DataType::str() { static_assert(sizeof(long) == 8, ""); return int64_str(); } template <> inline std::string DataType::str() { static_assert(sizeof(long long) == 8, ""); return int64_str(); } template <> inline std::string DataType::str() { static_assert(sizeof(long long) == 8, ""); return int64_str(); } template <> inline std::string DataType::str() { static_assert(sizeof(float) == 4, ""); return real32_str(); } template <> inline std::string DataType::str() { static_assert(sizeof(float) == 4, ""); return real32_str(); } template <> inline std::string DataType::str() { static_assert(sizeof(double) == 8, ""); return real64_str(); } template <> inline std::string DataType::str() { static_assert(sizeof(double) == 8, ""); return real64_str(); } template <> inline std::string DataType::str() { static_assert(sizeof(unsigned int) == 4, ""); return uint32_str(); } template <> inline std::string DataType::str() { static_assert(sizeof(unsigned int) == 4, ""); return uint32_str(); } template <> inline std::string DataType::str() { static_assert(sizeof(unsigned long) == 8, ""); return uint64_str(); } template <> inline std::string DataType::str() { static_assert(sizeof(unsigned long) == 8, ""); return uint64_str(); } template <> inline std::string DataType::str() { static_assert(sizeof(unsigned long long) == 8, ""); return uint64_str(); } template <> inline std::string DataType::str() { static_assert(sizeof(unsigned long long) == 8, ""); return uint64_str(); } template <> inline std::string DataType::str(const int&) { return str(); } template <> inline std::string DataType::str(const long&) { return str(); } template <> inline std::string DataType::str(const long long&) { return str(); } template <> inline std::string DataType::str(const unsigned int&) { return str(); } template <> inline std::string DataType::str(const unsigned long&) { return str(); } template <> inline std::string DataType::str(const unsigned long long&) { return str(); } template <> inline std::string DataType::str(const float&) { return str(); } template <> inline std::string DataType::str(const double&) { return str(); } template <> inline constexpr DataType::kind_t DataType::kind() { static_assert(sizeof(std::byte) == 1, ""); return KIND_BYTE; } template <> inline constexpr DataType::kind_t DataType::kind() { static_assert(sizeof(std::byte) == 1, ""); return KIND_BYTE; } template <> inline constexpr DataType::kind_t DataType::kind() { static_assert(sizeof(int) == 4, ""); return KIND_INT32; } template <> inline constexpr DataType::kind_t DataType::kind() { static_assert(sizeof(int) == 4, ""); return KIND_INT32; } template <> inline constexpr DataType::kind_t DataType::kind() { static_assert(sizeof(long) == 8, ""); return KIND_INT64; } template <> inline constexpr DataType::kind_t DataType::kind() { static_assert(sizeof(long) == 8, ""); return KIND_INT64; } template <> inline constexpr DataType::kind_t DataType::kind() { static_assert(sizeof(long long) == 8, ""); return KIND_INT64; } template <> inline constexpr DataType::kind_t DataType::kind() { static_assert(sizeof(long long) == 8, ""); return KIND_INT64; } template <> inline constexpr DataType::kind_t DataType::kind() { static_assert(sizeof(unsigned int) == 4, ""); return KIND_UINT32; } template <> inline constexpr DataType::kind_t DataType::kind() { static_assert(sizeof(unsigned int) == 4, ""); return KIND_UINT32; } template <> inline constexpr DataType::kind_t DataType::kind() { static_assert(sizeof(unsigned long) == 8, ""); return KIND_UINT64; } template <> inline constexpr DataType::kind_t DataType::kind() { static_assert(sizeof(unsigned long) == 8, ""); return KIND_UINT64; } template <> inline constexpr DataType::kind_t DataType::kind() { static_assert(sizeof(unsigned long long) == 8, ""); return KIND_UINT64; } template <> inline constexpr DataType::kind_t DataType::kind() { static_assert(sizeof(unsigned long long) == 8, ""); return KIND_UINT64; } template <> inline constexpr DataType::kind_t DataType::kind() { static_assert(sizeof(float) == 4, ""); return KIND_REAL32; } template <> inline constexpr DataType::kind_t DataType::kind() { static_assert(sizeof(float) == 4, ""); return KIND_REAL32; } template <> inline constexpr DataType::kind_t DataType::kind() { static_assert(sizeof(double) == 8, ""); return KIND_REAL64; } template <> inline constexpr DataType::kind_t DataType::kind() { static_assert(sizeof(double) == 8, ""); return KIND_REAL64; } template <> inline constexpr DataType::kind_t DataType::kind(const int&) { return kind(); } template <> inline constexpr DataType::kind_t DataType::kind(const long&) { return kind(); } template <> inline constexpr DataType::kind_t DataType::kind(const unsigned long&) { return kind(); } template <> inline constexpr DataType::kind_t DataType::kind(const unsigned int&) { return kind(); } template <> inline constexpr DataType::kind_t DataType::kind(const float&) { return kind(); } template <> inline constexpr DataType::kind_t DataType::kind(const double&) { return kind(); } inline DataType::kind_t DataType::str_to_kind(const std::string& datatype) { if (datatype == "int32") return KIND_INT32; else if (datatype == "int64") return KIND_INT64; else if (datatype == "uint32") return KIND_UINT32; else if (datatype == "uint64") return KIND_UINT64; else if (datatype == "real32") return KIND_REAL32; else if (datatype == "real64") return KIND_REAL64; else if (datatype == "byte") { return KIND_BYTE; } else { throw_not_recognised(datatype); } } inline std::string DataType::kind_to_str(kind_t kind) { switch (kind) { case KIND_INT32: return int32_str(); case KIND_INT64: return int64_str(); case KIND_UINT32: return uint32_str(); case KIND_UINT64: return uint64_str(); case KIND_REAL32: return real32_str(); case KIND_REAL64: return real64_str(); case KIND_BYTE: return byte_str(); default: throw_not_recognised(kind); } } inline bool DataType::kind_valid(kind_t kind) { switch (kind) { case KIND_BYTE: case KIND_INT32: case KIND_INT64: case KIND_UINT32: case KIND_UINT64: case KIND_REAL32: case KIND_REAL64: return true; default: return false; } } inline DataType::DataType(const DataType& other): kind_(other.kind_) {} inline DataType& DataType::operator=(const DataType& other) { kind_ = other.kind_; return *this; } inline DataType::DataType(const std::string& datatype): kind_(str_to_kind(datatype)) {} inline DataType::DataType(long kind): kind_(kind) {} inline bool operator==(DataType dt1, DataType dt2) { return dt1.kind_ == dt2.kind_; } inline bool operator!=(DataType dt1, DataType dt2) { return dt1.kind_ != dt2.kind_; } inline bool operator==(DataType dt, DataType::kind_t kind) { return dt.kind_ == kind; } inline bool operator!=(DataType dt, DataType::kind_t kind) { return dt.kind_ != kind; } inline bool operator==(DataType::kind_t kind, DataType dt) { return dt.kind_ == kind; } inline bool operator!=(DataType::kind_t kind, DataType dt) { return dt.kind_ != kind; } template inline DataType DataType::create() { return DataType(DataType::kind()); } template inline DataType make_datatype() { return DataType(DataType::kind()); } //------------------------------------------------------------------------------------------------------ } // namespace array using DataType= array::DataType; template inline DataType make_datatype() { return DataType(DataType::kind()); } } // namespace atlas atlas-0.46.0/src/atlas/util/ConvexSphericalPolygon.cc0000664000175000017500000004645515160212070022725 0ustar alastairalastair/* * (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 "atlas/util/Point.h" #include "eckit/geometry/Sphere.h" #include "eckit/types/FloatCompare.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Log.h" #include "atlas/util/ConvexSphericalPolygon.h" #include "atlas/util/CoordinateEnums.h" #include "atlas/util/NormaliseLongitude.h" #include "atlas/library/FloatingPointExceptions.h" namespace atlas { namespace util { using GreatCircleSegment = ConvexSphericalPolygon::GreatCircleSegment; namespace { constexpr double EPS = std::numeric_limits::epsilon(); constexpr double EPS2 = EPS * EPS; inline double distance2(const PointXYZ& p1, const PointXYZ& p2) { double dx = p2[0] - p1[0]; double dy = p2[1] - p1[1]; double dz = p2[2] - p1[2]; return dx * dx + dy * dy + dz * dz; } inline double dot(const PointXYZ& p1, const PointXYZ& p2) { return p1[0] * p2[0] + p1[1] * p2[1] + p1[2] * p2[2]; } inline bool approx_eq(const double& v1, const double& v2) { return std::abs(v1 - v2) <= EPS; } inline bool approx_eq(const PointXYZ& v1, const PointXYZ& v2) { //return approx_eq( v1[0], v2[0], t ) && approx_eq( v1[1], v2[1], t ) && approx_eq( v1[2], v2[2], t ); return distance2(v1, v2) <= EPS2; } void lonlat2xyz(const PointLonLat& lonlat, PointXYZ& xyz) { eckit::geometry::Sphere::convertSphericalToCartesian(1., lonlat, xyz); } void xyz2lonlat(const PointXYZ& xyz, PointLonLat& lonlat) { eckit::geometry::Sphere::convertCartesianToSpherical(1., xyz, lonlat); } #if DEBUG_OUTPUT PointLonLat xyz2lonlat(const PointXYZ& xyz) { PointLonLat lonlat; eckit::geometry::Sphere::convertCartesianToSpherical(1., xyz, lonlat); return lonlat; } #endif } // namespace //------------------------------------------------------------------------------------------------------ bool ConvexSphericalPolygon::fpe_ = true; ConvexSphericalPolygon::ConvexSphericalPolygon(const PointLonLat points[], size_t size): size_{size} { ATLAS_ASSERT(size_ > 2, "Polygon must have at least 3 points"); ATLAS_ASSERT(size_ < MAX_SIZE, "Number of polygon points exceeds compile time MAX_SIZE"); lonlat2xyz(points[0], sph_coords_[0]); size_t isp = 1; for (size_t i = 1; i < size_ - 1; ++i) { lonlat2xyz(points[i], sph_coords_[isp]); // Log::info() << " d : " << PointXYZ::distance(sph_coords_[isp], sph_coords_[isp - 1]) << std::endl; if (approx_eq(sph_coords_[isp], sph_coords_[isp - 1])) { continue; } ++isp; } lonlat2xyz(points[size_ - 1], sph_coords_[isp]); if (approx_eq(sph_coords_[isp], sph_coords_[0]) or approx_eq(sph_coords_[isp], sph_coords_[isp - 1])) { } else { ++isp; } size_ = isp; validate(); if (not valid_) { invalidate_this_polygon(); } } ConvexSphericalPolygon::ConvexSphericalPolygon(const PointXYZ points[], size_t size): size_{size} { ATLAS_ASSERT(size_ > 2, "Polygon must have at least 3 points"); ATLAS_ASSERT(size_ < MAX_SIZE, "Number of polygon points exceeds compile time MAX_SIZE"); sph_coords_[0] = points[0]; size_t isp = 1; for (size_t i = 1; i < size_ - 1; ++i) { sph_coords_[isp] = points[i]; if (approx_eq(sph_coords_[isp], sph_coords_[isp - 1])) { continue; } ++isp; } sph_coords_[isp] = points[size_ - 1]; if (approx_eq(sph_coords_[isp], sph_coords_[0]) or approx_eq(sph_coords_[isp], sph_coords_[isp - 1])) { } else { ++isp; } size_ = isp; validate(); if (not valid_) { invalidate_this_polygon(); } } void ConvexSphericalPolygon::compute_centroid_and_area() const { const auto triangles = triangulate(); area_ = triangles.area(); computed_area_ = true; // Compute centroid based on triangles rather than on vertices of polygon centroid_[0] = 0.; centroid_[1] = 0.; centroid_[2] = 0.; if (area_ > 0.) { for (auto& triangle : triangles) { for (size_t i = 0; i < 3; ++i) { centroid_[i] += triangle.centroid[i] * triangle.area; } } centroid_ /= PointXYZ::norm(centroid_); } // centroid_ = PointXYZ::zero(); // for (int i = 0; i < size_; ++i) { // centroid_ = centroid_ + sph_coords_[i]; // } // PointXYZ::normalise(centroid_); computed_centroid_ = true; } // cf. M. Floater, “Generalized barycentric coordinates and applications” Acta Numerica, p. 001, 2016. int ConvexSphericalPolygon::compute_vertex_weights(const PointXYZ& candidatePoint, double vertex_weights[], size_t vertex_weights_size) { ATLAS_ASSERT(vertex_weights_size == size(), "vertex_weights container size does not match number of vertices"); ATLAS_ASSERT(edge_normals().size() == this->size(), "Incorrect number of edge normals computed - should equal number of polygon edges."); std::array greatCircleProducts = {0}; for (size_t i = 0; i < size_; ++i) { GreatCircleSegment segment = GreatCircleSegment(sph_coords_[i], sph_coords_[(i + 1) % size_]); greatCircleProducts[i] = dot(edge_normals_[i], candidatePoint); if (!segment.inLeftHemisphere(candidatePoint, -5 * EPS)) { return 0; } } // TODO - set vertex_weights to all zeros for (size_t v = 0; v < size_; ++v) { if (PointXYZ::distance2(candidatePoint, sph_coords_[v]) < 25 * EPS2) { for (size_t vv = 0; vv < size_; ++vv) { vertex_weights[vv] = 0; } vertex_weights[v] = 1.; return 1; } } std::array spokeNormals; std::array spokeNorms; double denominator = 0.; for (size_t v = 0; v < size_; ++v) { spokeNormals[v] = PointXYZ::cross(sph_coords_[v], candidatePoint); spokeNorms[v] = PointXYZ::norm(spokeNormals[v]); } for (size_t w = 0; w < size_; ++w) { int pw = previous(w); int nw = next(w); double product = 1.; for (size_t i = 0; i < size_; ++i) { if ((i != w) && (i != pw)) { product *= greatCircleProducts[i]; } } double leftSideAngle = spokeNorms[nw] - dot(spokeNormals[nw], spokeNormals[w]) / spokeNorms[w]; double rightSideAngle = spokeNorms[pw] - dot(spokeNormals[pw], spokeNormals[w]) / spokeNorms[w]; vertex_weights[w] = (greatCircleProducts[pw] * (leftSideAngle) + greatCircleProducts[w] * (rightSideAngle)) * product; denominator += vertex_weights[w] * dot(sph_coords_[w], candidatePoint); } for (size_t w = 0; w < size_; ++w) { vertex_weights[w] /= denominator; } return 1; } void ConvexSphericalPolygon::validate() { valid_ = size_ > 2; if (valid_) { for (int i = 0; i < size(); i++) { int ni = next(i); int nni = next(ni); const PointXYZ& P = sph_coords_[i]; const PointXYZ& nextP = sph_coords_[ni]; if( std::abs(dot(P, P) - 1.) >= 10 * EPS ) { valid_ = false; break; } ATLAS_ASSERT(not approx_eq(P, PointXYZ::mul(nextP, -1.))); if (not GreatCircleSegment{P, nextP}.inLeftHemisphere(sph_coords_[nni], -0.5 * EPS)) { valid_ = false; break; } } } } void ConvexSphericalPolygon::compute_edge_normals() const { if (valid_) { std::vector().swap(edge_normals_); edge_normals_.reserve(size_); for (int i = 0; i < size_; i++) { int ni = next(i); const PointXYZ& P = sph_coords_[i]; const PointXYZ& nextP = sph_coords_[ni]; edge_normals_.emplace_back(GreatCircleSegment{P, nextP}.cross()); } computed_edge_normals_ = true; } } bool ConvexSphericalPolygon::equals(const ConvexSphericalPolygon& plg, const double deg_prec) const { if (size_ == 0 and plg.size_ == 0) { return true; } if (not valid_) { Log::info() << " ConvexSphericalPolygon::equals : this polygon is not valid\n"; return false; } if (not plg.valid_) { Log::info() << " ConvexSphericalPolygon::equals : other polygon passed as argument is not valid\n"; return false; } if (size_ != plg.size()) { Log::info() << " ConvexSphericalPolygon::equals : incompatible sizes: " << size_ << " != " << plg.size() << "\n"; return false; } int offset = 0; const double le = 2. * std::sin(M_PI * deg_prec / 360.); const double le2 = le * le; for (; offset < size_; ++offset) { if (distance2(plg.sph_coords_[0], sph_coords_[offset]) < le2) { break; } } if (offset == size_) { Log::info() << "ConvexSphericalPolygon::equals : no point equal\n"; return false; } for (int j = 0; j < size_; j++) { int idx = (offset + j) % size_; auto dist2 = distance2(plg.sph_coords_[j], sph_coords_[idx]); if (dist2 > le2) { Log::info() << " ConvexSphericalPolygon::equals : point distance " << std::sqrt(dist2) << " < " << le2 << "(= "<< deg_prec << " deg)" << "\n"; return false; } } return true; } // cf. Folke Eriksson, "On the Measure of Solid Angles", Mathematics Magazine, Vol. 63, No. 3, pp. 184-187 (1990) ConvexSphericalPolygon::SubTriangles ConvexSphericalPolygon::triangulate() const { SubTriangles triangles; if (size_ < 3) { return triangles; } size_t itri{0}; const PointXYZ& a = sph_coords_[0]; for (size_t i = 1; i < size_ - 1; i++) { const PointXYZ& b = sph_coords_[i]; const PointXYZ& c = sph_coords_[i + 1]; triangles[itri].centroid = PointXYZ::normalize(a + b + c); // if (PointXYZ::distance(a, b) + PointXYZ::distance(b, c) < 1e-10) { // triangles[itri].area = 0.5 * PointXYZ::norm(PointXYZ::cross(b - a, c - b)); // } // else { auto abc = dot(a, b) + dot(b, c) + dot(c, a); auto a_bc = dot(a, PointXYZ::cross(b, c)); triangles[itri].area = 2. * std::atan(std::abs(a_bc) / (1. + abc)); // } ++itri; } triangles.size() = itri; return triangles; } double ConvexSphericalPolygon::SubTriangles::area() const { double area = 0.; for (auto& triangle : *this) { area += triangle.area; } return area; } template void ConvexSphericalPolygon::clip(const GreatCircleSegment& great_circle, ClippedCoords& clipped_sph_coords, const double& pointsSameEPS, std::ostream* out) { size_t clipped_sph_coords_size{0}; #if DEBUG_OUTPUT int add_point_num = 0; ATLAS_ASSERT(valid_); ATLAS_ASSERT(not approx_eq(great_circle.first(), great_circle.second())); #endif #if ATLAS_BUILD_TYPE_DEBUG ATLAS_ASSERT(valid_); ATLAS_ASSERT(not approx_eq(great_circle.first(), great_circle.second())); #endif bool first_in = great_circle.inLeftHemisphere(sph_coords_[0], -1.5 * EPS, out); for (int i = 0; i < size_; i++) { int in = (i+1) % size_; bool second_in = great_circle.inLeftHemisphere(sph_coords_[in], -1.5 * EPS, out); #if DEBUG_OUTPUT if (out) { out->precision(18); *out << " ** first: " << xyz2lonlat(sph_coords_[i]) << ", in ? " << first_in << std::endl; *out << " second: " << xyz2lonlat(sph_coords_[in]) << ", in ? " << second_in << std::endl; } #endif if (first_in and second_in) { clipped_sph_coords[clipped_sph_coords_size++] = sph_coords_[in]; #if DEBUG_OUTPUT if (out) { *out << " ((" << ++add_point_num << ")) both_in add second: " << xyz2lonlat(sph_coords_[in]) << std::endl; } #endif } else if (not first_in and not second_in) { // continue to update first_in } else { const GreatCircleSegment segment(sph_coords_[i], sph_coords_[in]); PointXYZ ip = great_circle.intersect(segment, out, pointsSameEPS); #if DEBUG_OUTPUT if (out) { *out << " ip : " << xyz2lonlat(ip) << std::endl; } #endif if (ip[0] == 1 and ip[1] == 1 and ip[2] == 1) { // consider the segments parallel #if DEBUG_OUTPUT if (out) { *out << " ((" << ++add_point_num << ")) ip=(1,1,1) add second: " << xyz2lonlat(sph_coords_[in]) << std::endl; } #endif clipped_sph_coords[clipped_sph_coords_size++] = sph_coords_[in]; first_in = second_in; continue; } if (second_in) { int inn = (in+1) % size_; const GreatCircleSegment segment_n(sph_coords_[in], sph_coords_[inn]); if (segment.inLeftHemisphere(ip, -1.5 * EPS, out) and segment_n.inLeftHemisphere(ip, -1.5 * EPS, out) and (distance2(ip, sph_coords_[in]) > pointsSameEPS * pointsSameEPS)) { clipped_sph_coords[clipped_sph_coords_size++] = ip; #if DEBUG_OUTPUT if (out) { *out << " ((" << ++add_point_num << ")) second_in add ip: " << xyz2lonlat(ip) << std::endl; } #endif } clipped_sph_coords[clipped_sph_coords_size++] = sph_coords_[in]; #if DEBUG_OUTPUT if (out) { *out << " ((" << ++add_point_num << ")) second_in add second: " << xyz2lonlat(sph_coords_[in]) << std::endl; } #endif } else { if (distance2(ip, sph_coords_[i]) > pointsSameEPS * pointsSameEPS) { clipped_sph_coords[clipped_sph_coords_size++] = ip; #if DEBUG_OUTPUT if (out) { *out << " ((" << ++add_point_num << ")) first_in add ip: " << xyz2lonlat(ip) << std::endl; } #endif } } } first_in = second_in; } // Update polygon { size_ = clipped_sph_coords_size; if (size_ < 3) { invalidate_this_polygon(); } else { for (size_t i = 0; i < size_; i++) { sph_coords_[i] = clipped_sph_coords[i]; } } } } // intersect a polygon with this polygon // @param[in] pol clipping polygon // @param[out] intersecting polygon ConvexSphericalPolygon ConvexSphericalPolygon::intersect(const ConvexSphericalPolygon& plg, std::ostream* out, double pointsSameEPS) const { bool fpe_disabled = fpe_ ? atlas::library::disable_floating_point_exception(FE_INVALID) : false; auto restore_fpe = [fpe_disabled] { if (fpe_disabled) { atlas::library::enable_floating_point_exception(FE_INVALID); } }; ConvexSphericalPolygon intersection; const ConvexSphericalPolygon* intersector; bool this_intersector = (area() > plg.area()); if (approx_eq(area(), plg.area())) { PointXYZ dc = centroid() - plg.centroid(); if (dc[0] > 0. or (dc[0] == 0. and (dc[1] > 0. or (dc[1] == 0. and dc[2] > 0.)))) { this_intersector = true; } } if (this_intersector) { intersector = this; intersection = plg; } else { intersector = &plg; intersection = *this; } #if DEBUG_OUTPUT std::string intor_id = "P1"; std::string inted_id = "P2"; if (! this_intersector) { intor_id = "P2"; inted_id = "P1"; } if (out) { *out << inted_id << " : "; print(*out); *out << std::endl << intor_id << " : "; plg.print(*out); *out << std::endl; } #endif if (intersection.valid_) { std::array clipped_sph_coords; for (size_t i = 0; i < intersector->size_; i++) { const PointXYZ& s1 = intersector->sph_coords_[i]; const PointXYZ& s2 = intersector->sph_coords_[(i != intersector->size_ - 1) ? i + 1 : 0]; #if DEBUG_OUTPUT if (out) { *out << std::endl << "Clip with [" << intor_id << "_" << i << ", " << intor_id << "_" << (i+1) % intersector->size_ << "]" << std::endl; } #endif intersection.clip(GreatCircleSegment(s1, s2), clipped_sph_coords, pointsSameEPS, out); if (not intersection.valid_) { restore_fpe(); return intersection; } } } intersection.computed_area_ = false; intersection.computed_radius_ = false; intersection.computed_centroid_ = false; restore_fpe(); return intersection; } void ConvexSphericalPolygon::print(std::ostream& out) const { out << "{"; for (size_t i = 0; i < size(); ++i) { if (i > 0) { out << ","; } PointLonLat ip_ll; xyz2lonlat(sph_coords_[i], ip_ll); out << ip_ll; } out << "}"; } std::string ConvexSphericalPolygon::json(int precision) const { std::stringstream ss; if( precision ) { ss.precision(16); } ss << "["; for (size_t i = 0; i < size(); ++i) { if (i > 0) { ss << ","; } PointLonLat ip_ll; xyz2lonlat(sph_coords_[i], ip_ll); ss << "[" << ip_ll.lon() << "," << ip_ll.lat() << "]"; } ss << "]"; return ss.str(); } double ConvexSphericalPolygon::compute_radius() const { double radius{0.}; if (valid_) { if (not computed_centroid_) { compute_centroid_and_area(); } for (size_t i = 0; i < size_; ++i) { radius = std::max(radius, PointXYZ::distance(sph_coords_[i], centroid_)); } } return radius; } // 'this' great circle's intersection with the segment 'p': [p.first(), p.second()) PointXYZ ConvexSphericalPolygon::GreatCircleSegment::intersect(const GreatCircleSegment& p, std::ostream* out, double pointsSameEPS) const { const auto& s = *this; PointXYZ sp = PointXYZ::cross(s.cross(), p.cross()); double sp_norm = PointXYZ::norm(sp); bool gcircles_distinct = (sp_norm > EPS); #if DEBUG_OUTPUT if (out) { *out << " Great circles distinct ? " << sp_norm << " > " << EPS << " ? " << gcircles_distinct << std::endl; } #endif if (gcircles_distinct) { sp /= sp_norm; auto sp_2 = sp * -1.; double d1 = std::max(distance2(sp, p.first()), distance2(sp, p.second())); double d2 = std::max(distance2(sp_2, p.first()), distance2(sp_2, p.second())); if (d1 < d2) { return sp; } else { return sp_2; } } else { return PointXYZ(1, 1, 1); } } //------------------------------------------------------------------------------------------------------ } // namespace util } // namespace atlas atlas-0.46.0/src/atlas/util/QhullSphericalTriangulation.h0000664000175000017500000000363515160212070023574 0ustar alastairalastair/* * (C) Copyright 2023 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 #include "atlas/library/config.h" namespace atlas { namespace util { class QhullSphericalTriangulation { public: template QhullSphericalTriangulation(const LonLatVector& lonlat); QhullSphericalTriangulation(size_t N, const double lonlat[]); QhullSphericalTriangulation(size_t N, const double lon[], const double lat[]); QhullSphericalTriangulation(size_t N, const double lon[], const double lat[], int lon_stride, int lat_stride); ~QhullSphericalTriangulation(); /// @return number of triangles size_t size() const; void triangles(std::array[]) const; void triangles(std::array[]) const; void triangles(std::array[]) const; void triangles(std::array[]) const; template std::vector> triangles() const; std::vector> triangles() const; private: struct Qhull; std::unique_ptr qhull_; std::vector> points_xyz_; }; template inline QhullSphericalTriangulation::QhullSphericalTriangulation(const LonLatVector& lonlat) : QhullSphericalTriangulation(lonlat.size(), reinterpret_cast(lonlat.data())) {} template inline std::vector> QhullSphericalTriangulation::triangles() const { std::vector> vector(size()); triangles(vector.data()); return vector; } } } atlas-0.46.0/src/atlas/util/ObjectHandle.cc0000664000175000017500000000307615160212070020572 0ustar alastairalastair/* * (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/runtime/Log.h" #include "atlas/util/Object.h" #include "atlas/util/ObjectHandle.h" namespace atlas { namespace util { //---------------------------------------------------------------------------------------------------------------------- int ObjectHandleBase::owners() const { return static_cast(object_->owners()); } void ObjectHandleBase::release() { object_->lock(); object_->detach(); /* lock/unlock in detach() isn't sufficient, else there is race condition on owners() */ if (object_->owners() == 0) { object_->unlock(); delete object_; object_ = nullptr; return; } object_->unlock(); object_ = nullptr; } void ObjectHandleBase::assign(const ObjectHandleBase& other) { assign(other.object_); } void ObjectHandleBase::assign(const Object* other) { if (object_) { release(); } object_ = const_cast(other); attach(); } void ObjectHandleBase::attach() { if (!null()) { object_->attach(); } } // ------------------------------------------------------------------ } // namespace util } // namespace atlas atlas-0.46.0/src/atlas/util/Point.cc0000664000175000017500000000171115160212070017333 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "atlas/util/Point.h" #include "atlas/util/NormaliseLongitude.h" namespace atlas { void PointLonLat::normalise() { constexpr util::NormaliseLongitude normalize_from_zero; lon() = normalize_from_zero(lon()); } void PointLonLat::normalise(double west) { util::NormaliseLongitude normalize_from_west(west); lon() = normalize_from_west(lon()); } void PointLonLat::normalise(double west, double east) { util::NormaliseLongitude normalize_between_west_and_east(west, east); lon() = normalize_between_west_and_east(lon()); } } // namespace atlas atlas-0.46.0/src/atlas/util/Object.h0000664000175000017500000000105615160212070017314 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include "eckit/memory/Owned.h" namespace atlas { namespace util { class Object : public eckit::Owned {}; } // namespace util } // namespace atlas atlas-0.46.0/src/atlas/util/Registry.h0000664000175000017500000000317015160212070017715 0ustar alastairalastair/* * (C) Copyright 2021 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 #include #include #include "atlas/runtime/Exception.h" #include "atlas/runtime/Log.h" namespace atlas { namespace util { template class Registry { public: Registry() = default; void add(const std::string& key, T&& value) { ATLAS_ASSERT_MSG(m_.emplace(key, std::move(value)).second, "ObjectRegistry: duplicate '" + key + "'"); } bool has(const std::string& key) { return m_.find(key) != m_.end(); } T get(const std::string& key) { auto j = m_.find(key); if (j != m_.end()) { return j->second; } list(Log::error() << "Registry: unknown '" << key << "', choices are: "); throw_Exception("Registry: unknown '" + key + "'"); } void list(std::ostream& out) { const char* sep = ""; for (const auto& j : m_) { out << sep << j.first; sep = ", "; } } std::vector keys() { std::vector keys; for (const auto& j : m_) { keys.push_back(j.first); } return keys; } private: std::map m_; }; } // namespace util } // namespace atlas atlas-0.46.0/src/atlas/util/Checksum.h0000664000175000017500000000147015160212070017650 0ustar alastairalastair/* * (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 namespace atlas { namespace util { typedef unsigned long checksum_t; checksum_t checksum(const int values[], size_t size); checksum_t checksum(const long values[], size_t size); checksum_t checksum(const float values[], size_t size); checksum_t checksum(const double values[], size_t size); checksum_t checksum(const checksum_t values[], size_t size); } // namespace util } // namespace atlas atlas-0.46.0/src/atlas/util/CGALSphericalTriangulation.h0000664000175000017500000000362115160212070023210 0ustar alastairalastair/* * (C) Copyright 2023 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 #include "atlas/library/config.h" namespace atlas { namespace util { class CGALSphericalTriangulation { public: template CGALSphericalTriangulation(const LonLatVector& lonlat); CGALSphericalTriangulation(size_t N, const double lonlat[]); CGALSphericalTriangulation(size_t N, const double lon[], const double lat[]); CGALSphericalTriangulation(size_t N, const double lon[], const double lat[], int lon_stride, int lat_stride); ~CGALSphericalTriangulation(); /// @return number of triangles size_t size() const; void triangles(std::array[]) const; void triangles(std::array[]) const; void triangles(std::array[]) const; void triangles(std::array[]) const; template std::vector> triangles() const; std::vector> triangles() const; private: struct CGAL; std::unique_ptr cgal_; std::vector> points_xyz_; }; template inline CGALSphericalTriangulation::CGALSphericalTriangulation(const LonLatVector& lonlat) : CGALSphericalTriangulation(lonlat.size(), reinterpret_cast(lonlat.data())) {} template inline std::vector> CGALSphericalTriangulation::triangles() const { std::vector> vector(size()); triangles(vector.data()); return vector; } } } atlas-0.46.0/src/atlas/util/Factory.h0000664000175000017500000000702515160212070017517 0ustar alastairalastair/* * (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 #include #include namespace eckit { class Parametrisation; } namespace atlas { namespace util { class FactoryBase; class FactoryRegistry { private: struct Private{ explicit Private() = default; }; public: // Constructor, essentially private because nobody can create an explicit Private object. // Construction must happen via instance() function below FactoryRegistry(const std::string& factory, Private); virtual ~FactoryRegistry(); private: mutable std::mutex mutex_; std::string factory_; std::map factories_; public: const std::string& factory() const { return factory_; } std::vector keys() const; void list(std::ostream&) const; bool has(const std::string& builder) const; void add(const std::string& builder, FactoryBase*); void remove(const std::string& builder); FactoryBase* get(const std::string& builder) const; static std::shared_ptr instance(const std::string& factory); }; class FactoryDeprecated { public: operator bool() const { return value_; } FactoryDeprecated(bool v) : value_(v) { } FactoryDeprecated() : value_(true) { } FactoryDeprecated(const char* message) : value_(true), message_{message} { } const std::string& message() const { return message_; } private: bool value_; std::string message_; }; class FactoryBase { private: FactoryRegistry& registry_; std::string builder_; std::shared_ptr attached_registry_; FactoryDeprecated deprecated_; protected: FactoryBase(FactoryRegistry&, const std::string& builder, const FactoryDeprecated& deprecated = FactoryDeprecated(false)); virtual ~FactoryBase(); void attach_registry(const std::shared_ptr& registry) { attached_registry_ = registry; } friend class FactoryRegistry; public: const std::string& factoryBuilder() const { return builder_; } const std::string& factoryName() const { return registry_.factory(); } const FactoryDeprecated& deprecated() const { return deprecated_; } }; template class Factory : public FactoryBase { public: static std::vector keys() { return registry().keys(); } static void list(std::ostream& out) { return registry().list(out); } static bool has(const std::string& builder) { return registry().has(builder); } static T* get(const std::string& builder) { return dynamic_cast(registry().get(builder)); } Factory(const std::string& builder = "", const FactoryDeprecated& deprecated = FactoryDeprecated(false)): FactoryBase(registry(), builder, deprecated) { if (not builder.empty()) { attach_registry(FactoryRegistry::instance(T::className())); } } protected: virtual ~Factory(){}; static FactoryRegistry& registry() { return *FactoryRegistry::instance(T::className()).get(); } }; //---------------------------------------------------------------------------------------------------------------------- } // namespace util } // namespace atlas atlas-0.46.0/src/atlas/util/Polygon.cc0000664000175000017500000002354615160212070017703 0ustar alastairalastair/* * (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 "eckit/types/FloatCompare.h" #include "atlas/domain/Domain.h" #include "atlas/projection/Projection.h" #include "atlas/runtime/Exception.h" #include "atlas/util/CoordinateEnums.h" #include "atlas/util/Polygon.h" namespace atlas { namespace util { namespace { //------------------------------------------------------------------------------------------------------ double cross_product_analog(const Point2& A, const Point2& B, const Point2& C) { return (A[XX] - C[XX]) * (B[YY] - C[YY]) - (A[YY] - C[YY]) * (B[XX] - C[XX]); } } // namespace //------------------------------------------------------------------------------------------------------ Polygon::Polygon() = default; Polygon::Polygon(const Polygon::edge_set_t& edges) { setup(edges); } void Polygon::setup(const Polygon::edge_set_t& edges) { if (edges.empty()) { return; } // get external edges by attempting to remove reversed edges, if any edge_set_t extEdges; for (const edge_t& e : edges) { if (!extEdges.erase(e.reverse())) { extEdges.insert(e); } } ATLAS_ASSERT(extEdges.size() >= 2); // set one polygon cycle, by picking next edge with first node same as second // node of last edge clear(); reserve(extEdges.size() + 1); 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()))) { emplace_back(e->second); extEdges.erase(*e); } ATLAS_ASSERT(front() == back()); // exhaust remaining edges, should represent additional cycles, if any while (!extEdges.empty()) { operator+=(Polygon(extEdges)); } } Polygon::operator bool() const { return !Polygon::empty(); } Polygon& Polygon::operator+=(const Polygon& other) { if (empty()) { return operator=(other); } // polygon can have multiple cycles, but must be connected graphs // Note: a 'cycle' is handled by repeating the indices, excluding (repeated) // last index ATLAS_ASSERT(other.front() == other.back()); const difference_type N = difference_type(other.size()) - 1; container_t cycle(2 * size_t(N)); std::copy(other.begin(), other.begin() + N, cycle.begin()); std::copy(other.begin(), other.begin() + N, cycle.begin() + N); for (const_iterator c = cycle.begin(); c != cycle.begin() + N; ++c) { iterator here = std::find(begin(), end(), *c); if (here != end()) { insert(here, c, c + N); return *this; } } throw_AssertionFailed("Polygon: could not merge polygons, they are not connected", Here()); } void Polygon::print(std::ostream& s) const { char z = '{'; for (auto n : static_cast(*this)) { s << z << n; z = ','; } s << '}'; } const PartitionPolygon::RectangleXY& PartitionPolygon::inscribedDomain() const { static RectangleXY inscribed; return inscribed; } //------------------------------------------------------------------------------------------------------ PolygonCoordinates::PolygonCoordinates(const Polygon& poly, const double x[], const double y[], size_t xstride, size_t ystride, bool removeAlignedPoints) { ATLAS_ASSERT(poly.size() > 2); ATLAS_ASSERT(poly.front() == poly.back()); // Point coordinates // - use a bounding box to quickly discard points, // - except when that is above/below bounding box but poles should be included coordinates_.clear(); coordinates_.reserve(poly.size()); coordinatesMin_ = Point2(x[poly[0]], y[poly[0]]); coordinatesMax_ = coordinatesMin_; size_t nb_removed_points_due_to_alignment = 0; for (size_t i = 0; i < poly.size(); ++i) { Point2 A(x[poly[i*xstride]], y[poly[i*ystride]]); coordinatesMin_ = Point2::componentsMin(coordinatesMin_, A); coordinatesMax_ = Point2::componentsMax(coordinatesMax_, A); // if new point is aligned with existing edge (cross product ~= 0) make the // edge longer if ((coordinates_.size() >= 2) && removeAlignedPoints) { const Point2& B = coordinates_.back(); const Point2& C = coordinates_[coordinates_.size() - 2]; if (eckit::types::is_approximately_equal(0., cross_product_analog(A, B, C), 1.e-10)) { coordinates_.back() = A; ++nb_removed_points_due_to_alignment; continue; } } coordinates_.emplace_back(A); } ATLAS_ASSERT(coordinates_.size() == poly.size() - nb_removed_points_due_to_alignment); } template PolygonCoordinates::PolygonCoordinates(const PointContainer& points) { coordinates_.assign(points.begin(), points.end()); ATLAS_ASSERT(coordinates_.size() > 2); ATLAS_ASSERT(eckit::geometry::points_equal(coordinates_.front(), coordinates_.back())); coordinatesMin_ = coordinates_.front(); coordinatesMax_ = coordinatesMin_; centroid_ = Point2{0., 0.}; for (const Point2& P : coordinates_) { coordinatesMin_ = Point2::componentsMin(coordinatesMin_, P); coordinatesMax_ = Point2::componentsMax(coordinatesMax_, P); centroid_[XX] += P[XX]; centroid_[YY] += P[YY]; } centroid_[XX] /= coordinates_.size(); centroid_[YY] /= coordinates_.size(); } template PolygonCoordinates::PolygonCoordinates(const PointContainer& points, bool removeAlignedPoints) { coordinates_.clear(); coordinates_.reserve(points.size()); coordinatesMin_ = Point2{std::numeric_limits::max(), std::numeric_limits::max()}; coordinatesMax_ = Point2{std::numeric_limits::lowest(), std::numeric_limits::lowest()}; centroid_ = Point2{0., 0.}; for (size_t i = 0; i < points.size(); ++i) { const Point2& A = points[i]; coordinatesMin_ = Point2::componentsMin(coordinatesMin_, A); coordinatesMax_ = Point2::componentsMax(coordinatesMax_, A); centroid_[XX] += A[XX]; centroid_[YY] += A[YY]; // if new point is aligned with existing edge (cross product ~= 0) make the // edge longer if ((coordinates_.size() >= 2) && removeAlignedPoints) { const Point2& B = coordinates_.back(); const Point2& C = coordinates_[coordinates_.size() - 2]; if (eckit::types::is_approximately_equal(0., cross_product_analog(A, B, C), 1.e-10)) { coordinates_.back() = A; continue; } } coordinates_.emplace_back(A); } centroid_[XX] /= points.size(); centroid_[YY] /= points.size(); ATLAS_ASSERT(coordinates_.size() > 2); ATLAS_ASSERT(eckit::geometry::points_equal(coordinates_.front(), coordinates_.back())); } template PolygonCoordinates::PolygonCoordinates(const std::vector&); template PolygonCoordinates::PolygonCoordinates(const std::vector&); template PolygonCoordinates::PolygonCoordinates(const std::vector&); template PolygonCoordinates::PolygonCoordinates(const std::vector&, bool); template PolygonCoordinates::PolygonCoordinates(const std::vector&, bool); template PolygonCoordinates::PolygonCoordinates(const std::vector&, bool); PolygonCoordinates::~PolygonCoordinates() = default; const Point2& PolygonCoordinates::coordinatesMax() const { return coordinatesMax_; } const Point2& PolygonCoordinates::coordinatesMin() const { return coordinatesMin_; } const Point2& PolygonCoordinates::centroid() const { return centroid_; } void PolygonCoordinates::print(std::ostream& out) const { out << "["; for (size_t i = 0; i < coordinates_.size(); ++i) { if (i > 0) { out << " "; } out << coordinates_[i]; } out << "]"; } std::ostream& operator<<(std::ostream& out, const PolygonCoordinates& pc) { pc.print(out); return out; } Polygon::edge_set_t ExplicitPartitionPolygon::compute_edges(idx_t points_size) { util::Polygon::edge_set_t edges; if (points_size > 0) { auto add_edge = [&](idx_t p1, idx_t p2) { util::Polygon::edge_t edge = {p1, p2}; edges.insert(edge); // Log::info() << edge.first << " " << edge.second << std::endl; }; for (idx_t p = 0; p < points_size - 2; ++p) { add_edge(p, p + 1); } add_edge(points_size - 2, 0); } return edges; } void ExplicitPartitionPolygon::allGather(PartitionPolygons&) const { ATLAS_NOTIMPLEMENTED; } std::string PartitionPolygons::json(const eckit::Configuration& config) const { std::ostringstream out; out << "[\n"; for (size_t i=0; i& lonlat ) { // p_[LON]=microdeg(lonlat[LON]); p_[LAT]=microdeg(lonlat[LAT]); } LonLatMicroDeg(const PointLonLat& p) { p_[LON] = microdeg(p.lon()); p_[LAT] = microdeg(p.lat()); } // -- Methods int lon() const { return p_[LON]; } int lat() const { return p_[LAT]; } void set_lon(int lon) { p_[LON] = lon; } void set_lat(int lat) { p_[LAT] = lat; } const int* data() const { return p_; } uidx_t unique() { return unique_lonlat(*this); } // -- Comparison operator bool operator<(const LonLatMicroDeg& other) const; private: int p_[2]; }; // ------------------------------------------------------------------------------------ inline bool LonLatMicroDeg::operator<(const LonLatMicroDeg& other) const { if (p_[LAT] > other.p_[LAT]) return true; if (p_[LAT] == other.p_[LAT]) return (p_[LON] < other.p_[LON]); return false; } // ------------------------------------------------------------------------------------ } // namespace util } // namespace atlas #include "atlas/util/Unique.h" atlas-0.46.0/src/atlas/util/MicroDeg.h0000664000175000017500000000135115160212070017575 0ustar alastairalastair/* * (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 namespace atlas { namespace util { inline int microdeg(const double& deg) { assert(deg < 2145.); // Since INT_MAX == 2147483647 assert(deg > -2145.); // Since INT_MIN == –2147483648 return static_cast(deg < 0 ? deg * 1.e6 - 0.5 : deg * 1.e6 + 0.5); } } // namespace util } // namespace atlas atlas-0.46.0/src/atlas/util/ObjectHandle.h0000664000175000017500000000466415160212070020440 0ustar alastairalastair/* * (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/library/config.h" namespace atlas { namespace util { class Object; class ObjectHandleBase { public: ObjectHandleBase() = default; ObjectHandleBase(const Object* object): object_(const_cast(object)) { attach(); } virtual ~ObjectHandleBase() { if (object_) { release(); } } const ObjectHandleBase& operator=(const ObjectHandleBase& other) { if (object_ != other.object_) { assign(other); } return *this; } operator bool() const { return object_ != nullptr; } void reset(const Object* other) { if (object_ != other) { assign(other); } } int owners() const; private: void release(); void assign(const ObjectHandleBase& other); void assign(const Object* other); void attach(); bool null() const { return (object_ == nullptr); } protected: Object* object_{nullptr}; }; template class ObjectHandle : public ObjectHandleBase { public: using Implementation = T; using Handle = ObjectHandle; public: ObjectHandle() = default; ObjectHandle(const T* object): ObjectHandleBase(reinterpret_cast(object)) {} ObjectHandle(const ObjectHandle& handle): ObjectHandleBase(reinterpret_cast(handle.get())) {} ObjectHandle& operator=(const ObjectHandle& handle) { reset(handle.get()); return *this; } ATLAS_ALWAYS_INLINE T* get() { return reinterpret_cast(object_); } ATLAS_ALWAYS_INLINE const T* get() const { return reinterpret_cast(object_); } ATLAS_ALWAYS_INLINE const T* operator->() const { return get(); } ATLAS_ALWAYS_INLINE T* operator->() { return get(); } ATLAS_ALWAYS_INLINE const T& operator*() const { return *get(); } ATLAS_ALWAYS_INLINE T& operator*() { return *get(); } ATLAS_ALWAYS_INLINE void reset(const T* object) { ObjectHandleBase::reset(reinterpret_cast(object)); } }; } // namespace util } // namespace atlas atlas-0.46.0/src/atlas/util/Metadata.h0000664000175000017500000000765215160212070017636 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include "eckit/config/LocalConfiguration.h" #include "eckit/config/Parametrisation.h" #include "atlas/library/config.h" namespace atlas { namespace util { class Metadata : public eckit::LocalConfiguration { public: Metadata(): eckit::LocalConfiguration() {} Metadata(const eckit::LocalConfiguration& p): eckit::LocalConfiguration(p) {} //Metadata(const Metadata& p): eckit::LocalConfiguration(p) {} using eckit::LocalConfiguration::set; template Metadata& set(const std::string& name, const ValueT& value) { eckit::LocalConfiguration::set(name, value); return *this; } Metadata& set(const eckit::Configuration&); using eckit::LocalConfiguration::get; template ValueT get(const std::string& name) const { ValueT value; if (not eckit::LocalConfiguration::get(name, value)) throw_not_found(name); return value; } friend std::ostream& operator<<(std::ostream& s, const Metadata& v) { v.print(s); return s; } void broadcast(); void broadcast(idx_t root); void broadcast(Metadata&); void broadcast(Metadata&, idx_t root); void broadcast(Metadata&) const; void broadcast(Metadata&, idx_t root) const; size_t footprint() const; private: [[noreturn]] void throw_not_found(const std::string&) const; Metadata(const eckit::Value&); }; // ------------------------------------------------------------------ // C wrapper interfaces to C++ routines extern "C" { Metadata* atlas__Metadata__new(); void atlas__Metadata__delete(Metadata* This); void atlas__Metadata__print(Metadata* This, std::ostream* channel); void atlas__Metadata__json(Metadata* This, char*& json, int& size, int& allocated); int atlas__Metadata__has(Metadata* This, const char* name); void atlas__Metadata__set_int(Metadata* This, const char* name, int value); void atlas__Metadata__set_long(Metadata* This, const char* name, long value); void atlas__Metadata__set_float(Metadata* This, const char* name, float value); void atlas__Metadata__set_double(Metadata* This, const char* name, double value); void atlas__Metadata__set_string(Metadata* This, const char* name, const char* value); void atlas__Metadata__set_array_int(Metadata* This, const char* name, int value[], int size); void atlas__Metadata__set_array_long(Metadata* This, const char* name, long value[], int size); void atlas__Metadata__set_array_float(Metadata* This, const char* name, float value[], int size); void atlas__Metadata__set_array_double(Metadata* This, const char* name, double value[], int size); int atlas__Metadata__get_int(Metadata* This, const char* name); long atlas__Metadata__get_long(Metadata* This, const char* name); float atlas__Metadata__get_float(Metadata* This, const char* name); double atlas__Metadata__get_double(Metadata* This, const char* name); void atlas__Metadata__get_string(Metadata* This, const char* name, char* output_str, int max_len); void atlas__Metadata__get_array_int(Metadata* This, const char* name, int*& value, int& size, int& allocated); void atlas__Metadata__get_array_long(Metadata* This, const char* name, long*& value, int& size, int& allocated); void atlas__Metadata__get_array_float(Metadata* This, const char* name, float*& value, int& size, int& allocated); void atlas__Metadata__get_array_double(Metadata* This, const char* name, double*& value, int& size, int& allocated); } // ------------------------------------------------------------------ } // namespace util } // namespace atlas atlas-0.46.0/src/atlas/util/KDTree.cc0000664000175000017500000001262115160212070017362 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "eckit/geometry/Point3.h" #include "atlas/grid.h" #include "atlas/runtime/Exception.h" #include "atlas/util/KDTree.h" #include "atlas/util/detail/KDTree.h" namespace atlas { namespace util { using Handle = typename ObjectHandle>::Handle; using Implementation = typename Handle::Implementation; using Value = typename Implementation::Value; using ValueList = typename Implementation::ValueList; // C wrapper interfaces to C++ routines IndexKDTree::Implementation* atlas__IndexKDTree__new() { IndexKDTree::Implementation* tree; { IndexKDTree handle; tree = handle.get(); tree->attach(); } tree->detach(); return tree; } IndexKDTree::Implementation* atlas__IndexKDTree__new_geometry(const Geometry::Implementation* geometry) { IndexKDTree::Implementation* tree; { IndexKDTree handle(Geometry{geometry}); tree = handle.get(); tree->attach(); } tree->detach(); return tree; } void atlas__IndexKDTree__delete(IndexKDTree::Implementation* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_IndexKDTree"); delete This; } void atlas__IndexKDTree__reserve(IndexKDTree::Implementation* This, const idx_t size) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_IndexKDTree"); return This->reserve(size); } void atlas__IndexKDTree__insert(IndexKDTree::Implementation* This, const double lon, const double lat, const idx_t index) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_IndexKDTree"); return This->insert(PointLonLat{lon, lat}, index); } void atlas__IndexKDTree__build(IndexKDTree::Implementation* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_IndexKDTree"); return This->build(); } void atlas__IndexKDTree__closestPoints(const IndexKDTree::Implementation* This, const double plon, const double plat, const size_t k, double*& lons, double*& lats, idx_t*& indices, double*& distances) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_IndexKDTree"); IndexKDTree::ValueList vl(This->closestPoints(PointLonLat{plon, plat}, k)); lons = new double[k]; lats = new double[k]; indices = new idx_t[k]; distances = new double[k]; for (size_t i = 0; i < k; ++i) { PointLonLat lonlat; This->geometry().xyz2lonlat(vl[i].point(), lonlat); lonlat.normalise(); lons[i] = lonlat.lon(); lats[i] = lonlat.lat(); indices[i] = vl[i].payload(); distances[i] = vl[i].distance(); } } void atlas__IndexKDTree__closestPoint(const IndexKDTree::Implementation* This, const double plon, const double plat, double& lon, double& lat, idx_t& index, double& distance) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_IndexKDTree"); IndexKDTree::Value v(This->closestPoint(PointLonLat{plon, plat})); PointLonLat lonlat; This->geometry().xyz2lonlat(v.point(), lonlat); lonlat.normalise(); lon = lonlat.lon(); lat = lonlat.lat(); index = v.payload(); distance = v.distance(); } void atlas__IndexKDTree__closestPointsWithinRadius(const IndexKDTree::Implementation* This, const double plon, const double plat, double radius, size_t& k, double*& lons, double*& lats, idx_t*& indices, double*& distances) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_IndexKDTree"); IndexKDTree::ValueList vl = This->closestPointsWithinRadius(PointLonLat{plon, plat}, radius); k = vl.size(); lons = new double[k]; lats = new double[k]; indices = new idx_t[k]; distances = new double[k]; for (size_t i = 0; i < k; ++i) { PointLonLat lonlat; This->geometry().xyz2lonlat(vl[i].point(), lonlat); lonlat.normalise(); lons[i] = lonlat.lon(); lats[i] = lonlat.lat(); indices[i] = vl[i].payload(); distances[i] = vl[i].distance(); } } const Geometry::Implementation* atlas__IndexKDTree__geometry(const IndexKDTree::Implementation* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_IndexKDTree"); return This->geometry().get(); } int atlas__IndexKDTree__empty(const IndexKDTree::Implementation* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_IndexKDTree"); return bool(This->empty()); } idx_t atlas__IndexKDTree__size(const IndexKDTree::Implementation* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_IndexKDTree"); return This->size(); } // ------------------------------------------------------------------ } // namespace util } // namespace atlas atlas-0.46.0/src/atlas/util/PolygonXY.cc0000664000175000017500000001320715160212070020155 0ustar alastairalastair/* * (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/domain/Domain.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Log.h" #include "atlas/util/CoordinateEnums.h" #include "atlas/util/PolygonXY.h" namespace atlas { namespace util { //------------------------------------------------------------------------------------------------------ namespace { double cross_product_analog(const Point2& A, const Point2& B, const Point2& C) { return (A.x() - C.x()) * (B.y() - C.y()) - (A.y() - C.y()) * (B.x() - C.x()); } template PointLonLat compute_centroid(const PointContainer& points) { ATLAS_ASSERT(eckit::geometry::points_equal(points.front(), points.back())); PointLonLat centroid = {0, 0}; double signed_area = 0.; double a = 0.; // Partial signed area for (size_t i = 0; i < points.size() - 1; ++i) { const Point2& p0 = points[i]; const Point2& p1 = points[i + 1]; a = p0[0] * p1[1] - p1[0] * p0[1]; signed_area += a; centroid[0] += (p0[0] + p1[0]) * a; centroid[1] += (p0[1] + p1[1]) * a; } signed_area *= 0.5; centroid[0] /= (6. * signed_area); centroid[1] /= (6. * signed_area); return centroid; } template double compute_inner_radius_squared(const PointContainer& points, const PointLonLat& centroid) { auto distance2 = [](const Point2& a, const Point2& b) { double dx = (a[0] - b[0]); double dy = (a[1] - b[1]); return dx * dx + dy * dy; }; auto dot = [](const Point2& a, const Point2& b) { return a[0] * b[0] + a[1] * b[1]; }; double R2 = std::numeric_limits::max(); Point2 projection; for (size_t i = 0; i < points.size() - 1; ++i) { double d2 = distance2(points[i], points[i + 1]); double t = std::max(0., std::min(1., dot(centroid - points[i], points[i + 1] - points[i]) / d2)); projection[0] = points[i][0] + t * (points[i + 1][0] - points[i][0]); projection[1] = points[i][1] + t * (points[i + 1][1] - points[i][1]); R2 = std::min(R2, distance2(projection, centroid)); //Log::info() << "Segment " << points[i] << " - " << points[i+1] << " : projection = " << projection << " \t distance = " << std::sqrt(distance2(projection,centroid) ) << std::endl; } return R2; } } // namespace //------------------------------------------------------------------------------------------------------ PolygonXY::PolygonXY(const PartitionPolygon& partition_polygon): PolygonCoordinates(partition_polygon.xy(), true) { const auto& inscribed = partition_polygon.inscribedDomain(); if( inscribed.xmin() != inscribed.xmax() && inscribed.ymin() != inscribed.ymax() ) { inner_coordinatesMin_ = {inscribed.xmin(), inscribed.ymin()}; inner_coordinatesMax_ = {inscribed.xmax(), inscribed.ymax()}; } else { centroid_ = compute_centroid(coordinates_); inner_radius_squared_ = compute_inner_radius_squared(coordinates_, centroid_); ATLAS_ASSERT(PolygonXY::contains(centroid_)); } } bool PolygonXY::contains(const Point2& P) const { auto distance2 = [](const Point2& p, const Point2& centroid) { double dx = (p[0] - centroid[0]); double dy = (p[1] - centroid[1]); return dx * dx + dy * dy; }; constexpr double eps = 1.e-10; // check first bounding box if (coordinatesMax_.y() + eps < P.y() || P.y() < coordinatesMin_.y() - eps || coordinatesMax_.x() + eps < P.x() || P.x() < coordinatesMin_.x() - eps) { return false; } if (inner_radius_squared_ == 0) { // check inner bounding box if (inner_coordinatesMin_.x() <= P.x() && P.x() <= inner_coordinatesMax_.x() && inner_coordinatesMin_.y() <= P.y() && P.y() <= inner_coordinatesMax_.y()) { return true; } } else { // check inscribed circle if (distance2(P, centroid_) < inner_radius_squared_) { return true; } } // winding number int wn = 0; // loop on polygon edges for (size_t i = 1; i < coordinates_.size(); ++i) { const Point2& A = coordinates_[i - 1]; const Point2& B = coordinates_[i]; // check point-edge side and direction, using 2D-analog cross-product; // tests if P is left|on|right of a directed A-B infinite line, by // intersecting either: // - "up" on upward crossing & P left of edge, or // - "down" on downward crossing & P right of edge const bool APB = (A.y() <= P.y() && P.y() <= B.y()); const bool BPA = (B.y() <= P.y() && P.y() <= A.y()); if (APB != BPA) { const double side = cross_product_analog(P, A, B); bool on_edge = std::abs(side) < eps; if (on_edge) { return true; } if (APB && side > 0) { ++wn; } else if (BPA && side < 0) { --wn; } } } // wn == 0 only when P is outside return wn != 0; } //------------------------------------------------------------------------------------------------------ } // namespace util } // namespace atlas atlas-0.46.0/src/atlas/util/VectorOfAbstract.h0000664000175000017500000000567715160212070021336 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ /// @file Polygon.h /// @author Pedro Maciel /// @author Willem Deconinck /// @date September 2017 #pragma once #include #include #include "atlas/library/config.h" namespace atlas { namespace util { //------------------------------------------------------------------------------------------------------ template class DereferenceIterator : DOXYGEN_HIDE(public BaseIterator) { public: using value_type = typename BaseIterator::value_type::element_type; using pointer = value_type*; using reference = value_type&; DereferenceIterator(const BaseIterator& other): BaseIterator(other) {} reference operator*() const { return *(this->BaseIterator::operator*()); } pointer operator->() const { return this->BaseIterator::operator*().get(); } reference operator[](size_t n) const { return *(this->BaseIterator::operator[](n)); } }; //------------------------------------------------------------------------------------------------------ template DereferenceIterator make_dereference_iterator(Iterator t) { return DereferenceIterator(t); } //------------------------------------------------------------------------------------------------------ template class VectorOfAbstract { public: using value_type = Abstract; using container_type = std::vector >; using const_reference = const value_type&; using reference = const_reference; using const_iterator = DereferenceIterator; public: VectorOfAbstract() = default; VectorOfAbstract(VectorOfAbstract&& other): container_(std::move(other.container_)) {} const_iterator begin() const { return make_dereference_iterator(container_.begin()); } const_iterator end() const { return make_dereference_iterator(container_.end()); } const_reference operator[](idx_t i) const { return *container_[i]; } const_reference at(idx_t i) const { return *container_[i]; } idx_t size() const { return container_.size(); } void reserve(size_t size) { container_.reserve(size); } template void emplace_back(Args&&... args) { container_.emplace_back(std::forward(args)...); } container_type& get() { return container_; } void clear() { return container_.clear(); } private: container_type container_; }; //------------------------------------------------------------------------------------------------------ } // namespace util } // namespace atlas atlas-0.46.0/src/atlas/util/PolygonXY.h0000664000175000017500000000371015160212070020015 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include "atlas/util/Polygon.h" namespace atlas { namespace util { class PartitionPolygon; class PolygonXY; class ListPolygonXY; //------------------------------------------------------------------------------------------------------ /// @brief Implement PolygonCoordinates::contains for a polygon defined in XY space. class PolygonXY : public PolygonCoordinates { public: using Vector = ListPolygonXY; PolygonXY(const PartitionPolygon&); /// @brief Point-in-polygon test based on winding number /// @note reference Inclusion of a Point in a Polygon /// @param[in] P given point /// @return if point (x,y) is in polygon bool contains(const Point2& Pxy) const override; private: Point2 centroid_; double inner_radius_squared_{0}; Point2 inner_coordinatesMin_; Point2 inner_coordinatesMax_; }; //------------------------------------------------------------------------------------------------------ /// @brief Vector of all polygons with functionality to find partition using a KDTree class ListPolygonXY : public PolygonCoordinates::Vector { public: ListPolygonXY(const PartitionPolygons& partition_polygons) { reserve(partition_polygons.size()); for (auto& partition_polygon : partition_polygons) { emplace_back(new PolygonXY(partition_polygon)); } } }; //------------------------------------------------------------------------------------------------------ } // namespace util } // namespace atlas atlas-0.46.0/src/atlas/util/Earth.h0000664000175000017500000000241515160212070017151 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include "eckit/geometry/SphereT.h" #include "eckit/geometry/UnitSphere.h" //------------------------------------------------------------------------------------------------------ namespace atlas { namespace util { //------------------------------------------------------------------------------------------------------ struct DatumIFS { static constexpr double radius() { return 6371229.; } }; struct DatumGRIB1 { static constexpr double radius() { return 6367470.; } }; struct DatumWGS84SemiMajorAxis { static constexpr double radius() { return 6378137.; } }; //------------------------------------------------------------------------------------------------------ typedef eckit::geometry::SphereT Earth; //------------------------------------------------------------------------------------------------------ } // namespace util } // namespace atlas atlas-0.46.0/src/atlas/util/Bitflags.h0000664000175000017500000000765415160212070017653 0ustar alastairalastair/* * (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 namespace atlas { namespace util { //---------------------------------------------------------------------------------------------------------------------- /// @brief Convenience class to modify and interpret bitflags class Bitflags { public: static void reset(int& flags, int bit = 0) { flags = bit; } static void set(int& flags, int bit) { flags |= bit; } static void unset(int& flags, int bit) { flags &= (~bit); } static void toggle(int& flags, int bit) { flags ^= bit; } static bool check(int flags, int bits) { return (flags & bits) == bits; } static bool check_all(int flags, int bits) { return (flags & bits) == bits; } static bool check_any(int flags, int bits) { return flags & bits; } static std::string bitstr(int flags) { char str[9] = {0}; int i; for (i = 7; i >= 0; i--) { str[i] = (flags & 1) ? '1' : '0'; flags >>= 1; } return std::string(str, 9); } /// @brief Create convenience accessor to modify flags /// @note `auto` for return type! BitflagsView is a detail static auto view(int& flags); /// @brief Create convenience accessor to modify flags /// @note `auto` for return type! BitflagsView is a detail static auto view(const int& flags); }; //---------------------------------------------------------------------------------------------------------------------- namespace detail { //---------------------------------------------------------------------------------------------------------------------- template class BitflagsView {}; //---------------------------------------------------------------------------------------------------------------------- // Template specialication for constant flags. There are no functions to edit the flags template <> class BitflagsView { const int flags_; public: BitflagsView(const int flags): flags_(flags) {} bool check(int bit) const { return Bitflags::check(flags_, bit); } bool check_all(int bit) const { return Bitflags::check_all(flags_, bit); } bool check_any(int bit) const { return Bitflags::check_any(flags_, bit); } }; //---------------------------------------------------------------------------------------------------------------------- // Template specialication for nonconst flags. There are functions to edit the flags template <> class BitflagsView { int& flags_; public: BitflagsView(int& flags): flags_(flags) {} void reset(int bit = 0) { Bitflags::reset(flags_, bit); } void set(int bit) { Bitflags::set(flags_, bit); } void unset(int bit) { Bitflags::unset(flags_, bit); } void toggle(int bit) { Bitflags::toggle(flags_, bit); } bool check(int bit) const { return Bitflags::check(flags_, bit); } bool check_all(int bit) const { return Bitflags::check_all(flags_, bit); } bool check_any(int bit) const { return Bitflags::check_any(flags_, bit); } }; //---------------------------------------------------------------------------------------------------------------------- } // namespace detail //---------------------------------------------------------------------------------------------------------------------- inline auto Bitflags::view(const int& flags) { return detail::BitflagsView(flags); } inline auto Bitflags::view(int& flags) { return detail::BitflagsView(flags); } //---------------------------------------------------------------------------------------------------------------------- } // namespace util } // namespace atlas atlas-0.46.0/src/atlas/util/Object.cc0000664000175000017500000000064715160212070017457 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "atlas/util/Object.h" atlas-0.46.0/src/atlas/util/SphericalPolygon.h0000664000175000017500000000240515160212070021367 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include "atlas/util/Polygon.h" namespace atlas { namespace util { class PartitionPolygon; //------------------------------------------------------------------------------------------------------ class SphericalPolygon : public PolygonCoordinates { public: SphericalPolygon(const PartitionPolygon&); SphericalPolygon(const std::vector& points); /* * Point-in-polygon test based on winding number * @note reference Inclusion of a Point * in a Polygon * @param[in] P given point in (lon,lat) coordinates * @return if point is in polygon */ bool contains(const Point2& lonlat) const override; }; //------------------------------------------------------------------------------------------------------ } // namespace util } // namespace atlas atlas-0.46.0/src/atlas/util/GPUClonable.h0000664000175000017500000000257615160212070020211 0ustar alastairalastair/* * (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/library/config.h" #include "hic/hic.h" namespace atlas { namespace util { template struct GPUClonable { GPUClonable(Base* base_ptr): base_ptr_(base_ptr), gpu_object_ptr_(nullptr) { if constexpr (ATLAS_HAVE_GPU) { HIC_CALL(hicMalloc(&gpu_object_ptr_, sizeof(Base))); } } ~GPUClonable() { if (gpu_object_ptr_ != nullptr) { HIC_CALL(hicFree(gpu_object_ptr_)); } } Base* gpu_object_ptr() { return static_cast(gpu_object_ptr_); } void updateDevice() { if constexpr (ATLAS_HAVE_GPU) { HIC_CALL(hicMemcpy(gpu_object_ptr_, base_ptr_, sizeof(Base), hicMemcpyHostToDevice)); } } void updateHost() { if constexpr (ATLAS_HAVE_GPU) { HIC_CALL(hicMemcpy(base_ptr_, gpu_object_ptr_, sizeof(Base), hicMemcpyDeviceToHost)); } } Base* base_ptr_; void* gpu_object_ptr_; }; } // namespace util } // namespace atlas atlas-0.46.0/src/atlas/util/GaussianLatitudes.h0000664000175000017500000000367215160212070021545 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ /// @file GaussianLatitudes.h /// @author Willem Deconinck /// @date Jan 2014 #pragma once #include namespace atlas { namespace util { /** * @brief Compute gaussian latitudes between North pole and equator * @param N [in] Number of latitudes between pole and equator (Gaussian * N number) * @param latitudes [out] latitudes in degrees */ void gaussian_latitudes_npole_equator(const size_t N, double latitudes[]); /** * @brief Compute gaussian latitudes and quadrature weights between North pole * and equator * @param N [in] Number of latitudes between pole and equator (Gaussian * N number) * @param latitudes [out] latitudes in degrees * @param weights [out] quadrature weights */ void gaussian_quadrature_npole_equator(const size_t N, double latitudes[], double weights[]); /** * @brief Compute gaussian latitudes between North pole and South pole * @param N [in] Number of latitudes between pole and equator (Gaussian * N number) * @param latitudes [out] latitudes in degrees (size 2*N) */ void gaussian_latitudes_npole_spole(const size_t N, double latitudes[]); /** * @brief Compute gaussian latitudes and quadrature weights between North pole * and South pole * @param N [in] Number of latitudes between pole and equator (Gaussian * N number) * @param latitudes [out] latitudes in degrees (size 2*N) * @param weights [out] quadrature weights (size 2*N) */ void gaussian_quadrature_npole_spole(const size_t N, double latitudes[], double weights[]); } // namespace util } // namespace atlas atlas-0.46.0/src/atlas/util/Config.cc0000664000175000017500000002736615160212070017465 0ustar alastairalastair/* * (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 "eckit/filesystem/PathName.h" #include "eckit/log/JSON.h" #include "eckit/parser/YAMLParser.h" #include "atlas/grid/Grid.h" #include "atlas/mesh/Mesh.h" #include "atlas/runtime/Exception.h" #include "atlas/util/Config.h" using std::string; namespace atlas { namespace util { namespace { static eckit::Value yaml_from_stream(std::istream& stream) { eckit::YAMLParser parser(stream); return parser.parse(); } static eckit::Value yaml_from_path(const eckit::PathName& path) { if (!path.exists()) { throw_Exception("File " + std::string(path) + " does not exist."); } std::ifstream file(path.localPath()); if (!file.is_open()) { throw_Exception("Unable to open json file " + std::string(path), Here()); } eckit::Value value = yaml_from_stream(file); file.close(); return value; } } // namespace Config::Config(): eckit::LocalConfiguration() {} Config::Config(const eckit::Configuration& p): eckit::LocalConfiguration(p) {} Config::Config(std::istream& stream, const std::string&): eckit::LocalConfiguration(yaml_from_stream(stream)) {} Config::Config(const eckit::PathName& path): eckit::LocalConfiguration(yaml_from_path(path)) {} Config Config::operator|(const eckit::Configuration& other) const { Config config(*this); config.set(other); return config; } Config& Config::set(const eckit::Configuration& other) { #if ATLAS_ECKIT_VERSION_AT_LEAST(1, 26, 0) || ATLAS_ECKIT_DEVELOP eckit::LocalConfiguration::set(other); #else eckit::Value& root = const_cast(get()); auto& other_root = other.get(); std::vector other_keys; eckit::fromValue(other_keys, other_root.keys()); for (auto& key : other_keys) { root[key] = other_root[key]; } #endif return *this; } Config& Config::remove(const std::string& name) { #if ATLAS_ECKIT_VERSION_AT_LEAST(1, 26, 0) || ATLAS_ECKIT_DEVELOP LocalConfiguration::remove(name); #else eckit::Value& root = const_cast(get()); root.remove(name); #endif return *this; } Config& Config::set(const std::string& name, const std::vector& values) { std::vector metadatavalues(values.size()); for (size_t i = 0; i < metadatavalues.size(); ++i) { metadatavalues[i] = values[i]; } set(name, metadatavalues); return *this; } bool Config::get(const std::string& name, std::vector& value) const { bool found = has(name); if (found) { auto properties = getSubConfigurations(name); value.resize(properties.size()); for (size_t i = 0; i < value.size(); ++i) { value[i].set(properties[i]); } } return found; } #if ! (ATLAS_ECKIT_VERSION_AT_LEAST(1, 26, 0) || ATLAS_ECKIT_DEVELOP) std::vector Config::keys() const { std::vector result; eckit::fromValue(result, get().keys()); return result; } #endif std::string Config::json(eckit::JSON::Formatting formatting) const { std::stringstream json; eckit::JSON js(json,formatting); js << *this; return json.str(); } //================================================================== // ------------------------------------------------------------------ // C wrapper interfaces to C++ routines Config* atlas__Config__new() { return new Config(); } Config* atlas__Config__new_from_json(const char* json) { std::stringstream s; s << json; return new Config(s); } Config* atlas__Config__new_from_file(const char* path) { return new Config(eckit::PathName(path)); } void atlas__Config__delete(Config* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Config"); delete This; } void atlas__Config__set_config(Config* This, const char* name, const Config* value) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Config"); This->set(std::string(name), *value); } void atlas__Config__set_config_list(Config* This, const char* name, const Config* value[], int size) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Config"); std::vector params(size); for (int i = 0; i < size; ++i) { params[i] = Config(*value[i]); } This->set(std::string(name), params); } void atlas__Config__set_int(Config* This, const char* name, int value) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Config"); This->set(std::string(name), long(value)); } void atlas__Config__set_long(Config* This, const char* name, long value) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Config"); This->set(std::string(name), value); } void atlas__Config__set_float(Config* This, const char* name, float value) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Config"); This->set(std::string(name), double(value)); } void atlas__Config__set_double(Config* This, const char* name, double value) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Config"); This->set(std::string(name), value); } void atlas__Config__set_string(Config* This, const char* name, const char* value) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Config"); This->set(std::string(name), std::string(value)); } void atlas__Config__set_array_int(Config* This, const char* name, int value[], int size) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Config"); std::vector v; v.assign(value, value + size); This->set(std::string(name), v); } void atlas__Config__set_array_long(Config* This, const char* name, long value[], int size) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Config"); std::vector v; v.assign(value, value + size); This->set(std::string(name), v); } void atlas__Config__set_array_float(Config* This, const char* name, float value[], int size) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Config"); std::vector v; v.assign(value, value + size); This->set(std::string(name), v); } void atlas__Config__set_array_double(Config* This, const char* name, double value[], int size) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Config"); std::vector v; v.assign(value, value + size); This->set(std::string(name), v); } int atlas__Config__get_config(Config* This, const char* name, Config* value) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Config"); if (!This->get(std::string(name), *value)) { return false; } return true; } int atlas__Config__get_config_list(Config* This, const char* name, Config**& value, int& size, int& allocated) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Config"); value = nullptr; std::vector vector; if (!This->get(std::string(name), vector)) { return false; } size = vector.size(); value = new Config*[size]; allocated = true; for (int i = 0; i < size; ++i) { value[i] = new Config(vector[i]); } return true; } int atlas__Config__get_int(Config* This, const char* name, int& value) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Config"); long long_value = value; if (!This->get(std::string(name), long_value)) { return false; } ATLAS_ASSERT(int(long_value) == long_value); value = long_value; return true; } int atlas__Config__get_long(Config* This, const char* name, long& value) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Config"); if (!This->get(std::string(name), value)) { return false; } return true; } int atlas__Config__get_float(Config* This, const char* name, float& value) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Config"); double double_value; if (!This->get(std::string(name), double_value)) { return false; } value = static_cast(double_value); return true; } int atlas__Config__get_double(Config* This, const char* name, double& value) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Config"); if (!This->get(std::string(name), value)) { return false; } return true; } int atlas__Config__get_string(Config* This, const char* name, char*& value, int& size, int& allocated) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Config"); std::string s; if (!This->get(std::string(name), s)) { value = nullptr; return false; } size = static_cast(s.size()); value = new char[size + 1]; std::strncpy(value, s.c_str(), size + 1); allocated = true; return true; } int atlas__Config__get_array_int(Config* This, const char* name, int*& value, int& size, int& allocated) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Config"); std::vector v; if (!This->get(std::string(name), v)) { return false; } size = v.size(); value = new int[size]; for (size_t j = 0; j < v.size(); ++j) { ATLAS_ASSERT(int(v[j]) == v[j]); value[j] = v[j]; } allocated = true; return true; } int atlas__Config__get_array_long(Config* This, const char* name, long*& value, int& size, int& allocated) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Config"); std::vector v; if (!This->get(std::string(name), v)) { return false; } size = v.size(); value = new long[size]; for (size_t j = 0; j < v.size(); ++j) { value[j] = v[j]; } allocated = true; return true; } int atlas__Config__get_array_float(Config* This, const char* name, float*& value, int& size, int& allocated) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Config"); std::vector v; if (!This->get(std::string(name), v)) { return false; } size = v.size(); value = new float[size]; for (size_t j = 0; j < v.size(); ++j) { value[j] = static_cast(v[j]); } allocated = true; return true; } int atlas__Config__get_array_double(Config* This, const char* name, double*& value, int& size, int& allocated) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Config"); std::vector v; if (!This->get(std::string(name), v)) { return false; } size = v.size(); value = new double[size]; for (size_t j = 0; j < v.size(); ++j) { value[j] = v[j]; } allocated = true; return true; } int atlas__Config__has(Config* This, const char* name) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Config"); return This->has(std::string(name)); } void atlas__Config__json(Config* This, char*& json, int& size, int& allocated) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Config"); std::stringstream s; eckit::JSON j(s); j.precision(16); j << *This; std::string json_str = s.str(); size = static_cast(json_str.size()); json = new char[size + 1]; allocated = true; std::strncpy(json, json_str.c_str(), size + 1); allocated = true; } // ------------------------------------------------------------------ } // namespace util } // namespace atlas atlas-0.46.0/src/atlas/util/Metadata.cc0000664000175000017500000002367315160212070017775 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "atlas/util/Metadata.h" #include #include #include #include "eckit/log/JSON.h" #include "eckit/parser/JSONParser.h" #include "eckit/utils/Hash.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/runtime/Exception.h" using std::string; namespace atlas { namespace util { void Metadata::throw_not_found(const std::string& name) const { std::stringstream msg; msg << "Could not find metadata \"" << name << "\""; throw_Exception(msg.str(), Here()); } size_t Metadata::footprint() const { // TODO size_t size = sizeof(*this); return size; } void Metadata::broadcast() { idx_t root = 0; get("owner", root); broadcast(*this, root); } void Metadata::broadcast(idx_t root) { broadcast(*this, root); } void Metadata::broadcast(Metadata& dest) { idx_t root = 0; get("owner", root); broadcast(dest, root); } void Metadata::broadcast(Metadata& dest, idx_t root) { std::string buffer; int buffer_size{0}; if (mpi::rank() == root) { std::stringstream s; eckit::JSON json(s); json.precision(17); json << *this; buffer = s.str(); buffer_size = static_cast(buffer.size()); } ATLAS_TRACE_MPI(BROADCAST) { atlas::mpi::comm().broadcast(buffer_size, root); } if (mpi::rank() != root) { buffer.resize(buffer_size); } ATLAS_TRACE_MPI(BROADCAST) { mpi::comm().broadcast(buffer.begin(), buffer.end(), root); } if (not(&dest == this && mpi::rank() == root)) { std::stringstream s; s << buffer; eckit::JSONParser parser(s); dest = Metadata(parser.parse()); } } void Metadata::broadcast(Metadata& dest) const { idx_t root = 0; get("owner", root); broadcast(dest, root); } void Metadata::broadcast(Metadata& dest, idx_t root) const { std::string buffer; int buffer_size{0}; if (mpi::rank() == root) { std::stringstream s; eckit::JSON json(s); json.precision(17); json << *this; buffer = s.str(); buffer_size = static_cast(buffer.size()); } ATLAS_TRACE_MPI(BROADCAST) { mpi::comm().broadcast(buffer_size, root); } if (mpi::rank() != root) { buffer.resize(buffer_size); } ATLAS_TRACE_MPI(BROADCAST) { mpi::comm().broadcast(buffer.begin(), buffer.end(), root); } // Fill in dest { std::stringstream s; s << buffer; eckit::JSONParser parser(s); dest = Metadata(parser.parse()); } } Metadata& Metadata::set(const eckit::Configuration& other) { #if ATLAS_ECKIT_VERSION_AT_LEAST(1, 26, 0) || ATLAS_ECKIT_DEVELOP LocalConfiguration::set(other); #else eckit::Value& root = const_cast(get()); auto& other_root = other.get(); std::vector other_keys; eckit::fromValue(other_keys, other_root.keys()); for (auto& key : other_keys) { root[key] = other_root[key]; } #endif return *this; } Metadata::Metadata(const eckit::Value& value): eckit::LocalConfiguration(value) {} // ------------------------------------------------------------------ // C wrapper interfaces to C++ routines Metadata* atlas__Metadata__new() { return new Metadata(); } void atlas__Metadata__delete(Metadata* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Metadata"); delete This; } void atlas__Metadata__set_int(Metadata* This, const char* name, int value) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Metadata"); This->set(std::string(name), long(value)); } void atlas__Metadata__set_long(Metadata* This, const char* name, long value) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Metadata"); This->set(std::string(name), value); } void atlas__Metadata__set_float(Metadata* This, const char* name, float value) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Metadata"); This->set(std::string(name), double(value)); } void atlas__Metadata__set_double(Metadata* This, const char* name, double value) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Metadata"); This->set(std::string(name), value); } void atlas__Metadata__set_string(Metadata* This, const char* name, const char* value) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Metadata"); This->set(std::string(name), std::string(value)); } void atlas__Metadata__set_array_int(Metadata* This, const char* name, int value[], int size) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Metadata"); std::vector v; v.assign(value, value + size); This->set(std::string(name), v); } void atlas__Metadata__set_array_long(Metadata* This, const char* name, long value[], int size) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Metadata"); std::vector v; v.assign(value, value + size); This->set(std::string(name), v); } void atlas__Metadata__set_array_float(Metadata* This, const char* name, float value[], int size) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Metadata"); std::vector v; v.assign(value, value + size); This->set(std::string(name), v); } void atlas__Metadata__set_array_double(Metadata* This, const char* name, double value[], int size) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Metadata"); std::vector v; v.assign(value, value + size); This->set(std::string(name), v); } int atlas__Metadata__get_int(Metadata* This, const char* name) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Metadata"); return This->get(std::string(name)); } long atlas__Metadata__get_long(Metadata* This, const char* name) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Metadata"); return This->get(std::string(name)); } float atlas__Metadata__get_float(Metadata* This, const char* name) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Metadata"); return This->get(std::string(name)); } double atlas__Metadata__get_double(Metadata* This, const char* name) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Metadata"); return This->get(std::string(name)); } void atlas__Metadata__get_string(Metadata* This, const char* name, char* output_str, int max_len) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Metadata"); std::string s = This->get(std::string(name)); if (s.size() + 1 > size_t(max_len)) { std::stringstream msg; msg << "Cannot copy string `" << s << "` of metadata `" << name << "`" "in buffer of length " << max_len; throw_Exception(msg.str(), Here()); } std::strncpy(output_str, s.c_str(), max_len); } void atlas__Metadata__get_array_int(Metadata* This, const char* name, int*& value, int& size, int& allocated) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Metadata"); std::vector v = This->get>(std::string(name)); size = static_cast(v.size()); value = new int[size]; for (size_t j = 0; j < v.size(); ++j) { value[j] = v[j]; } allocated = true; } void atlas__Metadata__get_array_long(Metadata* This, const char* name, long*& value, int& size, int& allocated) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Metadata"); std::vector v = This->get>(std::string(name)); size = static_cast(v.size()); value = new long[size]; for (size_t j = 0; j < v.size(); ++j) { value[j] = v[j]; } allocated = true; } void atlas__Metadata__get_array_float(Metadata* This, const char* name, float*& value, int& size, int& allocated) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Metadata"); std::vector v = This->get>(std::string(name)); size = static_cast(v.size()); value = new float[size]; for (size_t j = 0; j < v.size(); ++j) { value[j] = v[j]; } allocated = true; } void atlas__Metadata__get_array_double(Metadata* This, const char* name, double*& value, int& size, int& allocated) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Metadata"); std::vector v = This->get>(std::string(name)); size = static_cast(v.size()); value = new double[size]; for (size_t j = 0; j < v.size(); ++j) { value[j] = v[j]; } allocated = true; } int atlas__Metadata__has(Metadata* This, const char* name) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Metadata"); return This->has(std::string(name)); } void atlas__Metadata__print(Metadata* This, std::ostream* channel) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Metadata"); ATLAS_ASSERT(channel != nullptr); *channel << *This; } void atlas__Metadata__json(Metadata* This, char*& json, int& size, int& allocated) { std::stringstream s; eckit::JSON j(s); j.precision(16); j << *This; std::string json_str = s.str(); size = static_cast(json_str.size()); json = new char[size + 1]; allocated = true; std::strncpy(json, json_str.c_str(), size + 1); allocated = true; } // ------------------------------------------------------------------ } // namespace util } // namespace atlas atlas-0.46.0/src/atlas/util/Point.h0000664000175000017500000001206415160212070017200 0ustar alastairalastair/* * (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 /// @file Point.h /// /// This file contains classes and functions working on points. /// The Point classes are inherited from eckit::geometry::Point2 /// or eckit::geometry::Point3. #include #include #include "eckit/geometry/Point2.h" #include "eckit/geometry/Point3.h" #include "atlas/util/Earth.h" namespace atlas { using Point2 = eckit::geometry::Point2; using Point3 = eckit::geometry::Point3; inline bool operator==(const Point2& p1, const Point2& p2) { return eckit::geometry::points_equal(p1, p2); } inline bool operator!=(const Point2& p1, const Point2& p2) { return !eckit::geometry::points_equal(p1, p2); } inline bool operator==(const Point3& p1, const Point3& p2) { return eckit::geometry::points_equal(p1, p2); } inline bool operator!=(const Point3& p1, const Point3& p2) { return !eckit::geometry::points_equal(p1, p2); } /// @brief Point in arbitrary XY-coordinate system class PointXY : public eckit::geometry::Point2 { using array_t = std::array; public: using Point2::Point2; PointXY(): Point2() {} // Allow initialization through PointXY xy = {0,0}; PointXY(std::initializer_list list): PointXY(list.begin()) {} PointXY(const array_t& arr): PointXY(arr.data()) {} double x() const { return x_[0]; } double y() const { return x_[1]; } double& x() { return x_[0]; } double& y() { return x_[1]; } using Point2::assign; void assign(double x, double y) { x_[0] = x; x_[1] = y; } }; /// @brief Point in arbitrary XYZ-coordinate system class PointXYZ : public eckit::geometry::Point3 { using array_t = std::array; PointXYZ(double, double) { /* No automatic converion allowed, otherwise inherited from Point3 */ } public: using Point3::Point3; using Point3::x; PointXYZ(): Point3() {} // Allow initialization through PointXYZ xyz = {0,0,0}; PointXYZ(std::initializer_list list): PointXYZ(list.begin()) {} PointXYZ(const array_t& arr): PointXYZ(arr.data()) {} double x() const { return x_[0]; } double y() const { return x_[1]; } double z() const { return x_[2]; } double& x() { return x_[0]; } double& y() { return x_[1]; } double& z() { return x_[2]; } using Point3::assign; void assign(double x, double y, double z) { x_[0] = x; x_[1] = y; x_[2] = z; } PointXYZ& operator*=(double m) { x_[0] *= m; x_[1] *= m; x_[2] *= m; return *this; } PointXYZ& operator/=(double m) { x_[0] /= m; x_[1] /= m; x_[2] /= m; return *this; } }; /// @brief Point in longitude-latitude coordinate system. /// This class does *not* normalise the longitude by default, /// but contains a normalise function. class PointLonLat : public eckit::geometry::Point2 { using array_t = std::array; public: using Point2::Point2; PointLonLat(): Point2() {} // Allow initialization through PointXY lonlat = {0,0}; PointLonLat(std::initializer_list list): PointLonLat(list.begin()) {} PointLonLat(const array_t& arr): PointLonLat(arr.data()) {} double lon() const { return x_[0]; } double lat() const { return x_[1]; } double& lon() { return x_[0]; } double& lat() { return x_[1]; } using Point2::assign; void assign(double lon, double lat) { x_[0] = lon; x_[1] = lat; } PointLonLat& operator*=(double a) { x_[0] *= a; x_[1] *= a; return *this; } void normalise(); void normalise(double west); void normalise(double west, double east); }; } // namespace atlas #ifndef DOXYGEN_SHOULD_SKIP_THIS namespace eckit { // When including "eckit/types/Types.h" we get an overload for // std::ostream& operator<<( std::ostream&, std::vector ) // The default VectorPrintSelector is however the [VectorPrintContracted], // which does not compile when [T={PointXY,PointLonLat,PointXYZ}] // Following changes the default for these types to [VectorPrintSimple] class VectorPrintSimple; template struct VectorPrintSelector; template <> struct VectorPrintSelector { typedef VectorPrintSimple selector; }; template <> struct VectorPrintSelector { typedef VectorPrintSimple selector; }; template <> struct VectorPrintSelector { typedef VectorPrintSimple selector; }; template <> struct VectorPrintSelector { typedef VectorPrintSimple selector; }; template <> struct VectorPrintSelector { typedef VectorPrintSimple selector; }; } // namespace eckit #endif atlas-0.46.0/src/atlas/util/Allocate.h0000664000175000017500000000506415160212070017635 0ustar alastairalastair/* * (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 namespace atlas { namespace util { //------------------------------------------------------------------------------ namespace detail { void allocate_managed(void** ptr, size_t bytes); void deallocate_managed(void* ptr, size_t bytes); void allocate_device(void** ptr, size_t bytes); void deallocate_device(void* ptr, size_t bytes); void allocate_host(void** ptr, size_t bytes); void deallocate_host(void* ptr, size_t bytes); } // namespace detail template void allocate_managedmem(T*& data, size_t N) { if (N != 0) { detail::allocate_managed(reinterpret_cast(&data), N * sizeof(T)); } } template void delete_managedmem(T*& data, size_t N) { if (data) { detail::deallocate_managed(data, N * sizeof(T)); data = nullptr; } } template void allocate_devicemem(T*& data, size_t N) { if (N != 0) { detail::allocate_device(reinterpret_cast(&data), N * sizeof(T)); } } template void delete_devicemem(T*& data, size_t N) { if (data) { detail::deallocate_device(data, N * sizeof(T)); data = nullptr; } } template void allocate_hostmem(T*& data, size_t N) { if (N != 0) { detail::allocate_host(reinterpret_cast(&data), N * sizeof(T)); } } template void delete_hostmem(T*& data, size_t N) { if (data) { detail::deallocate_host(data, N * sizeof(T)); data = nullptr; } } //------------------------------------------------------------------------------ extern "C" { void atlas__allocate_managedmem_double(double*& a, size_t N); void atlas__allocate_managedmem_float(float*& a, size_t N); void atlas__allocate_managedmem_int(int*& a, size_t N); void atlas__allocate_managedmem_long(long*& a, size_t N); void atlas__deallocate_managedmem_double(double*& a, size_t N); void atlas__deallocate_managedmem_float(float*& a, size_t N); void atlas__deallocate_managedmem_int(int*& a, size_t N); void atlas__deallocate_managedmem_long(long*& a, size_t N); } //------------------------------------------------------------------------------ } // namespace util } // namespace atlas atlas-0.46.0/src/atlas/util/PeriodicTransform.h0000664000175000017500000000370215160212070021540 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include "atlas/util/LonLatMicroDeg.h" namespace atlas { namespace util { class PeriodicTransform { protected: double x_translation_; public: PeriodicTransform() { x_translation_ = 360.; } PeriodicTransform(const double& translation) { x_translation_ = translation; } void operator()(double source[2], double dest[2], double direction, double scale = 1.) const { dest[0] = source[0] + direction * x_translation_ * scale; dest[1] = source[1]; } void operator()(int source[2], int dest[2], int direction, int scale = 1) const { dest[0] = source[0] + direction * static_cast(x_translation_ * scale); dest[1] = source[1]; } void operator()(double inplace[2], double direction, double scale = 1.) const { inplace[0] = inplace[0] + direction * x_translation_ * scale; // inplace[1] = inplace[1]; null operation } void operator()(int inplace[2], int direction, int scale = 1) const { inplace[0] = inplace[0] + direction * static_cast(x_translation_ * scale); // inplace[1] = inplace[1]; null operation } void operator()(util::LonLatMicroDeg& inplace, int direction) const { inplace.set_lon(inplace.lon() + direction * util::microdeg(x_translation_)); // inplace.set_lat( inplace.lat() ); null operation } void operator()(std::array& inplace) const { inplace[0] = inplace[0] + x_translation_; // inplace[1] = inplace[1]; null operation } }; } // namespace util } // namespace atlas atlas-0.46.0/src/atlas/util/Checksum.cc0000664000175000017500000000400315160212070020001 0ustar alastairalastair/* * (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 "atlas/util/Checksum.h" namespace atlas { namespace util { namespace { // anonymous // Inefficient implementation of Fletcher's checksum algorithm static uint16_t fletcher16(const uint8_t* data, int size) { uint16_t s1(0), s2(0); for (int i = 0; i < size; ++i) { s1 = (s1 + data[i]) % 255; s2 = (s2 + s1) % 255; } s2 <<= 8; s2 |= s1; return s2; } } // namespace static checksum_t checksum(const char* data, size_t size) { // return fletcher64( reinterpret_cast(data), // size/sizeof(uint32_t) ); // return fletcher32( reinterpret_cast(data), // size/sizeof(uint16_t) ); return fletcher16(reinterpret_cast(data), size / sizeof(uint8_t)); } checksum_t checksum(const int values[], size_t size) { return checksum(reinterpret_cast(&values[0]), size * sizeof(int) / sizeof(char)); } checksum_t checksum(const long values[], size_t size) { return checksum(reinterpret_cast(&values[0]), size * sizeof(long) / sizeof(char)); } checksum_t checksum(const float values[], size_t size) { return checksum(reinterpret_cast(&values[0]), size * sizeof(float) / sizeof(char)); } checksum_t checksum(const double values[], size_t size) { return checksum(reinterpret_cast(&values[0]), size * sizeof(double) / sizeof(char)); } checksum_t checksum(const checksum_t values[], size_t size) { return checksum(reinterpret_cast(&values[0]), size * sizeof(checksum_t) / sizeof(char)); } } // namespace util } // namespace atlas atlas-0.46.0/src/atlas/util/PackVectorFields.h0000664000175000017500000000257115160212070021301 0ustar alastairalastair/* * (C) Crown Copyright 2024 Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ #pragma once #include "atlas/field.h" namespace eckit { class LocalConfiguration; } namespace atlas { namespace util { /// @brief Packs vector field components into vector fields /// /// @details Iterates through @param fields and creates vector fields from any /// component field with the "vector name" string metadata. These, as /// well as any present scalar fields, are added to the return-value /// field set. /// Note, a mutable @param packedFields field set can be supplied if /// one needs to guarantee the order of the packed fields FieldSet pack_vector_fields(const FieldSet& fields, FieldSet packedFields = FieldSet{}); /// @brief Unpacks vector field into vector field components. /// /// @details Undoes "pack" operation when a set of packed fields are supplied /// as @param fields. A mutable @param unpackedFields field set can be /// supplied if one needs to guarantee the order of the unpacked /// fields. FieldSet unpack_vector_fields(const FieldSet& fields, FieldSet unpackedFields = FieldSet{}); } // namespace util } // namespace atlas atlas-0.46.0/src/atlas/util/detail/0000775000175000017500000000000015160212070017175 5ustar alastairalastairatlas-0.46.0/src/atlas/util/detail/KDTree.h0000664000175000017500000003765415160212070020503 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include #include "eckit/container/KDTree.h" #include "atlas/library/config.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Log.h" #include "atlas/util/Geometry.h" #include "atlas/util/Object.h" #include "atlas/util/Point.h" namespace atlas { namespace util { namespace detail { //------------------------------------------------------------------------------------------------------ // Abstract KDTree, intended to erase the internal KDTree type from eckit (Mapped or Memory) // For usage, see atlas::util::KDTree template class KDTreeBase : public Object { #define ENABLE_IF_3D_AND_IS_LONLAT(POINT) \ typename = typename std::enable_if < PointT::DIMS == 3 && POINT::DIMS == 2 > ::type private: Geometry geometry_; public: using Payload = PayloadT; using Point = PointT; using PayloadList = std::vector; struct KDTreeTraits { using Point = PointT; using Payload = PayloadT; }; struct Value { using Point = typename KDTreeTraits::Point; using Payload = typename KDTreeTraits::Payload; template Value(const Node& node): Value(node.point(), node.payload(), node.distance()) {} Value(const Point& point, const Payload& payload): point_(point), payload_(payload) {} Value(const Point& point, const Payload& payload, double distance): point_(point), payload_(payload), distance_(distance) {} const Point& point() const { return point_; } const Payload& payload() const { return payload_; } const double& distance() const { return distance_; } private: Point point_; Payload payload_; double distance_; }; class ValueList : public std::vector { public: PayloadList payloads() const { PayloadList list; list.reserve(this->size()); for (auto& item : *this) { list.emplace_back(item.payload()); } return list; } template ValueList(const NodeList& _list) { this->reserve(_list.size()); for (const auto& item : _list) { this->emplace_back(item); } } void print(std::ostream& out) const { out << "["; for (size_t i = 0; i < this->size(); ++i) { const auto& value = this->at(i); out << "{payload:" << value.payload() << ",distance:" << value.distance() << ",point:" << value.point() << "}"; if (i < this->size() - 1) { out << ","; } } out << "]"; } friend std::ostream& operator<<(std::ostream& out, ValueList& This) { This.print(out); return out; } }; public: KDTreeBase() = default; KDTreeBase(const Geometry& geometry): geometry_(geometry) {} virtual ~KDTreeBase() = default; const Geometry& geometry() const { return geometry_; } bool empty() const { return size() == 0; } virtual idx_t size() const = 0; virtual size_t footprint() const = 0; /// @brief Reserve memory for building the kdtree in one shot (optional, at cost of extra memory) /// Implementation depends in derived classes virtual void reserve(idx_t /*size*/) {} /// @brief Insert spherical point (lon,lat) or 3D cartesian point (x,y,z) /// If memory has been reserved with reserve(), insertion will be delayed until build() is called. template void insert(const Point& p, const Payload& payload) { do_insert(p, payload); } /// @brief Insert Value /// If memory has been reserved with reserve(), insertion will be delayed until build() is called. virtual void insert(const Value&) = 0; /// @brief Build the kd-tree in one shot, if memory has been reserved, depending on derived class implementation /// This will need to be called before all search functions like closestPoints(). virtual void build() {} /// @brief Build the kd-tree in one shot virtual void build(std::vector& values) = 0; /// @brief Build with spherical points (lon,lat) where longitudes, latitudes, and payloads are separate containers. /// Memory will be reserved with reserve() to match the size template void build(const Longitudes& longitudes, const Latitudes& latitudes, const Payloads& payloads) { build(longitudes.begin(), longitudes.end(), latitudes.begin(), latitudes.end(), payloads.begin(), payloads.end()); } /// @brief Build with spherical points (lon,lat) given separate iterator ranges for longitudes, latitudes, and payloads. /// Memory will be reserved with reserve() to match the size template void build(const LongitudesIterator& longitudes_begin, const LongitudesIterator& longitudes_end, const LatitudesIterator& latitudes_begin, const LatitudesIterator& latitudes_end, const PayloadsIterator& payloads_begin, const PayloadsIterator& payloads_end) { reserve(std::distance(payloads_begin, payloads_end)); auto lon = longitudes_begin; auto lat = latitudes_begin; auto payload = payloads_begin; for (; lon != longitudes_end && lat != latitudes_end && payload != payloads_end; lon++, lat++, payload++) { insert(Point2{*lon, *lat}, *payload); } ATLAS_ASSERT(lon == longitudes_end); ATLAS_ASSERT(lat == latitudes_end); ATLAS_ASSERT(payload == payloads_end); build(); } /// @brief Build with spherical points (lon,lat) where longitudes, latitudes, and payloads are separate containers. /// Memory will be reserved with reserve() to match the size template void build(const Points& points, const Payloads& payloads) { build(points.begin(), points.end(), payloads.begin(), payloads.end()); } /// @brief Build with spherical points (lon,lat) given separate iterator ranges for longitudes, latitudes, and payloads. /// Memory will be reserved with reserve() to match the size template void build(const PointIterator& points_begin, const PointIterator& points_end, const PayloadsIterator& payloads_begin, const PayloadsIterator& payloads_end) { reserve(std::distance(points_begin, points_end)); PointIterator point = points_begin; PayloadsIterator payload = payloads_begin; for (; point != points_end && payload != payloads_end; ++point, ++payload) { insert(*point, *payload); } ATLAS_ASSERT(point == points_end); ATLAS_ASSERT(payload == payloads_end); build(); } /// @brief Find k nearest neighbours given a 3D cartesian point (x,y,z) or 2D lonlat point(lon,lat) template ValueList closestPoints(const Point& p, size_t k) const { return do_closestPoints(p, k); } /// @brief Find nearest neighbour given a 3D cartesian point (x,y,z) template Value closestPoint(const Point& p) const { return do_closestPoint(p); } /// @brief Find all points within a distance of given radius from a given point (x,y,z) template ValueList closestPointsWithinRadius(const Point& p, double radius) const { return do_closestPointsWithinRadius(p, radius); } private: /// @brief Insert spherical point (lon,lat) /// If memory has been reserved with reserve(), insertion will be delayed until build() is called. void do_insert(const Point& p, const Payload& payload) { insert(Value{p, payload}); } /// @brief Insert spherical point (lon,lat) /// If memory has been reserved with reserve(), insertion will be delayed until build() is called. template void do_insert(const LonLat& p, const Payload& payload) { do_insert(make_Point(p), payload); } /// @brief Find k nearest neighbours given a 3D cartesian point (x,y,z) virtual ValueList do_closestPoints(const Point&, size_t k) const = 0; /// @brief Find nearest neighbour given a 3D cartesian point (x,y,z) virtual Value do_closestPoint(const Point&) const = 0; /// @brief Find all points within a distance of given radius from a given point (x,y,z) virtual ValueList do_closestPointsWithinRadius(const Point&, double radius) const = 0; /// @brief Find k nearest neighbour given a 2D lonlat point (lon,lat) template ValueList do_closestPoints(const LonLat& p, size_t k) const { return do_closestPoints(make_Point(p), k); } /// @brief Find nearest neighbour given a 2D lonlat point (lon,lat) template Value do_closestPoint(const LonLat& p) const { return do_closestPoint(make_Point(p)); } /// @brief Find all points within a distance of given radius from a given point (lon,lat) template ValueList do_closestPointsWithinRadius(const LonLat& p, double radius) const { return do_closestPointsWithinRadius(make_Point(p), radius); } template Point make_Point(const LonLat& lonlat) const { static_assert(std::is_base_of::value, "LonLat must be derived from Point2"); static_assert(std::is_base_of::value, "Point must be derived from Point3"); Point xyz; geometry().lonlat2xyz(lonlat, xyz); return xyz; } #undef ENABLE_IF_3D_AND_IS_LONLAT }; //------------------------------------------------------------------------------------------------------ // Concrete implementation template class KDTree_eckit : public KDTreeBase { using Tree = TreeT; using Base = KDTreeBase; public: using Point = typename Base::Point; using Payload = typename Base::Payload; using PayloadList = typename Base::PayloadList; using Value = typename Base::Value; using ValueList = typename Base::ValueList; using Base::build; using Base::closestPoint; using Base::closestPoints; using Base::closestPointsWithinRadius; using Base::insert; using Base::reserve; public: template KDTree_eckit(const Geometry& geometry, Args... args): KDTreeBase(geometry), tree_(new Tree(args...)) { static_asserts(); } template KDTree_eckit(Args... args): tree_(new Tree(args...)) { static_asserts(); } KDTree_eckit(const std::shared_ptr& tree): tree_(tree) { static_asserts(); } KDTree_eckit(const std::shared_ptr& tree, const Geometry& geometry): KDTreeBase(geometry), tree_(tree) { static_asserts(); } idx_t size() const override { #if ATLAS_ECKIT_VERSION_AT_LEAST(1, 13, 2) return static_cast(tree_->size()); #else // Assume ECKIT-515 not implemented. // Very bad implementation, with a workaround for empty tree idx_t size{0}; try { for (const auto& item : *tree_) { size++; } } catch (const eckit::AssertionFailed&) { } return size; #endif } size_t footprint() const override { return size() * sizeof(typename Tree::Node); } void reserve(idx_t size) override; void build() override; void build(std::vector&) override; /// @brief Insert 3D cartesian point (x,y,z) /// If memory has been reserved with reserve(), insertion will be delayed until build() is called. void insert(const Value& value) override; /// @brief Find k nearest neighbours given a 3D cartesian point (x,y,z) ValueList do_closestPoints(const Point&, size_t k) const override; /// @brief Find nearest neighbour given a 3D cartesian point (x,y,z) Value do_closestPoint(const Point&) const override; /// @brief Find all points within a distance of given radius from a given point (x,y,z) ValueList do_closestPointsWithinRadius(const Point&, double radius) const override; const Tree& tree() const { return *tree_; } private: void assert_built() const; static void static_asserts() { static_assert(std::is_convertible::value, "Tree::Payload must be convertible this Payload type"); static_assert(std::is_convertible::value, "Tree::Point must be convertible to this Point type"); } private: std::vector tmp_; mutable std::shared_ptr tree_; // mutable because its member functions are non-const... }; //------------------------------------------------------------------------------------------------------ template using KDTreeMemory = KDTree_eckit::KDTreeTraits>>; //------------------------------------------------------------------------------------------------------ template void KDTree_eckit::reserve(idx_t size) { tmp_.reserve(size); } template void KDTree_eckit::insert(const Value& value) { if (tmp_.capacity()) { tmp_.emplace_back(value); } else { tree_->insert(value); } } template void KDTree_eckit::build() { if (tmp_.size()) { build(tmp_); tmp_.clear(); tmp_.shrink_to_fit(); } } template void KDTree_eckit::build(std::vector& values) { tree_->build(values); } template typename KDTree_eckit::ValueList KDTree_eckit::do_closestPoints( const Point& p, size_t k) const { assert_built(); return tree_->kNearestNeighbours(p, k); } template typename KDTree_eckit::Value KDTree_eckit::do_closestPoint( const Point& p) const { assert_built(); return tree_->nearestNeighbour(p); } template typename KDTree_eckit::ValueList KDTree_eckit::do_closestPointsWithinRadius(const Point& p, double radius) const { assert_built(); return tree_->findInSphere(p, radius); } template void KDTree_eckit::assert_built() const { if (tmp_.capacity()) { throw_AssertionFailed("KDTree was used before calling build()"); } } //------------------------------------------------------------------------------------------------------ } // namespace detail } // namespace util } // namespace atlas atlas-0.46.0/src/atlas/util/detail/filesystem.cc0000664000175000017500000000467715160212070021706 0ustar alastairalastair#include "filesystem.h" // POSIX dependencies #include #include #include #include #include "atlas/runtime/Exception.h" namespace atlas::filesystem { static void create_directory(const char path[]) { if (::mkdir(path, 0777) != 0) { ATLAS_THROW_EXCEPTION("Could not create directory: " << path); } } void create_directory(const std::string& path) { return create_directory(path.c_str()); } static bool exists(const char path[]) { struct stat info; return stat( path, &info ) == 0; } bool exists(const std::string& path) { return exists(path.c_str()); } static void remove_all(const char path[]) { size_t path_len; char *full_path; DIR *dir; struct stat stat_path, stat_entry; struct dirent *entry; // stat for the path ::stat(path, &stat_path); // if path does not exists or is not dir, throw if (S_ISDIR(stat_path.st_mode) == 0) { ATLAS_THROW_EXCEPTION("Is not a directory: " << path); } // if not possible to read the directory for this user if ((dir = opendir(path)) == nullptr) { ATLAS_THROW_EXCEPTION("Cannot open directory: " << path); } // the length of the path path_len = ::strlen(path); // iteration through entries in the directory while ((entry = readdir(dir)) != nullptr) { // skip entries "." and ".." if (!::strcmp(entry->d_name, ".") || !::strcmp(entry->d_name, "..")) { continue; } // determinate a full path of an entry full_path = (char*) ::calloc(path_len + 1 + ::strlen(entry->d_name) + 1, sizeof(char)); ::strcpy(full_path, path); ::strcat(full_path, "/"); ::strcat(full_path, entry->d_name); // stat for the entry ::stat(full_path, &stat_entry); // recursively remove a nested directory if (S_ISDIR(stat_entry.st_mode) != 0) { remove_all(full_path); ::free(full_path); continue; } // remove a file object if (::unlink(full_path) != 0) { ATLAS_THROW_EXCEPTION("Can't remove a file: " << full_path); } ::free(full_path); } // remove the devastated directory and close the object of it if (::rmdir(path) != 0) { ATLAS_THROW_EXCEPTION("Can't remove a directory: " << path); } ::closedir(dir); } void remove_all(const std::string& path) { remove_all(path.c_str()); } } atlas-0.46.0/src/atlas/util/detail/Cache.h0000664000175000017500000000654415160212070020362 0ustar alastairalastair/* * (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/runtime/Log.h" #include "atlas/util/ObjectHandle.h" namespace atlas { namespace util { template class Cache { public: using key_type = Key; using value_type = Value; using creator_type = std::function; Cache(const std::string& name): name_(name) {} virtual ~Cache() {} ObjectHandle get_or_create(const key_type& key, const creator_type& creator) { return get_or_create(key, key, creator); } ObjectHandle get_or_create(const key_type& key, const key_type& remove_key, const creator_type& creator) { std::lock_guard guard(lock_); auto it = map_.find(key); if (it != map_.end()) { ObjectHandle value = it->second; if (value) { Log::debug() << "Key \"" << key << "\" was found in cache \"" << name_ << "\"" << std::endl; return value; } else { Log::debug() << "Key \"" << key << "\" was found in cache \"" << name_ << "\" but is not valid. Revert to not found." << std::endl; } } Log::debug() << "Key \"" << key << "\" not found in cache \"" << name_ << "\" , creating new, removable with key \"" << remove_key << "\"" << std::endl; ObjectHandle value(creator()); map_[key] = value; remove_[remove_key].emplace_back(key); return value; } void remove(const key_type& remove_key) { std::lock_guard guard(lock_); if (remove_.find(remove_key) != remove_.end()) { for (auto& key : remove_[remove_key]) { bool erased = map_.erase(key); if (erased) { if (remove_key == key) { Log::debug() << "Erased key \"" << key << "\" from cache \"" << name_ << "\"." << std::endl; } else { Log::debug() << "Erased key \"" << key << "\" via remove_key \"" << remove_key << "\" from cache \"" << name_ << "\"." << std::endl; } } else { Log::debug() << "Tried to erase key \"" << key << "\" from cache \"" << name_ << "\" but it was not found." << std::endl; } } } else { Log::debug() << "Tried to erase key \"" << remove_key << "\" from cache \"" << name_ << "\" but it was not found." << std::endl; } } private: std::string name_; std::mutex lock_; std::map> map_; std::map> remove_; }; } // namespace util } // namespace atlas atlas-0.46.0/src/atlas/util/detail/filesystem.h0000664000175000017500000000034615160212070021535 0ustar alastairalastair#pragma once #include namespace atlas::filesystem { // See c++ std::filesystem for API void create_directory(const std::string& path); bool exists(const std::string& path); void remove_all(const std::string& path); } atlas-0.46.0/src/atlas/util/Geometry.cc0000664000175000017500000001267615160212070020051 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "atlas/util/Geometry.h" #include #include "atlas/library/config.h" #include "atlas/runtime/Exception.h" #include "atlas/util/Constants.h" namespace atlas { namespace geometry { namespace detail { void GeometrySphere::lonlat2xyz(const Point2& lonlat, Point3& xyz) const { #if ATLAS_ECKIT_VERSION_AT_LEAST(1, 24, 0) Sphere::convertSphericalToCartesian(radius_, lonlat, xyz, 0., true); #else Sphere::convertSphericalToCartesian(radius_, lonlat, xyz); #endif } void GeometrySphere::xyz2lonlat(const Point3& xyz, Point2& lonlat) const { Sphere::convertCartesianToSpherical(radius_, xyz, lonlat); } /// @brief Calculate great-cricle course between points /// /// @details Calculates the direction (clockwise from north) of a great-circle /// arc between lonLat1 and lonLat2. Returns the direction of the arc /// at lonLat1 (first) and lonLat2 (second). Angle is normalised to the /// range of atan2 (usually (-180, 180]). All input and output values /// are in units of degrees. /// @ref https://en.wikipedia.org/wiki/Great-circle_navigation /// std::pair greatCircleCourse(const Point2& lonLat1, const Point2& lonLat2) { if (lonLat1 == lonLat2) { return std::make_pair(0., 0.); } const auto lambda1 = lonLat1[0] * util::Constants::degreesToRadians(); const auto lambda2 = lonLat2[0] * util::Constants::degreesToRadians(); const auto phi1 = lonLat1[1] * util::Constants::degreesToRadians(); const auto phi2 = lonLat2[1] * util::Constants::degreesToRadians(); const auto sinLambda12 = std::sin(lambda2 - lambda1); const auto cosLambda12 = std::cos(lambda2 - lambda1); const auto sinPhi1 = std::sin(phi1); const auto sinPhi2 = std::sin(phi2); const auto cosPhi1 = std::cos(phi1); const auto cosPhi2 = std::cos(phi2); const auto alpha1 = std::atan2(cosPhi2 * sinLambda12, cosPhi1 * sinPhi2 - sinPhi1 * cosPhi2 * cosLambda12); const auto alpha2 = std::atan2(cosPhi1 * sinLambda12, -cosPhi2 * sinPhi1 + sinPhi2 * cosPhi1 * cosLambda12); return std::make_pair(alpha1 * util::Constants::radiansToDegrees(), alpha2 * util::Constants::radiansToDegrees()); }; } // namespace detail } // namespace geometry extern "C" { // ------------------------------------------------------------------ // C wrapper interfaces to C++ routines Geometry::Implementation* atlas__Geometry__new_name(const char* name) { Geometry::Implementation* geometry; { Geometry handle{std::string{name}}; geometry = handle.get(); geometry->attach(); } geometry->detach(); return geometry; } geometry::detail::GeometryBase* atlas__Geometry__new_radius( const double radius) { Geometry::Implementation* geometry; { Geometry handle{radius}; geometry = handle.get(); geometry->attach(); } geometry->detach(); return geometry; } void atlas__Geometry__delete(Geometry::Implementation* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Geometry"); delete This; } void atlas__Geometry__xyz2lonlat(Geometry::Implementation* This, const double x, const double y, const double z, double& lon, double& lat) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Geometry"); PointLonLat lonlat; This->xyz2lonlat(PointXYZ{x, y, z}, lonlat); lon = lonlat.lon(); lat = lonlat.lat(); } void atlas__Geometry__lonlat2xyz(Geometry::Implementation* This, const double lon, const double lat, double& x, double& y, double& z) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Geometry"); PointXYZ xyz; This->lonlat2xyz(PointLonLat{lon, lat}, xyz); x = xyz.x(); y = xyz.y(); z = xyz.z(); } double atlas__Geometry__distance_lonlat(Geometry::Implementation* This, const double lon1, const double lat1, const double lon2, const double lat2) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Geometry"); return This->distance(PointLonLat{lon1, lat1}, PointLonLat{lon2, lat2}); } double atlas__Geometry__distance_xyz(Geometry::Implementation* This, const double x1, const double y1, const double z1, const double x2, const double y2, const double z2) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Geometry"); return This->distance(PointXYZ{x1, y1, z1}, PointXYZ{x2, y2, z2}); } double atlas__Geometry__radius(Geometry::Implementation* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Geometry"); return This->radius(); } double atlas__Geometry__area(Geometry::Implementation* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Geometry"); return This->area(); } } // ------------------------------------------------------------------ } // namespace atlas atlas-0.46.0/src/atlas/util/Rotation.cc0000664000175000017500000001454715160212070020054 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "atlas/util/Rotation.h" #include #include #include "eckit/config/Parametrisation.h" #include "eckit/types/FloatCompare.h" #include "atlas/util/Constants.h" #include "atlas/util/CoordinateEnums.h" #include "atlas/util/UnitSphere.h" namespace atlas { namespace util { namespace { PointLonLat wrap_latitude(const PointLonLat& p) { double lon = p.lon(); double lat = p.lat(); while (lat > 90.) { lon += 180.; lat = 180. - lat; } while (lat < -90.) { lon -= 180.; lat = -180. - lat; } return {lon, lat}; } double wrap_angle(double a) { PointLonLat angle{a, 0}; angle.normalise(); return angle.lon(); } } // namespace void Rotation::print(std::ostream& out) const { out << "north_pole:" << npole_ << ", south_pole:" << spole_ << ", rotation_angle:" << angle_; } std::ostream& operator<<(std::ostream& out, const Rotation& r) { r.print(out); return out; } PointLonLat Rotation::rotate(const PointLonLat& p) const { PointLonLat rotated(p); rotate(rotated.data()); return rotated; } PointLonLat Rotation::unrotate(const PointLonLat& p) const { PointLonLat unrotated(p); unrotate(unrotated.data()); return unrotated; } void Rotation::precompute() { const double theta = -(90. + spole_.lat()) * Constants::degreesToRadians(); const double phi = -spole_.lon() * Constants::degreesToRadians(); const double sin_theta = std::sin(theta); const double cos_theta = std::cos(theta); const double sin_phi = std::sin(phi); const double cos_phi = std::cos(phi); auto eq = [](double a, double b) { return eckit::types::is_approximately_equal(a, b, 1.e-12); }; rotated_ = !eq(angle_, 0) || !eq(sin_theta, 0) || !eq(cos_theta, 1) || !eq(sin_phi, 0) || !eq(cos_phi, 1); rotation_angle_only_ = eq(sin_theta, 0) && eq(cos_theta, 1) && rotated_; if (!rotated_) { rotate_ = unrotate_ = RotationMatrix{{{1., 0., 0.}, {0., 1., 0.}, {0., 0., 1.}}}; return; } // Pt = Rot(z) * Rot(y) * P, rotate about y axes then z // Since we're undoing the rotation described in the definition // of the coordinate system, // we first rotate by ϑ = -(90 + spole_.lat()) around the y axis // (along the rotated Greenwich meridian) // and then by φ = -spole_.lon() degrees around the z axis): // (xt) ( cos(φ), sin(φ), 0) ( cos(ϑ), 0, sin(ϑ)) (x) // (yt) = (-sin(φ), cos(φ), 0).( 0 , 1, 0 ).(y) // (zt) ( 0 , 0 , 1) ( -sin(ϑ), 0, cos(ϑ)) (z) // Expanded // xt = cos(ϑ) cos(φ) x + sin(φ) y + sin(ϑ) cos(φ) z // yt = -cos(ϑ) sin(φ) x + cos(φ) y - sin(ϑ) sin(φ) z // zt = -sin(ϑ) x + cos(ϑ) z rotate_ = RotationMatrix{{{cos_theta * cos_phi, sin_phi, sin_theta * cos_phi}, {-cos_theta * sin_phi, cos_phi, -sin_theta * sin_phi}, {-sin_theta, 0., cos_theta}}}; // Assume right hand rule, rotate about z axes and then y // P = Rot(y) * Rot(z) * Pt // x ( cos(ϑ), 0, -sin(ϑ)) ( cos(φ), -sin(φ), 0) (xt) // y = ( 0 , 1, 0 ) ( sin(φ), cos(φ), 0) (yt) // z ( sin(ϑ), 0, cos(ϑ)) ( 0 , 0 , 1) (zt) // Expanded // x ( cos(ϑ)cos(φ) , -cos(ϑ)sin(φ) , -sin(ϑ)) (xt) // y = ( sin(φ) , cos(φ) , 0 ).(yt) // z ( sin(ϑ) cos(φ), -sin(ϑ) sin(φ), cos(ϑ)) (zt) unrotate_ = RotationMatrix{{{cos_theta * cos_phi, -cos_theta * sin_phi, -sin_theta}, {sin_phi, cos_phi, 0.}, {sin_theta * cos_phi, -sin_theta * sin_phi, cos_theta}}}; } Rotation::Rotation(const PointLonLat& south_pole, double rotation_angle) { spole_ = south_pole; npole_ = PointLonLat(spole_.lon() - 180., -spole_.lat()); if (npole_.lon() < 0.) { npole_.lon() += 360.; } angle_ = wrap_angle(rotation_angle); precompute(); } Rotation::Rotation(const eckit::Parametrisation& p) { // get rotation angle p.get("rotation_angle", angle_); angle_ = wrap_angle(angle_); // get pole std::vector pole(2); if (p.get("north_pole", pole)) { npole_ = PointLonLat(pole.data()); spole_ = PointLonLat(npole_.lon() - 180., -npole_.lat()); if (spole_.lon() < 0.) { spole_.lon() += 360.; } } else if (p.get("south_pole", pole)) { spole_ = PointLonLat(pole.data()); npole_ = PointLonLat(spole_.lon() - 180., -spole_.lat()); if (npole_.lon() < 0.) { npole_.lon() += 360.; } } precompute(); } using RotationMatrix = std::array, 3>; inline PointXYZ rotate_geocentric(const PointXYZ& p, const RotationMatrix& R) { return PointXYZ(R[XX][XX] * p.x() + R[XX][YY] * p.y() + R[XX][ZZ] * p.z(), R[YY][XX] * p.x() + R[YY][YY] * p.y() + R[YY][ZZ] * p.z(), R[ZZ][XX] * p.x() + R[ZZ][YY] * p.y() + R[ZZ][ZZ] * p.z()); } void Rotation::rotate(double crd[]) const { if (!rotated_) { return; } crd[LON] -= angle_; if (!rotation_angle_only_) { const PointLonLat L(wrap_latitude({crd[LON], crd[LAT]})); PointXYZ P; UnitSphere::convertSphericalToCartesian(L, P); const PointXYZ Pt = rotate_geocentric(P, rotate_); PointLonLat Lt; UnitSphere::convertCartesianToSpherical(Pt, Lt); crd[LON] = Lt.lon(); crd[LAT] = Lt.lat(); } } void Rotation::unrotate(double crd[]) const { if (!rotated_) { return; } if (!rotation_angle_only_) { const PointLonLat Lt(crd); PointXYZ Pt; UnitSphere::convertSphericalToCartesian(Lt, Pt); const PointXYZ P = rotate_geocentric(Pt, unrotate_); PointLonLat L; UnitSphere::convertCartesianToSpherical(P, L); crd[LON] = L.lon(); crd[LAT] = L.lat(); } crd[LON] += angle_; } } // namespace util } // namespace atlas atlas-0.46.0/src/atlas/util/GaussianLatitudes.cc0000664000175000017500000000246215160212070021677 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ /// @file GaussianLatitudes.cc /// @author Willem Deconinck /// @date Jan 2014 #include "atlas/util/GaussianLatitudes.h" #include "atlas/grid/detail/spacing/gaussian/Latitudes.h" namespace atlas { namespace util { void gaussian_latitudes_npole_equator(const size_t N, double latitudes[]) { grid::spacing::gaussian::gaussian_latitudes_npole_equator(N, latitudes); } void gaussian_quadrature_npole_equator(const size_t N, double latitudes[], double weights[]) { grid::spacing::gaussian::gaussian_quadrature_npole_equator(N, latitudes, weights); } void gaussian_latitudes_npole_spole(const size_t N, double latitudes[]) { grid::spacing::gaussian::gaussian_latitudes_npole_spole(N, latitudes); } void gaussian_quadrature_npole_spole(const size_t N, double latitudes[], double weights[]) { grid::spacing::gaussian::gaussian_quadrature_npole_spole(N, latitudes, weights); } } // namespace util } // namespace atlas atlas-0.46.0/src/atlas/util/DataType.cc0000664000175000017500000000214615160212070017760 0ustar alastairalastair/* * (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 "DataType.h" #include #include "atlas/runtime/Exception.h" //------------------------------------------------------------------------------------------------------ namespace atlas { namespace array { void DataType::throw_not_recognised(kind_t kind) { std::stringstream msg; msg << "kind " << kind << " not recognised."; throw_Exception(msg.str(), Here()); } void DataType::throw_not_recognised(std::string datatype) { std::stringstream msg; msg << "datatype " << datatype << " not recognised."; throw_Exception(msg.str(), Here()); } //------------------------------------------------------------------------------------------------------ } // namespace array } // namespace atlas atlas-0.46.0/src/atlas/util/Unique.cc0000664000175000017500000000304715160212070017514 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "atlas/util/Unique.h" #include "atlas/util/PeriodicTransform.h" namespace atlas { namespace util { uidx_t unique_lonlat(const double& lon, const double& lat, const PeriodicTransform& transform) { std::array lonlat{lon, lat}; transform(lonlat); return unique_lonlat(lonlat); } uidx_t UniqueLonLat::operator()(const mesh::Connectivity::Row& elem_nodes, const PeriodicTransform& transform) const { std::array centroid; centroid[LON] = 0.; centroid[LAT] = 0.; size_t npts = elem_nodes.size(); for (size_t jnode = 0; jnode < npts; ++jnode) { centroid[LON] += lonlat(elem_nodes(jnode), LON); centroid[LAT] += lonlat(elem_nodes(jnode), LAT); } centroid[LON] /= static_cast(npts); centroid[LAT] /= static_cast(npts); transform(centroid); return unique_lonlat(centroid); } uidx_t atlas::util::UniqueLonLat::operator()(int node, const PeriodicTransform& transform) const { std::array _lonlat{lonlat(node, LON), lonlat(node, LAT)}; transform(_lonlat); return unique_lonlat(_lonlat[LON], _lonlat[LAT]); } } // namespace util } // namespace atlas atlas-0.46.0/src/atlas/util/Allocate.cc0000664000175000017500000000572515160212070017777 0ustar alastairalastair/* * (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 "Allocate.h" #include "eckit/log/CodeLocation.h" #include "pluto/pluto.h" #include "atlas/library/config.h" #include "atlas/runtime/Exception.h" namespace atlas { namespace util { //------------------------------------------------------------------------------ namespace detail { //------------------------------------------------------------------------------ void allocate_managed(void** ptr, size_t bytes) { if constexpr (not ATLAS_HAVE_GPU) { return allocate_host(ptr, bytes); } *ptr = pluto::managed_resource()->allocate(bytes, pluto::default_alignment()); } void deallocate_managed(void* ptr, size_t bytes) { if constexpr (not ATLAS_HAVE_GPU) { return deallocate_host(ptr, bytes); } pluto::wait(); pluto::managed_resource()->deallocate(ptr, bytes, pluto::default_alignment()); } void allocate_device(void** ptr, size_t bytes) { if constexpr (not ATLAS_HAVE_GPU) { return allocate_host(ptr, bytes); } *ptr = pluto::device_resource()->allocate(bytes, pluto::default_alignment()); } void deallocate_device(void* ptr, size_t bytes) { if constexpr (not ATLAS_HAVE_GPU) { return deallocate_host(ptr, bytes); } pluto::wait(); pluto::device_resource()->deallocate(ptr, bytes, pluto::default_alignment()); } void allocate_host(void** ptr, size_t bytes) { *ptr = pluto::host_resource()->allocate(bytes, pluto::default_alignment()); } void deallocate_host(void* ptr, size_t bytes) { pluto::host_resource()->deallocate(ptr, bytes, pluto::default_alignment()); } //------------------------------------------------------------------------------ } // namespace detail //------------------------------------------------------------------------------ extern "C" { void atlas__allocate_managedmem_double(double*& a, size_t N) { allocate_managedmem(a, N); } void atlas__allocate_managedmem_float(float*& a, size_t N) { allocate_managedmem(a, N); } void atlas__allocate_managedmem_int(int*& a, size_t N) { allocate_managedmem(a, N); } void atlas__allocate_managedmem_long(long*& a, size_t N) { allocate_managedmem(a, N); } void atlas__deallocate_managedmem_double(double*& a, size_t N) { delete_managedmem(a, N); } void atlas__deallocate_managedmem_float(float*& a, size_t N) { delete_managedmem(a, N); } void atlas__deallocate_managedmem_int(int*& a, size_t N) { delete_managedmem(a, N); } void atlas__deallocate_managedmem_long(long*& a, size_t N) { delete_managedmem(a, N); } } //------------------------------------------------------------------------------ } // namespace util } // namespace atlas atlas-0.46.0/src/atlas/util/GridPointsJSONWriter.cc0000664000175000017500000002710715160212070022222 0ustar alastairalastair/* * (C) Copyright 2023 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 "GridPointsJSONWriter.h" #include #include #include #include #include "eckit/utils/Tokenizer.h" namespace atlas { namespace util { //------------------------------------------------------------------------------ namespace { std::vector points_from_list(const std::string& list, long base) { std::vector points; if (list.empty()) { return points; } if (list[0] == '[' && list[list.size()-1] == ']') { return points_from_list(std::string(list.begin()+1,list.begin()+list.size()-1), base); } std::vector points_ranges; eckit::Tokenizer{","}(list,points_ranges); auto tokenize_range = eckit::Tokenizer{"-"}; auto to_int = [](const std::string& s, const eckit::CodeLocation& here) -> long { try { return std::stol(s); } catch( std::exception& e) { throw_Exception("Could not convert '" + s + "' to integer",here); } }; for (const auto& point_range: points_ranges) { std::vector point_range_split; tokenize_range(point_range,point_range_split); if (point_range_split.size() == 1) { points.emplace_back(to_int(point_range,Here())-base); } else { for (long p = to_int(point_range_split[0],Here()); p <= to_int(point_range_split[1],Here()); ++p) { points.emplace_back(p-base); } } } return points; } } //------------------------------------------------------------------------------ GridPointsJSONWriter::GridPointsJSONWriter(Grid grid, const eckit::Parametrisation& args) : grid_{grid} { args.get("json.precision",precision_=-1); args.get("verbose",verbose_=0); if (not args.get("partitions",nb_partitions_=0)) { args.get("partitioner.partitions",nb_partitions_); } if (not args.get("partition",partition_=-1)) { args.get("partition",partition_); } args.get("json.pretty", pretty_=false); args.get("field",field_="lonlat"); args.get("field_base",field_base_=0); std::string points_list; if (args.get("index",points_list)) { args.get("index_base",points_base_ = 0); points_ = points_from_list(points_list,points_base_); } if( nb_partitions_ > 0 ) { auto partitioner_config = grid_.partitioner(); // recommended by the grid itself std::string partitioner; if (args.get("partitioner.type",partitioner)) { partitioner_config.set("type", partitioner); } partitioner_config.set("partitions", nb_partitions_); distribution_ = grid::Distribution{grid_,partitioner_config}; } } //------------------------------------------------------------------------------ void GridPointsJSONWriter::write(std::ostream& out, eckit::Channel& info) const { write(out, &info); } //------------------------------------------------------------------------------ void GridPointsJSONWriter::write(std::ostream& out, std::ostream* info) const { if (field_ == "none") { return; } int points_newline = pretty_ ? true : false; int points_indent = pretty_ ? 2 : 0; int partition_indent = 0; size_t chunk_size = 1000000; auto saved_fmtflags = out.flags(); if (precision_ >= 0) { out << std::fixed << std::setprecision(precision_); } auto writePoint = [&out](const Point2& p) { out << "[" << p[0] << "," << p[1] << "]"; }; if(nb_partitions_ == 0 || field_ == "partition") { out << "["; if (pretty_) { out << '\n'; } std::string join = points_newline ? ",\n" : ","; std::string indent(points_indent,' '); size_t n{0}; if (points_.size()) { if (field_ == "lonlat") { auto lonlat = grid_.lonlat(); for (auto p: points_) { if (p < grid_.size()) { if (n!=0) { out << join; } out << indent; auto it = lonlat.begin()+(p); writePoint(*it); ++n; } } } else if (field_ == "index") { for (auto p: points_) { if (p < grid_.size()) { if (n!=0) { out << join; } out << indent; out << p + field_base_; ++n; } } } else if (field_ == "partition") { for (auto p: points_) { if (p < grid_.size()) { if (n!=0) { out << join; } out << indent; out << distribution_.partition(p) + field_base_; ++n; } } } else { ATLAS_THROW_EXCEPTION("Cannot output field \""< chunk_size) { *info << std::fixed << std::setprecision(1) << double(begin)/double(grid_.size()) * 100 << "% completed" << std::endl; } }; auto write_progress_end = [&]() { if (info && verbose_ && grid_.size() > chunk_size) { *info << "100% completed" << std::endl; } }; while(end != grid_.size()) { begin = end; end = std::min(grid_.size(),begin+chunk_size); write_progress(); if (field_ == "lonlat") { auto it = grid_.lonlat().begin()+begin; for (size_t n=begin; n part(std::min(chunk_size,grid_.size())); auto write_chunk = [&](int partition, size_t begin, size_t end, size_t& n) { size_t size{end - begin}; if (partition != -1) { distribution_.partition(begin, end, part); auto part_begin = part.begin(); auto part_end = part.begin()+size; if (std::find(part_begin,part_end,partition) == part_end) { return; // no points in this chunk to write } } else { part.assign(part.size(),-1); } std::string join = points_newline ? ",\n" : ","; std::string indent(points_indent,' '); if( field_ == "lonlat" ) { auto it = grid_.lonlat().begin() + begin; for (size_t j=0; j chunk_size) { *info << std::fixed << std::setprecision(1) << double(begin)/double(grid_.size()) * 100 << "% completed" << std::endl; } end = std::min(grid_.size(),begin+chunk_size); write_chunk(partition,begin,end,n); } if (verbose_) { if (info && grid_.size() > chunk_size) { *info << "100% completed" << std::endl; } } if (pretty_) { out << '\n'; } out << std::string(partition_indent,' ') << "]" << std::flush; return n; }; if (partition_ >= 0) { auto n = write_partition(partition_); out << std::endl; if (info) { *info << "Partition " << partition_ << " contains " << n << " points." << std::endl; } } else { if (pretty_) { partition_indent += 2; points_indent += 2; } out << "[\n"; std::vector points_per_partition(nb_partitions_); for( int p = 0; p < nb_partitions_; ++p ) { if (p != 0) { out << ",\n"; } points_per_partition[p] = write_partition(p); } out << "\n]"; out << std::endl; if (info) { *info << "Points per partition:" << std::endl; for (size_t p=0; p #include "atlas/util/Constants.h" #include "atlas/util/function/SolidBodyRotation.h" namespace atlas { namespace util { namespace function { SolidBodyRotation::SolidBodyRotation(const double beta, const double radius) { sin_beta_ = std::sin(beta * Constants::degreesToRadians()); cos_beta_ = std::cos(beta * Constants::degreesToRadians()); radius_ = radius; } void SolidBodyRotation::wind(const double lon, const double lat, double& u, double& v) const { double x = lon * Constants::degreesToRadians(); double y = lat * Constants::degreesToRadians(); double cos_x = std::cos(x); double cos_y = std::cos(y); double sin_x = std::sin(x); double sin_y = std::sin(y); u = cos_y * cos_beta_ + cos_x * sin_y * sin_beta_; v = -sin_x * sin_beta_; } double SolidBodyRotation::u(const double lon, const double lat) const { double x = lon * Constants::degreesToRadians(); double y = lat * Constants::degreesToRadians(); double cos_x = std::cos(x); double cos_y = std::cos(y); double sin_y = std::sin(y); return cos_y * cos_beta_ + cos_x * sin_y * sin_beta_; } double SolidBodyRotation::v(const double lon, const double lat) const { double x = lon * Constants::degreesToRadians(); double sin_x = std::sin(x); return -sin_x * sin_beta_; } void SolidBodyRotation::vordiv(const double lon, const double lat, double& vor, double& div) const { double x = lon * Constants::degreesToRadians(); double y = lat * Constants::degreesToRadians(); double cos_x = std::cos(x); double cos_y = std::cos(y); double sin_x = std::sin(x); double sin_y = std::sin(y); // Divergence = 1./(R*cos(y)) * ( d/dx( u ) + d/dy( v * cos(y) ) ) // Vorticity = 1./(R*cos(y)) * ( d/dx( v ) - d/dy( u * cos(y) ) ) double ddx_u = -sin_x * sin_y * sin_beta_; double ddy_cosy_v = (-sin_x * sin_beta_) * (-sin_y); double ddx_v = -cos_x * sin_beta_; double ddy_cosy_u = 2 * cos_y * (-sin_y) * cos_beta_ + (-sin_y) * cos_x * sin_y * sin_beta_ + cos_y * cos_x * cos_y * sin_beta_; double metric = 1. / (radius_ * cos_y); div = metric * (ddx_u + ddy_cosy_v); vor = metric * (ddx_v - ddy_cosy_u); } double SolidBodyRotation::vorticity(const double lon, const double lat) const { double x = lon * Constants::degreesToRadians(); double y = lat * Constants::degreesToRadians(); double cos_x = std::cos(x); double cos_y = std::cos(y); double sin_y = std::sin(y); // Vorticity = 1./(R*cos(y)) * ( d/dx( v ) - d/dy( u * cos(y) ) ) double ddx_v = -cos_x * sin_beta_; double ddy_cosy_u = 2 * cos_y * (-sin_y) * cos_beta_ + (-sin_y) * cos_x * sin_y * sin_beta_ + cos_y * cos_x * cos_y * sin_beta_; double metric = 1. / (radius_ * cos_y); return metric * (ddx_v - ddy_cosy_u); } double SolidBodyRotation::divergence(const double lon, const double lat) const { double x = lon * Constants::degreesToRadians(); double y = lat * Constants::degreesToRadians(); double cos_y = std::cos(y); double sin_x = std::sin(x); double sin_y = std::sin(y); // Divergence = 1./(R*cos(y)) * ( d/dx( u ) + d/dy( v * cos(y) ) ) double ddx_u = -sin_x * sin_y * sin_beta_; double ddy_cosy_v = (-sin_x * sin_beta_) * (-sin_y); double metric = 1. / (radius_ * cos_y); return metric * (ddx_u + ddy_cosy_v); } double SolidBodyRotation::windMagnitude(const double lon, const double lat) const { return std::sqrt(windMagnitudeSquared(lon, lat)); } double SolidBodyRotation::windMagnitudeSquared(const double lon, const double lat) const { double u, v; wind(lon, lat, u, v); return u * u + v * v; } void SolidBodyRotation::windMagnitudeSquaredGradient(const double lon, const double lat, double& dfdx, double& dfdy) const { double x = lon * Constants::degreesToRadians(); double y = lat * Constants::degreesToRadians(); double cos_x = std::cos(x); double cos_y = std::cos(y); double sin_x = std::sin(x); double sin_y = std::sin(y); double metric_y = 1. / radius_; double metric_x = metric_y / cos_y; double u = cos_y * cos_beta_ + cos_x * sin_y * sin_beta_; double v = -sin_x * sin_beta_; double dudx = metric_x * (-sin_x * sin_y * sin_beta_); double dudy = metric_y * (-sin_y * cos_beta_ + cos_x * cos_y * sin_beta_); double dvdx = metric_x * (-cos_x * sin_beta_); double dvdy = metric_y * (0.); dfdx = 2 * u * dudx + 2 * v * dvdx; dfdy = 2 * u * dudy + 2 * v * dvdy; } } // namespace function } // namespace util } // namespace atlas atlas-0.46.0/src/atlas/util/function/VortexRollup.h0000664000175000017500000000241215160212070022415 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation nor * does it submit to any jurisdiction. */ #pragma once namespace atlas { namespace util { namespace function { /// \brief An analytic function that provides a vortex rollup on a 2D sphere /// /// \detailed The formula is found in /// "A Lagrangian Particle Method with Remeshing for Tracer Transport on the Sphere" /// by Peter Bosler, James Kent, Robert Krasny, Christiane Jablonowski, JCP 2015 /// as the tracer density in Test Case 1. /// The longitude (lon) and latitude (lat) are assumed to be in degrees, /// The time parameter associated with the vortex rollup is set by (t). /// /// The time it takes for the counter-rotating vortices along /// the equator to return to its original position takes /// time t = 1.0; /// double vortex_rollup(double lon, double lat, double t); } // namespace function } // namespace util } // namespace atlas atlas-0.46.0/src/atlas/util/function/MDPI_functions.cc0000664000175000017500000001100615160212070022706 0ustar alastairalastair/* * (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/util/Constants.h" #include "atlas/util/Earth.h" #include "atlas/util/function/MDPI_functions.h" namespace atlas { namespace util { namespace function { double MDPI_sinusoid(double lon, double lat) { lon *= Constants::degreesToRadians(); lat *= Constants::degreesToRadians(); constexpr double length = 1.2 * M_PI; return 2. - std::cos(M_PI * std::acos(std::cos(lon) * std::cos(lat)) / length); } double MDPI_harmonic(double lon, double lat) { lon *= Constants::degreesToRadians(); lat *= Constants::degreesToRadians(); return 2. + std::pow(std::sin(2. * lat), 16) * std::cos(16. * lon); } double MDPI_vortex(double lon, double lat) { lon *= Constants::degreesToRadians(); lat *= Constants::degreesToRadians(); constexpr double dLon0 = 5.5; constexpr double dLat0 = 0.2; constexpr double dR0 = 3.0; constexpr double dD = 5.0; constexpr double dT = 6.0; auto sqr = [](const double x) { return x * x; }; auto sech = [](const double x) { return 1. / std::cosh(x); }; double dSinC = std::sin(dLat0); double dCosC = std::cos(dLat0); double dCosT = std::cos(lat); double dSinT = std::sin(lat); double dTrm = dCosT * std::cos(lon - dLon0); double dX = dSinC * dTrm - dCosC * dSinT; double dY = dCosT * std::sin(lon - dLon0); double dZ = dSinC * dSinT + dCosC * dTrm; double dlon = std::atan2(dY, dX); double dlat = std::asin(dZ); double dRho = dR0 * std::cos(dlat); double dVt = 1.5 * std::sqrt(3.) * sqr(sech(dRho)) * std::tanh(dRho); double dOmega; if (dRho == 0.) { dOmega = 0.; } else { dOmega = dVt / dRho; } return 2. * (1. + std::tanh(dRho / dD * std::sin(dlon - dOmega * dT))); } double MDPI_gulfstream(double lon, double lat) { constexpr double d2r = Constants::degreesToRadians(); auto sqr = [](const double x) { return x * x; }; constexpr double gf_coef = 1.0; // Coefficient for a Gult Stream term (0.0 = no Gulf Stream) constexpr double gf_ori_lon = -80.0 * d2r; // Origin of the Gulf Stream (longitude in deg) constexpr double gf_ori_lat = 25.0 * d2r; // Origin of the Gulf Stream (latitude in deg) constexpr double gf_end_lon = -1.8 * d2r; // End of the Gulf Stream (longitude in deg) constexpr double gf_end_lat = 50.0 * d2r; // End of the Gulf Stream (latitude in deg) constexpr double gf_dmp_lon = -25.5 * d2r; // Point of the Gulf Stream decrease (longitude in deg) constexpr double gf_dmp_lat = -55.5 * d2r; // Point of the Gulf Stream decrease (latitude in deg) double dr0 = std::sqrt(sqr(gf_end_lon - gf_ori_lon) + sqr(gf_end_lat - gf_ori_lat)); double dr1 = std::sqrt(sqr(gf_dmp_lon - gf_ori_lon) + sqr(gf_dmp_lat - gf_ori_lat)); double gf_per_lon = [lon]() { double gf_per_lon = lon; while (gf_per_lon > 180.) { gf_per_lon -= 360.; } while (gf_per_lon < -180.) { gf_per_lon += 360.; } return gf_per_lon * d2r; }(); double dx = gf_per_lon - gf_ori_lon; double dy = lat * d2r - gf_ori_lat; double dr = std::sqrt(sqr(dx) + sqr(dy)); double dth = std::atan2(dy, dx); double dc = 1.3 * gf_coef; if (dr > dr0) { dc = 0.; } if (dr > dr1) { dc *= std::cos(M_PI_2 * (dr - dr1)/(dr0 - dr1)); } double background_func = MDPI_sinusoid(lon, lat); return background_func + dc * (std::max(1000. * std::sin(0.4 * (0.5 * dr + dth) + 0.007 * std::cos(50. * dth) + 0.37 * M_PI), 999.) - 999.); } extern "C" { double atlas__functions__MDPI_sinusoid(double& lon, double& lat) { return MDPI_sinusoid(lon, lat); } double atlas__functions__MDPI_harmonic(double& lon, double& lat) { return MDPI_harmonic(lon, lat); } double atlas__functions__MDPI_vortex(double& lon, double& lat) { return MDPI_vortex(lon, lat); } double atlas__functions__MDPI_gulfstream(double& lon, double& lat) { return MDPI_gulfstream(lon, lat); } } } // namespace function } // namespace util } // namespace atlas atlas-0.46.0/src/atlas/util/function/SphericalHarmonic.cc0000664000175000017500000000731515160212070023470 0ustar alastairalastair/* * (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 "atlas/runtime/Exception.h" #include "atlas/util/Constants.h" #include "atlas/util/function/SphericalHarmonic.h" namespace atlas { namespace util { namespace function { namespace { static double factorial(double v) { if (v == 0) { return 1; } double result = v; while (--v > 0) { result *= v; } return result; } static double double_factorial(double x) { if (x == 0 || x == -1) { return 1; } double result = x; while ((x -= 2) > 0) { result *= x; } return result; } // Associated Legendre Polynomial static double P(const int n, const int m, const double x) { // No recursive calculation needed if (n == m) { return (std::pow(-1.0, m) * double_factorial(2 * m - 1) * std::pow(std::sqrt(1. - x * x), m)); } if (n == m + 1) { return x * (2 * m + 1) * P(m, m, x); } // Formula 1 return (x * (2 * n - 1) * P(n - 1, m, x) - (n + m - 1) * P(n - 2, m, x)) / (n - m); } static double K(const int n, const int m) { //When m is less than 0, multiply - 1 to pass in return std::sqrt(((2 * n + 1) * factorial(n - m)) / (4 * M_PI * factorial(n + m))); } } // namespace double spherical_harmonic(int n, int m, double lon, double lat) { const int abs_m = std::abs(m); ATLAS_ASSERT(n >= abs_m); double colat = (90. - lat) * Constants::degreesToRadians(); lon *= Constants::degreesToRadians(); if (m == 0) { return (K(n, 0) * P(n, 0, std::cos(colat))); } if (m > 0) { return (M_SQRT2 * K(n, m) * std::cos(m * lon) * P(n, m, std::cos(colat))); } // When m is less than 0, multiply - 1 in advance and send it to K return (M_SQRT2 * K(n, abs_m) * std::sin(abs_m * lon) * P(n, abs_m, std::cos(colat))); } SphericalHarmonic::SphericalHarmonic(int n, int m, bool caching) { const int abs_m = std::abs(m); ATLAS_ASSERT(n >= abs_m); const double Knm = K(n, abs_m); std::function Pnm = [n, abs_m](double x) { return P(n, abs_m, x); }; if (caching) { // Computation of associated legendre polynomials is // very expensive. With this trick (off by default), // we can cache repeated values, useful for structured // grids with constant latitude. Pnm = [Pnm](double x) { static std::map memo; auto it = memo.find(x); if (it != memo.end()) { return it->second; } auto result = Pnm(x); memo[x] = result; return result; }; } if (m == 0) { Y_ = [Knm, Pnm](double lon, double colat) { return Knm * Pnm(std::cos(colat)); }; } else if (m > 0) { Y_ = [m, Knm, Pnm](double lon, double colat) { return M_SQRT2 * Knm * std::cos(m * lon) * Pnm(std::cos(colat)); }; } else { Y_ = [m, Knm, Pnm](double lon, double colat) { return M_SQRT2 * Knm * std::sin(-m * lon) * Pnm(std::cos(colat)); }; } } double SphericalHarmonic::operator()(double lon, double lat) const { double colat = (90. - lat) * Constants::degreesToRadians(); lon *= Constants::degreesToRadians(); return Y_(lon, colat); } } // namespace function } // namespace util } // namespace atlas atlas-0.46.0/src/atlas/util/function/SolidBodyRotation.h0000664000175000017500000000314715160212070023346 0ustar alastairalastair/* * (C) Copyright 2023 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation nor * does it submit to any jurisdiction. */ #pragma once namespace atlas { namespace util { namespace function { /// \brief An analytic function that provides solid body rotation winds on a sphere. /// /// All angles must be provided in degrees. /// class SolidBodyRotation { public: SolidBodyRotation() : SolidBodyRotation(0., 1.) {} SolidBodyRotation(const double beta) : SolidBodyRotation(beta, 1.) {} SolidBodyRotation(const double beta, const double radius); void wind(const double lon, const double lat, double& u, double& v) const; void vordiv(const double lon, const double lat, double& vor, double& div) const; double windMagnitude(const double lon, const double lat) const; double u(const double lon, const double lat) const; double v(const double lon, const double lat) const; double vorticity(const double lon, const double lat) const; double divergence(const double lon, const double lat) const; double windMagnitudeSquared(const double lon, const double lat) const; void windMagnitudeSquaredGradient(const double lon, const double lat, double& dfdx, double& dfdy) const; private: double sin_beta_; double cos_beta_; double radius_; }; } // namespace function } // namespace util } // namespace atlas atlas-0.46.0/src/atlas/util/function/MDPI_functions.h0000664000175000017500000000262715160212070022561 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation nor * does it submit to any jurisdiction. */ #pragma once namespace atlas { namespace util { namespace function { /// \brief Analytic functions from MDPI paper on regridding /// /// \detailed The formula is found in /// "Benchmarking Regridding Libraries Used in Earth System Modelling" /// by Sophie Valcke, Andreas Piacentini, Gabriel Jonville, MDPI 2022 /// as the sinusoid analytical function in Sec 2.1.2. /// The longitude (lon) and latitude (lat) are assumed to be in degrees, /// double MDPI_sinusoid(double lon, double lat); double MDPI_harmonic(double lon, double lat); double MDPI_vortex(double lon, double lat); double MDPI_gulfstream(double lon, double lat); extern "C" { double atlas__functions__MDPI_sinusoid(double& lon, double& lat); double atlas__functions__MDPI_harmonic(double& lon, double& lat); double atlas__functions__MDPI_vortex(double& lon, double& lat); double atlas__functions__MDPI_gulfstream(double& lon, double& lat); } } // namespace function } // namespace util } // namespace atlas atlas-0.46.0/src/atlas/util/function/VortexRollup.cc0000664000175000017500000000265015160212070022557 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "atlas/util/Constants.h" #include "atlas/util/Earth.h" #include "atlas/util/function/VortexRollup.h" namespace atlas { namespace util { namespace function { double vortex_rollup(double lon, double lat, double t) { lon *= Constants::degreesToRadians(); lat *= Constants::degreesToRadians(); auto sqr = [](const double x) { return x * x; }; auto sech = [](const double x) { return 1. / std::cosh(x); }; constexpr double two_pi = 2. * M_PI; const double lambda_prime = std::atan2(-std::cos(lon - two_pi * t), std::tan(lat)); const double rho = 3. * std::sqrt(1. - sqr(std::cos(lat)) * sqr(std::sin(lon - two_pi * t))); double omega = 0.; double a = Earth::radius(); if (rho != 0.) { omega = 0.5 * 3 * std::sqrt(3) * a * two_pi * sqr(sech(rho)) * std::tanh(rho) / rho; } return -std::tanh(0.2 * rho * std::sin(lambda_prime - omega / a * t)); } } // namespace function } // namespace util } // namespace atlas atlas-0.46.0/src/atlas/util/function/SphericalHarmonic.h0000664000175000017500000000302015160212070023317 0ustar alastairalastair/* * (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 namespace atlas { namespace util { namespace function { /// \brief An analytic function that provides a spherical harmonic on a 2D sphere /// /// \param n Total wave number /// \param m Zonal wave number /// \param lon Longitude in degrees /// \param lat Latitude in degrees /// \return spherical harmonic double spherical_harmonic(int n, int m, double lon, double lat); /// \brief SphericalHarmonic operator that provides a spherical harmonic on a 2D sphere class SphericalHarmonic { public: /// \brief Constructor /// /// \param n Total wave number /// \param m Zonal wave number /// \param caching When true, internally cache the results of Associated Legendre Polynomials /// in a map. Warning: this is not thread-safe SphericalHarmonic(int n, int m, bool caching = false); /// \brief Evaluate the spherical harmonic function for given longitude and latitude double operator()(double lon, double lat) const; private: std::function Y_; }; } // namespace function } // namespace util } // namespace atlas atlas-0.46.0/src/atlas/util/Rotation.h0000664000175000017500000000325615160212070017711 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include #include "atlas/util/Point.h" namespace eckit { class Parametrisation; } namespace atlas { namespace util { // Compute coordinates of a point on a rotated sphere with specified pole class Rotation { public: Rotation(const PointLonLat& south_pole, double rotation_angle = 0.); Rotation(const eckit::Parametrisation&); bool rotated() const { return rotated_; } PointLonLat southPole() const { return spole_; } PointLonLat northPole() const { return npole_; } double rotationAngle() const { return angle_; } PointLonLat rotate(const PointLonLat&) const; PointLonLat unrotate(const PointLonLat&) const; void rotate(double crd[]) const; void unrotate(double crd[]) const; private: void precompute(); void print(std::ostream&) const; friend std::ostream& operator<<(std::ostream&, const Rotation&); private: PointLonLat npole_ = {-180., 90.}; // North Pole PointLonLat spole_ = {0., -90.}; // South Pole double angle_ = {0.}; using RotationMatrix = std::array, 3>; RotationMatrix rotate_; // rotate matrix RotationMatrix unrotate_; // unrotate matrix bool rotation_angle_only_; bool rotated_; }; } // namespace util } // namespace atlas atlas-0.46.0/src/atlas/util/CoordinateEnums.h0000664000175000017500000000170215160212070021203 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once //------------------------------------------------------------------------------------------------------ namespace atlas { //------------------------------------------------------------------------------------------------------ enum XYZ { XX = 0, YY = 1, ZZ = 2 }; // ---------------------------------------------------------------------------------------------------- enum LONLAT { LON = 0, LAT = 1 }; // ---------------------------------------------------------------------------------------------------- } // namespace atlas atlas-0.46.0/src/atlas/util/UnitSphere.h0000664000175000017500000000155715160212070020202 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include "eckit/geometry/UnitSphere.h" //------------------------------------------------------------------------------------------------------ namespace atlas { namespace util { //------------------------------------------------------------------------------------------------------ using eckit::geometry::UnitSphere; //------------------------------------------------------------------------------------------------------ } // namespace util } // namespace atlas atlas-0.46.0/src/atlas/util/PackVectorFields.cc0000664000175000017500000001650315160212070021437 0ustar alastairalastair/* * (C) Crown Copyright 2024 Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ #include "atlas/util/PackVectorFields.h" #include #include #include #include #include "atlas/array.h" #include "atlas/array/helpers/ArrayForEach.h" #include "atlas/functionspace.h" #include "atlas/option.h" #include "atlas/runtime/Exception.h" #include "atlas/util/Config.h" #include "eckit/config/LocalConfiguration.h" namespace atlas { namespace util { namespace { using array::helpers::arrayForEachDim; using eckit::LocalConfiguration; void addOrReplaceField(FieldSet& fieldSet, const Field& field) { const auto fieldName = field.name(); if (fieldSet.has(fieldName)) { fieldSet[fieldName] = field; } else { fieldSet.add(field); } } Field& getOrCreateField(FieldSet& fieldSet, const FunctionSpace& functionSpace, const Config& config) { const auto fieldName = config.getString("name"); if (!fieldSet.has(fieldName)) { auto field = functionSpace.createField(config); field->set_dirty(false); // We will inherit the dirty state from the component fields. fieldSet.add(field); } return fieldSet[fieldName]; } void checkFieldCompatibility(const Field& componentField, const Field& vectorField) { ATLAS_ASSERT(componentField.functionspace().size() == vectorField.functionspace().size()); ATLAS_ASSERT(componentField.levels() == vectorField.levels()); ATLAS_ASSERT(componentField.variables() == 0); ATLAS_ASSERT(vectorField.variables() > 0); const auto checkStandardShape = [](const Field& field) { // Check for "standard" Atlas field shape. auto dim = 0; const auto rank = field.rank(); const auto shape = field.shape(); if (field.functionspace().size() != shape[dim++]) { return false; } if (const auto levels = field.levels(); levels && (dim >= rank || levels != shape[dim++])) { return false; } if (const auto variables = field.variables(); variables && (dim >= rank || variables != shape[dim++])) { return false; } if (dim != rank) { return false; } return true; }; ATLAS_ASSERT(checkStandardShape(componentField)); ATLAS_ASSERT(checkStandardShape(vectorField)); } template void copyFieldData(ComponentField& componentField, VectorField& vectorField, const Functor& copier) { checkFieldCompatibility(componentField, vectorField); auto componentViewVariant = array::make_view_variant(componentField); const auto componentVisitor = [&](auto componentView) { if constexpr (array::is_rank<1, 2>(componentView)) { using ComponentView = std::decay_t; constexpr auto ComponentRank = ComponentView::rank(); using Value = typename ComponentView::non_const_value_type; auto vectorView = array::make_view(vectorField); constexpr auto Dims = std::make_integer_sequence{}; arrayForEachDim(Dims, execution::par, std::tie(componentView, vectorView), copier); } else { ATLAS_THROW_EXCEPTION("Unsupported component field rank: " + std::to_string(componentView.rank())); } }; std::visit(componentVisitor, componentViewVariant); } } // namespace FieldSet pack_vector_fields(const FieldSet& fields, FieldSet packedFields) { // Get the number of variables for each vector field. auto vectorSizeMap = std::map{}; for (const auto& field : fields) { auto vectorFieldName = std::string{}; if (field.metadata().get("vector_component.vector_field_name", vectorFieldName)) { ++vectorSizeMap[vectorFieldName]; } } // Pack vector fields. for (const auto& field : fields) { auto vectorFieldName = std::string{}; if (!field.metadata().get("vector_component.vector_field_name", vectorFieldName)) { // Not a vector component field. addOrReplaceField(packedFields, field); continue; } // Field is vector field component. auto vectorSize = vectorSizeMap[vectorFieldName]; auto vectorIndex = size_t{}; field.metadata().get("vector_component.index", vectorIndex); const auto& componentField = field; // Get or create vector field. const auto vectorFieldConfig = option::name(vectorFieldName) | option::levels(componentField.levels()) | option::vector(vectorSize) | option::datatype(componentField.datatype()); auto& vectorField = getOrCreateField(packedFields, componentField.functionspace(), vectorFieldConfig); // Copy field data. const auto copier = [&](auto&& componentElem, auto&& vectorElem) { vectorElem(vectorIndex) = componentElem; }; copyFieldData(componentField, vectorField, copier); // Copy metadata. const auto componentFieldMetadata = componentField.metadata(); auto componentFieldMetadataVector = std::vector(vectorSize); vectorField.metadata().get("component_field_metadata", componentFieldMetadataVector); componentFieldMetadataVector[vectorIndex] = componentFieldMetadata; vectorField.metadata().set("component_field_metadata", componentFieldMetadataVector); // If any component is dirty, the whole field is dirty. vectorField.set_dirty(vectorField.dirty() || componentField.dirty()); } return packedFields; } FieldSet unpack_vector_fields(const FieldSet& fields, FieldSet unpackedFields) { for (const auto& field : fields) { auto componentFieldMetadataVector = std::vector{}; if (!field.metadata().get("component_field_metadata", componentFieldMetadataVector)) { // Not a vector field. addOrReplaceField(unpackedFields, field); continue; } // Field is vector. const auto& vectorField = field; auto vectorIndex = 0; for (const auto& componentFieldMetadata : componentFieldMetadataVector) { // Get or create field. auto componentFieldName = std::string{}; componentFieldMetadata.get("name", componentFieldName); const auto componentFieldConfig = option::name(componentFieldName) | option::levels(vectorField.levels()) | option::datatype(vectorField.datatype()); auto& componentField = getOrCreateField(unpackedFields, vectorField.functionspace(), componentFieldConfig); // Copy field data. const auto copier = [&](auto&& componentElem, auto&& vectorElem) { componentElem = vectorElem(vectorIndex); }; copyFieldData(componentField, vectorField, copier); // Copy metadata. componentField.metadata() = componentFieldMetadata; componentField.set_dirty(vectorField.dirty()); ++vectorIndex; } } return unpackedFields; } } // namespace util } // namespace atlas atlas-0.46.0/src/atlas/util/CGALSphericalTriangulation.cc0000664000175000017500000001324115160212070023345 0ustar alastairalastair#include "CGALSphericalTriangulation.h" #include "atlas/library/defines.h" #include #include #include #include #include "atlas/runtime/Exception.h" #if ATLAS_HAVE_CGAL // CGAL needs -DCGAL_NDEBUG to reach peak performance ... #define CGAL_NDEBUG #include #include #include #include #include #include using K = ::CGAL::Exact_predicates_inexact_constructions_kernel; using Polyhedron_3 = ::CGAL::Polyhedron_3; using Point_3 = Polyhedron_3::Point_3; #endif namespace atlas{ namespace util{ #if ATLAS_HAVE_CGAL struct CGALSphericalTriangulation::CGAL { CGAL(const std::vector>& pts) { std::vector vertices(pts.size()); for (size_t i = 0, size = vertices.size(); i < size; ++i) { vertices[i] = Point_3(pts[i][0], pts[i][1], pts[i][2]); point_map_[vertices[i]] = i; } // compute convex hull of non-collinear points ::CGAL::convex_hull_3(vertices.begin(), vertices.end(), poly_); } std::unordered_map point_map_; Polyhedron_3 poly_; }; #else struct CGALSphericalTriangulation::CGAL { template CGAL(Args...) { throw_Exception("Atlas has not been compiled with CGAL",Here()); } }; #endif CGALSphericalTriangulation::~CGALSphericalTriangulation() = default; CGALSphericalTriangulation::CGALSphericalTriangulation(size_t N, const double lonlat[]) : CGALSphericalTriangulation(N, lonlat, lonlat+1, 2, 2) { } CGALSphericalTriangulation::CGALSphericalTriangulation(size_t N, const double lon[], const double lat[]) : CGALSphericalTriangulation(N, lon, lat, 1, 1) { } CGALSphericalTriangulation::CGALSphericalTriangulation(size_t N, const double lon[], const double lat[], int lon_stride, int lat_stride) { auto lonlat2xyz = [](double lon, double lat, auto& xyz) { constexpr double deg2rad = M_PI / 180.; const double lambda = deg2rad * lon; const double phi = deg2rad * lat; const double sin_phi = std::sin(phi); const double cos_phi = std::cos(phi); const double sin_lambda = std::sin(lambda); const double cos_lambda = std::cos(lambda); xyz[0] = cos_phi * cos_lambda; xyz[1] = cos_phi * sin_lambda; xyz[2] = sin_phi; }; points_xyz_.resize(N); for (size_t i = 0; i < N; ++i) { lonlat2xyz(lon[i*lon_stride], lat[i*lat_stride], points_xyz_[i]); } cgal_ = std::make_unique(points_xyz_); } size_t CGALSphericalTriangulation::size() const { #if ATLAS_HAVE_CGAL return cgal_->poly_.size_of_facets(); #else return 0; #endif } template static inline void CGAL_get_triangles(const CGAL& cgal, const Points& points_xyz_, std::array triangles[]) { #if ATLAS_HAVE_CGAL auto ensure_outward_normal = [&](auto& tri) { auto dot = [](const auto& p1, const auto& p2) { return p1[0]*p2[0] + p1[1]*p2[1] + p1[2]*p2[2]; }; auto cross = [](const auto& p1, const auto& p2) { return std::array{ p1[1] * p2[2] - p1[2] * p2[1], p1[2] * p2[0] - p1[0] * p2[2], p1[0] * p2[1] - p1[1] * p2[0] }; }; const std::array& a = points_xyz_[tri[0]]; const std::array& b = points_xyz_[tri[1]]; const std::array& c = points_xyz_[tri[2]]; std::array ba {a[0]-b[0], a[1]-b[1], a[2]-b[2]}; std::array bc {c[0]-b[0], c[1]-b[1], c[2]-b[2]}; bool outward = dot(b, cross(bc,ba)) > 0; if (not outward) { std::swap(tri[1], tri[2]); } }; size_t jtri{0}; const auto& poly = cgal.poly_; const auto& point_map = cgal.point_map_; const idx_t nb_points = idx_t(points_xyz_.size()); /* triangles */ const idx_t nb_triags = poly.size_of_facets(); idx_t idx[3]; Polyhedron_3::Vertex_const_handle vts[3]; idx_t tidx = 0; for (Polyhedron_3::Facet_const_iterator f = poly.facets_begin(); f != poly.facets_end(); ++f) { // loop over half-edges and take each vertex() auto& tri = triangles[jtri++]; size_t jvrt{0}; idx_t iedge = 0; Polyhedron_3::Halfedge_around_facet_const_circulator edge = f->facet_begin(); do { Polyhedron_3::Vertex_const_handle vh = edge->vertex(); const Polyhedron_3::Point_3& p = vh->point(); tri[jvrt++] = point_map.at(vh->point()); ++iedge; ++edge; } while (edge != f->facet_begin() && iedge < 3); ATLAS_ASSERT(iedge == 3); ensure_outward_normal(tri); } ATLAS_ASSERT(jtri == nb_triags); #endif } void CGALSphericalTriangulation::triangles(std::array triangles[]) const { CGAL_get_triangles(*cgal_,points_xyz_,triangles); } void CGALSphericalTriangulation::triangles(std::array triangles[]) const { CGAL_get_triangles(*cgal_,points_xyz_,triangles); } void CGALSphericalTriangulation::triangles(std::array triangles[]) const { CGAL_get_triangles(*cgal_,points_xyz_,triangles); } void CGALSphericalTriangulation::triangles(std::array triangles[]) const { CGAL_get_triangles(*cgal_,points_xyz_,triangles); } std::vector> CGALSphericalTriangulation::triangles() const { return triangles(); } } // namespace util } // namespace atlas atlas-0.46.0/src/atlas/util/Geometry.h0000664000175000017500000001777515160212070017720 0ustar alastairalastair/* * (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 #include #include "atlas/runtime/Exception.h" #include "atlas/util/Earth.h" #include "atlas/util/Object.h" #include "atlas/util/ObjectHandle.h" #include "atlas/util/Point.h" namespace atlas { namespace geometry { //------------------------------------------------------------------------------------------------------ namespace detail { // TODO: move greatCircleCourse to eckit::geometry::Sphere /// @brief Calculate great-cricle course between points /// /// @details Calculates the direction (clockwise from north) of a great-circle /// arc between lonLat1 and lonLat2. Returns the direction of the arc /// at lonLat1 (first) and lonLat2 (second). Angle is normalised to the /// range of atan2 (usually (-180, 180]). All input and output values /// are in units of degrees. /// @ref https://en.wikipedia.org/wiki/Great-circle_navigation std::pair greatCircleCourse(const Point2& lonLat1, const Point2& lonLat2); //------------------------------------------------------------------------------------------------------ class GeometryBase : public util::Object { public: virtual ~GeometryBase() = default; virtual void lonlat2xyz(const Point2&, Point3&) const = 0; virtual void xyz2lonlat(const Point3&, Point2&) const = 0; virtual double distance(const Point2& p1, const Point2& p2) const = 0; virtual double distance(const Point3& p1, const Point3& p2) const = 0; virtual double radius() const = 0; virtual double area() const = 0; virtual std::pair greatCircleCourse(const Point2& p1, const Point2& p2) const = 0; Point3 xyz(const Point2& lonlat) const { Point3 xyz; lonlat2xyz(lonlat, xyz); return xyz; } Point2 lonlat(const Point3& xyz) const { Point2 lonlat; xyz2lonlat(xyz, lonlat); return lonlat; } }; //------------------------------------------------------------------------------------------------------ template class GeometrySphereT : public GeometryBase { public: void lonlat2xyz(const Point2& lonlat, Point3& xyz) const override { #if ATLAS_ECKIT_VERSION_AT_LEAST(1, 24, 0) SphereT::convertSphericalToCartesian(lonlat, xyz, 0., true); #else SphereT::convertSphericalToCartesian(lonlat, xyz); #endif } void xyz2lonlat(const Point3& xyz, Point2& lonlat) const override { SphereT::convertCartesianToSpherical(xyz, lonlat); } double distance(const Point2& p1, const Point2& p2) const override { return SphereT::distance(p1, p2); } double distance(const Point3& p1, const Point3& p2) const override { return SphereT::distance(p1, p2); } double radius() const override { return SphereT::radius(); } double area() const override { return SphereT::area(); } std::pair greatCircleCourse(const Point2& p1, const Point2& p2) const override { return atlas::geometry::detail::greatCircleCourse(p1, p2); } }; class GeometrySphere : public GeometryBase { using Sphere = eckit::geometry::Sphere; public: GeometrySphere(double radius): radius_(radius) {} void lonlat2xyz(const Point2& lonlat, Point3& xyz) const override; void xyz2lonlat(const Point3& xyz, Point2& lonlat) const override; double distance(const Point2& p1, const Point2& p2) const override { return Sphere::distance(radius_, p1, p2); } double distance(const Point3& p1, const Point3& p2) const override { return Sphere::distance(radius_, p1, p2); } double radius() const override { return radius_; } double area() const override { return Sphere::area(radius_); } std::pair greatCircleCourse(const Point2& p1, const Point2& p2) const override { return atlas::geometry::detail::greatCircleCourse(p1, p2); } private: double radius_; }; } // namespace detail } // namespace geometry //------------------------------------------------------------------------------------------------------ class Geometry : public util::ObjectHandle { public: using Handle::Handle; Geometry(): Handle(build>()) {} Geometry(const std::string& name): Handle(build(name)) {} Geometry(const char* name): Handle(build(name)) {} Geometry(double radius): Handle(build(radius)) {} template Geometry(const SphereT&): Handle(build()) {} Point3 xyz(const Point2& lonlat) const { return get()->xyz(lonlat); } Point2 lonlat(const Point3& xyz) const { return get()->lonlat(xyz); } void xyz2lonlat(const Point3& xyz, Point2& lonlat) const { get()->xyz2lonlat(xyz, lonlat); } void lonlat2xyz(const Point2& lonlat, Point3& xyz) const { get()->lonlat2xyz(lonlat, xyz); } double distance(const Point2& p1, const Point2& p2) const { return get()->distance(p1, p2); } double distance(const Point3& p1, const Point3& p2) const { return get()->distance(p1, p2); } double radius() const { return get()->radius(); } double area() const { return get()->area(); } std::pair greatCircleCourse(const Point2& p1, const Point2& p2) const { return get()->greatCircleCourse(p1, p2); } protected: template static Implementation* build(Args... args) { return new GeometryT(args...); } static Implementation* build(const std::string& name) { // Factory without self registration if (name == "Earth") { return build>(); } else if (name == "UnitSphere") { return build>(); } else { ATLAS_THROW_EXCEPTION("name " << name << " is not a valid key for a Geometry"); } } }; //------------------------------------------------------------------------------------------------------ namespace geometry { using Earth = Geometry; // Sphere with util::Earth radius by default class UnitSphere : public Geometry { public: UnitSphere(): Geometry(/*radius*/ 1.) {} }; } // namespace geometry // ------------------------------------------------------------------ // C wrapper interfaces to C++ routines extern "C" { Geometry::Implementation* atlas__Geometry__new_name(const char* name); Geometry::Implementation* atlas__Geometry__new_radius(const double radius); void atlas__Geometry__delete(Geometry::Implementation* This); void atlas__Geometry__xyz2lonlat(Geometry::Implementation* This, const double x, const double y, const double z, double& lon, double& lat); void atlas__Geometry__lonlat2xyz(Geometry::Implementation* This, const double lon, const double lat, double& x, double& y, double& z); double atlas__Geometry__distance_lonlat(Geometry::Implementation* This, const double lon1, const double lat1, const double lon2, const double lat2); double atlas__Geometry__distance_xyz(Geometry::Implementation* This, const double x1, const double y1, const double z1, const double x2, const double y2, const double z2); double atlas__Geometry__radius(Geometry::Implementation* This); double atlas__Geometry__area(Geometry::Implementation* This); } //------------------------------------------------------------------------------------------------------ } // namespace atlas atlas-0.46.0/src/atlas/runtime/0000775000175000017500000000000015160212070016441 5ustar alastairalastairatlas-0.46.0/src/atlas/runtime/trace/0000775000175000017500000000000015160212070017537 5ustar alastairalastairatlas-0.46.0/src/atlas/runtime/trace/Barriers.h0000664000175000017500000000222115160212070021456 0ustar alastairalastair/* * (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 //----------------------------------------------------------------------------------------------------------- namespace atlas { namespace runtime { namespace trace { class NoBarriers { public: NoBarriers(bool state) {} void restore() {} public: // static methods static bool state() { return false; } static void execute() {} static double time(); static std::string report(); }; class Barriers { public: Barriers(bool state); ~Barriers(); void restore(); public: // static methods static bool state(); static void execute(); static double time(); static std::string report(); private: bool previous_state_; }; } // namespace trace } // namespace runtime } // namespace atlas atlas-0.46.0/src/atlas/runtime/trace/CodeLocation.h0000664000175000017500000000402215160212070022251 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include #include "eckit/log/CodeLocation.h" namespace atlas { class CodeLocation { public: CodeLocation(const CodeLocation& loc): CodeLocation(loc.file(), loc.line(), loc.func(), loc.stored_) {} CodeLocation(const eckit::CodeLocation& loc): loc_(loc), stored_(false) {} CodeLocation(const char* file, int line, const char* function, bool store = false): stored_(store) { if (stored_) { if (file) { file_str_ = std::string(file); file_ = file_str_.c_str(); } if (function) { function_str_ = std::string(function); function_ = function_str_.c_str(); } loc_ = eckit::CodeLocation(file_, line, function_); } else { loc_ = eckit::CodeLocation(file, line, function); } } operator const eckit::CodeLocation&() const { return loc_; } /// conversion to bool for checking if location was set operator bool() const { return loc_; } std::string asString() const { return loc_.asString(); } /// accessor to line int line() const { return loc_.line(); } /// accessor to file const char* file() const { return loc_.file(); } /// accessor to function const char* func() const { return loc_.func(); } friend std::ostream& operator<<(std::ostream& s, const CodeLocation& loc); private: eckit::CodeLocation loc_; bool stored_ = false; const char* file_ = nullptr; const char* function_ = nullptr; std::string file_str_; std::string function_str_; }; } // namespace atlas atlas-0.46.0/src/atlas/runtime/trace/Timings.h0000664000175000017500000000237215160212070021326 0ustar alastairalastair/* * (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 //----------------------------------------------------------------------------------------------------------- namespace eckit { class Configuration; } namespace atlas { class CodeLocation; } namespace atlas { namespace runtime { namespace trace { class CallStack; class Timings { public: using Configuration = eckit::Configuration; using CodeLocation = atlas::CodeLocation; using Identifier = size_t; using Labels = std::vector; public: // static methods static Identifier add(const CodeLocation&, const CallStack&, const std::string& title, const Labels&); static void update(const Identifier& id, double seconds); static std::string report(); static std::string report(const Configuration&); }; } // namespace trace } // namespace runtime } // namespace atlas atlas-0.46.0/src/atlas/runtime/trace/Nesting.cc0000664000175000017500000000121015160212070021447 0ustar alastairalastair/* * (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 "Nesting.h" //----------------------------------------------------------------------------------------------------------- namespace atlas { namespace runtime { namespace trace {} // namespace trace } // namespace runtime } // namespace atlas atlas-0.46.0/src/atlas/runtime/trace/Barriers.cc0000664000175000017500000000472215160212070021624 0ustar alastairalastair/* * (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 "Barriers.h" #include #include "atlas/library/Library.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/parallel/omp/omp.h" #include "atlas/runtime/trace/StopWatch.h" //----------------------------------------------------------------------------------------------------------- namespace atlas { namespace runtime { namespace trace { class BarriersState { private: BarriersState() { barriers_ = atlas::Library::instance().traceBarriers(); } bool barriers_; StopWatch stopwatch_; public: BarriersState(BarriersState const&) = delete; void operator=(BarriersState const&) = delete; static BarriersState& instance() { static BarriersState state; return state; } operator bool() const { return barriers_; } void set(bool state) { barriers_ = state; } StopWatch& stopwatch() { return stopwatch_; } std::string report() const { std::stringstream out; double time = stopwatch_.elapsed(); if (time) { out << "Total time spent in mpi barriers due to load imbalance : " << time << "s" << std::endl; } return out.str(); } }; Barriers::Barriers(bool state): previous_state_(BarriersState::instance()) { BarriersState::instance().set(state); } Barriers::~Barriers() { restore(); } void Barriers::restore() { BarriersState::instance().set(previous_state_); } bool Barriers::state() { return BarriersState::instance() && (atlas_omp_get_num_threads() <= 1); } void Barriers::execute() { if (state()) { BarriersState::instance().stopwatch().start(); mpi::comm().barrier(); BarriersState::instance().stopwatch().stop(); } } double Barriers::time() { return BarriersState::instance().stopwatch().elapsed(); } double NoBarriers::time() { return BarriersState::instance().stopwatch().elapsed(); } std::string Barriers::report() { return BarriersState::instance().report(); } std::string NoBarriers::report() { return BarriersState::instance().report(); } } // namespace trace } // namespace runtime } // namespace atlas atlas-0.46.0/src/atlas/runtime/trace/Logging.cc0000664000175000017500000000610615160212070021437 0ustar alastairalastair/* * (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 "Logging.h" #include #include #include "eckit/log/Channel.h" #include "atlas/library/Library.h" #include "atlas/parallel/omp/omp.h" //----------------------------------------------------------------------------------------------------------- namespace atlas { namespace runtime { namespace trace { //----------------------------------------------------------------------------------------------------------- bool Control::enabled() { return atlas_omp_get_thread_num() == 0; } class LoggingState { private: std::ostream* channel_; LoggingState() { channel_ = &atlas::Library::instance().traceChannel(); } public: static eckit::Channel& empty_channel() { static eckit::Channel channel; return channel; } static LoggingState& instance() { static LoggingState channel; return channel; } operator std::ostream&() { return *channel_; } operator std::ostream*() { return channel_; } void set(std::ostream& channel) { channel_ = &channel; } void set(bool state) { if (state == false) { channel_ = &empty_channel(); } } }; //----------------------------------------------------------------------------------------------------------- Logging::Logging(bool state): previous_state_(LoggingState::instance()) { LoggingState::instance().set(state); } Logging::Logging(std::ostream& channel): previous_state_(LoggingState::instance()) { LoggingState::instance().set(channel); } Logging::~Logging() { LoggingState::instance().set(*previous_state_); } std::ostream& Logging::channel() { return LoggingState::instance(); } bool Logging::enabled() { return LoggingState::instance(); } void Logging::start(const std::string& title) { if (enabled()) { channel() << title << " ..." << std::endl; } } void Logging::stop(const std::string& title, double seconds) { if (enabled()) { if (!std::uncaught_exceptions()){ channel() << title << " ... done : " << seconds << "s" << std::endl; } } } //----------------------------------------------------------------------------------------------------------- std::ostream& NoLogging::channel() { return LoggingState::empty_channel(); } //----------------------------------------------------------------------------------------------------------- void LoggingResult::stop(const std::string& title, double seconds) { if (enabled()) { channel() << title << " : " << seconds << "s" << std::endl; } } //----------------------------------------------------------------------------------------------------------- } // namespace trace } // namespace runtime } // namespace atlas atlas-0.46.0/src/atlas/runtime/trace/TraceT.h0000664000175000017500000001312515160212070021074 0ustar alastairalastair/* * (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 #include "atlas/parallel/omp/omp.h" #include "atlas/runtime/trace/CallStack.h" #include "atlas/runtime/trace/CodeLocation.h" #include "atlas/runtime/trace/Nesting.h" #include "atlas/runtime/trace/StopWatch.h" #include "atlas/runtime/trace/Timings.h" //----------------------------------------------------------------------------------------------------------- namespace eckit { class Configuration; } // namespace eckit //----------------------------------------------------------------------------------------------------------- namespace atlas { namespace runtime { namespace trace { //----------------------------------------------------------------------------------------------------------- template class TraceT { public: using Traits = TraceTraits; using Barriers = typename Traits::Barriers; using Tracing = typename Traits::Tracing; using Labels = std::vector; public: // static methods static std::string report(); static std::string report(const eckit::Configuration& config); public: TraceT(const CodeLocation&); TraceT(const CodeLocation&, const std::string& title); TraceT(const CodeLocation&, const std::string& title, const Labels&); ~TraceT(); bool running() const; void start(); void stop(); void pause(); void resume(); double elapsed() const; private: // types using Identifier = Timings::Identifier; private: // member functions void barrier() const; void updateTimings() const; void registerTimer(); static std::string formatTitle(const std::string&); private: // member data bool running_{false}; StopWatch stopwatch_; CodeLocation loc_; std::string title_; Identifier id_; CallStack callstack_; Labels labels_; }; //----------------------------------------------------------------------------------------------------------- // Definitions template inline std::string TraceT::formatTitle(const std::string& _title) { std::string title = _title + (Barriers::state() ? " [b]" : "") + (atlas_omp_get_num_threads() > 1 ? " @thread[" + std::to_string(atlas_omp_get_thread_num()) + "]" : ""); return title; } template inline TraceT::TraceT(const CodeLocation& loc, const std::string& title): loc_(loc), title_(formatTitle(title)) { start(); } template inline TraceT::TraceT(const CodeLocation& loc): loc_(loc), title_(loc_ ? loc_.func() : "") { start(); } template inline TraceT::TraceT(const CodeLocation& loc, const std::string& title, const Labels& labels): loc_(loc), title_(title), labels_(labels) { start(); } template inline TraceT::~TraceT() { stop(); } template inline void TraceT::barrier() const { Barriers::execute(); } template inline void TraceT::registerTimer() { std::string title = title_ + (Barriers::state() ? " [b]" : "") + (atlas_omp_get_num_threads() > 1 ? " @thread[" + std::to_string(atlas_omp_get_thread_num()) + "]" : ""); id_ = Timings::add(loc_, callstack_, title, labels_); } template inline void TraceT::updateTimings() const { Timings::update(id_, stopwatch_.elapsed()); } template inline bool TraceT::running() const { return running_; } template inline void TraceT::start() { if (Control::enabled()) { running_ = true; if (not callstack_) { callstack_ = CurrentCallStack::instance().push(loc_, title_); } registerTimer(); Tracing::start(title_); barrier(); stopwatch_.start(); } } template inline void TraceT::stop() { if (running_) { barrier(); stopwatch_.stop(); CurrentCallStack::instance().pop(); updateTimings(); Tracing::stop(title_, stopwatch_.elapsed()); running_ = false; } } template inline void TraceT::pause() { if (running_) { barrier(); stopwatch_.stop(); CurrentCallStack::instance().pop(); } } template inline void TraceT::resume() { if (running_) { barrier(); CurrentCallStack::instance().push(loc_, title_); stopwatch_.start(); } } template inline double TraceT::elapsed() const { return stopwatch_.elapsed(); } template inline std::string TraceT::report() { return Timings::report() + Barriers::report(); } template inline std::string TraceT::report(const eckit::Configuration& config) { return Timings::report(config) + Barriers::report(); } //----------------------------------------------------------------------------------------------------------- } // namespace trace } // namespace runtime } // namespace atlas atlas-0.46.0/src/atlas/runtime/trace/CodeLocation.cc0000664000175000017500000000106515160212070022413 0ustar alastairalastair/* * (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 "CodeLocation.h" namespace atlas { std::ostream& operator<<(std::ostream& s, const CodeLocation& loc) { s << loc.loc_; return s; } } // namespace atlas atlas-0.46.0/src/atlas/runtime/trace/Timings.cc0000664000175000017500000004167015160212070021470 0ustar alastairalastair/* * (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 "Timings.h" #include #include #include #include #include #include #include "eckit/config/Configuration.h" #include "eckit/filesystem/PathName.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Log.h" #include "atlas/runtime/trace/CallStack.h" #include "atlas/runtime/trace/CodeLocation.h" #include "atlas/util/Config.h" //----------------------------------------------------------------------------------------------------------- namespace atlas { namespace runtime { namespace trace { class TimingsRegistry { private: std::vector counts_; std::vector tot_timings_; std::vector min_timings_; std::vector max_timings_; std::vector var_timings_; std::vector titles_; std::vector locations_; std::vector nest_; std::vector stack_; std::map index_; std::map> labels_; TimingsRegistry() = default; public: static TimingsRegistry& instance() { static TimingsRegistry registry; return registry; } size_t add(const CodeLocation&, const CallStack& stack, const std::string& title, const Timings::Labels&); void update(size_t idx, double seconds); size_t size() const; void report(std::ostream& out, const eckit::Configuration& config); private: std::string filter_filepath(const std::string& filepath) const; friend struct Tree; friend struct Node; }; struct Node { Node(): index(-1) {} Node(size_t _index): index(_index) { size_t _nest = TimingsRegistry::instance().nest_[index]; auto this_stack_hash = TimingsRegistry::instance().stack_[index].hash(); auto is_child = [&](size_t i) -> bool { CallStack child_stack = TimingsRegistry::instance().stack_[i]; child_stack.pop(); auto child_stack_hash = child_stack.hash(); return child_stack_hash == this_stack_hash; }; for (size_t i = index + 1; i < TimingsRegistry::instance().size(); ++i) { if (TimingsRegistry::instance().nest_[i] == _nest + 1) { if (is_child(i)) { children.emplace_back(new Node(i)); } } } } std::vector> children; std::unique_ptr parent; long index; void print(std::ostream& out) { if (index >= 0) { size_t _nest = nest(); for (size_t i = 1; i < _nest; ++i) { out << " "; } out << TimingsRegistry::instance().titles_[index] << std::endl; } for (auto& child : children) { child->print(out); } } size_t nest() const { return TimingsRegistry::instance().nest_[index]; } void order(std::vector& order) const { if (index >= 0) { order.emplace_back(index); } for (auto& child : children) { child->order(order); } } }; struct Tree { Node root; Tree() { for (size_t j = 0; j < TimingsRegistry::instance().size(); ++j) { auto& nest = TimingsRegistry::instance().nest_[j]; if (nest == 1) { auto& children = root.children; children.emplace_back(new Node(j)); } } } void print(std::ostream& out) { root.print(out); } std::vector order() const { std::vector order; order.reserve(TimingsRegistry::instance().size()); root.order(order); ATLAS_ASSERT(order.size() == TimingsRegistry::instance().size(), "Likely a atlas_Trace has not finalised properly"); return order; } }; size_t TimingsRegistry::add(const CodeLocation& loc, const CallStack& stack, const std::string& title, const Timings::Labels& labels) { size_t key = stack.hash(); auto it = index_.find(key); if (it == index_.end()) { size_t idx = size(); index_[key] = idx; counts_.emplace_back(0); tot_timings_.emplace_back(0); min_timings_.emplace_back(std::numeric_limits::max()); max_timings_.emplace_back(0); var_timings_.emplace_back(0); titles_.emplace_back(title); locations_.emplace_back(loc); nest_.emplace_back(stack.size()); stack_.emplace_back(stack); for (const auto& label : labels) { labels_[label].emplace_back(idx); } return idx; } else { return it->second; } } void TimingsRegistry::update(size_t idx, double seconds) { auto sqr = [](double x) { return x * x; }; double n = counts_[idx] + 1; double avg_nm1 = tot_timings_[idx] / std::max(n, 1.); double var_nm1 = var_timings_[idx]; var_timings_[idx] = n == 1. ? 0. : (n - 2.) / (n - 1.) * var_nm1 + 1. / n * sqr(seconds - avg_nm1); min_timings_[idx] = std::min(seconds, min_timings_[idx]); max_timings_[idx] = std::max(seconds, max_timings_[idx]); tot_timings_[idx] += seconds; counts_[idx] += 1; } size_t TimingsRegistry::size() const { return counts_.size(); } void TimingsRegistry::report(std::ostream& out, const eckit::Configuration& config) { auto box_horizontal = [](int n) { std::string s; s.reserve(2 * n); for (int i = 0; i < n; ++i) { s += "\u2500"; } return s; }; std::string box_corner_tl("\u250c"); std::string box_corner_tr("\u2510"); std::string box_corner_bl("\u2514"); std::string box_corner_br("\u2518"); std::string box_vertical("\u2502"); std::string box_T_down("\u252C"); std::string box_T_up("\u2534"); std::string box_T_right("\u251C"); std::string box_T_left("\u2524"); std::string box_cross("\u253C"); long indent = config.getLong("indent", 2); long depth = config.getLong("depth", 0); long decimals = config.getLong("decimals", 5); bool header = config.getBool("header", true); std::vector excluded_labels_vector = config.getStringVector("exclude", std::vector()); std::vector include_back; auto order = Tree().order(); for (auto& label : excluded_labels_vector) { size_t found = label.find("/*"); if (found != std::string::npos) { label.erase(found, 2); include_back.push_back(label); } } std::set excluded_labels(excluded_labels_vector.begin(), excluded_labels_vector.end()); auto digits_before_decimal = [](double x) -> int { return std::floor(std::log10(std::trunc(std::max(1., x)))) + 1; }; auto digits = [](long x) -> long { return std::floor(std::log10(std::max(1l, x))) + 1l; }; std::vector excluded_timers_vector; for (auto& label : labels_) { auto name = label.first; if (excluded_labels.count(name)) { auto timers = label.second; for (size_t j : timers) { excluded_timers_vector.push_back(j); } } } std::set excluded_timers(excluded_timers_vector.begin(), excluded_timers_vector.end()); auto excluded = [&](size_t i) -> bool { if (depth and nest_[i] > depth) { return true; } return excluded_timers.count(i); }; std::vector excluded_nest_stored(size()); long excluded_nest = size(); for (size_t jj = 0; jj < size(); ++jj) { size_t j = order[jj]; if (nest_[j] > excluded_nest) { excluded_timers.insert(j); } if (not excluded(j)) { excluded_nest = nest_[j] + 1; } else { excluded_nest = std::min(excluded_nest, nest_[j]); } excluded_nest_stored[j] = excluded_nest; } for (auto& label : include_back) { auto timers = labels_[label]; for (size_t j : timers) { if (nest_[j] == excluded_nest_stored[j]) { excluded_timers.erase(j); } } } size_t max_title_length(2); size_t max_location_length(9); size_t max_nest(0); long max_count(0); double max_seconds(0); for (size_t j = 0; j < size(); ++j) { size_t nest = nest_[j]; max_nest = std::max(max_nest, nest); if (not excluded(j)) { const auto& loc = locations_[j]; max_title_length = std::max(max_title_length, titles_[j].size() + nest_[j] * indent); max_count = std::max(max_count, counts_[j]); max_seconds = std::max(max_seconds, tot_timings_[j]); size_t location_length = filter_filepath(loc.file()).size() + 2 + digits(loc.line()); max_location_length = std::max(max_location_length, location_length); } } size_t max_count_length = digits(max_count); if (header) { max_count_length = std::max(std::string("cnt").size(), max_count_length); } size_t max_digits_before_decimal = digits_before_decimal(max_seconds); auto print_time = [max_digits_before_decimal, decimals](double x) -> std::string { std::stringstream out; char unit = 's'; if (std::floor(x) >= 60) { x /= 60.; unit = 'm'; } out << std::right << std::fixed << std::setprecision(decimals) << std::setw(max_digits_before_decimal + decimals + 1) << x << unit; return out.str(); }; auto print_line = [&](size_t length) -> std::string { return box_horizontal(length); }; auto print_horizontal = [&](const std::string& sep) -> std::string { std::stringstream ss; ss << print_line(max_title_length + digits(size()) + 3) << sep << print_line(max_count_length) << sep << print_line(max_digits_before_decimal + decimals + 2) << sep << print_line(max_digits_before_decimal + decimals + 2) << sep << print_line(max_digits_before_decimal + decimals + 2) << sep << print_line(max_digits_before_decimal + decimals + 2) << sep << print_line(max_digits_before_decimal + decimals + 2) << sep << print_line(max_location_length); return ss.str(); }; std::string sept = box_horizontal(1) + box_T_down + box_horizontal(1); std::string seph = box_horizontal(1) + box_cross + box_horizontal(1); std::string sep = std::string(" ") + box_vertical + std::string(" "); std::string sepf = box_horizontal(1) + box_T_up + box_horizontal(1); out << print_horizontal(sept) << std::endl; out << std::left << std::setw(max_title_length + digits(size()) + 3) << "Timers" << sep << std::setw(max_count_length) << "cnt" << sep << std::setw(max_digits_before_decimal + decimals + 2ul) << "tot" << sep << std::setw(max_digits_before_decimal + decimals + 2ul) << "avg" << sep << std::setw(max_digits_before_decimal + decimals + 2ul) << "std" << sep << std::setw(max_digits_before_decimal + decimals + 2ul) << "min" << sep << std::setw(max_digits_before_decimal + decimals + 2ul) << "max" << sep << "location" << std::endl; out << print_horizontal(seph) << std::endl; std::vector prefix_(size()); if (indent) { std::vector active(max_nest, false); for (long kk = long(size()) - 1; kk >= 0; --kk) { long k = order[kk]; const auto& nest = nest_[k]; const CallStack& this_stack = stack_[k]; const CallStack* next_stack_ptr; if (kk == size() - 1) { next_stack_ptr = &this_stack; } else { next_stack_ptr = &stack_[order[kk + 1]]; } const CallStack& next_stack = *next_stack_ptr; auto this_it = this_stack.begin(); auto next_it = next_stack.begin(); for (size_t i = 0; this_it != this_stack.end() && next_it != next_stack.end(); ++i, ++this_it, ++next_it) { if (*this_it == *next_it) { active[i] = active[i] or false; } else { active[i] = true; } } for (size_t i = nest; i < active.size(); ++i) { active[i] = false; } std::stringstream out; for (long i = 0; i < nest - 1; ++i) { if (active[i]) { out << box_vertical; } else { out << " "; } for (long j = 1; j < indent; ++j) { out << " "; } } if (active[nest - 1]) { out << box_T_right; } else { out << box_corner_bl; } for (long j = 1; j < indent; ++j) { out << box_horizontal(1); } prefix_[k] = out.str(); } } for (size_t i = 0; i < size(); ++i) { size_t j = order[i]; auto& tot = tot_timings_[j]; auto& max = max_timings_[j]; auto& min = std::min(max, min_timings_[j]); auto& count = counts_[j]; auto& title = titles_[j]; auto& loc = locations_[j]; auto& nest = nest_[j]; auto std = std::sqrt(var_timings_[j]); auto avg = (count == 0 ? 0. : tot / double(count)); // mpi::comm().allReduceInPlace(min,eckit::mpi::min()); // mpi::comm().allReduceInPlace(max,eckit::mpi::max()); if (not excluded(j)) { out << std::setw(digits(long(size()))) << j << " : " << prefix_[j] // prefix(indent,nest,next_nest) << std::left << std::setw(max_title_length - nest * indent) << title << sep << std::string(header ? "" : "count: ") << std::left << std::setw(max_count_length) << count << sep << std::string(header ? "" : "tot: ") << print_time(tot) << sep << std::string(header ? "" : "avg: ") << print_time(avg) << sep << std::string(header ? "" : "std: ") << print_time(std) << sep << std::string(header ? "" : "min: ") << print_time(min) << sep << std::string(header ? "" : "max: ") << print_time(max) << sep << filter_filepath(loc.file()) << " +" << loc.line() << std::endl; } } out << print_horizontal(sepf) << std::endl; std::string sepc = box_horizontal(3); out << std::left << box_horizontal(40) << sept << box_horizontal(5) << sept << box_horizontal(12) << "\n"; out << std::left << std::setw(40) << "Timers accumulated by label" << sep << std::left << std::setw(5) << "count" << sep << "time" << std::endl; out << std::left << box_horizontal(40) << seph << box_horizontal(5) << seph << box_horizontal(12) << "\n"; for (auto& label : labels_) { auto name = label.first; auto timers = label.second; double tot(0); double count(0); for (size_t j : timers) { tot += tot_timings_[j]; count += counts_[j]; } out << std::left << std::setw(40) << name << sep << std::left << std::setw(5) << count << sep << print_time(tot) << std::endl; } out << std::left << box_horizontal(40) << sepf << box_horizontal(5) << sepf << box_horizontal(12) << "\n"; } std::string TimingsRegistry::filter_filepath(const std::string& filepath) const { std::smatch matches; std::string basename = eckit::PathName(filepath).baseName(); if (std::regex_search(filepath, matches, std::regex{"(.*)?/atlas/src/(.*)"})) { return "[atlas] " + basename; } if (std::regex_search(filepath, matches, std::regex{"(.*)?/atlas-io/src/(.*)"})) { return "[atlas-io] " + basename; } return basename; } Timings::Identifier Timings::add(const CodeLocation& loc, const CallStack& stack, const std::string& title, const Labels& labels) { return TimingsRegistry::instance().add(loc, stack, title, labels); } void Timings::update(const Identifier& id, double seconds) { TimingsRegistry::instance().update(id, seconds); } std::string Timings::report() { return report(util::NoConfig()); } std::string Timings::report(const Configuration& config) { std::ostringstream out; TimingsRegistry::instance().report(out, config); return out.str(); } } // namespace trace } // namespace runtime } // namespace atlas atlas-0.46.0/src/atlas/runtime/trace/StopWatch.h0000664000175000017500000000332515160212070021627 0ustar alastairalastair/* * (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 namespace atlas { namespace runtime { namespace trace { //----------------------------------------------------------------------------------------------------------- class StopWatch { public: StopWatch(); StopWatch(double elapsed); void start(); void stop(); void reset(); double elapsed() const; private: std::chrono::duration elapsed_; std::chrono::steady_clock::time_point start_; bool running_{false}; }; //----------------------------------------------------------------------------------------------------------- inline StopWatch::StopWatch(): elapsed_(0) {} inline StopWatch::StopWatch(double elapsed): elapsed_(elapsed) {} inline void StopWatch::start() { start_ = std::chrono::steady_clock::now(); running_ = true; } inline void StopWatch::stop() { if (running_) { elapsed_ += (std::chrono::steady_clock::now() - start_); running_ = false; } } inline void StopWatch::reset() { elapsed_ = std::chrono::seconds::zero(); start_ = std::chrono::steady_clock::now(); } inline double StopWatch::elapsed() const { return elapsed_.count(); } //----------------------------------------------------------------------------------------------------------- } // namespace trace } // namespace runtime } // namespace atlas atlas-0.46.0/src/atlas/runtime/trace/CallStack.h0000664000175000017500000000275515160212070021562 0ustar alastairalastair/* * (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 namespace atlas { class CodeLocation; } namespace atlas { namespace runtime { namespace trace { /// @class CallStack /// Instances of CallStack can keep track of nested CodeLocations class CallStack { public: using const_iterator = std::vector::const_iterator; public: void push(const CodeLocation&, const std::string& id = ""); void pop(); const_iterator begin() const { return stack_.begin(); } const_iterator end() const { return stack_.begin() + size_; } size_t hash() const; size_t size() const { return size_; } operator bool() const { return size_ > 0; } public: CallStack(): stack_(64){}; CallStack(const CallStack& other): stack_(other.stack_), size_(other.size_) {} CallStack& operator=(const CallStack& other) { stack_ = other.stack_; size_ = other.size_; hash_ = 0; return *this; } private: std::vector stack_; size_t size_{0}; mutable size_t hash_{0}; }; } // namespace trace } // namespace runtime } // namespace atlas atlas-0.46.0/src/atlas/runtime/trace/Nesting.h0000664000175000017500000000256415160212070021326 0ustar alastairalastair/* * (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/runtime/trace/CallStack.h" #include "atlas/runtime/trace/CodeLocation.h" #include "atlas/runtime/trace/Logging.h" //----------------------------------------------------------------------------------------------------------- namespace atlas { namespace runtime { namespace trace { class CurrentCallStack { private: CurrentCallStack() {} CallStack stack_; public: CurrentCallStack(CurrentCallStack const&) = delete; CurrentCallStack& operator=(CurrentCallStack const&) = delete; static CurrentCallStack& instance() { static CurrentCallStack state; return state; } operator CallStack() const { return stack_; } CallStack& push(const CodeLocation& loc, const std::string& id) { if (Control::enabled()) stack_.push(loc, id); return stack_; } void pop() { if (Control::enabled()) stack_.pop(); } }; } // namespace trace } // namespace runtime } // namespace atlas atlas-0.46.0/src/atlas/runtime/trace/CallStack.cc0000664000175000017500000000310515160212070021706 0ustar alastairalastair/* * (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 "CallStack.h" #include #include "atlas/runtime/trace/CodeLocation.h" namespace atlas { namespace runtime { namespace trace { namespace { static size_t hash_cstr(const char *s) { // http://www.cse.yorku.ca/~oz/hash.html size_t h = 5381; int c; while ((c = *s++)) { h = ((h << 5) + h) + c; } return h; } static size_t hash_combine(size_t h1, size_t h2) { return h1 ^ (h2 << 1); }; static size_t hash_codelocation( const CodeLocation& loc ) { return hash_combine( hash_cstr(loc.file()), std::hash{}(loc.line()) ); } } void CallStack::push(const CodeLocation& loc, const std::string& id) { if (stack_.size() == size_) { stack_.resize(2 * size_); } auto hash = hash_codelocation(loc); if( ! id.empty() ) { hash = hash_combine( hash, std::hash{}(id) ); } stack_[size_++] = hash; } void CallStack::pop() { --size_; } size_t CallStack::hash() const { if (hash_) { return hash_; } for (long i = size_ - 1; i >= 0; --i) { auto h = stack_[i]; hash_ ^= (h << 1); } return hash_; } } // namespace trace } // namespace runtime } // namespace atlas atlas-0.46.0/src/atlas/runtime/trace/Logging.h0000664000175000017500000000440315160212070021277 0ustar alastairalastair/* * (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 //----------------------------------------------------------------------------------------------------------- namespace atlas { namespace runtime { namespace trace { class Control { public: static bool enabled(); }; //----------------------------------------------------------------------------------------------------------- // Class used to avoid any printing before and after a timer class NoLogging { public: NoLogging(bool state); NoLogging(std::ostream& channel); public: // static methods static std::ostream& channel(); static bool enabled(); static void start(const std::string&) {} static void stop(const std::string&, double) {} }; //----------------------------------------------------------------------------------------------------------- // Class used to print message before and after a timer // Example print: // timer-name ... // timer-name ... done : 5s class Logging { public: Logging(bool state); Logging(std::ostream& channel); virtual ~Logging(); public: // static methods static std::ostream& channel(); static bool enabled(); static void start(const std::string& title); static void stop(const std::string& title, double seconds); private: std::ostream* previous_state_; }; //----------------------------------------------------------------------------------------------------------- // Class used to print message only upon end of a timer // Example print: // timer-name : 5s class LoggingResult : public Logging { public: using Logging::Logging; public: // static methods static void start(const std::string&) {} static void stop(const std::string& title, double seconds); }; //----------------------------------------------------------------------------------------------------------- } // namespace trace } // namespace runtime } // namespace atlas atlas-0.46.0/src/atlas/runtime/AtlasTool.cc0000664000175000017500000003000515160212070020650 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include #include #include #include "eckit/config/LibEcKit.h" #include "eckit/filesystem/LocalPathName.h" #include "eckit/log/FileTarget.h" #include "eckit/log/PrefixTarget.h" #include "atlas/library/config.h" #include "atlas/runtime/AtlasTool.h" #include "eckit/system/LibraryManager.h" namespace atlas { static void usage(const std::string& name) { Log::info() << "dummy usage" << std::endl; } } // namespace atlas namespace { int digits(int number) { int d = 0; while (number) { number /= 10; d++; } return d; } static std::string debug_prefix(const std::string& libname) { std::string s = libname; std::transform(s.begin(), s.end(), s.begin(), ::toupper); s += "_DEBUG"; return s; } void debug_addTarget(eckit::LogTarget* target) { for (std::string libname : eckit::system::LibraryManager::list()) { const eckit::system::Library& lib = eckit::system::LibraryManager::lookup(libname); if (lib.debug()) { lib.debugChannel().addTarget(new eckit::PrefixTarget(debug_prefix(libname), target)); } } if (eckit::Log::debug()) { eckit::Log::debug().addTarget(target); } } void debug_setTarget(eckit::LogTarget* target) { for (std::string libname : eckit::system::LibraryManager::list()) { const eckit::system::Library& lib = eckit::system::LibraryManager::lookup(libname); if (lib.debug()) { lib.debugChannel().setTarget(new eckit::PrefixTarget(debug_prefix(libname), target)); } } if (eckit::Log::debug()) { eckit::Log::debug().setTarget(target); } } void debug_reset() { for (std::string libname : eckit::system::LibraryManager::list()) { const eckit::system::Library& lib = eckit::system::LibraryManager::lookup(libname); if (lib.debug()) { lib.debugChannel().reset(); } } if (eckit::Log::debug()) { eckit::Log::debug().reset(); } } bool getEnv(const std::string& env, bool default_value) { const char* cenv = ::getenv(env.c_str()); if (cenv != nullptr) { return eckit::Translator()(cenv); } return default_value; } int getEnv(const std::string& env, int default_value) { const char* cenv = ::getenv(env.c_str()); if (cenv != nullptr) { return eckit::Translator()(cenv); } return default_value; } std::string getEnv(const std::string& env, const std::string& default_value) { const char* cenv = ::getenv(env.c_str()); if (cenv != nullptr) { return cenv; } return default_value; } void setEnv(const std::string& env, bool value) { constexpr int DO_NOT_REPLACE_IF_EXISTS = 0; ::setenv(env.c_str(), eckit::Translator()(value).c_str(), DO_NOT_REPLACE_IF_EXISTS); } } // namespace namespace atlas { static bool use_logfile; static std::string logfile_name; static std::string workdir; [[noreturn]] void atlas_terminate() { // This routine is called for uncaught exceptions. // It can be set with std::set_terminate( &atlas_terminate ) Log::flush(); if (not use_logfile and mpi::size() > 1) { eckit::LogTarget* logfile = new eckit::FileTarget(logfile_name); Log::error().addTarget(logfile); } if (std::exception_ptr eptr = std::current_exception()) { std::ostream& out = Log::error(); try { std::rethrow_exception(eptr); // throw to recognise the type } catch (const eckit::Abort& exception) { out << "\n" << "=========================================\n" << "[" << mpi::rank() << "] Aborting " << eckit::Main::instance().displayName() << "\n" << "-----------------------------------------\n" << exception.what() << "\n"; if (exception.location()) { out << "-----------------------------------------\n" << "LOCATION: " << exception.location() << "\n"; } out << "-----------------------------------------\n" << "BACKTRACE\n" << "-----------------------------------------\n" << exception.callStack() << "\n" << "=========================================\n" << std::endl; } catch (const eckit::Exception& exception) { out << "\n" << "=========================================\n" << "[" << mpi::rank() << "] TERMINATING " << eckit::Main::instance().displayName() << "\n" << "-----------------------------------------\n" << exception.what() << "\n" << "-----------------------------------------\n"; if (exception.location()) { out << "LOCATION: " << exception.location() << "\n" << "-----------------------------------------\n"; } out << "BACKTRACE\n" << "-----------------------------------------\n" << exception.callStack() << "\n" << "=========================================\n" << std::endl; } catch (const std::exception& exception) { out << "\n" << "=========================================\n" << "[" << mpi::rank() << "] TERMINATING " << eckit::Main::instance().displayName() << "\n" << "-----------------------------------------\n" << exception.what() << "\n" << "-----------------------------------------\n" << "BACKTRACE\n" << "-----------------------------------------\n" << backtrace() << "\n" << "=========================================\n" << std::endl; } catch (...) { out << "\n" << "=========================================\n" << "[" << mpi::rank() << "] TERMINATING " << eckit::Main::instance().displayName() << "\n" << "-----------------------------------------\n" << "BACKTRACE\n" << "-----------------------------------------\n" << backtrace() << "\n" << "=========================================" << std::endl; } } eckit::LibEcKit::instance().abort(); // Just in case we end up here, as last resort, exit immediately without // cleanup. std::_Exit(EXIT_FAILURE); } } // namespace atlas void atlas::AtlasTool::add_option(eckit::option::Option* option) { options_.push_back(option); } void atlas::AtlasTool::help(std::ostream& out) { auto indented = [&](const std::string& s) -> std::string { std::string str = indent() + s; size_t pos = 0; while ((pos = str.find('\n', pos)) != std::string::npos) { str.replace(pos, 1, '\n' + indent()); ++pos; } return str; }; out << "NAME\n" << indented(name()); std::string brief = briefDescription(); if (brief.size()) { out << " - " << brief << '\n'; } std::string usg = usage(); if (usg.size()) { out << '\n'; out << "SYNOPSIS\n" << indented(usg) << '\n'; } std::string desc = longDescription(); if (desc.size()) { out << '\n'; out << "DESCRIPTION\n" << indented(desc) << '\n'; } out << '\n'; out << "OPTIONS\n"; for (Options::const_iterator it = options_.begin(); it != options_.end(); ++it) { std::stringstream s; s << **it; out << indented(s.str()) << "\n\n"; } out << std::flush; } bool atlas::AtlasTool::handle_help() { for (int i = 1; i < argc(); ++i) { if (argv(i) == "--help" || argv(i) == "-h") { if (taskID() == 0) { help(std::cout); } return true; } } return false; } std::string atlas::AtlasTool::get_positional_arg(const Args& args, size_t p) const { int c = 0; for (int i = 0; i < args.count(); ++i) { if (args(i)[0] == '-') { i++; } else { if (c == p) { return args(i); } c++; } } return std::string{}; } atlas::AtlasTool::AtlasTool(int argc, char** argv): eckit::Tool(argc, argv) { eckit::LibEcKit::instance().setAbortHandler([] { std::cerr << "[" << atlas::mpi::rank() << "] " << "calling MPI_Abort"; if (not use_logfile and mpi::size() > 1) { std::cerr << ", logfile: " << logfile_name; } std::cerr << std::endl; std::this_thread::sleep_for(std::chrono::milliseconds(3000)); atlas::mpi::comm().abort(1); }); std::set_terminate(&atlas_terminate); setEnv("ECKIT_EXCEPTION_IS_SILENT", true); setEnv("ECKIT_ASSERT_FAILED_IS_SILENT", true); setEnv("ATLAS_FPE", true); setEnv("ATLAS_SIGNAL_HANDLER", true); add_option(new SimpleOption("help", "Print this help")); add_option(new SimpleOption("debug", "Debug level")); taskID(eckit::mpi::comm("world").rank()); workdir = getEnv("ATLAS_WORKDIR", eckit::PathName(eckit::LocalPathName::cwd()).fullName()); add_option(new SimpleOption("display-name", "Name to give to program")); } int atlas::AtlasTool::start() { try { if (handle_help()) { return success(); } if (argc() - 1 < minimumPositionalArguments()) { if (taskID() == 0) { std::cout << "Usage: " << usage() << std::endl; } return failed(); } atlas::initialize(); setupLogging(); Options opts = options_; Args args(&atlas::usage, opts, numberOfPositionalArguments(), minimumPositionalArguments() > 0); int err_code = execute(args); atlas::finalize(); atlas::mpi::finalize(); return err_code; } catch (...) { atlas_terminate(); } } void atlas::AtlasTool::run() {} void atlas::AtlasTool::setupLogging() { int log_rank = getEnv("ATLAS_LOG_RANK", 0); use_logfile = getEnv("ATLAS_LOG_FILE", false); int d = digits(mpi::size()); std::string rankstr = std::to_string(taskID()); for (int i = rankstr.size(); i < d; ++i) { rankstr = "0" + rankstr; } logfile_name = workdir + "/" + displayName() + ".log.p" + rankstr; if (use_logfile) { eckit::LogTarget* logfile = new eckit::FileTarget(logfile_name); if (int(mpi::rank()) == log_rank) { if (Log::info()) { Log::info().addTarget(logfile); } if (Log::warning()) { Log::warning().addTarget(logfile); } if (Log::error()) { Log::error().addTarget(logfile); } debug_addTarget(logfile); } else { if (Log::info()) { Log::info().setTarget(logfile); } if (Log::warning()) { Log::warning().setTarget(logfile); } if (Log::error()) { Log::error().setTarget(logfile); } debug_setTarget(logfile); } } else { if (int(mpi::rank()) != log_rank) { if (Log::info()) { Log::info().reset(); } if (Log::warning()) { Log::warning().reset(); } if (Log::error()) { Log::error().reset(); } debug_reset(); } } // codechecker_false_positive [NewDeleteLeaks] Potential leak of memory pointed to by 'logfile' } atlas-0.46.0/src/atlas/runtime/Trace.h0000664000175000017500000000532515160212070017655 0ustar alastairalastair/* * (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/library/config.h" #include "atlas/runtime/trace/Barriers.h" #include "atlas/runtime/trace/Logging.h" #include "atlas/runtime/trace/TraceT.h" //----------------------------------------------------------------------------------------------------------- /// Create scoped trace objects /// /// Example: /// /// void foo() { /// ATLAS_TRACE(); /// // trace "foo" starts /// /// /* interesting computations ... */ /// /// ATLAS_TRACE_SCOPE("bar") { /// // trace "bar" starts /// /// /* interesting computations ... */ /// /// // trace "bar" ends /// } /// /// // trace "foo" ends /// } /// /// Example 2: /// /// void foo() { /// ATLAS_TRACE("custom"); /// // trace "custom" starts /// /// /* interesting computations ... */ /// /// // trace "custom" ends /// } /// #define ATLAS_TRACE(...) #define ATLAS_TRACE_SCOPE(...) #define ATLAS_TRACE_BARRIERS(enabled) //----------------------------------------------------------------------------------------------------------- namespace atlas { namespace runtime { namespace trace { struct TraceTraits { #if ATLAS_HAVE_TRACE_BARRIERS using Barriers = ::atlas::runtime::trace::Barriers; #else using Barriers = ::atlas::runtime::trace::NoBarriers; #endif using Tracing = ::atlas::runtime::trace::Logging; }; } // namespace trace } // namespace runtime class Trace : public runtime::trace::TraceT { using Base = runtime::trace::TraceT; using Traits = runtime::trace::TraceTraits; public: using Base::Base; }; } // namespace atlas //----------------------------------------------------------------------------------------------------------- #if ATLAS_HAVE_TRACE #include "atlas/library/detail/BlackMagic.h" #undef ATLAS_TRACE #undef ATLAS_TRACE_SCOPE #undef ATLAS_TRACE_BARRIERS #define ATLAS_TRACE(...) __ATLAS_TYPE(::atlas::Trace, Here() __ATLAS_COMMA_ARGS(__VA_ARGS__)) #define ATLAS_TRACE_SCOPE(...) __ATLAS_TYPE_SCOPE(::atlas::Trace, Here() __ATLAS_COMMA_ARGS(__VA_ARGS__)) #define ATLAS_TRACE_BARRIERS(enabled) __ATLAS_TYPE(::atlas::Trace::Barriers, enabled) #endif //----------------------------------------------------------------------------------------------------------- atlas-0.46.0/src/atlas/runtime/Log.cc0000664000175000017500000000351215160212070017472 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "eckit/os/BackTrace.h" #include "atlas/library/Library.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/runtime/Log.h" #if ATLAS_HAVE_FORTRAN #include "fckit/Log.h" #endif namespace atlas { std::string backtrace() { return eckit::BackTrace::dump(); } namespace detail { void debug_parallel_here(const eckit::CodeLocation& here) { const auto& comm = mpi::comm(); comm.barrier(); Log::info() << "DEBUG_PARALLEL() @ " << here << std::endl; } void debug_parallel_what(const eckit::CodeLocation& here, const std::string& what) { const auto& comm = mpi::comm(); comm.barrier(); Log::info() << "DEBUG_PARALLEL(" << what << ") @ " << here << std::endl; } } // namespace detail Log::Channel& Log::info() { return atlas::Library::instance().infoChannel(); } Log::Channel& Log::warning() { return atlas::Library::instance().warningChannel(); } Log::Channel& Log::trace() { return atlas::Library::instance().traceChannel(); } Log::Channel& Log::debug() { return atlas::Library::instance().debugChannel(); } void Log::addFortranUnit(int unit, Style style, const char* prefix) { #if ATLAS_HAVE_FORTRAN fckit::Log::addFortranUnit(unit, fckit::Log::Style(style), prefix); #else /*NOTIMP*/ #endif } void Log::setFortranUnit(int unit, Style style, const char* prefix) { #if ATLAS_HAVE_FORTRAN fckit::Log::setFortranUnit(unit, fckit::Log::Style(style), prefix); #else /*NOTIMP*/ #endif } } // namespace atlas atlas-0.46.0/src/atlas/runtime/Exception.cc0000664000175000017500000000646715160212070020723 0ustar alastairalastair/* * (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 "eckit/exception/Exceptions.h" #include "atlas/runtime/Exception.h" namespace atlas { void throw_NotImplemented(const eckit::CodeLocation& loc) { throw eckit::NotImplemented(loc); } void throw_NotImplemented(const std::string& msg, const eckit::CodeLocation& loc) { throw eckit::NotImplemented(msg, loc); } void throw_AssertionFailed(const std::string& msg) { #if ATLAS_ECKIT_VERSION_AT_LEAST(1, 18, 5) throw_AssertionFailed(msg, eckit::CodeLocation{}); #else throw eckit::AssertionFailed(msg); #endif } void throw_AssertionFailed(const std::string& msg, const eckit::CodeLocation& loc) { #if ATLAS_ECKIT_VERSION_AT_LEAST(1, 18, 5) if (loc) { eckit::handle_assert(msg, loc); } else { eckit::handle_assert(msg, eckit::CodeLocation{"unspecified file", 0, "unspecified function"}); } std::abort(); // should never reach here, but makes sure we will never return from this #else throw eckit::AssertionFailed(msg, loc); #endif } void throw_AssertionFailed(const std::string& code, const std::string& msg, const eckit::CodeLocation& loc) { std::ostringstream ss; ss << " [[ " << code << " ]]\n" << msg; throw_AssertionFailed(ss.str(), loc); } void throw_Exception(const std::string& msg) { throw eckit::Exception(msg); } void throw_Exception(const std::string& msg, const eckit::CodeLocation& loc) { throw eckit::Exception(msg, loc); } void throw_CantOpenFile(const std::string& file) { throw eckit::CantOpenFile(file); } void throw_CantOpenFile(const std::string& file, const eckit::CodeLocation& loc) { throw eckit::CantOpenFile(file, loc); } void throw_OutOfRange(const std::string& varname, idx_t index, idx_t size) { std::ostringstream ss; ss << "OutOfRange: Tried to access " << varname << " index " << index << " but maximum allowed index is " << size - 1; throw eckit::Exception(ss.str()); } void throw_OutOfRange(const std::string& varname, idx_t index, idx_t size, const eckit::CodeLocation& loc) { std::ostringstream ss; if (index < 0) { ss << "OutOfRange: Tried to access index " << index << " of " << varname << " of size " << size << ". Negative index is invalid."; } else { ss << "OutOfRange: Tried to access index " << index << " of " << varname << " of size " << size << ". The index exceeds the maximum value " << size-1; } throw eckit::Exception(ss.str(), loc); } void throw_OutOfRange(idx_t index, idx_t size) { std::ostringstream ss; ss << "OutOfRange: Tried to access index " << index << " but maximum allowed index is " << size - 1; throw eckit::Exception(ss.str()); } void throw_OutOfRange(idx_t index, idx_t size, const eckit::CodeLocation& loc) { std::ostringstream ss; ss << "OutOfRange: Tried to access index " << index << " but maximum allowed index is " << size - 1; throw eckit::Exception(ss.str(), loc); } } // namespace atlas atlas-0.46.0/src/atlas/runtime/AtlasTool.h0000664000175000017500000000417315160212070020521 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include #include "eckit/option/CmdArgs.h" #include "eckit/option/Separator.h" #include "eckit/option/SimpleOption.h" #include "eckit/option/VectorOption.h" #include "eckit/runtime/Tool.h" #include "atlas/library/Library.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/runtime/Log.h" #include "atlas/util/Config.h" //-------------------------------------------------------------------------------- using atlas::util::Config; using eckit::option::CmdArgs; using eckit::option::Option; using eckit::option::Separator; using eckit::option::SimpleOption; using eckit::option::VectorOption; namespace atlas { class AtlasTool : public eckit::Tool { public: typedef std::vector Options; typedef eckit::option::CmdArgs Args; protected: virtual bool serial() { return false; } virtual std::string indent() { return " "; } virtual std::string briefDescription() { return ""; } virtual std::string longDescription() { return ""; } virtual std::string usage() { return name() + " [OPTION]... [--help,-h] [--debug]"; } void add_option(eckit::option::Option* option); virtual void help(std::ostream& out = Log::info()); virtual int numberOfPositionalArguments() { return -1; } virtual int minimumPositionalArguments() { return 0; } bool handle_help(); std::string get_positional_arg(const Args&, size_t pos) const; public: AtlasTool(int argc, char** argv); int start(); virtual void run(); // unused virtual int execute(const Args&) = 0; static constexpr int success() { return 0; } static constexpr int failed() { return 1; } private: void setupLogging(); private: Options options_; }; } // namespace atlas atlas-0.46.0/src/atlas/runtime/Log.h0000664000175000017500000000630715160212070017341 0ustar alastairalastair#pragma once #include "atlas/library/config.h" #include "eckit/log/Log.h" namespace atlas { class Log : public eckit::Log { public: using Channel = eckit::Channel; // derives from std::ostream static Channel& info(); static Channel& warning(); static Channel& trace(); static Channel& debug(); // Same as what fckit::Log provides enum Style { SIMPLE = 0, PREFIX = 1, TIMESTAMP = 2 }; static void addFortranUnit(int unit, Style = PREFIX, const char* prefix = ""); static void setFortranUnit(int unit, Style = PREFIX, const char* prefix = ""); // Fortran unit numbers static int output_unit() { return 6; } static int error_unit() { return 0; } }; std::string backtrace(); } // namespace atlas #include #include "atlas/library/detail/BlackMagic.h" #include "eckit/log/CodeLocation.h" namespace atlas { namespace detail { void debug_parallel_here(const eckit::CodeLocation&); void debug_parallel_what(const eckit::CodeLocation&, const std::string&); } // namespace detail } // namespace atlas #define ATLAS_DEBUG_HERE() \ do { \ ::atlas::Log::info() << "DEBUG() @ " << Here() << std::endl; \ } while (0) #define ATLAS_DEBUG_WHAT(WHAT) \ do { \ ::atlas::Log::info() << "DEBUG(" << WHAT << ") @ " << Here() << std::endl; \ } while (0) #define ATLAS_DEBUG_VAR(VAR) \ do { \ ::atlas::Log::info() << "DEBUG( " << #VAR << " : " << VAR << " ) @ " << Here() << std::endl; \ } while (0) #define ATLAS_DEBUG(...) __ATLAS_SPLICE(__ATLAS_DEBUG_, __ATLAS_NARG(__VA_ARGS__))(__VA_ARGS__) #define __ATLAS_DEBUG_0 ATLAS_DEBUG_HERE #define __ATLAS_DEBUG_1 ATLAS_DEBUG_WHAT #define ATLAS_DEBUG_BACKTRACE() \ do { \ ::atlas::Log::info() << "DEBUG() @ " << Here() << "Backtrace:\n" << ::atlas::backtrace() << std::endl; \ } while (0) #define ATLAS_DEBUG_PARALLEL_HERE() \ do { \ ::atlas::detail::debug_parallel_here(Here()); \ } while (0) #define ATLAS_DEBUG_PARALLEL_WHAT(WHAT) \ do { \ std::stringstream w; \ w << WHAT; \ ::atlas::detail::debug_parallel_what(Here(), w.str()); \ } while (0) #define ATLAS_DEBUG_PARALLEL(...) \ __ATLAS_SPLICE(__ATLAS_DEBUG_PARALLEL_, __ATLAS_NARG(__VA_ARGS__)) \ (__VA_ARGS__) #define __ATLAS_DEBUG_PARALLEL_0 ATLAS_DEBUG_PARALLEL_HERE #define __ATLAS_DEBUG_PARALLEL_1 ATLAS_DEBUG_PARALLEL_WHAT atlas-0.46.0/src/atlas/runtime/Exception.h0000664000175000017500000000573515160212070020562 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include #include "eckit/log/CodeLocation.h" #include "atlas/library/config.h" namespace atlas { #ifndef DOXYGEN_SHOULD_SKIP_THIS [[noreturn]] void throw_NotImplemented(const eckit::CodeLocation&); [[noreturn]] void throw_NotImplemented(const std::string&, const eckit::CodeLocation&); [[noreturn]] void throw_AssertionFailed(const std::string&); [[noreturn]] void throw_AssertionFailed(const std::string&, const eckit::CodeLocation&); [[noreturn]] void throw_AssertionFailed(const std::string&, const std::string&, const eckit::CodeLocation&); [[noreturn]] void throw_Exception(const std::string&); [[noreturn]] void throw_Exception(const std::string&, const eckit::CodeLocation&); [[noreturn]] void throw_CantOpenFile(const std::string&); [[noreturn]] void throw_CantOpenFile(const std::string&, const eckit::CodeLocation&); [[noreturn]] void throw_OutOfRange(const std::string& varname, idx_t index, idx_t size); [[noreturn]] void throw_OutOfRange(const std::string& varname, idx_t index, idx_t size, const eckit::CodeLocation&); [[noreturn]] void throw_OutOfRange(idx_t index, idx_t size); [[noreturn]] void throw_OutOfRange(idx_t index, idx_t size, const eckit::CodeLocation&); namespace detail { inline void Assert(bool success, const char* code, const char* file, int line, const char* func) { if (not success) { throw_AssertionFailed(code, eckit::CodeLocation(file, line, func)); } } inline void Assert(bool success, const char* code, const std::string& msg, const char* file, int line, const char* func) { if (not success) { throw_AssertionFailed(code, msg, eckit::CodeLocation(file, line, func)); } } } // namespace detail } // namespace atlas #include "atlas/library/detail/BlackMagic.h" #define ATLAS_NOTIMPLEMENTED ::atlas::throw_NotImplemented(Here()) #define ATLAS_ASSERT_NOMSG(a) \ static_cast(0), (a) ? (void)0 : ::atlas::detail::Assert(bool(a), #a, __FILE__, __LINE__, __func__) #define ATLAS_ASSERT_MSG(a, m) \ static_cast(0), (a) ? (void)0 : ::atlas::detail::Assert(bool(a), #a, m, __FILE__, __LINE__, __func__) #define ATLAS_ASSERT(...) __ATLAS_SPLICE(__ATLAS_ASSERT_, __ATLAS_NARG(__VA_ARGS__))(__VA_ARGS__) #define __ATLAS_ASSERT_1 ATLAS_ASSERT_NOMSG #define __ATLAS_ASSERT_2 ATLAS_ASSERT_MSG #define ATLAS_THROW_EXCEPTION(WHAT) \ do { \ std::ostringstream ss; \ ss << WHAT; \ ::atlas::throw_Exception(ss.str(), Here()); \ } while (false) #endif atlas-0.46.0/src/atlas/mdspan.h0000664000175000017500000000776015160212070016423 0ustar alastairalastair/* * (C) Copyright 2025- 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 "pluto/mdspan.h" namespace atlas { using ::pluto::dynamic_extent; using ::pluto::layout_left; using ::pluto::layout_right; using ::pluto::layout_stride; using ::pluto::default_accessor; using ::pluto::extents; using ::pluto::dextents; using ::pluto::dims; using ::pluto::mdspan; } // namespace atlas // ------------------------------------------------------------------------------------------------ #include namespace atlas { template class index_reference { public: using value_type = T; constexpr index_reference(value_type& idx): idx_(idx) { } constexpr void set(const value_type& value) { idx_ = value + base_; } constexpr value_type get() const { return idx_ - base_; } constexpr void operator =(const value_type& value) { set(value); } constexpr index_reference& operator=(const index_reference& other) noexcept { set(other.get()); return *this; } constexpr index_reference& operator--() noexcept { --idx_; return *this; } constexpr index_reference& operator++() noexcept { ++idx_; return *this; } constexpr index_reference& operator+=(value_type v) noexcept { idx_ += v; return *this; } constexpr index_reference& operator-=(value_type v) noexcept { idx_ -= v; return *this; } constexpr operator value_type() const noexcept{ return get(); } private: value_type& idx_; static constexpr value_type base_ = Base; }; template struct index_accessor { using element_type = ElementType; using reference = std::conditional_t, ElementType, // base applied to const accessor directly index_reference>>; // base applied within index_reference using data_handle_type = ElementType*; using offset_policy = index_accessor; constexpr index_accessor() = default; template && std::is_same_v>) ||( std::is_const_v && std::is_same_v>)>> constexpr index_accessor(const OtherAccessor&) {} template> constexpr ElementType& access(data_handle_type p, size_t i) const noexcept { return p[i]; } template>> constexpr index_reference access(data_handle_type p, size_t i) const noexcept { return p[i]; } template>> constexpr ElementType access(data_handle_type p, size_t i) const noexcept { return p[i] - base_; } constexpr data_handle_type offset(data_handle_type p, size_t i) const noexcept { return p + i; } constexpr operator default_accessor() const noexcept { return default_accessor(); } static constexpr ElementType base_{Base}; }; } // namespace atlas // ------------------------------------------------------------------------------------------------ atlas-0.46.0/src/atlas/option.h0000664000175000017500000000077515160212070016450 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ /// @author Willem Deconinck #pragma once #include "atlas/option/Options.h" #include "atlas/option/TransOptions.h" atlas-0.46.0/src/atlas/array.h0000664000175000017500000000207015160212070016244 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ /// @file array.h /// @brief Public API header that includes all relevant headers for using the atlas::array namespace /// @author Willem Deconinck /// @namespace atlas::array /// @brief Contains classes to operate with multidimensional arrays #pragma once #include "atlas/array/Array.h" #include "atlas/array/ArrayDataStore.h" #include "atlas/array/ArrayShape.h" #include "atlas/array/ArraySpec.h" #include "atlas/array/ArrayStrides.h" #include "atlas/array/ArrayView.h" #include "atlas/array/ArrayViewVariant.h" #include "atlas/array/DataType.h" #include "atlas/array/LocalView.h" #include "atlas/array/MakeView.h" #include "atlas/array/Table.h" //#include "atlas/array/TableView.h" atlas-0.46.0/src/atlas/interpolation/0000775000175000017500000000000015160212070017645 5ustar alastairalastairatlas-0.46.0/src/atlas/interpolation/Interpolation.cc0000664000175000017500000001657615160212070023022 0ustar alastairalastair/* * (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/field/Field.h" #include "atlas/field/FieldSet.h" #include "atlas/functionspace/FunctionSpace.h" #include "atlas/interpolation/Interpolation.h" #include "atlas/interpolation/method/MethodFactory.h" #include "atlas/runtime/Exception.h" namespace atlas { Interpolation::Interpolation(const Config& config, const FunctionSpace& source, const FunctionSpace& target): Handle([&]() -> Implementation* { std::string type; ATLAS_ASSERT(config.get("type", type)); Implementation* impl = interpolation::MethodFactory::build(type, config); impl->setup(source, target); return impl; }()) { std::string path; if (config.get("output", path)) { std::ofstream file(path); print(file); } } Interpolation::Interpolation(const Config& config, const Grid& source, const Grid& target): Handle([&]() -> Implementation* { std::string type; ATLAS_ASSERT(config.get("type", type)); Implementation* impl = interpolation::MethodFactory::build(type, config); impl->setup(source, target); return impl; }()) { std::string path; if (config.get("output", path)) { std::ofstream file(path); print(file); } } Interpolation::Interpolation(const Config& config, const FunctionSpace& source, const Field& target): Handle([&]() -> Implementation* { std::string type; ATLAS_ASSERT(config.get("type", type)); Implementation* impl = interpolation::MethodFactory::build(type, config); impl->setup(source, target); return impl; }()) { std::string path; if (config.get("output", path)) { std::ofstream file(path); print(file); } } Interpolation::Interpolation(const Interpolation::Config& config, const FunctionSpace& source, const FieldSet& target): Handle([&]() -> Implementation* { std::string type; ATLAS_ASSERT(config.get("type", type)); Implementation* impl = interpolation::MethodFactory::build(type, config); impl->setup(source, target); return impl; }()) { std::string path; if (config.get("output", path)) { std::ofstream file(path); print(file); } } Interpolation::Metadata Interpolation::execute(const FieldSet& source, FieldSet& target) const { return get()->execute(source, target); } Interpolation::Metadata Interpolation::execute(const Field& source, Field& target) const { return get()->execute(source, target); } Interpolation::Metadata Interpolation::execute_adjoint(FieldSet& source, const FieldSet& target) const { return get()->execute_adjoint(source, target); } Interpolation::Metadata Interpolation::execute_adjoint(Field& source, const Field& target) const { return get()->execute_adjoint(source, target); } void Interpolation::print(std::ostream& out) const { get()->print(out); } const FunctionSpace& Interpolation::source() const { return get()->source(); } const FunctionSpace& Interpolation::target() const { return get()->target(); } Interpolation::Cache Interpolation::createCache() const { return get()->createCache(); } Interpolation::Interpolation(const Interpolation::Config& config, const Grid& source, const Grid& target, const Interpolation::Cache& cache): Handle([&]() -> Implementation* { std::string type; ATLAS_ASSERT(config.get("type", type)); Implementation* impl = interpolation::MethodFactory::build(type, config); impl->setup(source, target, cache); return impl; }()) {} Interpolation::Interpolation(const Interpolation::Config& config, const FunctionSpace& fs_in, const FunctionSpace& fs_out, const Interpolation::Cache& cache): Handle([&]() -> Implementation* { std::string type; ATLAS_ASSERT(config.get("type", type)); Implementation* impl = interpolation::MethodFactory::build(type, config); impl->setup(fs_in, fs_out, cache); return impl; }()) {} extern "C" { Interpolation::Implementation* atlas__Interpolation__new(const eckit::Parametrisation* config, const functionspace::FunctionSpaceImpl* source, const functionspace::FunctionSpaceImpl* target) { Interpolation::Implementation* interpolator; { Interpolation im(*config, FunctionSpace(source), FunctionSpace(target)); interpolator = const_cast(im.get()); interpolator->attach(); } interpolator->detach(); return interpolator; } Interpolation::Implementation* atlas__Interpolation__new_tgt_field(const eckit::Parametrisation* config, const functionspace::FunctionSpaceImpl* source, const field::FieldImpl* target) { Interpolation::Implementation* interpolator; { Interpolation im(*config, FunctionSpace(source), Field(target)); interpolator = const_cast(im.get()); interpolator->attach(); } interpolator->detach(); return interpolator; } Interpolation::Implementation* atlas__Interpolation__new_tgt_fieldset(const eckit::Parametrisation* config, const functionspace::FunctionSpaceImpl* source, const field::FieldSetImpl* target) { Interpolation::Implementation* interpolator; { Interpolation im(*config, FunctionSpace(source), FieldSet(target)); interpolator = const_cast(im.get()); interpolator->attach(); } interpolator->detach(); return interpolator; } void atlas__Interpolation__delete(Interpolation::Implementation* This) { delete This; } void atlas__Interpolation__execute_field(Interpolation::Implementation* This, const field::FieldImpl* source, field::FieldImpl* target) { Field t(target); This->execute(Field(source), t); } void atlas__Interpolation__execute_fieldset(Interpolation::Implementation* This, const field::FieldSetImpl* source, field::FieldSetImpl* target) { FieldSet t(target); This->execute(FieldSet(source), t); } void atlas__Interpolation__execute_adjoint_field(Interpolation::Implementation* This, field::FieldImpl* source, const field::FieldImpl* target) { Field s(source); This->execute_adjoint(s, Field(target)); } void atlas__Interpolation__execute_adjoint_fieldset(Interpolation::Implementation* This, field::FieldSetImpl* source, const field::FieldSetImpl* target) { FieldSet s(source); This->execute_adjoint(s, FieldSet(target)); } } // extern "C" } // namespace atlas atlas-0.46.0/src/atlas/interpolation/AssembleGlobalMatrix.h0000664000175000017500000000177015160212070024064 0ustar alastairalastair/* * (C) Copyright 2025 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/interpolation/Interpolation.h" #include "atlas/linalg/sparse/SparseMatrixStorage.h" namespace atlas::interpolation { atlas::linalg::SparseMatrixStorage assemble_global_matrix(size_t rows, size_t cols, const Interpolation& interpolation, int mpi_root = 0); atlas::linalg::SparseMatrixStorage assemble_global_matrix(const Interpolation& interpolation, int mpi_root = 0); atlas::linalg::SparseMatrixStorage distribute_global_matrix(const FunctionSpace& src_fs, const FunctionSpace& tgt_fs, const linalg::SparseMatrixStorage&, int mpi_root = 0); } // namespace atlas::interpolation atlas-0.46.0/src/atlas/interpolation/Vector2D.cc0000664000175000017500000000121615160212070021604 0ustar alastairalastair/* * (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 "Vector2D.h" #if !ATLAS_HAVE_EIGEN #include namespace atlas { namespace interpolation { void Vector2D::print(std::ostream& s) const { s << "[" << x() << "," << y() << "]"; } } // namespace interpolation } // namespace atlas #endif atlas-0.46.0/src/atlas/interpolation/nonlinear/0000775000175000017500000000000015160212070021632 5ustar alastairalastairatlas-0.46.0/src/atlas/interpolation/nonlinear/Missing.cc0000664000175000017500000002604015160212070023554 0ustar alastairalastair/* * (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 "atlas/interpolation/nonlinear/Missing.h" #include "eckit/types/FloatCompare.h" #include "atlas/field/MissingValue.h" #include "atlas/util/DataType.h" #include "atlas/util/Metadata.h" namespace atlas { namespace interpolation { namespace nonlinear { // Factory builders static NonLinearFactoryBuilder __nl1(MissingIfAllMissing::static_type()); static NonLinearFactoryBuilder __nl2(MissingIfAnyMissing::static_type()); static NonLinearFactoryBuilder __nl3(MissingIfHeaviestMissing::static_type()); // Deprecated factory entries with "-real32" and "-real64" suffix for backwards compatibility. // static NonLinearFactoryBuilder __nl1_real32(MissingIfAllMissing::static_type()+"-real32"); // static NonLinearFactoryBuilder __nl2_real32(MissingIfAnyMissing::static_type()+"-real32"); // static NonLinearFactoryBuilder __nl3_real32(MissingIfHeaviestMissing::static_type()+"-real32"); // static NonLinearFactoryBuilder __nl1_real64(MissingIfAllMissing::static_type()+"-real64"); // static NonLinearFactoryBuilder __nl2_real64(MissingIfAnyMissing::static_type()+"-real64"); // static NonLinearFactoryBuilder __nl3_real64(MissingIfHeaviestMissing::static_type()+"-real64"); namespace { struct force_link { template void load_builder() { NonLinearFactoryBuilder("tmp"); } force_link() { load_builder(); load_builder(); load_builder(); } }; std::string missing_value_type(const DataType& datatype, const field::MissingValue::Config& c) { std::string result; if (c.get("missing_value_type", result)) { result += "-" + datatype.str(); } return result; } } // namespace void force_link_missing() { static force_link static_linking; } bool Missing::applicable(const Field& f) const { return field::MissingValue(f); } bool MissingIfAllMissing::execute(NonLinear::Matrix& W, const Field& field) const { return execute(W, field.array(), field.metadata()); } bool MissingIfAllMissing::execute(NonLinear::Matrix& W, const array::Array& array, const Config& config) const { switch(array.datatype().kind()) { case (DataType::kind()): return executeT(W, array, config); case (DataType::kind()): return executeT(W, array, config); case (DataType::kind()): return executeT(W, array, config); case (DataType::kind()): return executeT(W, array, config); case (DataType::kind()): return executeT(W, array, config); default: ATLAS_NOTIMPLEMENTED; } } template bool MissingIfAllMissing::executeT(NonLinear::Matrix& W, const array::Array& array, const Config& config) const { field::MissingValue mv(missing_value_type(array.datatype(), config), config); auto& missingValue = mv.ref(); ATLAS_ASSERT(array.rank() == 1); // NOTE only for scalars (for now) auto values = make_view_array_values(array); ATLAS_ASSERT(idx_t(W.cols()) == values.shape(0)); auto data = const_cast(W.data()); bool modif = false; bool zeros = false; Size i = 0; Matrix::iterator it(W); for (Size r = 0; r < W.rows(); ++r) { const Matrix::iterator end = W.end(r); // count missing values, accumulate weights (disregarding missing values) size_t i_missing = i; size_t N_missing = 0; size_t N_entries = 0; Scalar sum = 0.; Matrix::iterator kt(it); Size k = i; for (; it != end; ++it, ++i, ++N_entries) { const bool miss = missingValue(values[it.col()]); if (miss) { ++N_missing; i_missing = i; } else { sum += *it; } } // weights redistribution: zero-weight all missing values, linear re-weighting for the others; // the result is missing value if all values in row are missing if (N_missing > 0) { if (N_missing == N_entries || eckit::types::is_approximately_equal(sum, 0.)) { for (Size j = k; j < k + N_entries; ++j) { data[j] = j == i_missing ? 1. : 0.; } } else { const Scalar factor = 1. / sum; for (Size j = k; j < k + N_entries; ++j, ++kt) { if (missingValue(values[kt.col()])) { data[j] = 0.; zeros = true; } else { data[j] *= factor; } } } modif = true; } } if (zeros && missingValue.isnan()) { W.prune(0.); } return modif; } bool MissingIfAnyMissing::execute(NonLinear::Matrix& W, const Field& field) const { return execute(W, field.array(), field.metadata()); } bool MissingIfAnyMissing::execute(NonLinear::Matrix& W, const array::Array& array, const Config& config) const { switch(array.datatype().kind()) { case (DataType::kind()): return executeT(W, array, config); case (DataType::kind()): return executeT(W, array, config); case (DataType::kind()): return executeT(W, array, config); case (DataType::kind()): return executeT(W, array, config); case (DataType::kind()): return executeT(W, array, config); default: ATLAS_NOTIMPLEMENTED; } } template bool MissingIfAnyMissing::executeT(NonLinear::Matrix& W, const array::Array& array, const Config& config) const { field::MissingValue mv(missing_value_type(array.datatype(), config), config); auto& missingValue = mv.ref(); // NOTE only for scalars (for now) auto values = make_view_array_values(array); ATLAS_ASSERT(idx_t(W.cols()) == values.size()); auto data = const_cast(W.data()); bool modif = false; bool zeros = false; Size i = 0; Matrix::iterator it(W); for (Size r = 0; r < W.rows(); ++r) { const Matrix::iterator end = W.end(r); // count missing values, accumulate weights (disregarding missing values) size_t i_missing = i; size_t N_missing = 0; size_t N_entries = 0; Matrix::iterator kt(it); Size k = i; for (; it != end; ++it, ++i, ++N_entries) { const bool miss = missingValue(values[it.col()]); if (miss) { ++N_missing; i_missing = i; } } // if any values in row are missing, force missing value if (N_missing > 0) { for (Size j = k; j < k + N_entries; ++j) { if (j == i_missing) { data[j] = 1.; } else { data[j] = 0.; zeros = true; } } modif = true; } } if (zeros && missingValue.isnan()) { W.prune(0.); } return modif; } bool MissingIfHeaviestMissing::execute(NonLinear::Matrix& W, const Field& field) const { return execute(W, field.array(), field.metadata()); } bool MissingIfHeaviestMissing::execute(NonLinear::Matrix& W, const array::Array& array, const Config& config) const { switch(array.datatype().kind()) { case (DataType::kind()): return executeT(W, array, config); case (DataType::kind()): return executeT(W, array, config); case (DataType::kind()): return executeT(W, array, config); case (DataType::kind()): return executeT(W, array, config); case (DataType::kind()): return executeT(W, array, config); default: ATLAS_NOTIMPLEMENTED; } } template bool MissingIfHeaviestMissing::executeT(NonLinear::Matrix& W, const array::Array& array, const Config& config) const { field::MissingValue mv(missing_value_type(array.datatype(), config), config); auto& missingValue = mv.ref(); // NOTE only for scalars (for now) auto values = make_view_array_values(array); ATLAS_ASSERT(idx_t(W.cols()) == values.size()); auto data = const_cast(W.data()); bool modif = false; bool zeros = false; Size i = 0; Matrix::iterator it(W); for (Size r = 0; r < W.rows(); ++r) { const Matrix::iterator end = W.end(r); // count missing values, accumulate weights (disregarding missing values) and find maximum weight in row size_t i_missing = i; size_t N_missing = 0; size_t N_entries = 0; Scalar sum = 0.; Scalar heaviest = -1.; bool heaviest_is_missing = false; Matrix::iterator kt(it); Size k = i; for (; it != end; ++it, ++i, ++N_entries) { const bool miss = missingValue(values[it.col()]); if (miss) { ++N_missing; i_missing = i; } else { sum += *it; } if (heaviest < data[i]) { heaviest = data[i]; heaviest_is_missing = miss; } } // weights redistribution: zero-weight all missing values, linear re-weighting for the others; // if all values are missing, or the closest value is missing, force missing value if (N_missing > 0) { if (N_missing == N_entries || heaviest_is_missing || eckit::types::is_approximately_equal(sum, 0.)) { for (Size j = k; j < k + N_entries; ++j) { data[j] = j == i_missing ? 1. : 0.; } } else { const Scalar factor = 1. / sum; for (Size j = k; j < k + N_entries; ++j, ++kt) { if (missingValue(values[kt.col()])) { data[j] = 0.; zeros = true; } else { data[j] *= factor; } } } modif = true; } } if (zeros && missingValue.isnan()) { W.prune(0.); } return modif; } } // namespace nonlinear } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/nonlinear/NonLinear.h0000664000175000017500000000641515160212070023676 0ustar alastairalastair/* * (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 #include "eckit/config/Parametrisation.h" #include "eckit/linalg/SparseMatrix.h" #include "atlas/array.h" #include "atlas/field/Field.h" #include "atlas/runtime/Exception.h" #include "atlas/util/Factory.h" #include "atlas/util/ObjectHandle.h" namespace atlas { namespace interpolation { namespace nonlinear { /** * @brief NonLinear class applies non-linear corrections to an interpolation matrix, given a field with missing values. * The interpolatation are re-weighted to factor those values out of the resulting field. */ class NonLinear : public util::Object { public: using Config = eckit::Parametrisation; using Matrix = eckit::linalg::SparseMatrix; using Scalar = eckit::linalg::Scalar; using Size = eckit::linalg::Size; /** * @brief ctor */ NonLinear() = default; /** * @brief dtor */ virtual ~NonLinear() = default; /** * @bried if NonLinear applies to field * @param [in] f field * @return if NonLinear applies to field */ virtual bool applicable(const Field& f) const = 0; /** * @brief Apply non-linear corrections to interpolation matrix * @param [inout] W interpolation matrix * @param [in] f field with missing values information * @return if W was modified */ virtual bool execute(Matrix& W, const Field& f) const = 0; /** * @brief Apply non-linear corrections to interpolation matrix * @param [inout] W interpolation matrix * @param [in] a array with possibly missing values * @param [in] c configuration with missing values diagnostic information * @return if W was modified */ virtual bool execute(Matrix& W, const array::Array& a, const Config& c) const = 0; protected: template static array::ArrayView::type, Rank> make_view_array_values(const array::Array& array) { if( array.datatype().kind() != array::DataType::kind() ) { throw_AssertionFailed("Array(DataType:" + array.datatype().str() + ") is not of required DataType", Here()); } return array::make_view::type, Rank>(array); } }; class NonLinearFactory : public util::Factory { public: using Config = NonLinear::Config; static std::string className() { return "NonLinearFactory"; } static const NonLinear* build(const std::string&, const Config&); using Factory::Factory; private: virtual const NonLinear* make(const Config&) = 0; }; template class NonLinearFactoryBuilder : public NonLinearFactory { private: virtual const NonLinear* make(const Config& /*config*/) override { return new T(/*config*/); } public: using NonLinearFactory::NonLinearFactory; }; } // namespace nonlinear } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/nonlinear/NonLinear.cc0000664000175000017500000000144515160212070024032 0ustar alastairalastair/* * (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 "atlas/interpolation/nonlinear/NonLinear.h" namespace atlas { namespace interpolation { namespace nonlinear { void force_link_missing(); const NonLinear* NonLinearFactory::build(const std::string& builder, const NonLinearFactory::Config& config) { force_link_missing(); return get(builder)->make(config); } } // namespace nonlinear } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/nonlinear/Missing.h0000664000175000017500000000355515160212070023424 0ustar alastairalastair/* * (C) Copyright 1996- ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation nor * does it submit to any jurisdiction. */ #pragma once #include "atlas/interpolation/nonlinear/NonLinear.h" namespace atlas { namespace interpolation { namespace nonlinear { struct Missing : NonLinear { private: bool applicable(const Field& f) const override; }; struct MissingIfAllMissing : Missing { bool execute(NonLinear::Matrix& W, const Field& field) const override; bool execute(NonLinear::Matrix& W, const array::Array&, const Config&) const override; template bool executeT(NonLinear::Matrix& W, const array::Array&, const Config&) const; static std::string static_type() { return "missing-if-all-missing"; } }; struct MissingIfAnyMissing : Missing { bool execute(NonLinear::Matrix& W, const Field& field) const override; bool execute(NonLinear::Matrix& W, const array::Array&, const Config&) const override; template bool executeT(NonLinear::Matrix& W, const array::Array&, const Config&) const; static std::string static_type() { return "missing-if-any-missing"; } }; struct MissingIfHeaviestMissing : Missing { bool execute(NonLinear::Matrix& W, const Field& field) const override; bool execute(NonLinear::Matrix& W, const array::Array&, const Config&) const override; template bool executeT(NonLinear::Matrix& W, const array::Array&, const Config&) const; static std::string static_type() { return "missing-if-heaviest-missing"; } }; } // namespace nonlinear } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/Vector2D.h0000664000175000017500000000514415160212070021452 0ustar alastairalastair/* * (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/library/config.h" #if ATLAS_HAVE_EIGEN #define EIGEN_NO_AUTOMATIC_RESIZING //#define EIGEN_DONT_ALIGN //#define EIGEN_DONT_VECTORIZE ATLAS_SUPPRESS_WARNINGS_PUSH ATLAS_SUPPRESS_WARNINGS_INTEGER_SIGN_CHANGE ATLAS_SUPPRESS_WARNINGS_CODE_IS_UNREACHABLE #include #include ATLAS_SUPPRESS_WARNINGS_POP #else #include #include #endif namespace atlas { namespace interpolation { //---------------------------------------------------------------------------------------------------------------------- #if ATLAS_HAVE_EIGEN typedef Eigen::Vector2d Vector2D; #else class Vector2D { public: Vector2D(const double* d) { xy_[0] = d[0]; xy_[1] = d[1]; } Vector2D(double x, double y) { xy_[0] = x; xy_[1] = y; } Vector2D() { // Warning, data_ is uninitialised } static Vector2D Map(const double* data) { return Vector2D(data); } // Operators double operator[](size_t i) const { return xy_[i]; } // Vector2D operator*(const Vector2D &) const; Vector2D operator-(const Vector2D& other) const { return Vector2D(x() - other.x(), y() - other.y()); } Vector2D operator+(const Vector2D& other) const { return Vector2D(x() + other.x(), y() + other.y()); } Vector2D operator-() const { return Vector2D(-x(), -y()); } double norm() const { return sqrt(squaredNorm()); } double squaredNorm() const { return x() * x() + y() * y(); } double dot(const Vector2D& other) const { return x() * other.x() + y() * other.y(); } double cross(const Vector2D& other) const { return x() * other.y() - y() * other.x(); } void print(std::ostream& s) const; friend std::ostream& operator<<(std::ostream& s, const Vector2D& p) { p.print(s); return s; } double* data() { return xy_; } const double* data() const { return xy_; } double x() const { return xy_[0]; } double y() const { return xy_[1]; } private: double xy_[2]; }; Vector2D operator*(double, const Vector2D&); #endif //---------------------------------------------------------------------------------------------------------------------- } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/method/0000775000175000017500000000000015160212070021125 5ustar alastairalastairatlas-0.46.0/src/atlas/interpolation/method/PointIndex3.cc0000664000175000017500000000446615160212070023612 0ustar alastairalastair /* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "eckit/config/Resource.h" #include "atlas/array/ArrayView.h" #include "atlas/array/MakeView.h" #include "atlas/interpolation/method/PointIndex3.h" #include "atlas/mesh/HybridElements.h" #include "atlas/runtime/Trace.h" #include "atlas/util/Topology.h" namespace atlas { namespace interpolation { namespace method { ElemIndex3* create_element_kdtree(const Mesh& mesh, const Field& field_centres) { ATLAS_TRACE(); const array::ArrayView centres = array::make_view(field_centres); const array::ArrayView flags = array::make_view(mesh.cells().flags()); auto include_element = [&](unsigned int e) { using util::Topology; return not Topology::view(flags(e)).check(Topology::INVALID); }; static bool fastBuildKDTrees = eckit::Resource("$ATLAS_FAST_BUILD_KDTREES", true); ElemIndex3* tree = new ElemIndex3(); const size_t nb_cells = centres.shape(0); if (fastBuildKDTrees) { std::vector p; p.reserve(nb_cells); for (unsigned int j = 0; j < nb_cells; ++j) { if (include_element(j)) { p.emplace_back(ElemIndex3::Point(centres(j, XX), centres(j, YY), centres(j, ZZ)), ElemIndex3::Payload(j)); } } tree->build(p.begin(), p.end()); } else { for (unsigned int j = 0; j < nb_cells; ++j) { if (include_element(j)) { tree->insert(ElemIndex3::Value(ElemIndex3::Point(centres(j, XX), centres(j, YY), centres(j, ZZ)), ElemIndex3::Payload(j))); } } } return tree; } ElemIndex3* create_element_centre_index(const Mesh& mesh) { return create_element_kdtree(mesh, mesh.cells().field("centre")); } } // namespace method } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/method/PointIndex2.h0000664000175000017500000000426615160212070023451 0ustar alastairalastair/* * (C) Crown Copyright 2021 Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ #pragma once #include "eckit/container/KDMapped.h" #include "eckit/container/KDMemory.h" #include "eckit/container/KDTree.h" #include "eckit/geometry/Point2.h" #include "atlas/field/Field.h" #include "atlas/mesh/Mesh.h" #include "atlas/util/CoordinateEnums.h" namespace atlas { namespace interpolation { namespace method { //---------------------------------------------------------------------------------------------------------------------- template class Point2KdTree : public eckit::KDTreeMemory { public: typedef eckit::KDTreeMemory Tree; typedef typename Tree::Alloc Alloc; typedef typename Tree::NodeInfo NodeInfo; typedef typename Tree::NodeList NodeList; typedef typename Tree::PayloadType Payload; typedef typename Tree::Point Point; typedef typename Tree::Value Value; Point2KdTree(): eckit::KDTreeMemory() {} Point2KdTree(Alloc& alloc): Tree(alloc) {} using Tree::findInSphere; using Tree::kNearestNeighbours; using Tree::nearestNeighbour; using Tree::findInSphereBruteForce; using Tree::kNearestNeighboursBruteForce; using Tree::nearestNeighbourBruteForce; }; //---------------------------------------------------------------------------------------------------------------------- struct PointIndex2TreeTrait { typedef eckit::geometry::Point2 Point; typedef size_t Payload; }; typedef Point2KdTree PointIndex2; //---------------------------------------------------------------------------------------------------------------------- struct ElemIndex2TreeTrait { typedef eckit::geometry::Point2 Point; typedef size_t Payload; }; typedef Point2KdTree ElemIndex2; ElemIndex2* create_element2D_kdtree(const Mesh& mesh, const Field& field_centres); //---------------------------------------------------------------------------------------------------------------------- } // namespace method } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/method/unstructured/0000775000175000017500000000000015160212070023674 5ustar alastairalastairatlas-0.46.0/src/atlas/interpolation/method/unstructured/SphericalMeanValue.cc0000664000175000017500000003746015160212070027725 0ustar alastairalastair/* * (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. and Interpolation */ #include #include #include #include "SphericalMeanValue.h" #include "eckit/log/Plural.h" #include "eckit/log/ProgressTimer.h" #include "eckit/log/Seconds.h" #include "atlas/functionspace/NodeColumns.h" #include "atlas/functionspace/PointCloud.h" #include "atlas/grid.h" #include "atlas/interpolation/element/Quad3D.h" #include "atlas/interpolation/element/Triag3D.h" #include "atlas/interpolation/method/MethodFactory.h" #include "atlas/interpolation/method/Ray.h" #include "atlas/linalg/sparse/MakeEckitSparseMatrix.h" #include "atlas/mesh/ElementType.h" #include "atlas/mesh/Nodes.h" #include "atlas/mesh/actions/BuildCellCentres.h" #include "atlas/mesh/actions/BuildXYZField.h" #include "atlas/meshgenerator.h" #include "atlas/parallel/GatherScatter.h" #include "atlas/parallel/mpi/Buffer.h" #include "atlas/parallel/mpi/mpi.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/ConvexSphericalPolygon.h" #include "atlas/util/CoordinateEnums.h" #include "atlas/util/Earth.h" #include "atlas/util/Point.h" namespace atlas { namespace interpolation { namespace method { namespace { MethodBuilder __builder("spherical-mean-value"); } // namespace void SphericalMeanValue::do_setup(const Grid& source, const Grid& target, const Cache& cache) { allow_halo_exchange_ = false; // no halo_exchange because we don't have any halo with delaunay or 3d structured meshgenerator if (interpolation::MatrixCache(cache)) { setMatrix(cache); ATLAS_ASSERT(matrix().rows() == target.size()); ATLAS_ASSERT(matrix().cols() == source.size()); return; } if (mpi::size() > 1) { ATLAS_NOTIMPLEMENTED; } auto make_nodecolumns = [](const Grid& grid) { Mesh mesh; if (StructuredGrid{grid}) { mesh = MeshGenerator("structured", util::Config("3d", true)).generate(grid); } else { mesh = MeshGenerator("delaunay").generate(grid); } return functionspace::NodeColumns(mesh); }; do_setup(make_nodecolumns(source), functionspace::PointCloud{target}); } void SphericalMeanValue::do_setup(const FunctionSpace& source, const FunctionSpace& target, const Cache& cache) { if (interpolation::MatrixCache(cache)) { setMatrix(cache); source_ = source; target_ = target; ATLAS_ASSERT(matrix().rows() == target.size()); ATLAS_ASSERT(matrix().cols() == source.size()); return; } do_setup(source, target); } void SphericalMeanValue::do_setup(const FunctionSpace& source, const FunctionSpace& target) { ATLAS_TRACE("atlas::interpolation::method::FiniteElement::do_setup()"); source_ = source; target_ = target; ATLAS_TRACE_SCOPE("Setup target") { auto create_xyz = [](Field lonlat_field) { auto xyz_field = Field("xyz", array::make_datatype(), array::make_shape(lonlat_field.shape(0), 3)); auto lonlat = array::make_view(lonlat_field); auto xyz = array::make_view(xyz_field); PointXYZ p2; for (idx_t n = 0; n < lonlat.shape(0); ++n) { const PointLonLat p1(lonlat(n, 0), lonlat(n, 1)); util::Earth::convertSphericalToCartesian(p1, p2); xyz(n, 0) = p2.x(); xyz(n, 1) = p2.y(); xyz(n, 2) = p2.z(); } return xyz_field; }; target_ghost_ = target.ghost(); target_lonlat_ = target.lonlat(); if (functionspace::NodeColumns tgt = target) { auto meshTarget = tgt.mesh(); target_xyz_ = mesh::actions::BuildXYZField("xyz")(meshTarget); } else { target_xyz_ = create_xyz(target_lonlat_); } } setup(source); } struct Stencil { enum { max_stencil_size = 4 }; }; void SphericalMeanValue::print(std::ostream& out) const { functionspace::NodeColumns src(source_); functionspace::NodeColumns tgt(target_); out << "atlas::interpolation::method::FiniteElement{" << std::endl; out << "max_fraction_elems_to_try: " << max_fraction_elems_to_try_; out << ", treat_failure_as_missing_value: " << treat_failure_as_missing_value_; if (not tgt) { out << "}" << std::endl; return; } out << ", NodeColumns to NodeColumns stencil weights: " << std::endl; auto gidx_src = array::make_view(src.nodes().global_index()); ATLAS_ASSERT(tgt.nodes().size() == idx_t(matrix().rows())); auto field_stencil_points_loc = tgt.createField(option::variables(Stencil::max_stencil_size)); auto field_stencil_weights_loc = tgt.createField(option::variables(Stencil::max_stencil_size)); auto field_stencil_size_loc = tgt.createField(); auto stencil_points_loc = array::make_view(field_stencil_points_loc); auto stencil_weights_loc = array::make_view(field_stencil_weights_loc); auto stencil_size_loc = array::make_view(field_stencil_size_loc); stencil_size_loc.assign(0); const auto m = atlas::linalg::make_non_owning_eckit_sparse_matrix(matrix()); for (auto it = m.begin(); it != m.end(); ++it) { idx_t p = idx_t(it.row()); idx_t& i = stencil_size_loc(p); stencil_points_loc(p, i) = gidx_src(it.col()); stencil_weights_loc(p, i) = *it; ++i; } gidx_t global_size = tgt.gather().glb_dof(); auto field_stencil_points_glb = tgt.createField(option::variables(Stencil::max_stencil_size) | option::global(0)); auto field_stencil_weights_glb = tgt.createField(option::variables(Stencil::max_stencil_size) | option::global(0)); auto field_stencil_size_glb = tgt.createField(option::global(0)); auto stencil_points_glb = array::make_view(field_stencil_points_glb); auto stencil_weights_glb = array::make_view(field_stencil_weights_glb); auto stencil_size_glb = array::make_view(field_stencil_size_glb); tgt.gather().gather(stencil_size_loc, stencil_size_glb); tgt.gather().gather(stencil_points_loc, stencil_points_glb); tgt.gather().gather(stencil_weights_loc, stencil_weights_glb); int precision = std::numeric_limits::max_digits10; for (idx_t i = 0; i < global_size; ++i) { out << std::setw(10) << i + 1 << " : "; for (idx_t j = 0; j < stencil_size_glb(i); ++j) { out << std::setw(10) << stencil_points_glb(i, j); } for (idx_t j = stencil_size_glb(i); j < Stencil::max_stencil_size; ++j) { out << " "; } for (idx_t j = 0; j < stencil_size_glb(i); ++j) { out << std::setw(precision + 5) << std::left << std::setprecision(precision) << stencil_weights_glb(i, j); } out << std::endl; } out << "}" << std::endl; } void SphericalMeanValue::setup(const FunctionSpace& source) { const functionspace::NodeColumns src = source; ATLAS_ASSERT(src); ocoords_.reset(new array::ArrayView(array::make_view(target_xyz_))); idx_t out_npts = ocoords_->shape(0); // return early if no output points on this partition reserve is called on // the triplets but also during the sparseMatrix constructor. This won't // work for empty matrices if (out_npts == 0) { return; } Mesh meshSource = src.mesh(); auto trace_setup_source = atlas::Trace{Here(), "Setup source"}; // generate 3D point coordinates Field source_xyz = mesh::actions::BuildXYZField("xyz")(meshSource); // generate barycenters of each triangle & insert them on a kd-tree util::Config config; config.set("name", "centre "); config.set("flatten_virtual_elements", false); Field cell_centres = mesh::actions::BuildCellCentres(config)(meshSource); std::unique_ptr eTree(create_element_kdtree(meshSource, cell_centres)); trace_setup_source.stop(); icoords_.reset(new array::ArrayView(array::make_view(source_xyz))); igidx_.reset(new array::ArrayView(array::make_view(src.nodes().global_index()))); connectivity_ = &meshSource.cells().node_connectivity(); const mesh::Nodes& i_nodes = meshSource.nodes(); idx_t inp_npts = i_nodes.size(); auto target_point = [this](idx_t ip) { return PointXYZ{(*ocoords_)(ip, 0), (*ocoords_)(ip, 1), (*ocoords_)(ip, 2)}; }; array::ArrayView out_ghosts = array::make_view(target_ghost_); array::ArrayView out_lonlat = array::make_view(target_lonlat_); idx_t Nelements = meshSource.cells().size(); // weights -- one per vertex of element, triangles (3) or quads (4) Triplets weights_triplets; // structure to fill-in sparse matrix weights_triplets.resize(out_npts * 4); // preallocate space as if all elements where quads auto insert_triplets = [&weights_triplets](idx_t n, const Triplets& triplets) -> bool { if (triplets.size()) { std::copy(triplets.begin(), triplets.end(), weights_triplets.begin() + 4 * n); return true; } return false; }; double search_radius = 0.; if (meshSource.metadata().has("cell_maximum_diagonal_on_unit_sphere")) { search_radius = Geometry("Earth").radius() * meshSource.metadata().getDouble("cell_maximum_diagonal_on_unit_sphere"); ASSERT(search_radius > 0.); Log::debug() << "k-d tree: search radius = " << search_radius / 1000. << " km" << std::endl; } auto find_element_candidates_in_search_radius = [&eTree, &search_radius](const PointXYZ& p) { return eTree->findInSphere(p, search_radius); }; auto find_k_nearest_element_candidates = [&eTree](const PointXYZ& p, size_t k) { return eTree->kNearestNeighbours(p, k); }; auto try_interpolate_with_element_candidates = [&insert_triplets, this](idx_t n, const ElemIndex3::NodeList& element_candidates) -> bool { if (element_candidates.empty()) { return false; } return insert_triplets(n, projectPointToElements(n, element_candidates)); }; // search nearest k cell centres const idx_t maxNbElemsToTry = std::max(8, idx_t(Nelements * max_fraction_elems_to_try_)); size_t diagnosed_max_neighbours = 0; bool allowed_to_diagnose_max_neighbours = Log::debug() && atlas_omp_get_max_threads() > 1; std::vector failures; ATLAS_TRACE_SCOPE("Computing interpolation matrix") { std::unique_ptr progress; if (atlas_omp_get_max_threads() == 1) { progress.reset(new eckit::ProgressTimer{"Computing interpolation weights", static_cast(out_npts), "point", double(5), Log::debug()}); } atlas_omp_parallel_for(idx_t ip = 0; ip < out_npts; ++ip) { if (out_ghosts(ip)) { continue; } bool success = false; if (search_radius != 0.) { auto p = target_point(ip); success = try_interpolate_with_element_candidates(ip, find_element_candidates_in_search_radius(p)); } else { size_t k = 1; auto p = target_point(ip); while (!success && k <= maxNbElemsToTry) { if (allowed_to_diagnose_max_neighbours) { // avoid race condition diagnosed_max_neighbours = std::max(k, diagnosed_max_neighbours); } success = try_interpolate_with_element_candidates(ip, find_k_nearest_element_candidates(p, k)); k *= 2; } } if (!success) { atlas_omp_critical { failures.emplace_back(ip); } } if (progress) { ++(*progress); } } } if (diagnosed_max_neighbours) { Log::debug() << "Maximum neighbours searched was " << eckit::Plural(diagnosed_max_neighbours, "element") << std::endl; } if (failures.size()) { if (treat_failure_as_missing_value_) { missing_.resize(failures.size()); std::copy(std::begin(failures), std::end(failures), missing_.begin()); } else { // If this fails, consider lowering atlas::grid::parametricEpsilon std::ostringstream msg; msg << "Rank " << mpi::rank() << " failed to project points:\n"; for (std::vector::const_iterator i = failures.begin(); i != failures.end(); ++i) { const PointLonLat pll{out_lonlat(*i, (size_t)0), out_lonlat(*i, (size_t)1)}; // lookup point msg << "\t(lon,lat) = " << pll << "\n"; } Log::error() << msg.str() << std::endl; throw_Exception(msg.str()); } } // fill sparse matrix and return, this cannot be multithreaded! setMatrix(out_npts, inp_npts, weights_triplets); } Method::Triplets SphericalMeanValue::projectPointToElements(size_t ip, const ElemIndex3::NodeList& elems) const { ATLAS_ASSERT(elems.begin() != elems.end()); const size_t inp_points = icoords_->shape(0); Triplets triplets; triplets.reserve(4); const PointXYZ candidatePoint = PointXYZ::normalize( PointXYZ{(*ocoords_)(ip, size_t(0)), (*ocoords_)(ip, size_t(1)), (*ocoords_)(ip, size_t(2))}); for (ElemIndex3::NodeList::const_iterator itc = elems.begin(); itc != elems.end(); ++itc) { const idx_t elem_id = idx_t((*itc).value().payload()); ATLAS_ASSERT(elem_id < connectivity_->rows()); size_t nb_cols = connectivity_->cols(elem_id); std::vector idx; idx.reserve(nb_cols); for (idx_t i = 0; i < nb_cols; ++i) { idx.emplace_back((*connectivity_)(elem_id, i)); ATLAS_ASSERT(idx[i] < inp_points); } std::vector listVertices; listVertices.reserve(util::ConvexSphericalPolygon::MAX_SIZE); for (idx_t i = 0; i < nb_cols; ++i) { listVertices.emplace_back(PointXYZ::normalize(PointXYZ{ (*icoords_)(idx[i], size_t(0)), (*icoords_)(idx[i], size_t(1)), (*icoords_)(idx[i], size_t(2))})); } util::ConvexSphericalPolygon currentPolygon(listVertices.data(), listVertices.size()); nb_cols = currentPolygon.size(); ATLAS_ASSERT(nb_cols == 3 || nb_cols == 4); std::vector polygonWeights(nb_cols); if (currentPolygon.compute_vertex_weights(candidatePoint, polygonWeights.data(), polygonWeights.size()) == 1) { if (normalisation_) { double weightsSum = 0; for (size_t i = 0; i < nb_cols; ++i) { weightsSum += (polygonWeights)[i]; } for (size_t i = 0; i < nb_cols; ++i) { (polygonWeights)[i] /= weightsSum; } } for (size_t i = 0; i < nb_cols; ++i) { triplets.emplace_back(ip, idx[i], (polygonWeights)[i]); } break; } } return triplets; } } // namespace method } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/method/unstructured/SphericalMeanValue.h0000664000175000017500000000566415160212070027570 0ustar alastairalastair/* * (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/interpolation/method/Method.h" #include #include "eckit/config/Configuration.h" #include "atlas/array/ArrayView.h" #include "atlas/functionspace/FunctionSpace.h" #include "atlas/interpolation/method/PointIndex3.h" #include "atlas/mesh/Elements.h" namespace atlas { namespace interpolation { namespace method { class SphericalMeanValue : public Method { public: SphericalMeanValue(const Config& config): Method(config) { config.get("max_fraction_elems_to_try", max_fraction_elems_to_try_); config.get("treat_failure_as_missing_value", treat_failure_as_missing_value_); config.get("normalisation", normalisation_); } virtual ~SphericalMeanValue() override {} virtual void print(std::ostream&) const override; protected: /** * @brief Create an interpolant sparse matrix relating two (pre-partitioned) * meshes, * using elements as per the Finite Element Method and ray-tracing to * calculate * source mesh elements intersections (and interpolation weights) with target * grid * node-containing rays * @param meshSource mesh containing source elements * @param meshTarget mesh containing target points */ void setup(const FunctionSpace& source); /** * Find in which element the point is contained by projecting (ray-tracing) * the * point to the nearest element(s), returning the (normalized) interpolation * weights */ Triplets projectPointToElements(size_t ip, const ElemIndex3::NodeList& elems) const; virtual const FunctionSpace& source() const override { return source_; } virtual const FunctionSpace& target() const override { return target_; } private: using Method::do_setup; virtual void do_setup(const FunctionSpace& source, const FunctionSpace& target) override; virtual void do_setup(const Grid& source, const Grid& target, const Cache&) override; virtual void do_setup(const FunctionSpace& source, const FunctionSpace& target, const Cache&) override; protected: mesh::MultiBlockConnectivity* connectivity_; std::unique_ptr> icoords_; std::unique_ptr> ocoords_; std::unique_ptr> igidx_; Field target_lonlat_; Field target_xyz_; Field target_ghost_; FunctionSpace source_; FunctionSpace target_; bool treat_failure_as_missing_value_{true}; double max_fraction_elems_to_try_{0.2}; bool normalisation_{true}; }; } // namespace method } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/method/unstructured/UnstructuredBilinearLonLat.cc0000664000175000017500000004461415160212070031503 0ustar alastairalastair/* * (C) Crown Copyright 2021 Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ #include #include #include #include "UnstructuredBilinearLonLat.h" #include "eckit/log/Plural.h" #include "eckit/log/ProgressTimer.h" #include "eckit/log/Seconds.h" #include "atlas/functionspace/NodeColumns.h" #include "atlas/functionspace/PointCloud.h" #include "atlas/grid.h" #include "atlas/interpolation/element/Quad2D.h" #include "atlas/interpolation/element/Triag2D.h" #include "atlas/interpolation/method/MethodFactory.h" #include "atlas/interpolation/method/Ray.h" #include "atlas/linalg/sparse/MakeEckitSparseMatrix.h" #include "atlas/mesh/ElementType.h" #include "atlas/mesh/Nodes.h" #include "atlas/mesh/actions/BuildCellCentres.h" #include "atlas/mesh/actions/BuildXYZField.h" #include "atlas/meshgenerator.h" #include "atlas/parallel/GatherScatter.h" #include "atlas/parallel/mpi/Buffer.h" #include "atlas/parallel/mpi/mpi.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/CoordinateEnums.h" #include "atlas/util/Earth.h" #include "atlas/util/Point.h" namespace atlas { namespace interpolation { namespace method { namespace { MethodBuilder __builder_2("unstructured-bilinear-lonlat"); // epsilon used to scale edge tolerance when projecting ray to intesect element static const double parametricEpsilon = 1e-15; } // namespace void UnstructuredBilinearLonLat::do_setup(const Grid& source, const Grid& target, const Cache& cache) { allow_halo_exchange_ = false; // no halo_exchange because we don't have any halo with delaunay or 3d structured meshgenerator if (interpolation::MatrixCache(cache)) { setMatrix(cache); ATLAS_ASSERT(matrix().rows() == target.size()); ATLAS_ASSERT(matrix().cols() == source.size()); return; } if (mpi::size() > 1) { ATLAS_NOTIMPLEMENTED; } auto make_nodecolumns = [](const Grid& grid) { Mesh mesh; if (StructuredGrid{grid}) { mesh = MeshGenerator("structured", util::Config("3d", true)).generate(grid); } else { mesh = MeshGenerator("delaunay").generate(grid); } return functionspace::NodeColumns(mesh); }; do_setup(make_nodecolumns(source), functionspace::PointCloud{target}); } void UnstructuredBilinearLonLat::do_setup(const FunctionSpace& source, const FunctionSpace& target, const Cache& cache) { allow_halo_exchange_ = false; // no halo_exchange because we don't have any halo with delaunay or 3d structured meshgenerator if (interpolation::MatrixCache(cache)) { setMatrix(cache); source_ = source; target_ = target; ATLAS_ASSERT(matrix().rows() == target.size()); ATLAS_ASSERT(matrix().cols() == source.size()); return; } do_setup(source, target); } void UnstructuredBilinearLonLat::do_setup(const FunctionSpace& source, const FunctionSpace& target) { ATLAS_TRACE("atlas::interpolation::method::BilinearRemapping::do_setup()"); source_ = source; target_ = target; ATLAS_TRACE_SCOPE("Setup target") { auto create_xyz = [](Field lonlat_field) { auto xyz_field = Field("xyz", array::make_datatype(), array::make_shape(lonlat_field.shape(0), 3)); auto lonlat = array::make_view(lonlat_field); auto xyz = array::make_view(xyz_field); PointXYZ p2; for (idx_t n = 0; n < lonlat.shape(0); ++n) { const PointLonLat p1(lonlat(n, 0), lonlat(n, 1)); util::Earth::convertSphericalToCartesian(p1, p2); xyz(n, 0) = p2.x(); xyz(n, 1) = p2.y(); xyz(n, 2) = p2.z(); } return xyz_field; }; target_ghost_ = target.ghost(); target_lonlat_ = target.lonlat(); if (functionspace::NodeColumns tgt = target) { auto meshTarget = tgt.mesh(); target_xyz_ = mesh::actions::BuildXYZField("xyz")(meshTarget); } else { target_xyz_ = create_xyz(target_lonlat_); } } setup(source); } struct Stencil { enum { max_stencil_size = 4 }; }; void UnstructuredBilinearLonLat::print(std::ostream& out) const { functionspace::NodeColumns src(source_); functionspace::NodeColumns tgt(target_); out << "atlas::interpolation::method::BilinearRemapping{" << std::endl; out << "treat_failure_as_missing_value: " << treat_failure_as_missing_value_; if (not tgt) { out << "}" << std::endl; return; } out << ", NodeColumns to NodeColumns stencil weights: " << std::endl; auto gidx_src = array::make_view(src.nodes().global_index()); ATLAS_ASSERT(tgt.nodes().size() == idx_t(matrix().rows())); auto field_stencil_points_loc = tgt.createField(option::variables(Stencil::max_stencil_size)); auto field_stencil_weights_loc = tgt.createField(option::variables(Stencil::max_stencil_size)); auto field_stencil_size_loc = tgt.createField(); auto stencil_points_loc = array::make_view(field_stencil_points_loc); auto stencil_weights_loc = array::make_view(field_stencil_weights_loc); auto stencil_size_loc = array::make_view(field_stencil_size_loc); stencil_size_loc.assign(0); const auto m = atlas::linalg::make_non_owning_eckit_sparse_matrix(matrix()); for (auto it = m.begin(); it != m.end(); ++it) { idx_t p = idx_t(it.row()); idx_t& i = stencil_size_loc(p); stencil_points_loc(p, i) = gidx_src(it.col()); stencil_weights_loc(p, i) = *it; ++i; } gidx_t global_size = tgt.gather().glb_dof(); auto field_stencil_points_glb = tgt.createField(option::variables(Stencil::max_stencil_size) | option::global(0)); auto field_stencil_weights_glb = tgt.createField(option::variables(Stencil::max_stencil_size) | option::global(0)); auto field_stencil_size_glb = tgt.createField(option::global(0)); auto stencil_points_glb = array::make_view(field_stencil_points_glb); auto stencil_weights_glb = array::make_view(field_stencil_weights_glb); auto stencil_size_glb = array::make_view(field_stencil_size_glb); tgt.gather().gather(stencil_size_loc, stencil_size_glb); tgt.gather().gather(stencil_points_loc, stencil_points_glb); tgt.gather().gather(stencil_weights_loc, stencil_weights_glb); int precision = std::numeric_limits::max_digits10; for (idx_t i = 0; i < global_size; ++i) { out << std::setw(10) << i + 1 << " : "; for (idx_t j = 0; j < stencil_size_glb(i); ++j) { out << std::setw(10) << stencil_points_glb(i, j); } for (idx_t j = stencil_size_glb(i); j < Stencil::max_stencil_size; ++j) { out << " "; } for (idx_t j = 0; j < stencil_size_glb(i); ++j) { out << std::setw(precision + 5) << std::left << std::setprecision(precision) << stencil_weights_glb(i, j); } out << std::endl; } out << "}" << std::endl; } void UnstructuredBilinearLonLat::setup(const FunctionSpace& source) { const functionspace::NodeColumns src = source; ATLAS_ASSERT(src); Mesh meshSource = src.mesh(); auto trace_setup_source = atlas::Trace{Here(), "Setup source"}; if (target_lonlat_.shape(0) == 0) { // return early if no output points on this partition reserve is called on // the triplets but also during the sparseMatrix constructor. This won't // work for empty matrices return; } // 3D point coordinates Field source_xyz = mesh::actions::BuildXYZField("xyz")(meshSource); // generate barycenters of each triangle & insert them on a kd-tree util::Config config; config.set("name", "centre "); config.set("flatten_virtual_elements", false); Field cell_centres = mesh::actions::BuildCellCentres(config)(meshSource); std::unique_ptr eTree(create_element_kdtree(meshSource, cell_centres)); trace_setup_source.stop(); ilonlat_.reset(new array::ArrayView(array::make_view(meshSource.nodes().lonlat()))); olonlat_.reset(new array::ArrayView(array::make_view(target_lonlat_))); oxyz_.reset(new array::ArrayView(array::make_view(target_xyz_))); igidx_.reset(new array::ArrayView(array::make_view(src.nodes().global_index()))); connectivity_ = &meshSource.cells().node_connectivity(); const mesh::Nodes& i_nodes = meshSource.nodes(); idx_t inp_npts = i_nodes.size(); idx_t out_npts = olonlat_->shape(0); array::ArrayView out_ghosts = array::make_view(target_ghost_); // weights -- one per vertex of element, triangles (3) or quads (4) Triplets weights_triplets; // structure to fill-in sparse matrix weights_triplets.resize(out_npts * 4); // preallocate space auto insert_triplets = [&weights_triplets](idx_t n, const Triplets& triplets) -> bool { if (triplets.size()) { std::copy(triplets.begin(), triplets.end(), weights_triplets.begin()+4*n); return true; } return false; }; auto target_point = [this](idx_t ip) { return PointXYZ{(*oxyz_)(ip, 0), (*oxyz_)(ip, 1), (*oxyz_)(ip, 2)}; }; auto find_k_nearest_element_candidates = [&eTree](const PointXYZ& p, size_t k) { return eTree->kNearestNeighbours(p, k); }; auto try_interpolate_with_element_candidates = [&insert_triplets, this](idx_t n, const ElemIndex3::NodeList& element_candidates) -> bool { if (element_candidates.empty()) { return false; } return insert_triplets(n, projectPointToElements(n, element_candidates)); }; // search nearest k cell centres std::vector failures; ATLAS_TRACE_SCOPE("Computing interpolation matrix") { std::unique_ptr progress; if (atlas_omp_get_max_threads() == 1) { progress.reset(new eckit::ProgressTimer{"Computing interpolation weights", static_cast(out_npts), "point", double(5), Log::debug()}); } atlas_omp_parallel_for (idx_t ip = 0; ip < out_npts; ++ip) { if (out_ghosts(ip)) { continue; } auto p = target_point(ip); bool success = try_interpolate_with_element_candidates(ip, find_k_nearest_element_candidates(p, 8)); if (!success) { atlas_omp_critical { failures.emplace_back(ip); } } } } if (failures.size()) { if (treat_failure_as_missing_value_) { missing_.resize(failures.size()); std::copy(std::begin(failures), std::end(failures), missing_.begin()); } else { // If this fails, consider lowering atlas::grid::parametricEpsilon std::ostringstream msg; msg << "Rank " << mpi::rank() << " failed to project points:\n"; for (auto i : failures) { const PointLonLat pll{(*olonlat_)(i, LON), (*olonlat_)(i, LAT)}; // lookup point msg << "\t(lon,lat) = " << pll << "\n"; } Log::error() << msg.str() << std::endl; throw_Exception(msg.str()); } } // fill sparse matrix and return setMatrix(out_npts, inp_npts, weights_triplets); } Method::Triplets UnstructuredBilinearLonLat::projectPointToElements(size_t ip, const ElemIndex3::NodeList& elems) const { ATLAS_ASSERT(elems.begin() != elems.end()); const size_t inp_points = ilonlat_->shape(0); std::array idx; std::array w; std::array inv_dist_w; Triplets triplets; triplets.reserve(4); double o_lon{(*olonlat_)(ip, LON)}; while (o_lon >= 360.0) { o_lon -= 360.0; } while (o_lon < 0.0) { o_lon += 360.0; } PointLonLat o_loc{o_lon, (*olonlat_)(ip, LAT)}; // lookup point auto inv_dist_weight_quad = [](element::Quad2D& q, const PointXY& loc, std::array& w) { double d[4]; d[0] = util::Earth::distance({q.p(0).data()}, loc); d[1] = util::Earth::distance({q.p(1).data()}, loc); d[2] = util::Earth::distance({q.p(2).data()}, loc); d[3] = util::Earth::distance({q.p(3).data()}, loc); w[0] = d[1] * d[2] * d[3]; w[1] = d[0] * d[2] * d[3]; w[2] = d[1] * d[0] * d[3]; w[3] = d[1] * d[0] * d[2]; double suminv = 1. / (w[0] + w[1] + w[2] + w[3]); for (size_t i = 0; i < 4; ++i) { w[i] *= suminv; } }; auto inv_dist_weight_triag = [](element::Triag2D& q, const PointXY& loc, std::array& w) { double d[3]; d[0] = util::Earth::distance({q.p(0).data()}, loc); d[1] = util::Earth::distance({q.p(1).data()}, loc); d[2] = util::Earth::distance({q.p(2).data()}, loc); w[0] = d[1] * d[2]; w[1] = d[0] * d[2]; w[2] = d[1] * d[0]; w[3] = 0.; double suminv = 1. / (w[0] + w[1] + w[2]); for (size_t i = 0; i < 3; ++i) { w[i] *= suminv; } }; for (ElemIndex3::NodeList::const_iterator itc = elems.begin(); itc != elems.end(); ++itc) { const idx_t elem_id = idx_t((*itc).value().payload()); ATLAS_ASSERT(elem_id < connectivity_->rows()); const idx_t nb_cols = connectivity_->cols(elem_id); ATLAS_ASSERT(nb_cols == 3 || nb_cols == 4); for (idx_t i = 0; i < nb_cols; ++i) { idx[i] = (*connectivity_)(elem_id, i); ATLAS_ASSERT(idx[i] < inp_points); } if (nb_cols == 3) { /* triangle */ element::Triag2D triag(PointLonLat{(*ilonlat_)(idx[0], LON), (*ilonlat_)(idx[0], LAT)}, PointLonLat{(*ilonlat_)(idx[1], LON), (*ilonlat_)(idx[1], LAT)}, PointLonLat{(*ilonlat_)(idx[2], LON), (*ilonlat_)(idx[2], LAT)}); if (itc == elems.begin()) { inv_dist_weight_triag(triag, o_loc, inv_dist_w); } // pick an epsilon based on a characteristic length (sqrt(area)) // (this scales linearly so it better compares with linear weights u,v,w) const double edgeEpsilon = parametricEpsilon * std::sqrt(triag.area()); ATLAS_ASSERT(edgeEpsilon >= 0); Intersect is = triag.intersects(o_loc, edgeEpsilon); if (is) { // weights are the linear Lagrange function evaluated at u,v (aka // barycentric coordinates) w[0] = 1. - is.u - is.v; w[1] = is.u; w[2] = is.v; for (size_t i = 0; i < 3; ++i) { triplets.emplace_back(ip, idx[i], w[i]); } break; // stop looking for elements } } else { /* quadrilateral */ double lons[4]; lons[0] = (*ilonlat_)(idx[0], LON); lons[1] = (*ilonlat_)(idx[1], LON); lons[2] = (*ilonlat_)(idx[2], LON); lons[3] = (*ilonlat_)(idx[3], LON); // adjust quadrilaterals to lie within [0,360] for (idx_t i = 0; i < 4; ++i) { while (lons[i] > 360.0) { lons[i] -= 360.0; } while (lons[i] < 0.0) { lons[i] += 360.0; } } // shift cells on the east-west periodic boundary from the east to the west // so that the quad surrounds a point with output longitude in [0,360) double minlon = std::numeric_limits::max(); for ( int i = 0; i < 4; i++ ) { minlon = std::min( minlon, lons[i] ); } for ( int i = 0; i < 4; i++ ) { if ( (lons[i] - minlon) > 180 ) { lons[i] -= 360; } } element::Quad2D quad( PointLonLat{lons[0], (*ilonlat_)(idx[0], LAT)}, PointLonLat{lons[1], (*ilonlat_)(idx[1], LAT)}, PointLonLat{lons[2], (*ilonlat_)(idx[2], LAT)}, PointLonLat{lons[3], (*ilonlat_)(idx[3], LAT)}); ATLAS_ASSERT( quad.validate() ); if (itc == elems.begin()) { inv_dist_weight_quad(quad, o_loc, inv_dist_w); } // pick an epsilon based on a characteristic length (sqrt(area)) // (this scales linearly so it better compares with linear weights u,v,w) const double edgeEpsilon = parametricEpsilon * std::sqrt(quad.area()); ATLAS_ASSERT(edgeEpsilon >= 0); Intersect is = quad.localRemap(o_loc, edgeEpsilon); if (!is) { // repeat the calculation to catch points which are near the east boundary of the grid. is = quad.localRemap({o_lon - 360, (*olonlat_)(ip, LAT)}, edgeEpsilon); } if (is) { // weights are the bilinear Lagrange function evaluated at u,v w[0] = (1. - is.u) * (1. - is.v); w[1] = is.u * (1. - is.v); w[2] = is.u * is.v; w[3] = (1. - is.u) * is.v; for (size_t i = 0; i < 4; ++i) { triplets.emplace_back(ip, idx[i], w[i]); } break; // stop looking for elements } } } // loop over nearest elements if (triplets.empty()) { // Crude inverse distance weighting to catch cells that are difficult // to identify and interpolate in a lon/lat projection, near the north // pole const idx_t elem_id = idx_t((*elems.begin()).value().payload()); for (size_t i = 0; i < connectivity_->cols(elem_id); ++i) { idx[i] = (*connectivity_)(elem_id, i); triplets.emplace_back(ip, idx[i], inv_dist_w[i]); } } if (!triplets.empty()) { normalise(triplets); } return triplets; } } // namespace method } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/method/unstructured/FiniteElement.h0000664000175000017500000000552115160212070026600 0ustar alastairalastair/* * (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/interpolation/method/Method.h" #include #include "eckit/config/Configuration.h" #include "atlas/array/ArrayView.h" #include "atlas/functionspace/FunctionSpace.h" #include "atlas/interpolation/method/PointIndex3.h" #include "atlas/mesh/Elements.h" namespace atlas { namespace interpolation { namespace method { class FiniteElement : public Method { public: FiniteElement(const Config& config): Method(config) { config.get("max_fraction_elems_to_try", max_fraction_elems_to_try_); config.get("treat_failure_as_missing_value", treat_failure_as_missing_value_); } virtual ~FiniteElement() override {} virtual void print(std::ostream&) const override; protected: /** * @brief Create an interpolant sparse matrix relating two (pre-partitioned) * meshes, * using elements as per the Finite Element Method and ray-tracing to * calculate * source mesh elements intersections (and interpolation weights) with target * grid * node-containing rays * @param meshSource mesh containing source elements * @param meshTarget mesh containing target points */ void setup(const FunctionSpace& source); /** * Find in which element the point is contained by projecting (ray-tracing) * the * point to the nearest element(s), returning the (normalized) interpolation * weights */ Triplets projectPointToElements(size_t ip, const ElemIndex3::NodeList& elems) const; virtual const FunctionSpace& source() const override { return source_; } virtual const FunctionSpace& target() const override { return target_; } private: using Method::do_setup; virtual void do_setup(const FunctionSpace& source, const FunctionSpace& target) override; virtual void do_setup(const Grid& source, const Grid& target, const Cache&) override; virtual void do_setup(const FunctionSpace& source, const FunctionSpace& target, const Cache&) override; protected: mesh::MultiBlockConnectivity* connectivity_; std::unique_ptr> icoords_; std::unique_ptr> ocoords_; std::unique_ptr> igidx_; Field target_lonlat_; Field target_xyz_; Field target_ghost_; FunctionSpace source_; FunctionSpace target_; bool treat_failure_as_missing_value_{true}; double max_fraction_elems_to_try_{0.2}; }; } // namespace method } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/method/unstructured/FiniteElement.cc0000664000175000017500000005456115160212070026746 0ustar alastairalastair/* * (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. and Interpolation */ #include #include #include #include "FiniteElement.h" #include "eckit/log/Plural.h" #include "eckit/log/ProgressTimer.h" #include "eckit/log/Seconds.h" #include "atlas/functionspace/NodeColumns.h" #include "atlas/functionspace/PointCloud.h" #include "atlas/grid.h" #include "atlas/interpolation/element/Quad3D.h" #include "atlas/interpolation/element/Triag3D.h" #include "atlas/interpolation/method/MethodFactory.h" #include "atlas/interpolation/method/Ray.h" #include "atlas/linalg/sparse/MakeEckitSparseMatrix.h" #include "atlas/mesh/ElementType.h" #include "atlas/mesh/Nodes.h" #include "atlas/mesh/actions/BuildCellCentres.h" #include "atlas/mesh/actions/BuildXYZField.h" #include "atlas/meshgenerator.h" #include "atlas/parallel/GatherScatter.h" #include "atlas/parallel/mpi/Buffer.h" #include "atlas/parallel/mpi/mpi.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/CoordinateEnums.h" #include "atlas/util/Earth.h" #include "atlas/util/Point.h" namespace atlas { namespace interpolation { namespace method { namespace { MethodBuilder __builder("finite-element"); // epsilon used to scale edge tolerance when projecting ray to intesect element static const double parametricEpsilon = 1e-15; } // namespace void FiniteElement::do_setup(const Grid& source, const Grid& target, const Cache& cache) { allow_halo_exchange_ = false; // no halo_exchange because we don't have any halo with delaunay or 3d structured meshgenerator if (interpolation::MatrixCache(cache)) { setMatrix(cache); ATLAS_ASSERT(matrix().rows() == target.size()); ATLAS_ASSERT(matrix().cols() == source.size()); return; } if (mpi::size() > 1) { ATLAS_NOTIMPLEMENTED; } auto make_nodecolumns = [](const Grid& grid) { Mesh mesh; if (StructuredGrid{grid}) { mesh = MeshGenerator("structured", util::Config("3d", true)).generate(grid); } else { mesh = MeshGenerator("delaunay").generate(grid); } return functionspace::NodeColumns(mesh); }; do_setup(make_nodecolumns(source), functionspace::PointCloud{target}); } void FiniteElement::do_setup(const FunctionSpace& source, const FunctionSpace& target, const Cache& cache) { if (interpolation::MatrixCache(cache)) { setMatrix(cache); source_ = source; target_ = target; ATLAS_ASSERT(matrix().rows() == target.size()); ATLAS_ASSERT(matrix().cols() == source.size()); return; } do_setup(source, target); } void FiniteElement::do_setup(const FunctionSpace& source, const FunctionSpace& target) { ATLAS_TRACE("atlas::interpolation::method::FiniteElement::do_setup()"); source_ = source; target_ = target; ATLAS_TRACE_SCOPE("Setup target") { auto create_xyz = [](Field lonlat_field) { auto xyz_field = Field("xyz", array::make_datatype(), array::make_shape(lonlat_field.shape(0), 3)); auto lonlat = array::make_view(lonlat_field); auto xyz = array::make_view(xyz_field); PointXYZ p2; for (idx_t n = 0; n < lonlat.shape(0); ++n) { const PointLonLat p1(lonlat(n, 0), lonlat(n, 1)); util::Earth::convertSphericalToCartesian(p1, p2); xyz(n, 0) = p2.x(); xyz(n, 1) = p2.y(); xyz(n, 2) = p2.z(); } return xyz_field; }; target_ghost_ = target.ghost(); target_lonlat_ = target.lonlat(); if (functionspace::NodeColumns tgt = target) { auto meshTarget = tgt.mesh(); target_xyz_ = mesh::actions::BuildXYZField("xyz")(meshTarget); } else { target_xyz_ = create_xyz(target_lonlat_); } } setup(source); } struct Stencil { enum { max_stencil_size = 4 }; }; void FiniteElement::print(std::ostream& out) const { functionspace::NodeColumns src(source_); functionspace::NodeColumns tgt(target_); out << "atlas::interpolation::method::FiniteElement{" << std::endl; out << "max_fraction_elems_to_try: " << max_fraction_elems_to_try_; out << ", treat_failure_as_missing_value: " << treat_failure_as_missing_value_; if (not tgt) { out << "}" << std::endl; return; } out << ", NodeColumns to NodeColumns stencil weights: " << std::endl; auto gidx_src = array::make_view(src.nodes().global_index()); ATLAS_ASSERT(tgt.nodes().size() == idx_t(matrix().rows())); auto field_stencil_points_loc = tgt.createField(option::variables(Stencil::max_stencil_size)); auto field_stencil_weights_loc = tgt.createField(option::variables(Stencil::max_stencil_size)); auto field_stencil_size_loc = tgt.createField(); auto stencil_points_loc = array::make_view(field_stencil_points_loc); auto stencil_weights_loc = array::make_view(field_stencil_weights_loc); auto stencil_size_loc = array::make_view(field_stencil_size_loc); stencil_size_loc.assign(0); const auto m = atlas::linalg::make_non_owning_eckit_sparse_matrix(matrix()); for (auto it = m.begin(); it != m.end(); ++it) { idx_t p = idx_t(it.row()); idx_t& i = stencil_size_loc(p); stencil_points_loc(p, i) = gidx_src(it.col()); stencil_weights_loc(p, i) = *it; ++i; } gidx_t global_size = tgt.gather().glb_dof(); auto field_stencil_points_glb = tgt.createField(option::variables(Stencil::max_stencil_size) | option::global(0)); auto field_stencil_weights_glb = tgt.createField(option::variables(Stencil::max_stencil_size) | option::global(0)); auto field_stencil_size_glb = tgt.createField(option::global(0)); auto stencil_points_glb = array::make_view(field_stencil_points_glb); auto stencil_weights_glb = array::make_view(field_stencil_weights_glb); auto stencil_size_glb = array::make_view(field_stencil_size_glb); tgt.gather().gather(stencil_size_loc, stencil_size_glb); tgt.gather().gather(stencil_points_loc, stencil_points_glb); tgt.gather().gather(stencil_weights_loc, stencil_weights_glb); int precision = std::numeric_limits::max_digits10; for (idx_t i = 0; i < global_size; ++i) { out << std::setw(10) << i + 1 << " : "; for (idx_t j = 0; j < stencil_size_glb(i); ++j) { out << std::setw(10) << stencil_points_glb(i, j); } for (idx_t j = stencil_size_glb(i); j < Stencil::max_stencil_size; ++j) { out << " "; } for (idx_t j = 0; j < stencil_size_glb(i); ++j) { out << std::setw(precision + 5) << std::left << std::setprecision(precision) << stencil_weights_glb(i, j); } out << std::endl; } out << "}" << std::endl; } void FiniteElement::setup(const FunctionSpace& source) { const functionspace::NodeColumns src = source; ATLAS_ASSERT(src); ocoords_.reset(new array::ArrayView(array::make_view(target_xyz_))); idx_t out_npts = ocoords_->shape(0); // return early if no output points on this partition reserve is called on // the triplets but also during the sparseMatrix constructor. This won't // work for empty matrices if (out_npts == 0) { return; } Mesh meshSource = src.mesh(); auto trace_setup_source = atlas::Trace{Here(), "Setup source"}; // generate 3D point coordinates Field source_xyz = mesh::actions::BuildXYZField("xyz")(meshSource); // generate barycenters of each triangle & insert them on a kd-tree util::Config config; config.set("name", "centre "); config.set("flatten_virtual_elements", false); Field cell_centres = mesh::actions::BuildCellCentres(config)(meshSource); std::unique_ptr eTree(create_element_kdtree(meshSource, cell_centres)); trace_setup_source.stop(); icoords_.reset(new array::ArrayView(array::make_view(source_xyz))); igidx_.reset(new array::ArrayView(array::make_view(src.nodes().global_index()))); connectivity_ = &meshSource.cells().node_connectivity(); const mesh::Nodes& i_nodes = meshSource.nodes(); idx_t inp_npts = i_nodes.size(); auto target_point = [this](idx_t ip) { return PointXYZ{(*ocoords_)(ip, 0), (*ocoords_)(ip, 1), (*ocoords_)(ip, 2)}; }; array::ArrayView out_ghosts = array::make_view(target_ghost_); array::ArrayView out_lonlat = array::make_view(target_lonlat_); idx_t Nelements = meshSource.cells().size(); // weights -- one per vertex of element, triangles (3) or quads (4) Triplets weights_triplets; // structure to fill-in sparse matrix weights_triplets.resize(out_npts * 4); // preallocate space as if all elements where quads auto insert_triplets = [&weights_triplets](idx_t n, const Triplets& triplets) -> bool { if (triplets.size()) { std::copy(triplets.begin(), triplets.end(), weights_triplets.begin()+4*n); return true; } return false; }; double search_radius = 0.; if (meshSource.metadata().has("cell_maximum_diagonal_on_unit_sphere")) { search_radius = Geometry("Earth").radius() * meshSource.metadata().getDouble("cell_maximum_diagonal_on_unit_sphere"); ASSERT(search_radius > 0.); Log::debug() << "k-d tree: search radius = " << search_radius/1000. << " km" << std::endl; } auto find_element_candidates_in_search_radius = [&eTree,&search_radius](const PointXYZ& p) { return eTree->findInSphere(p, search_radius); }; auto find_k_nearest_element_candidates = [&eTree](const PointXYZ& p, size_t k) { return eTree->kNearestNeighbours(p, k); }; auto try_interpolate_with_element_candidates = [&insert_triplets, this](idx_t n, const ElemIndex3::NodeList& element_candidates) -> bool { if (element_candidates.empty()) { return false; } return insert_triplets(n, projectPointToElements(n, element_candidates)); }; // search nearest k cell centres const idx_t maxNbElemsToTry = std::max(8, idx_t(Nelements * max_fraction_elems_to_try_)); size_t diagnosed_max_neighbours = 0; bool allowed_to_diagnose_max_neighbours = Log::debug() && atlas_omp_get_max_threads() > 1; std::vector failures; ATLAS_TRACE_SCOPE("Computing interpolation matrix") { std::unique_ptr progress; if (atlas_omp_get_max_threads() == 1) { progress.reset(new eckit::ProgressTimer{"Computing interpolation weights", static_cast(out_npts), "point", double(5), Log::debug()}); } atlas_omp_parallel_for (idx_t ip = 0; ip < out_npts; ++ip) { if (out_ghosts(ip)) { continue; } bool success = false; if (search_radius != 0.) { auto p = target_point(ip); success = try_interpolate_with_element_candidates(ip, find_element_candidates_in_search_radius(p)); } else { size_t k = 1; auto p = target_point(ip); while (!success && k <= maxNbElemsToTry) { if (allowed_to_diagnose_max_neighbours) { // avoid race condition diagnosed_max_neighbours = std::max(k, diagnosed_max_neighbours); } success = try_interpolate_with_element_candidates(ip, find_k_nearest_element_candidates(p, k)); k *= 2; } } if (!success) { atlas_omp_critical { failures.emplace_back(ip); } } if (progress) { ++(*progress); } } } if (diagnosed_max_neighbours) { Log::debug() << "Maximum neighbours searched was " << eckit::Plural(diagnosed_max_neighbours, "element") << std::endl; } if (failures.size()) { if (treat_failure_as_missing_value_) { missing_.resize(failures.size()); std::copy(std::begin(failures), std::end(failures), missing_.begin()); } else { // If this fails, consider lowering atlas::grid::parametricEpsilon std::ostringstream msg; msg << "Rank " << mpi::rank() << " failed to project points:\n"; for (std::vector::const_iterator i = failures.begin(); i != failures.end(); ++i) { const PointLonLat pll{out_lonlat(*i, (size_t)0), out_lonlat(*i, (size_t)1)}; // lookup point msg << "\t(lon,lat) = " << pll << "\n"; } Log::error() << msg.str() << std::endl; throw_Exception(msg.str()); } } // fill sparse matrix and return, this cannot be multithreaded! setMatrix(out_npts, inp_npts, weights_triplets); } struct ElementEdge { std::array idx; void swap() { idx_t tmp = idx[0]; idx[0] = idx[1]; idx[1] = tmp; } }; Method::Triplets FiniteElement::projectPointToElements(size_t ip, const ElemIndex3::NodeList& elems) const { ATLAS_ASSERT(elems.begin() != elems.end()); const size_t inp_points = icoords_->shape(0); std::array idx; std::array w; Triplets triplets; triplets.reserve(4); Ray ray(PointXYZ{(*ocoords_)(ip, size_t(0)), (*ocoords_)(ip, size_t(1)), (*ocoords_)(ip, size_t(2))}); const Vector3D p{(*ocoords_)(ip, size_t(0)), (*ocoords_)(ip, size_t(1)), (*ocoords_)(ip, size_t(2))}; ElementEdge edge; idx_t single_point; for (ElemIndex3::NodeList::const_iterator itc = elems.begin(); itc != elems.end(); ++itc) { const idx_t elem_id = idx_t((*itc).value().payload()); ATLAS_ASSERT(elem_id < connectivity_->rows()); const idx_t nb_cols = [&]() { int nb_cols = connectivity_->cols(elem_id); if (nb_cols == 5) { // Check if pentagon degenerates to quad. Otherwise abort. // For now only check if the last point is a duplicate. auto get_p = [&](idx_t i) { auto n = (*connectivity_)(elem_id, i); return PointXYZ{(*icoords_)(n, XX), (*icoords_)(n, YY), (*icoords_)(n, ZZ)}; }; auto p4 = get_p(4); if (p4 == get_p(0)) { return 4; } if (p4 == get_p(3)) { return 4; } } return nb_cols; }(); ATLAS_ASSERT(nb_cols == 3 || nb_cols == 4); for (idx_t i = 0; i < nb_cols; ++i) { idx[i] = (*connectivity_)(elem_id, i); ATLAS_ASSERT(idx[i] < inp_points); } constexpr double tolerance = 1.e-12; auto on_triag_edge = [&]() { if (w[0] < tolerance) { edge.idx[0] = 1; edge.idx[1] = 2; w[0] = 0.; return true; } if (w[1] < tolerance) { edge.idx[0] = 0; edge.idx[1] = 2; w[1] = 0.; return true; } if (w[2] < tolerance) { edge.idx[0] = 0; edge.idx[1] = 1; w[2] = 0.; return true; } return false; }; auto on_quad_edge = [&]() { if (w[0] < tolerance && w[1] < tolerance) { edge.idx[0] = 2; edge.idx[1] = 3; w[0] = 0.; w[1] = 0.; return true; } if (w[1] < tolerance && w[2] < tolerance) { edge.idx[0] = 0; edge.idx[1] = 3; w[1] = 0.; w[2] = 0.; return true; } if (w[2] < tolerance && w[3] < tolerance) { edge.idx[0] = 0; edge.idx[1] = 1; w[2] = 0.; w[3] = 0.; return true; } if (w[3] < tolerance && w[0] < tolerance) { edge.idx[0] = 1; edge.idx[1] = 2; w[3] = 0.; w[0] = 0.; return true; } return false; }; auto on_single_point = [&]() { if (w[edge.idx[0]] < tolerance) { single_point = edge.idx[1]; w[edge.idx[0]] = 0.; return true; } if (w[edge.idx[1]] < tolerance) { single_point = edge.idx[0]; w[edge.idx[1]] = 0.; return true; } return false; }; auto interpolate_edge = [&](const Vector3D& p0, const Vector3D& p1) { /* * Given points p0,p1 defining the edge, and point p, find projected point pt * on edge to compute interpolation weights. * p * |`. * | `.v * | `. * p1--------------pt-----p0 * <--d---- */ Vector3D d = (p1 - p0) / (p1 - p0).norm(); Vector3D v = p - p0; double t = v.dot(d); Vector3D pt = p0 + d * t; t = (pt - p0).norm() / (p1 - p0).norm(); w[edge.idx[0]] = 1. - t; w[edge.idx[1]] = t; }; if (nb_cols == 3) { /* triangle */ element::Triag3D triag(PointXYZ{(*icoords_)(idx[0], XX), (*icoords_)(idx[0], YY), (*icoords_)(idx[0], ZZ)}, PointXYZ{(*icoords_)(idx[1], XX), (*icoords_)(idx[1], YY), (*icoords_)(idx[1], ZZ)}, PointXYZ{(*icoords_)(idx[2], XX), (*icoords_)(idx[2], YY), (*icoords_)(idx[2], ZZ)}); // pick an epsilon based on a characteristic length (sqrt(area)) // (this scales linearly so it better compares with linear weights u,v,w) const double edgeEpsilon = parametricEpsilon * std::sqrt(triag.area()); ATLAS_ASSERT(edgeEpsilon >= 0); Intersect is = triag.intersects(ray, edgeEpsilon); if (is) { // weights are the linear Lagrange function evaluated at u,v (aka // barycentric coordinates) w[0] = 1. - is.u - is.v; w[1] = is.u; w[2] = is.v; if (on_triag_edge()) { if (on_single_point()) { triplets.emplace_back(ip, idx[single_point], w[single_point]); } else { if ((*igidx_)(idx[edge.idx[1]]) < (*igidx_)(idx[edge.idx[0]])) { edge.swap(); } interpolate_edge(triag.p(edge.idx[0]), triag.p(edge.idx[1])); for (size_t i = 0; i < 2; ++i) { triplets.emplace_back(ip, idx[edge.idx[i]], w[edge.idx[i]]); } } } else { for (size_t i = 0; i < 3; ++i) { triplets.emplace_back(ip, idx[i], w[i]); } } break; // stop looking for elements } } else { /* quadrilateral */ element::Quad3D quad(PointXYZ{(*icoords_)(idx[0], XX), (*icoords_)(idx[0], YY), (*icoords_)(idx[0], ZZ)}, PointXYZ{(*icoords_)(idx[1], XX), (*icoords_)(idx[1], YY), (*icoords_)(idx[1], ZZ)}, PointXYZ{(*icoords_)(idx[2], XX), (*icoords_)(idx[2], YY), (*icoords_)(idx[2], ZZ)}, PointXYZ{(*icoords_)(idx[3], XX), (*icoords_)(idx[3], YY), (*icoords_)(idx[3], ZZ)}); // pick an epsilon based on a characteristic length (sqrt(area)) // (this scales linearly so it better compares with linear weights u,v,w) const double edgeEpsilon = parametricEpsilon * std::sqrt(quad.area()); ATLAS_ASSERT(edgeEpsilon >= 0); Intersect is = quad.intersects(ray, edgeEpsilon); if (is) { // weights are the bilinear Lagrange function evaluated at u,v w[0] = (1. - is.u) * (1. - is.v); w[1] = is.u * (1. - is.v); w[2] = is.u * is.v; w[3] = (1. - is.u) * is.v; if (on_quad_edge()) { if (on_single_point()) { triplets.emplace_back(ip, idx[single_point], w[single_point]); } else { if ((*igidx_)(idx[edge.idx[1]]) < (*igidx_)(idx[edge.idx[0]])) { edge.swap(); } interpolate_edge(quad.p(edge.idx[0]), quad.p(edge.idx[1])); for (size_t i = 0; i < 2; ++i) { triplets.emplace_back(ip, idx[edge.idx[i]], w[edge.idx[i]]); } } } else { for (size_t i = 0; i < 4; ++i) { triplets.emplace_back(ip, idx[i], w[i]); } } break; // stop looking for elements } } } // loop over nearest elements if (!triplets.empty()) { normalise(triplets); } return triplets; } } // namespace method } // namespace interpolation } // namespace atlas ././@LongLink0000644000000000000000000000014600000000000011604 Lustar rootrootatlas-0.46.0/src/atlas/interpolation/method/unstructured/ConservativeSphericalPolygonInterpolation.ccatlas-0.46.0/src/atlas/interpolation/method/unstructured/ConservativeSphericalPolygonInterpolation.c0000664000175000017500000033136015160212070034471 0ustar alastairalastair/* * (C) Copyright 2021- 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 "ConservativeSphericalPolygonInterpolation.h" #include "atlas/grid.h" #include "atlas/interpolation/Interpolation.h" #include "atlas/interpolation/method/MethodFactory.h" #include "atlas/library/FloatingPointExceptions.h" #include "atlas/mesh/actions/BuildHalo.h" #include "atlas/mesh/actions/BuildNode2CellConnectivity.h" #include "atlas/meshgenerator.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/ConvexSphericalPolygon.h" #include "atlas/util/KDTree.h" #include "atlas/util/Topology.h" #include "atlas/util/detail/filesystem.h" #include "eckit/log/Bytes.h" #include "eckit/log/ProgressTimer.h" #define PRINT_BAD_POLYGONS 0 namespace atlas { namespace interpolation { namespace method { MethodBuilder __builder("conservative-spherical-polygon"); using runtime::trace::StopWatch; using Polygon = util::ConvexSphericalPolygon; using PolygonArray = std::vector; namespace { template std::string to_json(const It& begin, const It& end, int precision = 0) { std::stringstream ss; ss << "[\n"; size_t size = std::distance(begin,end); size_t c=0; for( auto it = begin; it != end; ++it, ++c ) { ss << " " << it->json(precision); if( c < size-1 ) { ss << ",\n"; } } ss << "\n]"; return ss.str(); } template std::string to_json(const PolygonContainer& polygons, int precision = 0) { return to_json(polygons.begin(),polygons.end(),precision); } template std::string polygons_to_json(const It& begin, const It& end, int precision = 0) { std::stringstream ss; ss << "[\n"; size_t size = std::distance(begin,end); size_t c=0; for( auto it = begin; it != end; ++it, ++c ) { ss << " " << it->json(precision); if( c < size-1 ) { ss << ",\n"; } } ss << "\n]"; return ss.str(); } template std::string polygons_to_json(const Polygons& polygons, int precision = 0) { return polygons_to_json(polygons.begin(),polygons.end(),precision); } template void dump_polygons_to_json( const Polygon& t_csp, double pointsSameEPS, const Polygons& source_polygons, const std::vector& source_polygons_considered_indices, const std::string folder, const std::string name) { PolygonArray csp_arr{ t_csp }; PolygonArray csp_arr_intersecting {t_csp}; PolygonArray intersections; int count = 1; for( auto& s_idx : source_polygons_considered_indices ) { auto s_csp = source_polygons[s_idx]; csp_arr.emplace_back( s_csp ); std::fstream file_plg_debug(folder + name + "_" + std::to_string(count++) + ".debug", std::ios::out); Polygon iplg = t_csp.intersect(s_csp, &file_plg_debug, pointsSameEPS); file_plg_debug.close(); if (iplg) { if( iplg.area() > 0. ) { csp_arr_intersecting.emplace_back( s_csp ); intersections.emplace_back( iplg ); } } } double tot = 0.; Log::info().indent(); std::fstream file_info(folder + name + ".info", std::ios::out); file_info << "List of intersection weights: " << std::endl; count = 1; for( auto& iplg : intersections ) { csp_arr.emplace_back( iplg ); csp_arr_intersecting.emplace_back( iplg ); tot += iplg.area() / t_csp.area(); file_info << "\t" << count++ << ": " << iplg.area() / t_csp.area() << std::endl; } file_info << std::endl << name + ": " << 100. * tot << " % covered."<< std::endl << std::endl; file_info << "Target polygon + candidate source polygons + " << intersections.size() << " intersections in file:" << std::endl; file_info << "\t" << folder + name + ".candidates\n" << std::endl; std::fstream file_plg(folder + name + ".candidates", std::ios::out); file_plg << polygons_to_json(csp_arr, 16); file_plg.close(); file_info << "Target polygon + " << csp_arr_intersecting.size() << " intersecting source polygon + " << intersections.size() << " intersections in file:" << std::endl; file_info << "\t" << folder + name + ".intersections\n" << std::endl; file_plg.open(folder + name + ".intersections", std::ios::out); file_plg << polygons_to_json(csp_arr_intersecting, 16); file_plg.close(); Log::info().unindent(); } constexpr double unit_sphere_area() { return 4. * M_PI; } template size_t memory_of(const std::vector& vector) { return sizeof(T) * vector.capacity(); } template size_t memory_of(const std::vector>& vector_of_vector) { size_t mem = 0; for (const auto& vector : vector_of_vector) { mem += memory_of(vector); } return mem; } size_t memory_of( const std::vector& vector_of_params) { size_t mem = 0; for (const auto& params : vector_of_params) { mem += memory_of(params.csp_ids); mem += memory_of(params.centroids); mem += memory_of(params.weights); } return mem; } Mesh extract_mesh(FunctionSpace fs) { if (functionspace::CellColumns(fs)) { return functionspace::CellColumns(fs).mesh(); } else if (functionspace::NodeColumns(fs)) { return functionspace::NodeColumns(fs).mesh(); } else { ATLAS_THROW_EXCEPTION("Cannot extract mesh from FunctionSpace" << fs.type()); } } #if ATLAS_BUILD_TYPE_DEBUG int inside_vertices(const Polygon& plg1, const Polygon& plg2, int& pout) { int points_in = 0; pout = 0; for (int j = 0; j < plg2.size(); j++) { int i = 0; for (; i < plg1.size(); i++) { int in = (i != plg1.size() - 1) ? i + 1 : 0; auto gss = Polygon::GreatCircleSegment(plg1[i], plg1[in]); if (not gss.inLeftHemisphere(plg2[j], -std::numeric_limits::epsilon())) { pout++; break; }; } if (i == plg1.size()) { points_in++; } } ATLAS_ASSERT(points_in + pout == plg2.size()); return points_in; } #endif inline bool valid_point(idx_t node_idx, const array::ArrayView& node_flags) { return not util::Bitflags::view(node_flags(node_idx)).check(util::Topology::INVALID); } } // namespace ConservativeSphericalPolygonInterpolation::ConservativeSphericalPolygonInterpolation(const Config& config): Method(config), validate_(false), src_cell_data_(true), tgt_cell_data_(true), normalise_(false), order_(1), matrix_free_(false), n_spoints_(0), n_tpoints_(0) { config.get("validate", validate_ = false); config.get("order", order_ = 1); config.get("normalise", normalise_ = false); config.get("matrix_free", matrix_free_ = false); config.get("src_cell_data", src_cell_data_ = true); config.get("tgt_cell_data", tgt_cell_data_ = true); config.get("statistics.all", remap_stat_.all = false); config.get("statistics.accuracy", remap_stat_.accuracy = false); config.get("statistics.conservation", remap_stat_.conservation = false); config.get("statistics.intersection", remap_stat_.intersection = false); config.get("statistics.timings", remap_stat_.timings = false); if (remap_stat_.all) { Log::warning() << "statistics.all required. Enabling validate, statistics.timings, statistics.intersection, statistics.conservation, and statistics.accuracy." << std::endl; validate_ = true; remap_stat_.accuracy = true; remap_stat_.conservation = true; remap_stat_.intersection = true; remap_stat_.timings = true; } if (remap_stat_.intersection) { Log::warning() << "statistics.intersection required. Enabling validate." << std::endl; validate_ = true; } sharable_data_ = std::make_shared(); cache_ = Cache(sharable_data_); data_ = sharable_data_.get(); ATLAS_ASSERT(sharable_data_.use_count() == 2); } inline int ConservativeSphericalPolygonInterpolation::next_index(int current_index, int size) const { #if ATLAS_BUILD_TYPE_DEBUG ATLAS_ASSERT(current_index >= 0 && current_index < size); #endif return (current_index < size - 1) ? current_index + 1 : current_index + 1 - size; } inline int ConservativeSphericalPolygonInterpolation::prev_index(int current_index, int size) const { #if ATLAS_BUILD_TYPE_DEBUG ATLAS_ASSERT(current_index >= 0 && current_index < size); #endif return (current_index >= 1) ? current_index - 1 : current_index - 1 + size; } struct ConservativeSphericalPolygonInterpolation::Workspace_get_cell_neighbours { PointXYZ p0; PointLonLat p0_ll; PointXYZ p1; PointLonLat p1_ll; }; // get counter-clockwise sorted neighbours of a cell std::vector ConservativeSphericalPolygonInterpolation::get_cell_neighbours(Mesh& mesh, idx_t cell, Workspace_get_cell_neighbours& w) const { const auto& cell2node = mesh.cells().node_connectivity(); const auto& nodes_ll = array::make_view(mesh.nodes().lonlat()); const idx_t n_nodes = cell2node.cols(cell); std::vector nbr_cells; nbr_cells.reserve(n_nodes); if (mesh.nodes().cell_connectivity().rows() == 0) { mesh::actions::build_node_to_cell_connectivity(mesh); } const auto& node2cell = mesh.nodes().cell_connectivity(); const auto n2c_missval = node2cell.missing_value(); auto& p0 = w.p0; auto& p1 = w.p1; auto& p0_ll = w.p0_ll; auto& p1_ll = w.p1_ll; for (idx_t inode = 0; inode < n_nodes; ++inode) { idx_t node0 = cell2node(cell, inode); idx_t node1 = cell2node(cell, next_index(inode, n_nodes)); p0_ll[0] = nodes_ll(node0, 0); p0_ll[1] = nodes_ll(node0, 1); p1_ll[0] = nodes_ll(node1, 0); p1_ll[1] = nodes_ll(node1, 1); eckit::geometry::Sphere::convertSphericalToCartesian(1., p0_ll, p0); eckit::geometry::Sphere::convertSphericalToCartesian(1., p1_ll, p1); if (PointXYZ::norm(p0 - p1) < 1e-14) { continue; // edge = point } bool still_search = true; // still search the cell having vertices node0 & node1, not having index "cell" int n_cells0 = node2cell.cols(node0); int n_cells1 = node2cell.cols(node1); for (int icell0 = 0; still_search && icell0 < n_cells0; icell0++) { int cell0 = node2cell(node0, icell0); if (cell0 == cell) { continue; } for (int icell1 = 0; still_search && icell1 < n_cells1; icell1++) { int cell1 = node2cell(node1, icell1); if (cell0 == cell1 && cell0 != n2c_missval) { nbr_cells.emplace_back(cell0); still_search = false; } } } } return nbr_cells; } struct ConservativeSphericalPolygonInterpolation::Workspace_get_node_neighbours { std::vector nbr_nodes_od; std::vector< std::array > cnodes; }; // get cyclically sorted node neighbours without using edge connectivity std::vector ConservativeSphericalPolygonInterpolation::get_node_neighbours(Mesh& mesh, idx_t node_id, Workspace_get_node_neighbours& w) const { const auto& cell2node = mesh.cells().node_connectivity(); const auto node_flags = array::make_view(mesh.nodes().flags()); if (mesh.nodes().cell_connectivity().rows() == 0) { mesh::actions::build_node_to_cell_connectivity(mesh); } const auto& node2cell = mesh.nodes().cell_connectivity(); std::vector nbr_nodes; const int ncells = node2cell.cols(node_id); ATLAS_ASSERT(ncells > 0, "There is a node which does not connect to any cell"); nbr_nodes.reserve(ncells + 1); w.cnodes.resize(ncells); w.nbr_nodes_od.clear(); w.nbr_nodes_od.reserve(ncells + 1); for (idx_t icell = 0; icell < ncells; ++icell) { const idx_t cell = node2cell(node_id, icell); const int nnodes = cell2node.cols(cell); idx_t cnode = 0; for (; cnode < nnodes; ++cnode) { if (node_id == cell2node(cell, cnode)) { break; } } w.cnodes[icell][0] = cell2node(cell, prev_index(cnode, nnodes)); w.cnodes[icell][1] = cell2node(cell, next_index(cnode, nnodes)); } if (ncells == 1) { nbr_nodes.emplace_back(w.cnodes[0][0]); nbr_nodes.emplace_back(w.cnodes[0][1]); return nbr_nodes; } // cycle one direction idx_t find = w.cnodes[0][1]; idx_t prev = w.cnodes[0][0]; nbr_nodes.emplace_back(prev); nbr_nodes.emplace_back(find); for (idx_t icycle = 0; nbr_nodes[0] != find;) { idx_t jcell = 0; for (; jcell < ncells; ++jcell) { idx_t ocell = (icycle + jcell + 1) % ncells; idx_t cand0 = w.cnodes[ocell][0]; idx_t cand1 = w.cnodes[ocell][1]; if (find == cand0 && prev != cand1) { if (cand1 == nbr_nodes[0]) { return nbr_nodes; } nbr_nodes.emplace_back(cand1); prev = find; find = cand1; break; } if (find == cand1 && prev != cand0) { if (cand0 == nbr_nodes[0]) { return nbr_nodes; } nbr_nodes.emplace_back(cand0); prev = find; find = cand0; break; } } if (jcell == ncells) { // not found if (nbr_nodes[0] != find && find != nbr_nodes[nbr_nodes.size() - 1]) { nbr_nodes.emplace_back(find); } break; } else { icycle++; } } if (nbr_nodes[0] == find) { return nbr_nodes; } // cycle the oposite direction find = w.cnodes[0][0]; prev = w.cnodes[0][1]; w.nbr_nodes_od.emplace_back(prev); w.nbr_nodes_od.emplace_back(find); for (idx_t icycle = 0; w.nbr_nodes_od[0] != find;) { idx_t jcell = 0; for (; jcell < ncells; ++jcell) { idx_t ocell = (icycle + jcell + 1) % ncells; if (find == w.cnodes[ocell][0] && prev != w.cnodes[ocell][1]) { w.nbr_nodes_od.emplace_back(w.cnodes[ocell][1]); prev = find; find = w.cnodes[ocell][1]; break; } if (find == w.cnodes[ocell][1] && prev != w.cnodes[ocell][0]) { w.nbr_nodes_od.emplace_back(w.cnodes[ocell][0]); prev = find; find = w.cnodes[ocell][0]; break; } } if (jcell == ncells) { if (find != w.nbr_nodes_od[w.nbr_nodes_od.size() - 1]) { w.nbr_nodes_od.emplace_back(find); } break; } icycle++; } // put together int ow_size = w.nbr_nodes_od.size(); for (int i = 0; i < ow_size - 2; i++) { if (valid_point(w.nbr_nodes_od[ow_size - 1 - i], node_flags)) { nbr_nodes.emplace_back(w.nbr_nodes_od[ow_size - 1 - i]); } } return nbr_nodes; } void ConservativeSphericalPolygonInterpolation:: init_polygons_data( // input FunctionSpace fs, // output Data::PolygonsData& md) { using Context = Data::PolygonsData::Context; md.cell_data = functionspace::CellColumns(fs); std::vector& csp2node = md.csp2node; std::vector>& node2csp = md.node2csp; gidx_t& csp_size = md.csp_size; std::vector& csp_cell_index = md.csp_cell_index; std::vector& csp_index = md.csp_index; auto mesh = extract_mesh(fs); const auto& cell2node = mesh.cells().node_connectivity(); const auto cell_halo = array::make_view(mesh.cells().halo()); const auto cell_flags = array::make_view(mesh.cells().flags()); const auto cell_invalid = [&cell_flags](idx_t cell) { return util::Bitflags::view(cell_flags(cell)).check(util::Topology::INVALID); }; auto fs_halo = md.cell_data ? functionspace::CellColumns(fs).halo().size() : functionspace::NodeColumns(fs).halo().size(); csp_size = 0; csp_cell_index.reserve(mesh.cells().size()); csp_index.reserve(mesh.cells().size()); if (md.cell_data) { auto skip_cell = [&](idx_t cell) { if (md.context == Context::SOURCE) { return cell_halo(cell) > fs_halo || cell_invalid(cell); } else { return cell_halo(cell) > 0; } }; for (idx_t cell = 0; cell < mesh.cells().size(); ++cell) { if (skip_cell(cell)) { continue; } csp_cell_index.emplace_back(cell); csp_index.emplace_back(cell); csp_size++; } } else { auto skip_cell = [&](idx_t cell) { if (md.context == Context::SOURCE) { return cell_halo(cell) > fs_halo || cell_invalid(cell); } else { return cell_halo(cell) > 1 || cell_invalid(cell); } }; const auto nodes_ll = array::make_view(mesh.nodes().lonlat()); const auto node_flags = array::make_view(mesh.nodes().flags()); auto ll2xyz = [](const atlas::PointLonLat& p_ll) { PointXYZ p_xyz; eckit::geometry::Sphere::convertSphericalToCartesian(1., p_ll, p_xyz); return p_xyz; }; csp_index.emplace_back(0); for (idx_t cell = 0; cell < mesh.cells().size(); ++cell) { if (skip_cell(cell)) { continue; } const idx_t n_nodes = cell2node.cols(cell); for (idx_t inode = 0; inode < n_nodes; ++inode) { idx_t node0 = cell2node(cell, inode); if (not valid_point(node0, node_flags)) { continue; } idx_t node1 = cell2node(cell, next_index(inode, n_nodes)); const PointLonLat p0_ll = PointLonLat{nodes_ll(node0, 0), nodes_ll(node0, 1)}; const PointLonLat p1_ll = PointLonLat{nodes_ll(node1, 0), nodes_ll(node1, 1)}; PointXYZ p0 = ll2xyz(p0_ll); PointXYZ p1 = ll2xyz(p1_ll); if (PointXYZ::norm(p0 - p1) < 1e-14) { continue; // skip this edge = a pole point } csp_size++; } csp_index.emplace_back(csp_size); csp_cell_index.emplace_back(cell); } csp2node.resize(csp_size); std::fill(csp2node.begin(), csp2node.end(), -1); node2csp.resize(mesh.nodes().size()); } } ConservativeSphericalPolygonInterpolation::Polygon ConservativeSphericalPolygonInterpolation:: get_csp_celldata(idx_t csp_id, const Mesh& mesh, const Data::PolygonsData& md) { // TODO: Performance optimisations: // - Do not extract array views, mesh connectivity for each csp_id // - pts_ll should also not be created/destroyed for each csp_id const auto& cell2node = mesh.cells().node_connectivity(); std::vector pts_ll; idx_t cell = csp_to_cell(csp_id, md); const idx_t n_nodes = cell2node.cols(cell); const auto nodes_ll = array::make_view(mesh.nodes().lonlat()); pts_ll.clear(); pts_ll.resize(n_nodes); for (idx_t jnode = 0; jnode < n_nodes; ++jnode) { idx_t inode = cell2node(cell, jnode); pts_ll[jnode] = PointLonLat{nodes_ll(inode, 0), nodes_ll(inode, 1)}; } return Polygon(pts_ll); } ConservativeSphericalPolygonInterpolation::Polygon ConservativeSphericalPolygonInterpolation:: get_csp_nodedata(idx_t csp_id, const Mesh& mesh, Data::PolygonsData& md ) { std::vector& csp2node = md.csp2node; std::vector>& node2csp = md.node2csp; // TODO: Performance optimisations: // - Do not extract array views, mesh connectivity for each csp_id // - pts_ll, pts_xyz, pts_idx, subpol_pts_ll should also not be created/destroyed for each csp_id const auto& cell2node = mesh.cells().node_connectivity(); const auto node_flags = array::make_view(mesh.nodes().flags()); std::vector pts_ll; idx_t cell, isubcell; std::tie(cell,isubcell) = csp_to_cell_and_subcell(csp_id, md); // idx_t cell; // auto isubcell = get_owner_cell_nodedata(csp_id, csp_cell_index, csp_index, cell); const idx_t n_nodes = cell2node.cols(cell); const auto nodes_ll = array::make_view(mesh.nodes().lonlat()); pts_ll.clear(); pts_ll.resize(n_nodes); auto xyz2ll = [](const atlas::PointXYZ& p_xyz) { PointLonLat p_ll; eckit::geometry::Sphere::convertCartesianToSpherical(1., p_xyz, p_ll); return p_ll; }; auto ll2xyz = [](const atlas::PointLonLat& p_ll) { PointXYZ p_xyz; eckit::geometry::Sphere::convertSphericalToCartesian(1., p_ll, p_xyz); return p_xyz; }; PointXYZ cell_mid(0., 0., 0.); // cell centre std::vector pts_xyz; std::vector pts_idx; #if ATLAS_BUILD_TYPE_DEBUG ATLAS_ASSERT(cell < cell2node.rows()); ATLAS_ASSERT(n_nodes > 2); #endif pts_xyz.clear(); pts_ll.clear(); pts_idx.clear(); pts_xyz.reserve(n_nodes); pts_ll.reserve(n_nodes); pts_idx.reserve(n_nodes); for (idx_t inode = 0; inode < n_nodes; ++inode) { idx_t node0 = cell2node(cell, inode); if (not valid_point(node0, node_flags)) { continue; } idx_t node1 = cell2node(cell, next_index(inode, n_nodes)); const PointLonLat p0_ll = PointLonLat{nodes_ll(node0, 0), nodes_ll(node0, 1)}; const PointLonLat p1_ll = PointLonLat{nodes_ll(node1, 0), nodes_ll(node1, 1)}; PointXYZ p0 = ll2xyz(p0_ll); PointXYZ p1 = ll2xyz(p1_ll); if (PointXYZ::norm(p0 - p1) < 1e-14) { continue; // skip this edge = a pole point } pts_xyz.emplace_back(p0); pts_ll.emplace_back(p0_ll); pts_idx.emplace_back(inode); cell_mid = cell_mid + p0; cell_mid = cell_mid + p1; } cell_mid = PointXYZ::normalize(cell_mid); #if ATLAS_BUILD_TYPE_DEBUG ATLAS_ASSERT(pts_xyz.size() > 2); ATLAS_ASSERT(isubcell < pts_idx.size()); #endif PointLonLat cell_ll = xyz2ll(cell_mid); idx_t inode = isubcell; int inode_n = next_index(inode, pts_idx.size()); PointXYZ iedge_mid = PointXYZ::normalize(pts_xyz[inode] + pts_xyz[inode_n]); int inode_nn = next_index(inode_n, pts_idx.size()); if (PointXYZ::norm(pts_xyz[inode_nn] - pts_xyz[inode_n]) < 1e-14) { ATLAS_THROW_EXCEPTION("Three cell vertices on a same great arc!"); } PointXYZ jedge_mid; jedge_mid = pts_xyz[inode_nn] + pts_xyz[inode_n]; jedge_mid = PointXYZ::div(jedge_mid, PointXYZ::norm(jedge_mid)); std::array subpol_pts_ll; subpol_pts_ll[0] = cell_ll; subpol_pts_ll[1] = xyz2ll(iedge_mid); subpol_pts_ll[2] = pts_ll[inode_n]; subpol_pts_ll[3] = xyz2ll(jedge_mid); idx_t node_n = cell2node(cell, inode_n); if (csp2node[csp_id] == -1) { csp2node[csp_id] = node_n; node2csp[node_n].emplace_back(csp_id); } return Polygon(subpol_pts_ll); } // Create polygons for cell-centred data. Here, the polygons are mesh cells ConservativeSphericalPolygonInterpolation::PolygonArray ConservativeSphericalPolygonInterpolation:: get_polygons_celldata(FunctionSpace fs, Data::PolygonsData& md) { //std::vector& csp2node, std::vector>& node2csp, //gidx_t& csp_size, std::vector& csp_cell_index, std::vector& csp_index) { ATLAS_TRACE("ConservativeSphericalPolygonInterpolation: get_polygons_celldata"); PolygonArray cspolygons(md.csp_size); auto mesh = extract_mesh(fs); for(idx_t i = 0; i < md.csp_size; ++i) { cspolygons[i] = get_csp_celldata(i, mesh, md); } return cspolygons; } // Create polygons for cell-vertex data. Here, the polygons are subcells of mesh cells created as // (cell_centre, edge_centre, cell_vertex, edge_centre) // additionally, subcell-to-node and node-to-subcells mapping are computed ConservativeSphericalPolygonInterpolation::PolygonArray ConservativeSphericalPolygonInterpolation:: get_polygons_nodedata(FunctionSpace fs, Data::PolygonsData& md) { ATLAS_TRACE("ConservativeSphericalPolygonInterpolation: get_polygons_nodedata"); PolygonArray cspolygons(md.csp_size); auto mesh = extract_mesh(fs); for(idx_t csp_id = 0; csp_id < md.csp_size; ++csp_id) { cspolygons[csp_id] = get_csp_nodedata(csp_id, mesh, md); } return cspolygons; } void ConservativeSphericalPolygonInterpolation::do_setup_impl(const Grid& src_grid, const Grid& tgt_grid) { ATLAS_TRACE("ConservativeMethod: do_setup_impl"); ATLAS_ASSERT(src_grid); ATLAS_ASSERT(tgt_grid); tgt_fs_ = data_->tgt_fs_; src_fs_ = data_->src_fs_; if (not tgt_fs_) { auto tgt_mesh_config = tgt_grid.meshgenerator() | option::halo(0); ATLAS_TRACE_SCOPE("Generate target mesh") { tgt_mesh_ = MeshGenerator(tgt_mesh_config).generate(tgt_grid); } ATLAS_TRACE_SCOPE("Create target functionspace") { if (tgt_cell_data_) { tgt_fs_ = functionspace::CellColumns(tgt_mesh_, option::halo(0)); } else { tgt_fs_ = functionspace::NodeColumns(tgt_mesh_, option::halo(1)); } } sharable_data_->tgt_fs_ = tgt_fs_; ATLAS_ASSERT(data_->tgt_fs_); } if (not src_fs_) { auto src_mesh_config = src_grid.meshgenerator() | option::halo(2); ATLAS_TRACE_SCOPE("Generate source mesh") { if (mpi::size() > 1) { src_mesh_ = MeshGenerator(src_mesh_config).generate(src_grid, grid::MatchingPartitioner(tgt_mesh_)); } else { src_mesh_ = MeshGenerator(src_mesh_config).generate(src_grid); } } ATLAS_TRACE_SCOPE("Create source functionspace") { if (src_cell_data_) { src_fs_ = functionspace::CellColumns(src_mesh_, option::halo(2)); } else { src_fs_ = functionspace::NodeColumns(src_mesh_, option::halo(2)); } } sharable_data_->src_fs_ = src_fs_; ATLAS_ASSERT(data_->tgt_fs_); } do_setup(src_fs_, tgt_fs_); } void ConservativeSphericalPolygonInterpolation::do_setup(const Grid& src_grid, const Grid& tgt_grid, const interpolation::Cache& cache) { ATLAS_TRACE("ConservativeSphericalPolygonInterpolation: do_setup with grids"); if (Cache(cache)) { Log::debug() << "Interpolation data found in cache -> no polygon intersections required" << std::endl; cache_ = Cache(cache); data_ = cache_.get(); sharable_data_.reset(); src_fs_ = data_->src_fs_; tgt_fs_ = data_->tgt_fs_; src_cell_data_ = functionspace::CellColumns(src_fs_); tgt_cell_data_ = functionspace::CellColumns(tgt_fs_); src_mesh_ = extract_mesh(src_fs_); tgt_mesh_ = extract_mesh(tgt_fs_); if (order_ == 1 && matrix_free_) { // We don't need to continue with setups required for first order matrix-free // such as mesh generation and functionspace creation. return; } } if (not matrix_free_) { auto matrix_cache = interpolation::MatrixCache(cache); if (matrix_cache) { if (matrix_cache.uid() == std::to_string(order_) || matrix_cache.uid().empty()) { Log::debug() << "Matrix found in cache -> no setup required at all" << std::endl; setMatrix(matrix_cache); return; } } } do_setup_impl(src_grid, tgt_grid); } void ConservativeSphericalPolygonInterpolation::do_setup(const FunctionSpace& source, const FunctionSpace& target, const interpolation::Cache& cache) { ATLAS_TRACE("ConservativeSphericalPolygonInterpolation: do_setup with function spaces"); if (not matrix_free_) { auto matrix_cache = interpolation::MatrixCache(cache); if (matrix_cache) { if (matrix_cache.uid() == std::to_string(order_) || matrix_cache.uid().empty()) { Log::debug() << "Matrix found in cache -> no setup required at all" << std::endl; setMatrix(matrix_cache); src_fs_ = source; tgt_fs_ = target; return; } } else { Log::debug() << "Could not find matrix in cache" << std::endl; } } if (Cache(cache)) { Log::debug() << "Interpolation data found in cache -> no polygon intersections required" << std::endl; cache_ = Cache(cache); data_ = cache_.get(); sharable_data_.reset(new Data()); tgt_fs_ = target; src_fs_ = source; src_cell_data_ = functionspace::CellColumns(src_fs_); tgt_cell_data_ = functionspace::CellColumns(tgt_fs_); src_mesh_ = extract_mesh(src_fs_); tgt_mesh_ = extract_mesh(tgt_fs_); if (order_ == 1 && matrix_free_) { // We don't need to continue with setups required for first order matrix-free // such as mesh generation and functionspace creation. return; } } do_setup(source, target); } void ConservativeSphericalPolygonInterpolation::do_setup(const FunctionSpace& src_fs, const FunctionSpace& tgt_fs) { ATLAS_TRACE("ConservativeSphericalPolygonInterpolation: do_setup with function spaces"); ATLAS_ASSERT(src_fs); ATLAS_ASSERT(tgt_fs); bool compute_cache = data_->src_.points.empty(); if (not data_->tgt_fs_) { tgt_fs_ = tgt_fs; sharable_data_->tgt_fs_ = tgt_fs_; } if (not data_->src_fs_) { src_fs_ = src_fs; sharable_data_->src_fs_ = src_fs_; } src_cell_data_ = functionspace::CellColumns(src_fs_); tgt_cell_data_ = functionspace::CellColumns(tgt_fs_); src_mesh_ = extract_mesh(src_fs_); tgt_mesh_ = extract_mesh(tgt_fs_); auto& src = sharable_data_->src_; auto& tgt = sharable_data_->tgt_; auto& timings = sharable_data_->timings; if (mpi::size() > 1) { // we need src_halo_size >= 2, tgt_halo_size >= 0 for CellColumns // if target is NodeColumns, we need: // tgt_halo_size >= 1 and // src_halo_size large enough to cover the the target halo cells in the first row int halo_size = 0; src_mesh_.metadata().get("halo", halo_size); if (halo_size < 2 and order_ == 2) { Log::warning() << "The halo size on source mesh should be at least 2 for the 2nd order CSP interpolation." << std::endl; } if (not tgt_cell_data_) { Log::warning() << "The source cells should cover the half of the first target halo row." << std::endl; } halo_size = 0; tgt_mesh_.metadata().get("halo", halo_size); if (not tgt_cell_data_ and halo_size == 0) { Log::warning() << "The halo size on target mesh should be at least 1 for the target NodeColumns." << std::endl; } } PolygonArray src_csp; if (compute_cache) { ATLAS_TRACE_SCOPE("Get source polygons") { StopWatch stopwatch; stopwatch.start(); init_polygons_data(src_fs_, src); src_csp = get_polygons(src_fs_, src); stopwatch.stop(); remap_stat_.memory[Statistics::MEM_SRC_PLG] = memory_of(src_csp); timings.source_polygons_assembly = stopwatch.elapsed(); remap_stat_.counts[Statistics::NUM_SRC_PLG] = src_csp.size(); } ATLAS_TRACE_SCOPE("Get target polygons") { StopWatch stopwatch; stopwatch.start(); init_polygons_data(tgt_fs_, tgt); stopwatch.stop(); timings.target_polygons_assembly = stopwatch.elapsed(); remap_stat_.counts[Statistics::NUM_TGT_PLG] = tgt.csp_size; } } n_spoints_ = src_fs_.size(); n_tpoints_ = tgt_fs_.size(); if (compute_cache) { intersect_polygons(src_csp); // These arrays correspond to functionspace! auto& src_points = src.points; auto& src_areas = src.areas; src_points.resize(n_spoints_); src_areas.resize(n_spoints_); { ATLAS_TRACE("Store src_areas and src_point"); if (src.cell_data) { for (idx_t scsp_id = 0; scsp_id < src.csp_size; ++scsp_id) { const idx_t spt = csp_to_cell(scsp_id, src); const auto& s_csp = src_csp[scsp_id]; src_points[spt] = s_csp.centroid(); src_areas[spt] = s_csp.area(); } } else { auto& src_node2csp = src.node2csp; const auto lonlat = array::make_view(src_mesh_.nodes().lonlat()); for (idx_t spt = 0; spt < n_spoints_; ++spt) { if (src_node2csp[spt].size() == 0) { // this is a node to which no subpolygon is associated // maximal twice per mesh we end here, and that is only when mesh has nodes on poles auto p = PointLonLat{lonlat(spt, 0), lonlat(spt, 1)}; eckit::geometry::Sphere::convertSphericalToCartesian(1., p, src_points[spt]); } else { // .. in the other case, start computing the barycentre src_points[spt] = PointXYZ{0., 0., 0.}; } src_areas[spt] = 0.; for (idx_t isubcell = 0; isubcell < src_node2csp[spt].size(); ++isubcell) { idx_t subcell = src_node2csp[spt][isubcell]; const auto& s_csp = src_csp[subcell]; src_areas[spt] += s_csp.area(); src_points[spt] = src_points[spt] + PointXYZ::mul(s_csp.centroid(), s_csp.area()); } double src_point_norm = PointXYZ::norm(src_points[spt]); if (src_point_norm == 0.) { // Probably encountered degenerate subpolygons that lead to zero area. Can happen sometimes in ORCA grid auto p = PointLonLat{lonlat(spt, 0), lonlat(spt, 1)}; eckit::geometry::Sphere::convertSphericalToCartesian(1., p, src_points[spt]); src_point_norm = PointXYZ::norm(src_points[spt]); } src_points[spt] = PointXYZ::div(src_points[spt], src_point_norm); } } } auto& tgt_points = tgt.points; auto& tgt_areas = tgt.areas; tgt_points.resize(n_tpoints_); tgt_areas.resize(n_tpoints_); { ATLAS_TRACE("Store src_areas and src_point"); if (tgt_cell_data_) { for (idx_t tpt = 0; tpt < n_tpoints_; ++tpt) { auto tgt_csp = get_csp_celldata(tpt, tgt_mesh_, tgt); const auto& t_csp = tgt_csp; tgt_points[tpt] = t_csp.centroid(); tgt_areas[tpt] = t_csp.area(); } } else { auto& tgt_node2csp = tgt.node2csp; const auto lonlat = array::make_view(tgt_mesh_.nodes().lonlat()); for (idx_t tpt = 0; tpt < n_tpoints_; ++tpt) { if (tgt_node2csp[tpt].size() == 0) { // this is a node to which no subpolygon is associated // maximal twice per mesh we end here, and that is only when mesh has nodes on poles auto p = PointLonLat{lonlat(tpt, 0), lonlat(tpt, 1)}; eckit::geometry::Sphere::convertSphericalToCartesian(1., p, tgt_points[tpt]); } else { // .. in the other case, start computing the barycentre tgt_points[tpt] = PointXYZ{0., 0., 0.}; } tgt_areas[tpt] = 0.; for (idx_t isubcell = 0; isubcell < tgt_node2csp[tpt].size(); ++isubcell) { idx_t subcell = tgt_node2csp[tpt][isubcell]; auto tgt_csp = get_csp_nodedata(subcell, tgt_mesh_, tgt); const auto& t_csp = tgt_csp; tgt_areas[tpt] += t_csp.area(); tgt_points[tpt] = tgt_points[tpt] + PointXYZ::mul(t_csp.centroid(), t_csp.area()); } double tgt_point_norm = PointXYZ::norm(tgt_points[tpt]); if (tgt_point_norm == 0.) { // Probably encountered degenerate subpolygons cells with zero area as can happen with ORCA sometimes auto p = PointLonLat{lonlat(tpt, 0), lonlat(tpt, 1)}; eckit::geometry::Sphere::convertSphericalToCartesian(1., p, tgt_points[tpt]); tgt_point_norm = PointXYZ::norm(tgt_points[tpt]); } tgt_points[tpt] = PointXYZ::div(tgt_points[tpt], tgt_point_norm); } } } } if (not matrix_free_) { StopWatch stopwatch; stopwatch.start(); switch (order_) { case 1: { setMatrix(n_tpoints_, n_spoints_, compute_1st_order_triplets(), "1"); break; } case 2: { setMatrix(n_tpoints_, n_spoints_, compute_2nd_order_triplets(), "2"); break; } default: { ATLAS_NOTIMPLEMENTED; } } stopwatch.stop(); if (compute_cache) { timings.matrix_assembly = stopwatch.elapsed(); } } data_->print(Log::debug()); if (remap_stat_.intersection) { setup_stat(); } } namespace { uint64_t xorshift(const uint64_t& n, int i){ return n^(n>>i); } uint64_t hash(const uint64_t& n){ uint64_t p = 0x5555555555555555; // pattern of alternating 0 and 1 uint64_t c = 17316035218449499591ull;// random uneven integer constant; return c*xorshift(p*xorshift(n,32),32); } uint64_t hash_combine(const uint64_t& seed, const uint64_t& v) { uint64_t c = 17316035218449499591ull;// random integer constant; return hash(v)^(seed+c); } using PartitionAndRemoteIndex = std::pair; struct HashPartitionAndRemoteIndex { std::size_t operator()(const std::pair& pair) const { uint64_t h = 0; h = hash_combine(h, pair.first); h = hash_combine(h, pair.second); return h; } }; } void ConservativeSphericalPolygonInterpolation:: build_source_kdtree(util::KDTree& kdt_search, double& max_srccell_rad, const PolygonArray& src_csp) const { Log::debug() << "Building KDTree via mesh indices." << std::endl; const auto& src = sharable_data_->src_; const auto node_part = array::make_view(src_mesh_.nodes().partition()); const auto node_ridx = array::make_indexview(src_mesh_.nodes().remote_index()); const auto node_ghost = array::make_view(src_mesh_.nodes().ghost()); const auto cell_part = array::make_view(src_mesh_.cells().partition()); const auto cell_halo = array::make_view(src_mesh_.cells().halo()); const auto cell_ridx = array::make_indexview(src_mesh_.cells().remote_index()); auto mpi_rank = mpi::rank(); ATLAS_TRACE_SCOPE("build kd-tree for source polygons") { std::unordered_set src_kdtree_set; auto consider_src = [&](int part, idx_t ridx, int halo) { if (not halo) { return true; } else if (part != mpi_rank) { return src_kdtree_set.emplace(part, ridx).second; } return false; }; if (! src_cell_data_) { const auto& node2csp = src.node2csp; for (idx_t inode = 0; inode < src_mesh_.nodes().size(); ++inode) { const auto& csp_ids = node2csp[inode]; if (!csp_ids.empty()) { if (consider_src(node_part(inode), node_ridx(inode), node_ghost(inode))) { for (const auto& csp_id : csp_ids) { const auto& s_csp = src_csp[csp_id]; kdt_search.insert(s_csp.centroid(), csp_id); max_srccell_rad = std::max(max_srccell_rad, s_csp.radius()); } } } } } else { for (idx_t csp_id = 0; csp_id < src.csp_size; ++csp_id) { idx_t icell = csp_to_cell(csp_id, src); if (consider_src(cell_part(icell), cell_ridx(icell), cell_halo(icell))) { const auto& s_csp = src_csp[csp_id]; kdt_search.insert(s_csp.centroid(), icell); max_srccell_rad = std::max(max_srccell_rad, s_csp.radius()); } } } kdt_search.build(); } } void ConservativeSphericalPolygonInterpolation:: build_source_kdtree_centroid(util::KDTree& kdt_search, double& max_srccell_rad, const PolygonArray& src_csp) const { Log::warning() << "Building KDTree via centroid (obsolete)." << std::endl; // Treshold at which points are considered same double compare_pointxyz_eps = 1.e8 * std::numeric_limits::epsilon(); const char* ATLAS_COMPAREPOINTXYZ_EPS_FACTOR = ::getenv("ATLAS_COMPAREPOINTXYZ_EPS_FACTOR"); if (ATLAS_COMPAREPOINTXYZ_EPS_FACTOR != nullptr) { compare_pointxyz_eps = std::atof(ATLAS_COMPAREPOINTXYZ_EPS_FACTOR) * std::numeric_limits::epsilon(); } auto compare_pointxyz = [eps=compare_pointxyz_eps] (const PointXYZ& f, const PointXYZ& s) -> bool { if (f[0] < s[0] - eps) { return true; } else if (std::abs(f[0] - s[0]) < eps) { if (f[1] < s[1] - eps) { return true; } else if (std::abs(f[1] - s[1]) < eps) { if (f[2] < s[2] - eps) { return true; } } } return false; }; std::set src_cent(compare_pointxyz); auto src_already_in = [&](const PointXYZ& halo_cent) { if (src_cent.find(halo_cent) == src_cent.end()) { src_cent.insert(halo_cent); return false; } return true; }; ATLAS_TRACE_SCOPE("build kd-tree for source polygons") { for (idx_t scell = 0; scell < src_csp.size(); ++scell) { const auto& s_csp = src_csp[scell]; if (not src_already_in((s_csp.centroid()))) { kdt_search.insert(s_csp.centroid(), scell); max_srccell_rad = std::max(max_srccell_rad, s_csp.radius()); } } kdt_search.build(); } } void ConservativeSphericalPolygonInterpolation::intersect_polygons(const PolygonArray& src_csp) { ATLAS_TRACE(); auto& timings = sharable_data_->timings; auto& tgt = sharable_data_->tgt_; StopWatch stopwatch; util::KDTree kdt_search; double max_srccell_rad = 0.; { stopwatch.start(); kdt_search.reserve(src_csp.size()); int build_with_centroids = 0; const char* ATLAS_BUILD_KDTREE_CENTROID = ::getenv("ATLAS_BUILD_KDTREE_CENTROID"); if (ATLAS_BUILD_KDTREE_CENTROID != nullptr) { build_with_centroids = std::atof(ATLAS_BUILD_KDTREE_CENTROID); } if (build_with_centroids) { build_source_kdtree_centroid(kdt_search, max_srccell_rad, src_csp); } else { build_source_kdtree(kdt_search, max_srccell_rad, src_csp); } stopwatch.stop(); } timings.source_kdtree_assembly = stopwatch.elapsed(); StopWatch stopwatch_kdtree_search; StopWatch stopwatch_polygon_intersections; enum MeshSizeId { SRC, TGT, SRC_TGT_INTERSECT, TGT_NONINTERSECT }; enum AreaCoverageId { TOTAL_TGT, MAX_TGT }; std::array num_pol{0, 0, 0, 0, 0}; std::array area_coverage{0., 0.}; auto& tgt_iparam_ = sharable_data_->tgt_iparam_; auto& tgt_csp_size = tgt.csp_size; tgt_iparam_.resize(tgt_csp_size); std::vector src_iparam; // only used for debugging if (validate_) { src_iparam.resize(src_csp.size()); } // the worst target polygon coverage for analysis of intersection std::pair worst_tgt_overcover; std::pair worst_tgt_undercover; worst_tgt_overcover.first = -1; worst_tgt_overcover.second = -1.; worst_tgt_undercover.first = -1; worst_tgt_undercover.second = M_PI; // M_PI is the maximum area of a polygon on the unit sphere // NOTE: polygon vertex points at distance < pointsSameEPS will be replaced with one point constexpr double pointsSameEPS = 5.e6 * std::numeric_limits::epsilon(); std::vector intersection_scsp_ids; std::vector intersection_weights; std::vector intersection_src_centroids; bool fpe_for_polygon = Polygon::fpe(); Polygon::fpe(false); bool fpe_disabled = fpe_for_polygon ? atlas::library::disable_floating_point_exception(FE_INVALID) : false; auto restore_fpe = [fpe_disabled, fpe_for_polygon] { if (fpe_disabled) { atlas::library::enable_floating_point_exception(FE_INVALID); } Polygon::fpe(fpe_for_polygon); }; const auto cell_halo = array::make_view(tgt_mesh_.cells().halo()); const auto node_ghost = array::make_view(tgt_mesh_.nodes().ghost()); auto skip_target = [&] (idx_t tcsp_id) -> bool { if (tgt.cell_data) { auto tcell = csp_to_cell(tcsp_id, tgt); return cell_halo(tcell); } else { auto tnode = tgt.csp2node[tcsp_id]; return node_ghost(tnode); } }; ATLAS_TRACE_SCOPE("intersecting polygons") { eckit::Channel blackhole; eckit::ProgressTimer progress("Intersecting polygons ", 100, " percent", double(10), tgt_csp_size > 50 ? Log::info() : blackhole); float last_progress_percent = 0.00; for (idx_t tcsp_id = 0; tcsp_id < tgt_csp_size; ++tcsp_id) { auto tgt_csp = get_tgt_csp(tcsp_id); // This needs to be before 'skip_target' because it fills in tgt.csp2node node-data // A bit unfortunate... to be improved! if (skip_target(tcsp_id)) { continue; } intersection_scsp_ids.resize(0); intersection_weights.resize(0); intersection_src_centroids.resize(0); const auto& t_csp = tgt_csp; double tgt_cover_area = 0.; if (remap_stat_.timings) {stopwatch_kdtree_search.start(); } auto scsp_ids = kdt_search.closestPointsWithinRadius(t_csp.centroid(), t_csp.radius() + max_srccell_rad).payloads(); if (remap_stat_.timings) {stopwatch_kdtree_search.stop(); } for (const auto& scsp_id: scsp_ids) { const auto& s_csp = src_csp[scsp_id]; if (remap_stat_.timings) {stopwatch_polygon_intersections.start(); } Polygon csp_i = s_csp.intersect(t_csp, nullptr, pointsSameEPS); if (remap_stat_.timings) {stopwatch_polygon_intersections.stop(); } double csp_i_area = csp_i.area(); #if ATLAS_BUILD_TYPE_DEBUG if (validate_) { int pout; if (inside_vertices(t_csp, s_csp, pout) > 2 && csp_i.area() < 3e-16) { dump_intersection("Zero area intersections with inside_vertices", t_csp, src_csp, scsp_ids); } } #endif if (csp_i_area > 0) { intersection_scsp_ids.emplace_back(scsp_id); intersection_weights.emplace_back(csp_i_area); if (order_ == 2 or not matrix_free_ or not matrixAllocated()) { intersection_src_centroids.emplace_back(csp_i.centroid()); } tgt_cover_area += csp_i_area; if (std::abs(1. - tgt_cover_area / tgt_csp.area()) <= 1e-10) { break; } if (validate_) { src_iparam[scsp_id].csp_ids.emplace_back(tcsp_id); src_iparam[scsp_id].weights.emplace_back(csp_i_area); if (csp_i_area > 1.1 * t_csp.area()) { dump_intersection("Intersection larger than target", t_csp, src_csp, scsp_ids); } if (csp_i_area > 1.1 * s_csp.area()) { dump_intersection("Intersection larger than source", t_csp, src_csp, scsp_ids); } } } } const double tgt_cover_err = std::abs(t_csp.area() - tgt_cover_area); const double tgt_cover_err_percent = 100. * tgt_cover_err / t_csp.area(); if (validate_ && tgt_cover_err_percent > 0.1) { if (mpi::size() == 1) { dump_intersection("Target cell not exactly covered", t_csp, src_csp, scsp_ids); if (remap_stat_.intersection) { area_coverage[TOTAL_TGT] += tgt_cover_err; area_coverage[MAX_TGT] = std::max(area_coverage[MAX_TGT], tgt_cover_err); } } } if (intersection_scsp_ids.size() == 0 and remap_stat_.intersection) { num_pol[Statistics::NUM_UNCVR_FULL_TGT]++; } else if (tgt_cover_err_percent > 0.1 && remap_stat_.intersection) { num_pol[Statistics::NUM_UNCVR_PART_TGT]++; } // if (normalise_intersections_ && tgt_cover_err_percent < 1.) { // double wfactor = t_csp.area() / (tgt_cover_area > 0. ? tgt_cover_area : 1.); // for (idx_t i = 0; i < intersection_weights.size(); i++) { // intersection_weights[i] *= wfactor; // } // } tgt_iparam_[tcsp_id].csp_ids = intersection_scsp_ids; tgt_iparam_[tcsp_id].weights = intersection_weights; if (order_ == 2 or not matrix_free_ or not matrixAllocated()) { tgt_iparam_[tcsp_id].centroids = intersection_src_centroids; } if (remap_stat_.intersection) { num_pol[Statistics::NUM_INT_PLG] += tgt_iparam_[tcsp_id].weights.size(); } if ( double(tcsp_id) / double(tgt_csp_size) > last_progress_percent ) { last_progress_percent += 0.01; ++progress; } } } restore_fpe(); timings.polygon_intersections = stopwatch_polygon_intersections.elapsed(); timings.source_kdtree_search = stopwatch_kdtree_search.elapsed(); num_pol[Statistics::NUM_SRC_PLG] = src_csp.size(); num_pol[Statistics::NUM_TGT_PLG] = tgt_csp_size; ATLAS_TRACE_MPI(ALLREDUCE) { mpi::comm().allReduceInPlace(num_pol.data(), num_pol.size(), eckit::mpi::sum()); } remap_stat_.counts[Statistics::NUM_INT_PLG] = num_pol[Statistics::NUM_INT_PLG]; remap_stat_.counts[Statistics::NUM_UNCVR_FULL_TGT] = num_pol[Statistics::NUM_UNCVR_FULL_TGT]; remap_stat_.counts[Statistics::NUM_UNCVR_PART_TGT] = num_pol[Statistics::NUM_UNCVR_PART_TGT]; const std::string polygon_intersection_folder = "polygon_intersection/"; if (validate_ && mpi::rank() == 0) { if (filesystem::exists(polygon_intersection_folder)) { filesystem::remove_all(polygon_intersection_folder); } filesystem::create_directory(polygon_intersection_folder); Log::warning() << "The worst target polygon coverage on the task 0 is in the folder \e[1mpolygon_intersection\e[0m." << std::endl; } if (validate_) { ATLAS_TRACE_SCOPE("compute errors in coverting target cells with intersections") { double geo_err_l1 = 0.; double geo_err_linf = -1.; for (idx_t tcsp_id = 0; tcsp_id < tgt_csp_size; ++tcsp_id) { auto tgt_csp = get_tgt_csp(tcsp_id); if (skip_target(tcsp_id)) { continue; } auto& t_csp = tgt_csp; auto& tiparam = tgt_iparam_[tcsp_id]; double tgt_cover_err = 0.; for (idx_t icell = 0; icell < tiparam.weights.size(); ++icell) { tgt_cover_err += tiparam.weights[icell]; } tgt_cover_err = 100. * (tgt_cover_err - t_csp.area()) / t_csp.area(); if (worst_tgt_overcover.second < tgt_cover_err) { worst_tgt_overcover.second = tgt_cover_err;; worst_tgt_overcover.first = tcsp_id; } if (worst_tgt_undercover.second > tgt_cover_err) { worst_tgt_undercover.second = tgt_cover_err; worst_tgt_undercover.first = tcsp_id; } if (std::abs(tgt_cover_err) > 0.5) { std::fstream polygon_intersection_info("polygon_intersection", std::ios::out); PointLonLat centre_ll; eckit::geometry::Sphere::convertCartesianToSpherical(1., t_csp.centroid(), centre_ll); polygon_intersection_info << "WARNING tgt cell " << tcsp_id << " over-covering: \e[1m" << tgt_cover_err << "\e[0m %, cell-centre: " << centre_ll <<"\n"; polygon_intersection_info << "source indices: " << tiparam.csp_ids << std::endl; dump_intersection("Target cell not exaclty covered", t_csp, src_csp, tiparam.csp_ids); //dump_polygons_to_json(t_csp, src_csp, tiparam.csp_ids, "bad_polygon", 1.e-16); } geo_err_l1 += std::abs(0.01 * tgt_cover_err * t_csp.area()); geo_err_linf = std::max(geo_err_linf, std::abs(0.01 * tgt_cover_err * t_csp.area())); } ATLAS_TRACE_MPI(ALLREDUCE) { mpi::comm().allReduceInPlace(geo_err_l1, eckit::mpi::sum()); mpi::comm().allReduceInPlace(geo_err_linf, eckit::mpi::max()); } remap_stat_.errors[Statistics::ERR_TGT_INTERSECTPLG_L1] = geo_err_l1 / unit_sphere_area(); remap_stat_.errors[Statistics::ERR_TGT_INTERSECTPLG_LINF] = geo_err_linf; } ATLAS_TRACE_SCOPE("compute errors in covering source cells with intersections") { // NOTE: source cell at process boundary will not be covered by target cells, skip if MPI > 1 // TODO: mark these source cells beforehand and compute error in them among the processes if (remap_stat_.intersection) { // ATLAS_TRACE("computing covering source cells errors"); // double geo_err_l1 = 0.; // double geo_err_linf = -1.; // for (idx_t scell = 0; scell < src_csp.size(); ++scell) { // if (src_csp[scell].halo_type != 0) { // continue; // skip periodic & halo cells // } // const auto& s_csp = src_csp[scell]; // auto diff_cell = s_csp.area(); // const auto& siparam = src_iparam[scell]; // for (idx_t icell = 0; icell < siparam.weights.size(); ++icell) { // diff_cell -= siparam.weights[icell]; // } // geo_err_l1 += std::abs(diff_cell); // geo_err_linf = std::max(geo_err_linf, std::abs(diff_cell)); // } } #if PRINT_BAD_POLYGONS // TODO: need environment variable to print out intersection of source cell with target polygons // dump polygons in json format idx_t scell_printout = 120; if (scell == scell_printout) { dump_polygons_to_json(t_csp, 1.e-14, src_csp, siparam.csp_ids, "polygon_dump", "scell" + std::to_string(scell_printout)); } #endif /* // TODO: normalise to source cell double normm = s_csp.area() / (src_cover_area > 0. ? src_cover_area : s_csp.area()); for (idx_t icell = 0; icell < siparam.csp_ids.size(); ++icell) { idx_t scell = siparam.csp_ids[icell]; auto siparam = src_iparam_[scell]; size_t src_intersectors = siparam.csp_ids.size(); for (idx_t ticell = 0; ticell < src_intersectors; ticell++ ) { if (siparam.csp_ids[icell] == ticell) {; siparam.weights[icell] *= normm; siparam.tgt_weights[icell] *= normm; } } } */ } } if (validate_) { ATLAS_TRACE_SCOPE("compute and output worst target polygon coverage by its intersections with source polygons") { std::vector first(mpi::comm().size()); std::vector second(mpi::comm().size()); ATLAS_TRACE_MPI(ALLGATHER) { mpi::comm().allGather(worst_tgt_overcover.first, first.begin(), first.end()); mpi::comm().allGather(worst_tgt_overcover.second, second.begin(), second.end()); } auto max_over = std::max_element(second.begin(), second.end()); auto rank_over = std::distance(second.begin(), max_over); Log::info() << "WARNING The worst target polygon over-coveraging: \e[1m" << *max_over << "\e[0m %. For details, check the file: worst_target_cell_overcover.info " << std::endl; if (rank_over == mpi::rank()) { auto tcsp_id = worst_tgt_overcover.first; auto tgt_csp = get_tgt_csp(tcsp_id); dump_polygons_to_json(tgt_csp, pointsSameEPS, src_csp, tgt_iparam_[tcsp_id].csp_ids, polygon_intersection_folder, "worst_target_cell_overcover"); } ATLAS_TRACE_MPI(ALLGATHER) { mpi::comm().allGather(worst_tgt_undercover.first, first.begin(), first.end()); mpi::comm().allGather(worst_tgt_undercover.second, second.begin(), second.end()); } auto min_under = std::min_element(second.begin(), second.end()); auto rank_under = std::distance(second.begin(), min_under); Log::info() << "WARNING The worst target polygon under-coveraging: \e[1m" << *min_under << "\e[0m %. For details, check the file: worst_target_cell_undercover.info " << std::endl; if (rank_under == mpi::rank()) { auto tcsp_id = worst_tgt_undercover.first; auto tgt_csp = get_tgt_csp(tcsp_id); dump_polygons_to_json(tgt_csp, pointsSameEPS, src_csp, tgt_iparam_[tcsp_id].csp_ids, polygon_intersection_folder, "worst_target_cell_undercover"); } } } } ConservativeSphericalPolygonInterpolation::Triplets ConservativeSphericalPolygonInterpolation::compute_1st_order_triplets() { ATLAS_TRACE("ConservativeMethod::setup: build cons-1 interpolant matrix"); ATLAS_ASSERT(not matrix_free_); const auto& tgt_iparam = data_->tgt_iparam_; Triplets triplets; size_t triplets_size = 0; // determine the size of array of triplets used to define the sparse matrix if (tgt_cell_data_) { for (idx_t tcsp_id = 0; tcsp_id < data_->tgt_.csp_size; ++tcsp_id) { triplets_size += tgt_iparam[tcsp_id].csp_ids.size(); } } else { auto& tgt_node2csp = data_->tgt_.node2csp; for (idx_t tnode = 0; tnode < n_tpoints_; ++tnode) { for (idx_t isubcell = 0; isubcell < tgt_node2csp[tnode].size(); ++isubcell) { idx_t subcell = tgt_node2csp[tnode][isubcell]; triplets_size += tgt_iparam[subcell].weights.size(); } } } triplets.reserve(triplets_size); TargetTriplets target_triplets(normalise_); struct ScopedDisableFPE { ScopedDisableFPE() { fpe_disabled = atlas::library::disable_floating_point_exception(FE_DIVBYZERO); } ~ScopedDisableFPE() { if (fpe_disabled) { atlas::library::enable_floating_point_exception(FE_DIVBYZERO); } } bool fpe_disabled; } disable_fpe; // assemble triplets to define the sparse matrix const auto& tgt_areas = data_->tgt_.areas; if (tgt_cell_data_ && src_cell_data_) { for (idx_t tcsp_id = 0; tcsp_id < data_->tgt_.csp_size; ++tcsp_id) { idx_t tcell = csp_to_cell(tcsp_id, data_->tgt_); double inv_tgt_weight = (tgt_areas[tcell] > 0. ? 1. / tgt_areas[tcell] : 0.); const auto& iparam = tgt_iparam[tcsp_id]; target_triplets.reset(tcell); for (idx_t i_scsp = 0; i_scsp < iparam.csp_ids.size(); ++i_scsp) { idx_t scsp_id = iparam.csp_ids[i_scsp]; idx_t scell = csp_to_cell(scsp_id, data_->src_); double weight = iparam.weights[i_scsp] * inv_tgt_weight; target_triplets.add(scell, weight); } target_triplets.emplace_in(triplets); } } else if (not tgt_cell_data_ && src_cell_data_) { auto& tgt_node2csp = data_->tgt_.node2csp; for (idx_t tnode = 0; tnode < n_tpoints_; ++tnode) { double inv_tgt_weight = (tgt_areas[tnode] > 0. ? 1. / tgt_areas[tnode] : 0.); target_triplets.reset(tnode); for (idx_t isubcell = 0; isubcell < tgt_node2csp[tnode].size(); ++isubcell) { const idx_t subcell = tgt_node2csp[tnode][isubcell]; const auto& iparam = tgt_iparam[subcell]; for (idx_t i_scsp = 0; i_scsp < iparam.csp_ids.size(); ++i_scsp) { idx_t scsp_id = iparam.csp_ids[i_scsp]; idx_t scell = csp_to_cell(scsp_id, data_->src_); ATLAS_ASSERT(scsp_id < n_spoints_); volatile double weight = iparam.weights[i_scsp] * inv_tgt_weight; target_triplets.add(scell, weight); } } target_triplets.emplace_in(triplets); } } else if (tgt_cell_data_ && not src_cell_data_) { auto& src_csp2node = data_->src_.csp2node; for (idx_t tcsp_id = 0; tcsp_id < data_->tgt_.csp_size; ++tcsp_id) { idx_t tcell = csp_to_cell(tcsp_id, data_->tgt_); double inv_tgt_weight = (tgt_areas[tcell] > 0. ? 1. / tgt_areas[tcell] : 0.); const auto& iparam = tgt_iparam[tcsp_id]; target_triplets.reset(tcell); for (idx_t i_scsp = 0; i_scsp < iparam.csp_ids.size(); ++i_scsp) { idx_t scsp_id = iparam.csp_ids[i_scsp]; idx_t snode = src_csp2node[scsp_id]; ATLAS_ASSERT(snode < n_spoints_); double weight = iparam.weights[i_scsp] * inv_tgt_weight; target_triplets.add(snode, weight); } target_triplets.emplace_in(triplets); } } else if (not tgt_cell_data_ && not src_cell_data_) { auto& tgt_node2csp = data_->tgt_.node2csp; auto& src_csp2node = data_->src_.csp2node; for (idx_t tnode = 0; tnode < n_tpoints_; ++tnode) { double inv_tgt_weight = (tgt_areas[tnode] > 0. ? 1. / tgt_areas[tnode] : 0.); target_triplets.reset(tnode); for (const auto& tcsp_id: tgt_node2csp[tnode]) { const auto& iparam = tgt_iparam[tcsp_id]; for (idx_t i_scsp = 0; i_scsp < iparam.csp_ids.size(); ++i_scsp) { idx_t scsp_id = iparam.csp_ids[i_scsp]; idx_t snode = src_csp2node[scsp_id]; ATLAS_ASSERT(snode < n_spoints_); double weight = iparam.weights[i_scsp] * inv_tgt_weight; target_triplets.add(snode, weight); } } target_triplets.emplace_in(triplets); } } if (validate_) { ATLAS_TRACE("ConservativeMethod::setup: Validate the cons-1 matrix"); std::vector weight_sum(n_tpoints_); for (auto& triplet : triplets) { weight_sum[triplet.row()] += triplet.value(); } if (order_ == 1) { // first order should not give overshoots double eps = 1e4 * std::numeric_limits::epsilon(); for( auto& triplet : triplets ) { if (triplet.value() > 1. + eps or triplet.value() < -eps) { Log::info() << "target point " << triplet.row() << " weight: " << triplet.value() << std::endl; } } } auto ghost = array::make_view( data_->tgt_.cell_data ? tgt_mesh_.cells().halo() : tgt_mesh_.nodes().ghost() ); for (size_t row = 0; row < n_tpoints_; ++row) { if (ghost(row)) { continue; } if (std::abs(weight_sum[row] - 1.) > 1e-10) { Log::info() << "target weight in row " << row << " differs from 1 by " << std::abs(weight_sum[row] - 1.) << std::endl; } } } return triplets; } ConservativeSphericalPolygonInterpolation::Triplets ConservativeSphericalPolygonInterpolation::compute_2nd_order_triplets() { ATLAS_TRACE("ConservativeMethod: build cons-2 interpolant matrix"); Triplets triplets; ATLAS_ASSERT(not matrix_free_); const auto& src = data_->src_; const auto& tgt = data_->tgt_; const auto& src_points = src.points; const auto& tgt_iparam_ = data_->tgt_iparam_; size_t triplets_size = 0; const auto& tgt_areas = data_->tgt_.areas; // struct tripple{idx_t row; idx_t column; double value; }; // std::vector triplets; struct ScopedDisableFPE { ScopedDisableFPE() { fpe_disabled = atlas::library::disable_floating_point_exception(FE_DIVBYZERO); } ~ScopedDisableFPE() { if (fpe_disabled) { atlas::library::enable_floating_point_exception(FE_DIVBYZERO); } } bool fpe_disabled; } disable_fpe; Workspace_get_cell_neighbours w_cell; Workspace_get_node_neighbours w_node; TargetTriplets target_triplets(normalise_); if (tgt_cell_data_) { const auto tgt_halo = array::make_view(tgt_mesh_.cells().halo()); for (idx_t tcsp_id = 0; tcsp_id < data_->tgt_.csp_size; ++tcsp_id) { const auto& iparam = tgt_iparam_[tcsp_id]; idx_t tcell = csp_to_cell(tcsp_id, tgt); if (iparam.csp_ids.size() == 0 || tgt_halo(tcell)) { continue; } if (src_cell_data_) { for (idx_t i_scsp = 0; i_scsp < iparam.csp_ids.size(); ++i_scsp) { const idx_t scell = csp_to_cell(iparam.csp_ids[i_scsp], src); const auto src_neighbours = get_cell_neighbours(src_mesh_, scell, w_cell); triplets_size += 2 * src_neighbours.size() + 1; } } else { for (idx_t i_scsp = 0; i_scsp < iparam.csp_ids.size(); ++i_scsp) { const idx_t scsp_id = iparam.csp_ids[i_scsp]; const idx_t snode = src.csp2node[scsp_id]; const auto src_neighbours = get_node_neighbours(src_mesh_, snode, w_node); triplets_size += 2 * src_neighbours.size() + 1; } } } triplets.reserve(triplets_size); std::vector Rsj; std::vector Aik; for (idx_t tcsp_id = 0; tcsp_id < tgt.csp_size; ++tcsp_id) { idx_t tcell = csp_to_cell(tcsp_id, tgt); const auto& iparam = tgt_iparam_[tcsp_id]; if (iparam.csp_ids.size() == 0 || tgt_halo(tcell)) { continue; } target_triplets.reset(tcell); // better conservation after Kritsikis et al. (2017) // NOTE: ommited here at cost of conservation due to more involved implementation in parallel // TEMP: use barycentre computed based on the source polygon's vertices, rather then // the numerical barycentre based on barycentres of the intersections with target cells. // TODO: for a given source cell, collect the centroids of all its intersections with target cells // to compute the numerical barycentre of the cell bases on intersection. // PointXYZ Cs = {0., 0., 0.}; // for ( idx_t icell = 0; icell < iparam.csp_ids.size(); ++icell ) { // Cs = Cs + PointXYZ::mul( iparam.centroids[icell], iparam.weights[icell] ); // } double tcell_area_inv = ( tgt_areas[tcell] > 0.) ? 1. / tgt_areas[tcell] : 0.; Aik.resize(iparam.csp_ids.size()); for (idx_t i_scsp = 0; i_scsp < iparam.csp_ids.size(); ++i_scsp) { const PointXYZ& Csk = iparam.centroids[i_scsp]; const idx_t scsp_id = iparam.csp_ids[i_scsp]; const idx_t spt = src_cell_data_ ? csp_to_cell(scsp_id, src) : src.csp2node[scsp_id]; const PointXYZ& Cs = src_points[spt]; // !!! this is NOT barycentre of the subcell in case of NodeColumns Aik[i_scsp] = Csk - Cs - PointXYZ::mul(Cs, PointXYZ::dot(Cs, Csk - Cs)); std::vector src_neighbours; if (src_cell_data_) { idx_t scell = spt; src_neighbours = get_cell_neighbours(src_mesh_, scell, w_cell); } else { idx_t snode = spt; src_neighbours = get_node_neighbours(src_mesh_, snode, w_node); } Rsj.resize(src_neighbours.size()); PointXYZ Rs = {0., 0., 0.}; double dual_area_inv = 0.; for (idx_t j = 0; j < src_neighbours.size(); ++j) { idx_t sj = src_neighbours[j]; idx_t nsj = src_neighbours[ next_index(j, src_neighbours.size()) ]; const auto& Csj = src_points[sj]; const auto& Cnsj = src_points[nsj]; if (Polygon::GreatCircleSegment(Cs, Csj).inLeftHemisphere(Cnsj, -1e-16)) { Rsj[j] = PointXYZ::cross(Cnsj, Csj); dual_area_inv += Polygon({Cs, Csj, Cnsj}).area(); } else { Rsj[j] = PointXYZ::cross(Csj, Cnsj); dual_area_inv += Polygon({Cs, Cnsj, Csj}).area(); } Rs = Rs + Rsj[j]; } dual_area_inv = (dual_area_inv > 0.) ? 1. / dual_area_inv : 0.; Aik[i_scsp] = PointXYZ::mul(Aik[i_scsp], iparam.weights[i_scsp] * tcell_area_inv * dual_area_inv); if (src_cell_data_) { for (idx_t j = 0; j < src_neighbours.size(); ++j) { idx_t nj = next_index(j, src_neighbours.size()); idx_t sj = src_neighbours[j]; idx_t nsj = src_neighbours[nj]; double w = 0.5 * PointXYZ::dot(Rsj[j], Aik[i_scsp]); target_triplets.add(sj, w); target_triplets.add(nsj, w); } idx_t scell = spt; target_triplets.add(scell, iparam.weights[i_scsp] * tcell_area_inv - PointXYZ::dot(Rs, Aik[i_scsp])); } else { for (idx_t j = 0; j < src_neighbours.size(); ++j) { idx_t nj = next_index(j, src_neighbours.size()); idx_t sj = src_neighbours[j]; idx_t nsj = src_neighbours[nj]; double w = 0.5 * PointXYZ::dot(Rsj[j], Aik[i_scsp]); target_triplets.add(sj, w); target_triplets.add(nsj, w); } idx_t snode = spt; target_triplets.add(snode, iparam.weights[i_scsp] * tcell_area_inv - PointXYZ::dot(Rs, Aik[i_scsp])); } } target_triplets.emplace_in(triplets); } } else { // if ( not tgt_cell_data_ ) const auto tgt_ghost = array::make_view(tgt_mesh_.nodes().ghost()); auto& tgt_node2csp = tgt.node2csp; triplets_size = 0; for (idx_t tnode = 0; tnode < n_tpoints_; ++tnode) { if (tgt_ghost(tnode)) { continue; } for (idx_t i_tcsp = 0; i_tcsp < tgt_node2csp[tnode].size(); ++i_tcsp) { const idx_t tcsp_id = tgt_node2csp[tnode][i_tcsp]; const auto& iparam = tgt_iparam_[tcsp_id]; if (iparam.csp_ids.size() == 0) { continue; } if (src_cell_data_) { for (idx_t i_scsp = 0; i_scsp < iparam.csp_ids.size(); ++i_scsp) { const idx_t scsp_id = iparam.csp_ids[i_scsp]; // const idx_t scell = csp_to_cell(scsp_id, data_->src_); const idx_t scell = scsp_id; const auto src_neighbours = get_cell_neighbours(src_mesh_, scell, w_cell); triplets_size += 2 * src_neighbours.size() + 1; } } else { for (idx_t i_scsp = 0; i_scsp < iparam.csp_ids.size(); ++i_scsp) { const idx_t scsp_id = iparam.csp_ids[i_scsp]; const idx_t snode = src.csp2node[scsp_id]; const auto src_neighbours = get_node_neighbours(src_mesh_, snode, w_node); triplets_size += 2 * src_neighbours.size() + 1; } } } } triplets.reserve(triplets_size); std::vector Rsj; std::vector Aik; for (idx_t tnode = 0; tnode < n_tpoints_; ++tnode) { if (tgt_ghost(tnode)) { continue; } target_triplets.reset(tnode); for (idx_t i_tcsp = 0; i_tcsp < tgt_node2csp[tnode].size(); ++i_tcsp) { const idx_t tcsp_id = tgt_node2csp[tnode][i_tcsp]; const auto& iparam = tgt_iparam_[tcsp_id]; if (iparam.csp_ids.size() == 0) { continue; } auto tnode_area_inv = ( tgt_areas[tnode] > 0.) ? 1. / tgt_areas[tnode] : 0.; // get the barycentre of the dual cell // better conservation // PointXYZ Cs = {0., 0., 0.}; // for ( idx_t isubcell = 0; isubcell < src_.node2csp[snode].size(); ++isubcell ) { // idx_t subcell = src_.node2csp[snode][isubcell]; // const auto& iparam = src_iparam_[subcell]; // for ( idx_t icell = 0; icell < iparam.csp_ids.size(); ++icell ) { // Cs = Cs + PointXYZ::mul( iparam.centroids[icell], iparam.weights[icell] ); // } // const double Cs_norm = PointXYZ::norm( Cs ); // ATLAS_ASSERT( Cs_norm > 0. ); // Cs = PointXYZ::div( Cs, Cs_norm ); // } Aik.resize(iparam.csp_ids.size()); for (idx_t i_scsp = 0; i_scsp < iparam.csp_ids.size(); ++i_scsp) { const PointXYZ& Csk = iparam.centroids[i_scsp]; const idx_t scsp_id = iparam.csp_ids[i_scsp]; const idx_t spt = src_cell_data_ ? csp_to_cell(scsp_id, src) : src.csp2node[scsp_id]; const PointXYZ& Cs = src_points[spt]; // !!! this is NOT barycentre of the subcell in case of NodeColumns Aik[i_scsp] = Csk - Cs - PointXYZ::mul(Cs, PointXYZ::dot(Cs, Csk - Cs)); double dual_area_inv = 0.; std::vector src_neighbours; if (src_cell_data_) { idx_t scell = spt; src_neighbours = get_cell_neighbours(src_mesh_, scell, w_cell); } else { idx_t snode = spt; src_neighbours = get_node_neighbours(src_mesh_, snode, w_node); } Rsj.resize(src_neighbours.size()); PointXYZ Rs = {0., 0., 0.}; for (idx_t j = 0; j < src_neighbours.size(); ++j) { idx_t sj = src_neighbours[j]; idx_t nsj = src_neighbours[ next_index(j, src_neighbours.size()) ]; const auto& Csj = src_points[sj]; const auto& Cnsj = src_points[nsj]; if (Polygon::GreatCircleSegment(Cs, Csj).inLeftHemisphere(Cnsj, -1e-16)) { Rsj[j] = PointXYZ::cross(Cnsj, Csj); dual_area_inv += Polygon({Cs, Csj, Cnsj}).area(); } else { Rsj[j] = PointXYZ::cross(Csj, Cnsj); dual_area_inv += Polygon({Cs, Cnsj, Csj}).area(); } Rs = Rs + Rsj[j]; } dual_area_inv = (dual_area_inv > 0.) ? 1. / dual_area_inv : 0.; Aik[i_scsp] = PointXYZ::mul(Aik[i_scsp], iparam.weights[i_scsp] * tnode_area_inv * dual_area_inv); if (src_cell_data_) { for (idx_t j = 0; j < src_neighbours.size(); ++j) { idx_t nj = next_index(j, src_neighbours.size()); idx_t sj = src_neighbours[j]; idx_t nsj = src_neighbours[nj]; double w = 0.5 * PointXYZ::dot(Rsj[j], Aik[i_scsp]); target_triplets.add(sj, w); target_triplets.add(nsj, w); } idx_t scell = spt; target_triplets.add(scell, iparam.weights[i_scsp] * tnode_area_inv - PointXYZ::dot(Rs, Aik[i_scsp])); } else { for (idx_t j = 0; j < src_neighbours.size(); ++j) { idx_t nj = next_index(j, src_neighbours.size()); idx_t sj = src_neighbours[j]; idx_t nsj = src_neighbours[nj]; double w = 0.5 * PointXYZ::dot(Rsj[j], Aik[i_scsp]); target_triplets.add(sj, w); target_triplets.add(nsj, w); } idx_t snode = spt; target_triplets.add(snode, iparam.weights[i_scsp] * tnode_area_inv - PointXYZ::dot(Rs, Aik[i_scsp])); } } } target_triplets.emplace_in(triplets); } } return triplets; } void ConservativeSphericalPolygonInterpolation::do_execute(const FieldSet& src_fields, FieldSet& tgt_fields, Metadata& metadata) const { std::vector md_array; md_array.resize( src_fields.size() ); for (int i = 0; i < src_fields.size(); i++) { // TODO: memory-wise should we openmp this? do_execute(src_fields[i], tgt_fields[i], md_array[i]); } metadata = md_array[0]; // TODO: reduce metadata of a set of variables to a single metadata of a variable? } void ConservativeSphericalPolygonInterpolation::do_execute(const Field& src_field, Field& tgt_field, Metadata& metadata) const { ATLAS_TRACE("ConservativeMethod: do_execute"); { if (src_field.dirty()) { ATLAS_TRACE("halo exchange source"); src_field.haloExchange(); } } StopWatch stopwatch; stopwatch.start(); if (order_ == 1) { if (matrix_free_) { ATLAS_TRACE("matrix_free_order_1"); const auto src_vals = array::make_view(src_field); auto tgt_vals = array::make_view(tgt_field); const auto& tgt_iparam = data_->tgt_iparam_; const auto& tgt_areas = data_->tgt_.areas; // CASE: CELL TO CELL if (tgt_cell_data_ && src_cell_data_) { for (idx_t tcsp_id = 0; tcsp_id < data_->tgt_.csp_size; ++tcsp_id) { idx_t tcell = csp_to_cell(tcsp_id, data_->tgt_); double tgt_val = 0.; const auto& iparam = tgt_iparam[tcsp_id]; for (idx_t i_scsp = 0; i_scsp < iparam.csp_ids.size(); ++i_scsp) { idx_t scsp_id = iparam.csp_ids[i_scsp]; idx_t scell = csp_to_cell(scsp_id, data_->src_); tgt_val += iparam.weights[i_scsp] * src_vals(scell); } if (tgt_areas[tcell] > 0.) { tgt_val /= tgt_areas[tcell]; } tgt_vals(tcell) = tgt_val; } } // CASE: NODE TO CELL else if (not tgt_cell_data_ && src_cell_data_) { auto& tgt_node2csp = data_->tgt_.node2csp; for (idx_t tnode = 0; tnode < n_tpoints_; ++tnode) { double tgt_val = 0.; for( const auto& tcsp_id: tgt_node2csp[tnode]) { const auto& iparam = tgt_iparam[tcsp_id]; for (idx_t i_scsp = 0; i_scsp < iparam.csp_ids.size(); ++i_scsp) { idx_t scsp_id = iparam.csp_ids[i_scsp]; idx_t scell = csp_to_cell(scsp_id, data_->src_); tgt_val += iparam.weights[i_scsp] * src_vals(scell); } } if (tgt_areas[tnode] > 0.) { tgt_val /= tgt_areas[tnode]; } tgt_vals(tnode) = tgt_val; } } // CASE: CELL TO NODE else if (tgt_cell_data_ && not src_cell_data_) { const auto& src_csp2node = data_->src_.csp2node; for (idx_t tcsp_id = 0; tcsp_id < data_->tgt_.csp_size; ++tcsp_id) { idx_t tcell = csp_to_cell(tcsp_id, data_->tgt_); double tgt_val = 0.; const auto& iparam = tgt_iparam[tcsp_id]; for (idx_t i_scsp = 0; i_scsp < iparam.csp_ids.size(); ++i_scsp) { idx_t scsp_id = iparam.csp_ids[i_scsp]; idx_t snode = src_csp2node[scsp_id]; tgt_val += iparam.weights[i_scsp] * src_vals(snode); } if (tgt_areas[tcell] > 0.) { tgt_val /= tgt_areas[tcell]; } tgt_vals(tcell) = tgt_val; } } // CASE: NODE TO NODE else if (not tgt_cell_data_ && not src_cell_data_) { const auto& tgt_node2csp = data_->tgt_.node2csp; const auto& src_csp2node = data_->src_.csp2node; for (idx_t tnode = 0; tnode < n_tpoints_; ++tnode) { double tgt_val = 0.; for( const auto& tcsp_id: tgt_node2csp[tnode]) { const auto& iparam = tgt_iparam[tcsp_id]; for (idx_t i_scsp = 0; i_scsp < iparam.csp_ids.size(); ++i_scsp) { idx_t scsp_id = iparam.csp_ids[i_scsp]; idx_t snode = src_csp2node[scsp_id]; tgt_val += iparam.weights[i_scsp] * src_vals(snode); } } if (tgt_areas[tnode] > 0.) { tgt_val /= tgt_areas[tnode]; } tgt_vals(tnode) = tgt_val; } } } else { ATLAS_TRACE("matrix_order_1"); Method::do_execute(src_field, tgt_field, metadata); } } else if (order_ == 2) { if (matrix_free_) { ATLAS_NOTIMPLEMENTED; /* if (not src_cell_data_ or not tgt_cell_data_) { } ATLAS_TRACE("matrix_free_order_2"); const auto& src_iparam_ = data_->src_iparam_; const auto& tgt_.areas = data_->tgt_.areas; auto& src_.points = data_->src_.points; const auto src_vals = array::make_view(src_field); auto tgt_vals = array::make_view(tgt_field); const auto halo = array::make_view(src_mesh_.cells().halo()); for (idx_t tcell = 0; tcell < tgt_vals.size(); ++tcell) { tgt_vals(tcell) = 0.; } for (idx_t scell = 0; scell < src_vals.size(); ++scell) { const auto& iparam = src_iparam_[scell]; if (iparam.csp_ids.size() == 0 or halo(scell)) { continue; } const PointXYZ& Cs = src_.points[scell]; PointXYZ grad = {0., 0., 0.}; PointXYZ src_barycenter = {0., 0., 0.}; auto nb_cells = get_cell_neighbours(src_mesh_, scell); double dual_area_inv = 0.; for (idx_t j = 0; j < nb_cells.size(); ++j) { idx_t nj = next_index(j, nb_cells.size()); idx_t sj = nb_cells[j]; idx_t nsj = nb_cells[nj]; const auto& Csj = src_.points[sj]; const auto& Cnsj = src_.points[nsj]; double val = 0.5 * (src_vals(nj) + src_vals(nsj)) - src_vals(j); if (Polygon::GreatCircleSegment(Cs, Csj).inLeftHemisphere(Cnsj, -1e-16)) { dual_area_inv += Polygon({Cs, Csj, Cnsj}).area(); } else { //val *= -1; dual_area_inv += Polygon({Cs, Cnsj, Csj}).area(); } grad = grad + PointXYZ::mul(PointXYZ::cross(Csj, Cnsj), val); } dual_area_inv = ((dual_area_inv > 0.) ? 1. / dual_area_inv : 0.); grad = PointXYZ::mul(grad, dual_area_inv); for (idx_t icell = 0; icell < iparam.csp_ids.size(); ++icell) { src_barycenter = src_barycenter + PointXYZ::mul(iparam.centroids[icell], iparam.weights[icell]); } src_barycenter = PointXYZ::div(src_barycenter, PointXYZ::norm(src_barycenter)); grad = grad - PointXYZ::mul(src_barycenter, PointXYZ::dot(grad, src_barycenter)); ATLAS_ASSERT(std::abs(PointXYZ::dot(grad, src_barycenter)) < 1e-14); for (idx_t icell = 0; icell < iparam.csp_ids.size(); ++icell) { tgt_vals(iparam.csp_ids[icell]) += iparam.weights[icell] * (src_vals(scell) + PointXYZ::dot(grad, iparam.centroids[icell] - src_barycenter)); } } for (idx_t tcell = 0; tcell < tgt_vals.size(); ++tcell) { tgt_vals[tcell] /= tgt_.areas[tcell]; } */ } else { ATLAS_TRACE("matrix_order_2"); Method::do_execute(src_field, tgt_field, metadata); } } stopwatch.stop(); if (remap_stat_.conservation) { const auto src_cell_halo = array::make_view(src_mesh_.cells().halo()); const auto src_node_ghost = array::make_view(src_mesh_.nodes().ghost()); const auto src_node_halo = array::make_view(src_mesh_.nodes().halo()); const auto tgt_cell_halo = array::make_view(tgt_mesh_.cells().halo()); const auto tgt_node_ghost = array::make_view(tgt_mesh_.nodes().ghost()); const auto tgt_node_halo = array::make_view(tgt_mesh_.nodes().halo()); const auto& src_areas = data_->src_.areas; const auto& tgt_areas = data_->tgt_.areas; const auto src_vals = array::make_view(src_field); const auto tgt_vals = array::make_view(tgt_field); double err_remap_cons = 0.; if (src_cell_data_) { for (idx_t spt = 0; spt < src_vals.size(); ++spt) { if (src_cell_halo(spt)) { continue; } err_remap_cons += src_vals(spt) * src_areas[spt]; } } else { for (idx_t spt = 0; spt < src_vals.size(); ++spt) { if (src_node_halo(spt) or src_node_ghost(spt)) { continue; } err_remap_cons += src_vals(spt) * src_areas[spt]; } } if (tgt_cell_data_) { for (idx_t tpt = 0; tpt < tgt_vals.size(); ++tpt) { if (tgt_cell_halo(tpt)) { continue; } err_remap_cons -= tgt_vals(tpt) * tgt_areas[tpt]; } } else { for (idx_t tpt = 0; tpt < tgt_vals.size(); ++tpt) { if (tgt_node_halo(tpt) or tgt_node_ghost(tpt)) { continue; } err_remap_cons -= tgt_vals(tpt) * tgt_areas[tpt]; } } ATLAS_TRACE_MPI(ALLREDUCE) { mpi::comm().allReduceInPlace(&err_remap_cons, 1, eckit::mpi::sum()); } remap_stat_.errors[Statistics::ERR_REMAP_CONS] = err_remap_cons / unit_sphere_area(); } if (remap_stat_.intersection) { metadata.set("polygons.number_of_src_polygons", remap_stat_.counts[Statistics::NUM_SRC_PLG]); metadata.set("polygons.number_of_tgt_polygons", remap_stat_.counts[Statistics::NUM_TGT_PLG]); metadata.set("polygons.number_of_intersections", remap_stat_.counts[Statistics::NUM_INT_PLG]); metadata.set("polygons.number_of_full_noncovered_tgt_polygons", remap_stat_.counts[Statistics::NUM_UNCVR_FULL_TGT]); metadata.set("polygons.number_of_part_noncovered_tgt_polygons", remap_stat_.counts[Statistics::NUM_UNCVR_PART_TGT]); if (validate_) { metadata.set("errors.intersections_covering_tgt_cells_sum", remap_stat_.errors[Statistics::ERR_TGT_INTERSECTPLG_L1]); metadata.set("errors.intersections_covering_tgt_cells_max", remap_stat_.errors[Statistics::ERR_TGT_INTERSECTPLG_LINF]); } } if (remap_stat_.intersection || remap_stat_.conservation) { remap_stat_.fillMetadata(metadata); } auto& timings = data_->timings; remap_stat_.time[Statistics::TIME_SRC_PLG] = timings.source_polygons_assembly; remap_stat_.time[Statistics::TIME_TGT_PLG] = timings.source_polygons_assembly; remap_stat_.time[Statistics::TIME_KDTREE_BUILD] = timings.source_kdtree_assembly; remap_stat_.time[Statistics::TIME_MATRIX] = timings.matrix_assembly; remap_stat_.time[Statistics::TIME_INTERS] = timings.polygon_intersections; if (remap_stat_.timings) { remap_stat_.time[Statistics::TIME_KDTREE_SEARCH] = timings.source_kdtree_search; metadata.set("max_timings_in_seconds_per_task.source_kdtree_search", timings.source_kdtree_search); remap_stat_.time[Statistics::TIME_INTERP] = timings.polygon_intersections; metadata.set("max_timings_in_seconds_per_task.polygon_intersections", timings.polygon_intersections); } ATLAS_TRACE_MPI(ALLREDUCE) { mpi::comm().allReduceInPlace(remap_stat_.time.data(), remap_stat_.time.size(), eckit::mpi::max()); }metadata.set("max_timings_in_seconds_per_task.source_polygons_assembly", timings.source_polygons_assembly); metadata.set("max_timings_in_seconds_per_task.target_polygons_assembly", timings.target_polygons_assembly); metadata.set("max_timings_in_seconds_per_task.source_kdtree_assembly", timings.source_kdtree_assembly); metadata.set("max_timings_in_seconds_per_task.matrix_assembly", timings.matrix_assembly); metadata.set("max_timings_in_seconds_per_task.interpolation", stopwatch.elapsed()); remap_stat_.memory[Statistics::MEM_MATRIX] = (matrix_free_ ? 0 : matrix().footprint()); remap_stat_.memory[Statistics::MEM_SRC] = memory_of(data_->src_.points); remap_stat_.memory[Statistics::MEM_TGT] = memory_of(data_->tgt_.points); remap_stat_.memory[Statistics::MEM_SRC_AREAS] = memory_of(data_->src_.areas); remap_stat_.memory[Statistics::MEM_TGT_AREAS] = memory_of(data_->tgt_.areas); remap_stat_.memory[Statistics::MEM_SRC_CSP2N] = memory_of(data_->src_.csp2node); remap_stat_.memory[Statistics::MEM_SRC_N2CSP] = memory_of(data_->src_.node2csp); remap_stat_.memory[Statistics::MEM_SRC_CSP2CI] = memory_of(data_->src_.csp_cell_index); remap_stat_.memory[Statistics::MEM_SRC_CSP2C] = memory_of(data_->src_.csp_index); remap_stat_.memory[Statistics::MEM_TGT_CSP2N] = memory_of(data_->tgt_.csp2node); remap_stat_.memory[Statistics::MEM_TGT_N2CSP] = memory_of(data_->tgt_.node2csp); remap_stat_.memory[Statistics::MEM_TGT_CSP2CI] = memory_of(data_->tgt_.csp_cell_index); remap_stat_.memory[Statistics::MEM_TGT_CSP2C] = memory_of(data_->tgt_.csp_index); remap_stat_.memory[Statistics::MEM_IPARAM] = memory_of(data_->tgt_iparam_); ATLAS_TRACE_MPI(ALLREDUCE) { mpi::comm().allReduceInPlace(remap_stat_.memory.data(), remap_stat_.memory.size(), eckit::mpi::max()); } metadata.set("max_memory_in_bytes_per_task.matrix", remap_stat_.memory[Statistics::MEM_MATRIX]); metadata.set("max_memory_in_bytes_per_task.src_points", remap_stat_.memory[Statistics::MEM_SRC]); metadata.set("max_memory_in_bytes_per_task.tgt_points", remap_stat_.memory[Statistics::MEM_TGT]); metadata.set("max_memory_in_bytes_per_task.src_areas", remap_stat_.memory[Statistics::MEM_SRC_AREAS]); metadata.set("max_memory_in_bytes_per_task.tgt_areas", remap_stat_.memory[Statistics::MEM_TGT_AREAS]); metadata.set("max_memory_in_bytes_per_task.src_csp2node", remap_stat_.memory[Statistics::MEM_SRC_CSP2N]); metadata.set("max_memory_in_bytes_per_task.tgt_csp2node", remap_stat_.memory[Statistics::MEM_TGT_CSP2N]); metadata.set("max_memory_in_bytes_per_task.src_node2csp", remap_stat_.memory[Statistics::MEM_SRC_N2CSP]); metadata.set("max_memory_in_bytes_per_task.tgt_node2csp", remap_stat_.memory[Statistics::MEM_TGT_N2CSP]); metadata.set("max_memory_in_bytes_per_task.tgt_iparam", remap_stat_.memory[Statistics::MEM_IPARAM]); metadata.set("max_memory_in_bytes_per_task.src_csp_cell_index", remap_stat_.memory[Statistics::MEM_SRC_CSP2CI]); metadata.set("max_memory_in_bytes_per_task.src_csp_index", remap_stat_.memory[Statistics::MEM_SRC_CSP2C]); metadata.set("max_memory_in_bytes_per_task.tgt_csp_cell_index", remap_stat_.memory[Statistics::MEM_TGT_CSP2CI]); metadata.set("max_memory_in_bytes_per_task.tgt_csp_index", remap_stat_.memory[Statistics::MEM_TGT_CSP2C]); } void ConservativeSphericalPolygonInterpolation::print(std::ostream& out) const { out << "ConservativeMethod{"; out << "order:" << order_; int halo = 0; src_mesh_.metadata().get("halo", halo); out << ", source:" << (src_cell_data_ ? "cells(" : "nodes(") << src_mesh_.grid().name() << ",halo=" << halo << ")"; tgt_mesh_.metadata().get("halo", halo); out << ", target:" << (tgt_cell_data_ ? "cells(" : "nodes(") << tgt_mesh_.grid().name() << ",halo=" << halo << ")"; out << ", normalise:" << normalise_; out << ", matrix_free:" << matrix_free_; out << ", statistics.intersection:" << remap_stat_.intersection; out << ", statistics.conservation:" << remap_stat_.conservation; out << ", cached_matrix:" << not(matrixAllocated() || matrix_free_); out << ", cached_data:" << bool(sharable_data_.use_count() == 0); size_t footprint{}; if (not matrix_free_) { footprint += matrix().footprint(); } footprint += data_->footprint(); out << ", footprint:" << eckit::Bytes(footprint); out << "}"; } Cache ConservativeSphericalPolygonInterpolation::createCache() const { interpolation::Cache cache; if (not matrix_free_) { cache.add(Method::createCache()); } cache.add(cache_); return cache; } void ConservativeSphericalPolygonInterpolation::setup_stat() const { if (! remap_stat_.intersection) { Log::warning() << "Please enable statistics.intersection." << std::endl; return; } const auto src_cell_halo = array::make_view(src_mesh_.cells().halo()); const auto src_node_ghost = array::make_view(src_mesh_.nodes().ghost()); const auto& src_areas = data_->src_.areas; const auto& tgt_areas = data_->tgt_.areas; double geo_create_err = 0.; double src_tgt_sums[2] = {0., 0.}; if (src_cell_data_) { for (idx_t spt = 0; spt < src_areas.size(); ++spt) { if (not src_cell_halo(spt)) { src_tgt_sums[0] += src_areas[spt]; } } } else { for (idx_t spt = 0; spt < src_areas.size(); ++spt) { if (not src_node_ghost(spt)) { src_tgt_sums[0] += src_areas[spt]; } } } const auto tgt_cell_halo = array::make_view(tgt_mesh_.cells().halo()); const auto tgt_node_ghost = array::make_view(tgt_mesh_.nodes().ghost()); if (tgt_cell_data_) { for (idx_t tpt = 0; tpt < tgt_areas.size(); ++tpt) { if (not tgt_cell_halo(tpt)) { src_tgt_sums[1] += tgt_areas[tpt]; } } } else { for (idx_t tpt = 0; tpt < tgt_areas.size(); ++tpt) { if (not tgt_node_ghost(tpt)) { src_tgt_sums[1] += tgt_areas[tpt]; } } } ATLAS_TRACE_MPI(ALLREDUCE) { mpi::comm().allReduceInPlace(src_tgt_sums, 2, eckit::mpi::sum()); } remap_stat_.src_area_sum = src_tgt_sums[0]; remap_stat_.tgt_area_sum = src_tgt_sums[1]; geo_create_err = std::abs(src_tgt_sums[0] - src_tgt_sums[1]) / unit_sphere_area(); remap_stat_.errors[Statistics::ERR_SRCTGT_INTERSECTPLG_DIFF] = geo_create_err; } void ConservativeSphericalPolygonInterpolation::Statistics:: compute_accuracy(const Interpolation& interpolation, const Field target, std::function func, Metadata* metadata) { auto tgt_vals = array::make_view(target); auto cachable_data_ = ConservativeSphericalPolygonInterpolation::Cache(interpolation).get(); auto tgt_mesh_ = extract_mesh(cachable_data_->src_fs_); auto tgt_cell_data_ = extract_mesh(cachable_data_->tgt_fs_); const auto tgt_cell_halo = array::make_view(tgt_mesh_.cells().halo()); const auto tgt_node_ghost = array::make_view(tgt_mesh_.nodes().ghost()); const auto tgt_node_halo = array::make_view(tgt_mesh_.nodes().halo()); const auto& tgt_areas = cachable_data_->tgt_.areas; auto& tgt_points = cachable_data_->tgt_.points; double err_remap_l2 = 0.; double err_remap_linf = 0.; if (tgt_cell_data_) { size_t ncells = std::min(tgt_vals.size(), tgt_mesh_.cells().size()); for (idx_t tpt = 0; tpt < ncells; ++tpt) { ATLAS_ASSERT(tpt < tgt_cell_halo.size()); if (tgt_cell_halo(tpt)) { continue; } auto p = tgt_points[tpt]; PointLonLat pll; eckit::geometry::Sphere::convertCartesianToSpherical(1., p, pll); double err_l = std::abs(tgt_vals(tpt) - func(pll)); err_remap_l2 += err_l * err_l * tgt_areas[tpt]; err_remap_linf = std::max(err_remap_linf, err_l); } } else { size_t nnodes = std::min(tgt_vals.size(), tgt_mesh_.nodes().size()); for (idx_t tpt = 0; tpt < nnodes; ++tpt) { if (tgt_node_ghost(tpt) or tgt_node_halo(tpt)) { continue; } auto p = tgt_points[tpt]; PointLonLat pll; eckit::geometry::Sphere::convertCartesianToSpherical(1., p, pll); double err_l = std::abs(tgt_vals(tpt) - func(pll)); err_remap_l2 += err_l * err_l * tgt_areas[tpt]; err_remap_linf = std::max(err_remap_linf, err_l); } } ATLAS_TRACE_MPI(ALLREDUCE) { mpi::comm().allReduceInPlace(&err_remap_l2, 1, eckit::mpi::sum()); mpi::comm().allReduceInPlace(&err_remap_linf, 1, eckit::mpi::max()); } errors[Statistics::ERR_REMAP_L2] = std::sqrt(err_remap_l2 / unit_sphere_area()); errors[Statistics::ERR_REMAP_LINF] = err_remap_linf; if (metadata) { metadata->set("errors.to_solution_sum", errors[Statistics::ERR_REMAP_L2]); metadata->set("errors.to_solution_max", errors[Statistics::ERR_REMAP_LINF]); } } void ConservativeSphericalPolygonInterpolation::dump_intersection(const std::string msg, const Polygon& plg_1, const PolygonArray& plg_2_array, const std::vector& plg_2_idx_array) const { #if PRINT_BAD_POLYGONS double plg_1_coverage = 0.; std::vector int_areas; int_areas.resize(plg_2_idx_array.size()); for (int i = 0; i < plg_2_idx_array.size(); ++i) { const auto plg_2_idx = plg_2_idx_array[i]; const auto& plg_2 = plg_2_array[plg_2_idx].polygon; auto iplg = plg_1.intersect(plg_2, false, 1.e5 * std::numeric_limits::epsilon()); plg_1_coverage += iplg.area(); int_areas[i] = iplg.area(); } if (std::abs(plg_1.area() - plg_1_coverage) > 0.01 * plg_1.area()) { Log::info().indent(); Log::info() << msg << ", Polygon_1.area: " << plg_1.area() << ", covered: " << plg_1_coverage << std::endl; Log::info() << "Polygon_1 : "; Log::info().precision(18); plg_1.print(Log::info()); Log::info() << std::endl << "Printing " << plg_2_idx_array.size() << " covering polygons -->" << std::endl; Log::info().indent(); plg_1_coverage = plg_1.area(); for (int i = 0; i < plg_2_idx_array.size(); ++i) { const auto plg_2_idx = plg_2_idx_array[i]; const auto& plg_2 = plg_2_array[plg_2_idx].polygon; plg_2.print(Log::info()); Log::info() << "\ninters:"; plg_1_coverage -= int_areas[i]; auto iplg = plg_1.intersect(plg_2, false, 1.e5 * std::numeric_limits::epsilon()); Log::info().indent(); iplg.print(Log::info()); Log::info() << ", inters.-area: " << iplg.area() << ", plg1-area left: " << plg_1_coverage << std::endl; Log::info().unindent(); Log::info() << std::endl; } Log::info() << std::endl; Log::info().unindent(); Log::info().unindent(); } #endif } ConservativeSphericalPolygonInterpolation::Cache::Cache(std::shared_ptr entry): interpolation::Cache(entry), entry_(dynamic_cast(entry.get())) {} ConservativeSphericalPolygonInterpolation::Cache::Cache(const interpolation::Cache& c): interpolation::Cache(c, Data::static_type()), entry_{dynamic_cast(c.get(Data::static_type()))} {} ConservativeSphericalPolygonInterpolation::Cache::Cache(const Interpolation& interpolation): Cache(interpolation::Cache(interpolation)) {} size_t ConservativeSphericalPolygonInterpolation::Data::footprint() const { size_t mem_total{0}; mem_total += memory_of(src_.points); mem_total += memory_of(tgt_.points); mem_total += memory_of(src_.areas); mem_total += memory_of(tgt_.areas); mem_total += memory_of(src_.csp2node); mem_total += memory_of(tgt_.csp2node); mem_total += memory_of(src_.node2csp); mem_total += memory_of(tgt_.node2csp); mem_total += memory_of(tgt_iparam_); // mem_total += memory_of(src_.csp_index); // TODO need to be added // mem_total += memory_of(src_.csp_cell_index); // mem_total += memory_of(tgt_.csp_index); // mem_total += memory_of(src_.csp_cell_index); return mem_total; } void ConservativeSphericalPolygonInterpolation::Data::print(std::ostream& out) const { out << "Memory usage of ConservativeMethod: " << eckit::Bytes(footprint()) << "\n"; out << "- src_.points \t" << eckit::Bytes(memory_of(src_.points)) << "\n"; out << "- tgt_.points \t" << eckit::Bytes(memory_of(tgt_.points)) << "\n"; out << "- src_.areas \t" << eckit::Bytes(memory_of(src_.areas)) << "\n"; out << "- tgt_.areas \t" << eckit::Bytes(memory_of(tgt_.areas)) << "\n"; out << "- src_.csp2node \t" << eckit::Bytes(memory_of(src_.csp2node)) << "\n"; out << "- tgt_.csp2node \t" << eckit::Bytes(memory_of(tgt_.csp2node)) << "\n"; out << "- src_.node2csp \t" << eckit::Bytes(memory_of(src_.node2csp)) << "\n"; out << "- tgt_.node2csp \t" << eckit::Bytes(memory_of(tgt_.node2csp)) << "\n"; // out << "- src_.csp_index \t" << eckit::Bytes(memory_of(src_.csp_index)) << "\n"; // out << "- src_csp_cellindex_ \t" << eckit::Bytes(memory_of(src_.csp_cell_index)) << "\n"; // out << "- tgt_.csp_index \t" << eckit::Bytes(memory_of(tgt_.csp_index)) << "\n"; // out << "- tgt_csp_cellindex_ \t" << eckit::Bytes(memory_of(tgt_.csp_cell_index)) << "\n"; out << "- tgt_iparam_ \t" << eckit::Bytes(memory_of(tgt_iparam_)) << "\n"; } void ConservativeSphericalPolygonInterpolation::Statistics::fillMetadata(Metadata& metadata) { // errors if (intersection) { metadata.set("errors.intersections_covering_tgt_cells_sum", errors[ERR_TGT_INTERSECTPLG_L1]); metadata.set("errors.intersections_covering_tgt_cells_max", errors[ERR_TGT_INTERSECTPLG_LINF]); metadata.set("errors.sum_src_.areasminus_sum_tgt_areas", errors[ERR_SRCTGT_INTERSECTPLG_DIFF]); } if (conservation) { metadata.set("errors.conservation_error", errors[ERR_REMAP_CONS]); } if (accuracy) { metadata.set("errors.to_solution_sum", errors[ERR_REMAP_L2]); metadata.set("errors.to_solution_max", errors[ERR_REMAP_LINF]); } } ConservativeSphericalPolygonInterpolation::Statistics::Statistics() { std::fill(std::begin(counts), std::end(counts), -1); std::fill(std::begin(errors), std::end(errors), -1.); std::fill(std::begin(memory), std::end(memory), -1); std::fill(std::begin(time), std::end(time), -1.); intersection = false; timings = false; accuracy = false; conservation = false; } ConservativeSphericalPolygonInterpolation::Statistics::Statistics(const Metadata& metadata): Statistics() { if (intersection) { metadata.get("polygons.number_of_intersections", counts[NUM_INT_PLG]); metadata.get("polygons.number_of_full_noncovered_tgt_polygons", counts[NUM_UNCVR_FULL_TGT]); metadata.get("polygons.number_of_part_noncovered_tgt_polygons", counts[NUM_UNCVR_PART_TGT]); } metadata.get("errors.intersections_covering_tgt_cells_sum", errors[ERR_TGT_INTERSECTPLG_L1]); metadata.get("errors.intersections_covering_tgt_cells_max", errors[ERR_TGT_INTERSECTPLG_LINF]); metadata.get("errors.sum_src_.areasminus_sum_tgt_areas", errors[ERR_SRCTGT_INTERSECTPLG_DIFF]); metadata.get("polygons.number_of_src_polygons", counts[NUM_SRC_PLG]); metadata.get("polygons.number_of_tgt_polygons", counts[NUM_TGT_PLG]); } } // namespace method } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/method/unstructured/ConservativeSphericalPolygonInterpolation.h0000664000175000017500000003134015160212070034471 0ustar alastairalastair/* * (C) Copyright 2021- ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include "atlas/functionspace.h" #include "atlas/interpolation/method/Method.h" #include "atlas/util/ConvexSphericalPolygon.h" namespace atlas { namespace interpolation { namespace method { using Indices = std::vector; class ConservativeSphericalPolygonInterpolation : public Method { public: struct InterpolationParameters { // one polygon intersection Indices csp_ids; // target cells used for intersection std::vector centroids; // intersection cell centroids std::vector weights; // intersection cell areas }; private: class Data : public InterpolationCacheEntry { public: ~Data() override = default; size_t footprint() const override; static std::string static_type() { return "ConservativeSphericalPolygonInterpolation"; } std::string type() const override { return static_type(); } void print(std::ostream& out) const; private: friend class ConservativeSphericalPolygonInterpolation; struct PolygonsData { enum class Context { SOURCE, TARGET } context; // position and effective area of data points std::vector points; std::vector areas; // indexing of subpolygons Indices csp2node; std::vector node2csp; gidx_t csp_size; Indices csp_cell_index; Indices csp_index; bool cell_data; PolygonsData(PolygonsData::Context ctx) : context(ctx) {} } src_{PolygonsData::Context::SOURCE}, tgt_{PolygonsData::Context::TARGET}; // Timings struct Timings { double source_polygons_assembly{0}; double target_polygons_assembly{0}; double src_already_in{0}; double source_kdtree_assembly{0}; double source_kdtree_search{0}; double source_polygons_filter{0}; double polygon_intersections{0}; double matrix_assembly{0}; double interpolation{0}; } timings; std::vector tgt_iparam_; // Reconstructible if need be FunctionSpace src_fs_; FunctionSpace tgt_fs_; }; struct TargetTriplets { bool normalise_; int t; std::map tpoint_subweights; void add (idx_t s, double w) { auto pair = tpoint_subweights.emplace(s, w); bool inserted = pair.second; if (! inserted) { auto it = pair.first; it->second += w; } } void emplace_in(Triplets& triplets) { if (tpoint_subweights.size()) { double wfactor = 1.; if (normalise_) { volatile double sum_of_weights{0.}; for (auto& p : tpoint_subweights) { sum_of_weights += p.second; } ATLAS_ASSERT(sum_of_weights > 0.); wfactor = 1./sum_of_weights; } for (auto& p : tpoint_subweights) { triplets.emplace_back(t, p.first, p.second * wfactor); } } } void reset(int _t) { tpoint_subweights.clear(); t = _t; } size_t size() const { return tpoint_subweights.size(); } TargetTriplets(bool normalise) : normalise_(normalise) {} }; public: class Cache final : public interpolation::Cache { public: Cache() = default; Cache(const interpolation::Cache& c); Cache(const Interpolation&); operator bool() const { return entry_; } const Data* get() const { return entry_; } private: friend class ConservativeSphericalPolygonInterpolation; Cache(std::shared_ptr entry); const Data* entry_{nullptr}; }; struct Statistics { enum Counts { NUM_SRC_PLG = 0, // index, number of source polygons NUM_TGT_PLG, // index, number of target polygons NUM_INT_PLG, // index, number of intersection polygons NUM_UNCVR_FULL_TGT, // index, number of completely non covered target polygons NUM_UNCVR_PART_TGT, // index, number of partially non covered target polygons NUM_ENUM_SIZE }; enum Errors { ERR_TGT_INTERSECTPLG_L1 = 0, // see above ERR_TGT_INTERSECTPLG_LINF, // see above ERR_SRCTGT_INTERSECTPLG_DIFF, // index, 1/(unit_sphere.area) ( \sum_{scell} scell.area - \sum{tcell} tcell.area ) ERR_REMAP_CONS, // index, error in mass conservation ERR_REMAP_L2, // index, error accuracy for given analytical function ERR_REMAP_LINF, // index, like REMAP_L2 but in L_infinity norm ERR_ENUM_SIZE }; enum Timings { TIME_SRC_PLG = 0, // index, max time in second per task to build source polygons TIME_TGT_PLG, // index, max time in second per task to build target polygons TIME_KDTREE_BUILD, // index, max time in second per task to build kdtree of source polygons TIME_KDTREE_SEARCH, // index, max time in second per task to compute kdtree searches for all target polygons TIME_MATRIX, // index, max time in second per task to assemble the interpolation matrix from the weights TIME_INTERS, // index, max time in second per task to compute intersection polygons TIME_INTERP, // index, max time in second per task to interpolate a source to a target field TIME_ENUM_SIZE }; enum Memory { MEM_MATRIX = 0, // index, max memory size per task of the interpolation matrix MEM_SRC, // index, max memory size per task of source point values MEM_TGT, MEM_SRC_AREAS, // index, max memory size per task of source-area array MEM_TGT_AREAS, MEM_SRC_CSP2N, // index, max memory size per task of source polygon-to-node index-array MEM_SRC_N2CSP, // index, max memory size per task of source node to polygon index-array MEM_SRC_CSP2CI, // index, max memory size per task of source polygon-to-cell-index lookup-array MEM_SRC_CSP2C, // index, max memory size per task of source polygon-to-cell lookup-array MEM_SRC_PLG, // index, max memory size per task of source polygon array MEM_TGT_CSP2N, MEM_TGT_N2CSP, MEM_TGT_CSP2CI, MEM_TGT_CSP2C, MEM_IPARAM, // index, max memory size per task of stored intersection parameters MEM_ENUM_SIZE }; std::array counts; std::array errors; std::array memory; std::array time; double tgt_area_sum; double src_area_sum; bool all; bool accuracy; bool conservation; bool intersection; bool timings; Metadata metadata; Statistics(); Statistics(const Metadata&); void compute_accuracy(const Interpolation& interpolation, const Field target, std::function func, Metadata* metadata = nullptr); void fillMetadata(Metadata&); }; public: ConservativeSphericalPolygonInterpolation(const Config& = util::NoConfig()); using Method::do_setup; void do_setup(const FunctionSpace& src_fs, const FunctionSpace& tgt_fs) override; void do_setup(const FunctionSpace& source, const FunctionSpace& target, const interpolation::Cache&) override; void do_setup(const Grid& src_grid, const Grid& tgt_grid, const interpolation::Cache&) override; void do_execute(const Field& src_field, Field& tgt_field, Metadata&) const override; void do_execute(const FieldSet& src_fields, FieldSet& tgt_fields, Metadata&) const override; void print(std::ostream& out) const override; const FunctionSpace& source() const override { return src_fs_; } const FunctionSpace& target() const override { return tgt_fs_; } Statistics& statistics() { return remap_stat_; } inline const PointXYZ& src_points(size_t id) const { return data_->src_.points[id]; } inline const PointXYZ& tgt_points(size_t id) const { return data_->tgt_.points[id]; } interpolation::Cache createCache() const override; private: using Polygon = util::ConvexSphericalPolygon; using PolygonArray = std::vector; void do_setup_impl(const Grid& src_grid, const Grid& tgt_grid); void build_source_kdtree(util::KDTree&, double& max_srccell_rad, const PolygonArray&) const; void build_source_kdtree_centroid(util::KDTree&, double& max_srccell_rad, const PolygonArray&) const; void intersect_polygons(const PolygonArray& src_csp); Triplets compute_1st_order_triplets(); Triplets compute_2nd_order_triplets(); void dump_intersection(const std::string, const Polygon& plg_1, const PolygonArray& plg_2_array, const Indices& plg_2_idx_array) const; struct Workspace_get_cell_neighbours; std::vector get_cell_neighbours(Mesh&, idx_t jcell, Workspace_get_cell_neighbours&) const; struct Workspace_get_node_neighbours; std::vector get_node_neighbours(Mesh&, idx_t jcell, Workspace_get_node_neighbours&) const; void init_polygons_data(FunctionSpace fs, Data::PolygonsData& md); Polygon get_csp_celldata(idx_t csp_id, const Mesh& mesh, const Data::PolygonsData& md); Polygon get_csp_nodedata(idx_t csp_id, const Mesh& mesh, Data::PolygonsData& md); idx_t csp_to_cell(idx_t csp_id, const Data::PolygonsData& md) const { if (md.cell_data) { return md.csp_index[csp_id]; } else { auto iterator_upper_bound = std::upper_bound(md.csp_index.begin(), md.csp_index.end(), csp_id); idx_t idx = iterator_upper_bound-1 - md.csp_index.begin(); return md.csp_cell_index[idx]; } } std::pair csp_to_cell_and_subcell(idx_t csp_id, const Data::PolygonsData& md) const { auto iterator_upper_bound = std::upper_bound(md.csp_index.begin(), md.csp_index.end(), csp_id); idx_t idx = iterator_upper_bound-1 - md.csp_index.begin(); idx_t cell = md.csp_cell_index[idx]; idx_t subcell = csp_id - md.csp_index[idx]; return std::make_pair(cell, subcell); } // Polygon get_src_csp(idx_t csp_id) { // if (sharable_data_->src_.cell_data) { // return get_csp_celldata(csp_id, src_mesh_, sharable_data_->src_); // } // else { // return get_csp_nodedata(csp_id, src_mesh_, sharable_data_->src_); // } // } Polygon get_tgt_csp(idx_t csp_id) { if (sharable_data_->tgt_.cell_data) { return get_csp_celldata(csp_id, tgt_mesh_, sharable_data_->tgt_); } else { return get_csp_nodedata(csp_id, tgt_mesh_, sharable_data_->tgt_); } } PolygonArray get_polygons_celldata(FunctionSpace, Data::PolygonsData&); PolygonArray get_polygons_nodedata(FunctionSpace, Data::PolygonsData&); PolygonArray get_polygons(FunctionSpace fs, Data::PolygonsData& md) { return md.cell_data ? get_polygons_celldata(fs, md) : get_polygons_nodedata(fs, md); } int next_index(int current_index, int size) const; int prev_index(int current_index, int size) const; void setup_stat() const; private: bool validate_; bool src_cell_data_; bool tgt_cell_data_; FunctionSpace src_fs_; FunctionSpace tgt_fs_; mutable Mesh src_mesh_; mutable Mesh tgt_mesh_; bool normalise_; int order_; bool matrix_free_; mutable Statistics remap_stat_; Cache cache_; // Storage of cache if any was passed to constructor std::shared_ptr sharable_data_; // Storage of new data_, only allocated if cache is empty const Data* data_; // Read-only access to data, pointing either to cache_ or sharable_data_ // position and effective area of data points idx_t n_spoints_; idx_t n_tpoints_; }; } // namespace method } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/method/unstructured/UnstructuredBilinearLonLat.h0000664000175000017500000000504215160212070031335 0ustar alastairalastair/* * (C) Crown Copyright 2021 Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ #pragma once #include "atlas/interpolation/method/Method.h" #include #include "eckit/config/Configuration.h" #include "atlas/array/ArrayView.h" #include "atlas/functionspace/FunctionSpace.h" #include "atlas/interpolation/method/PointIndex3.h" #include "atlas/mesh/Elements.h" namespace atlas { namespace interpolation { namespace method { class UnstructuredBilinearLonLat : public Method { public: UnstructuredBilinearLonLat(const Config& config): Method(config) { } virtual ~UnstructuredBilinearLonLat() override {} virtual void print(std::ostream&) const override; protected: /** * @brief Create an interpolant sparse matrix relating two (pre-partitioned) * meshes, * using elements as per the Finite Element Method and ray-tracing to * calculate * source mesh elements intersections (and interpolation weights) with target * grid * node-containing rays * @param meshSource mesh containing source elements * @param meshTarget mesh containing target points */ void setup(const FunctionSpace& source); /** * Find in which element the point is contained by projecting (ray-tracing) * the * point to the nearest element(s), returning the (normalized) interpolation * weights */ Triplets projectPointToElements(size_t ip, const ElemIndex3::NodeList& elems) const; virtual const FunctionSpace& source() const override { return source_; } virtual const FunctionSpace& target() const override { return target_; } private: using Method::do_setup; virtual void do_setup(const FunctionSpace& source, const FunctionSpace& target) override; virtual void do_setup(const Grid& source, const Grid& target, const Cache&) override; virtual void do_setup(const FunctionSpace& source, const FunctionSpace& target, const Cache&) override; protected: mesh::MultiBlockConnectivity* connectivity_; std::unique_ptr> ilonlat_; std::unique_ptr> olonlat_; std::unique_ptr> oxyz_; std::unique_ptr> igidx_; Field target_lonlat_; Field target_xyz_; Field target_ghost_; FunctionSpace source_; FunctionSpace target_; bool treat_failure_as_missing_value_{true}; }; } // namespace method } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/method/cubedsphere/0000775000175000017500000000000015160212070023416 5ustar alastairalastairatlas-0.46.0/src/atlas/interpolation/method/cubedsphere/CubedSphereBilinear.h0000664000175000017500000000375715160212070027442 0ustar alastairalastair/* * (C) Crown Copyright 2022 Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ #pragma once #include "atlas/functionspace/FunctionSpace.h" #include "atlas/interpolation/method/Method.h" namespace atlas { namespace interpolation { namespace method { /// @brief Cubed sphere bilinear interpolation method. /// /// @details Performs bilinear (quads) and baycentric (triangles) interpolation /// accross cubed sphere tiles in (alpha, beta) coordinates. /// Adding int "halo" (default 0) to the config controls the amount of /// halo to consider when seraching for interpolation polygons. Adding /// int "list_size" (defualt 8) will change the number of cells /// returned by the internal kd-tree search. Increasing both values /// may resolve errors if setup method fails to find cells. /// The automatic halo exchange in the execute method can be disabled /// by setting "halo_exchange" to false. class CubedSphereBilinear : public Method { public: CubedSphereBilinear(const Config& config): Method(config) { config.get("halo", halo_); config.get("list_size", listSize_); } virtual ~CubedSphereBilinear() override {} virtual void print(std::ostream&) const override; virtual const FunctionSpace& source() const override { return source_; } virtual const FunctionSpace& target() const override { return target_; } private: using Method::do_setup; void do_setup(const FunctionSpace& source, const FunctionSpace& target) override; void do_setup(const Grid& source, const Grid& target, const Cache&) override; void do_setup(const FunctionSpace& source, const FunctionSpace& target, const Cache&) override; FunctionSpace source_; FunctionSpace target_; int halo_{0}; int listSize_{8}; }; } // namespace method } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/method/cubedsphere/CellFinder.h0000664000175000017500000000351415160212070025601 0ustar alastairalastair/* * (C) Crown Copyright 2022 Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ #pragma once #include #include "atlas/interpolation/method/Intersect.h" #include "atlas/mesh/Mesh.h" #include "atlas/util/Config.h" #include "atlas/util/Geometry.h" #include "atlas/util/KDTree.h" #include "atlas/util/Point.h" namespace atlas { namespace projection { namespace detail { class CubedSphereProjectionBase; } // namespace detail } // namespace projection } // namespace atlas namespace atlas { namespace interpolation { namespace method { namespace cubedsphere { using namespace util; /// @brief class to find points within cells of cubedsphere mesh. class CellFinder { public: struct Cell { idx_t idx; std::vector nodes; Intersect isect; }; /// @brief Constructor. CellFinder(const Mesh& mesh, const util::Config& config = util::Config("halo", 0)); /// @brief Find a cell which encompasses an xy point. Cell getCell(const PointXY& xy, size_t listSize = 4, double edgeEpsilon = 5. * std::numeric_limits::epsilon(), double epsilon = 5. * std::numeric_limits::epsilon()) const; /// @brief Find a cell which encompasses a lonlat point. Cell getCell(const PointLonLat& lonlat, size_t listSize = 4, double edgeEpsilon = 5. * std::numeric_limits::epsilon(), double epsilon = 5. * std::numeric_limits::epsilon()) const; private: Mesh mesh_{}; const projection::detail::CubedSphereProjectionBase* projection_{}; util::IndexKDTree tree_{Geometry{}}; }; } // namespace cubedsphere } // namespace method } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/method/cubedsphere/CubedSphereBilinear.cc0000664000175000017500000000746015160212070027573 0ustar alastairalastair/* * (C) Crown Copyright 2022 Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ #include "atlas/interpolation/method/cubedsphere/CubedSphereBilinear.h" #include "atlas/functionspace/NodeColumns.h" #include "atlas/grid/CubedSphereGrid.h" #include "atlas/interpolation/method/MethodFactory.h" #include "atlas/interpolation/method/cubedsphere/CellFinder.h" #include "atlas/parallel/omp/omp.h" #include "atlas/util/CoordinateEnums.h" namespace atlas { namespace interpolation { namespace method { namespace { MethodBuilder __builder("cubedsphere-bilinear"); } // namespace void CubedSphereBilinear::do_setup(const Grid& source, const Grid& target, const Cache&) { ATLAS_NOTIMPLEMENTED; } void CubedSphereBilinear::do_setup(const FunctionSpace& source, const FunctionSpace& target, const Cache&) { ATLAS_NOTIMPLEMENTED; } void CubedSphereBilinear::do_setup(const FunctionSpace& source, const FunctionSpace& target) { source_ = source; target_ = target; const auto ncSource = functionspace::NodeColumns(source); ATLAS_ASSERT(ncSource); ATLAS_ASSERT(target_); // return early if no output points on this partition reserve is called on // the triplets but also during the sparseMatrix constructor. This won't // work for empty matrices if (target_.size() == 0) { return; } const auto finder = cubedsphere::CellFinder(ncSource.mesh(), util::Config("halo", halo_)); // Numeric tolerance should scale with N. const auto N = CubedSphereGrid(ncSource.mesh().grid()).N(); const auto tolerance = 2. * std::numeric_limits::epsilon() * N; // Weights vector is oversized. Empty triplets are ignored by Matrix constructor. auto weights = Triplets(4 * target_.size()); const auto ghostView = array::make_view(target_.ghost()); const auto lonlatView = array::make_view(target_.lonlat()); atlas_omp_parallel_for(idx_t i = 0; i < target_.size(); ++i) { if (!ghostView(i)) { const auto cell = finder.getCell(PointLonLat(lonlatView(i, LON), lonlatView(i, LAT)), listSize_, tolerance, tolerance); if (!cell.isect) { ATLAS_THROW_EXCEPTION( "Cannot find a cell surrounding target" "point " + std::to_string(i) + "."); } const auto& isect = cell.isect; const auto& j = cell.nodes; switch (cell.nodes.size()) { case (3): { // Cell is a triangle. weights[4 * i] = Triplet(i, j[0], 1. - isect.u - isect.v); weights[4 * i + 1] = Triplet(i, j[1], isect.u); weights[4 * i + 2] = Triplet(i, j[2], isect.v); break; } case (4): { // Cell is quad. weights[4 * i] = Triplet(i, j[0], (1. - isect.u) * (1. - isect.v)); weights[4 * i + 1] = Triplet(i, j[1], isect.u * (1. - isect.v)); weights[4 * i + 2] = Triplet(i, j[2], isect.u * isect.v); weights[4 * i + 3] = Triplet(i, j[3], (1. - isect.u) * isect.v); break; } default: { ATLAS_THROW_EXCEPTION("Unknown cell type with " + std::to_string(cell.nodes.size()) + " nodes."); } } } } // fill sparse matrix and return. setMatrix(target_.size(), source_.size(), weights); } void CubedSphereBilinear::print(std::ostream&) const { ATLAS_NOTIMPLEMENTED; } } // namespace method } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/method/cubedsphere/CellFinder.cc0000664000175000017500000001116415160212070025737 0ustar alastairalastair/* * (C) Crown Copyright 2022 Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ #include "atlas/interpolation/method/cubedsphere/CellFinder.h" #include "atlas/grid/CubedSphereGrid.h" #include "atlas/interpolation/Vector2D.h" #include "atlas/interpolation/element/Quad2D.h" #include "atlas/interpolation/element/Triag2D.h" #include "atlas/mesh/HybridElements.h" #include "atlas/mesh/Nodes.h" #include "atlas/util/Topology.h" namespace atlas { namespace interpolation { namespace method { namespace cubedsphere { CellFinder::CellFinder(const Mesh& mesh, const util::Config& config): mesh_{mesh} { // Check mesh and get projection. const auto csGrid = CubedSphereGrid(mesh_.grid()); ATLAS_ASSERT_MSG(csGrid, "cubedsphere::CellFinder requires a cubed sphere mesh."); projection_ = &(csGrid.cubedSphereProjection()); // Get views to cell data. const auto lonlatView = array::make_view(mesh_.cells().field("lonlat")); const auto haloView = array::make_view(mesh_.cells().halo()); // make points and payloads vectors. auto points = std::vector{}; auto payloads = std::vector{}; // Iterate over cells. auto halo = config.getInt("halo", 0); for (idx_t i = 0; i < mesh_.cells().size(); ++i) { if (haloView(i) <= halo) { points.emplace_back(lonlatView(i, LON), lonlatView(i, LAT)); payloads.emplace_back(i); } } // build cell kd-tree. tree_.build(points, payloads); } CellFinder::Cell CellFinder::getCell(const PointLonLat& lonlat, size_t listSize, double edgeEpsilon, double epsilon) const { // Convert xy to alphabeta and t; // const auto& tiles = projection_->getCubedSphereTiles(); // Get mesh nodes and connectivity table. const auto nodeXyView = array::make_view(mesh_.nodes().xy()); const auto& nodeConnectivity = mesh_.cells().node_connectivity(); // Get four nearest cell-centres to xy. if (tree_.size() == 0) { return Cell{-1, {}, Intersect{}.fail()}; } const auto valueList = tree_.closestPoints(lonlat, std::min(listSize, tree_.size())); // Get view to cell flags. const auto cellFlagsView = array::make_view(mesh_.cells().flags()); const auto cellTijView = array::make_view(mesh_.cells().field("tij")); for (const auto& value : valueList) { const auto i = value.payload(); // Ignore invalid cells. if ((cellFlagsView(i) & Topology::INVALID)) { break; } const auto t = cellTijView(i,0); const auto row = nodeConnectivity.row(i); if (row.size() == 4) { auto quadAlphabeta = std::array{}; for (size_t k = 0; k < 4; ++k) { const auto xyNode = PointXY(nodeXyView(row(k), XX), nodeXyView(row(k), YY)); quadAlphabeta[k] = Vector2D(projection_->xy2alphabeta(xyNode, t).data()); } const auto quad = element::Quad2D(quadAlphabeta[0], quadAlphabeta[1], quadAlphabeta[2], quadAlphabeta[3]); const auto alphabeta = projection_->lonlat2alphabeta(lonlat, t); const auto isect = quad.localRemap(alphabeta, edgeEpsilon, epsilon); if (isect) { return Cell{i, {row(0), row(1), row(2), row(3)}, isect}; } } else { // Cell is triangle. auto triagAlphabeta = std::array{}; for (size_t k = 0; k < 3; ++k) { const auto xyNode = PointXY(nodeXyView(row(k), XX), nodeXyView(row(k), YY)); triagAlphabeta[k] = Vector2D(projection_->xy2alphabeta(xyNode, t).data()); } const auto triag = element::Triag2D(triagAlphabeta[0], triagAlphabeta[1], triagAlphabeta[2]); const auto alphabeta = projection_->lonlat2alphabeta(lonlat, t); const auto isect = triag.intersects(alphabeta, edgeEpsilon, epsilon); if (isect) { return Cell{i, {row(0), row(1), row(2)}, isect}; } } } // Couldn't find a cell. return Cell{-1, {}, Intersect{}.fail()}; } CellFinder::Cell CellFinder::getCell(const PointXY& xy, size_t listSize, double edgeEpsilon, double epsilon) const { const auto lonlat = projection_->lonlat(xy); return getCell(lonlat, listSize, edgeEpsilon, epsilon); } } // namespace cubedsphere } // namespace method } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/method/Method.h0000664000175000017500000001534215160212070022523 0ustar alastairalastair/* * (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 #include "atlas/interpolation/Cache.h" #include "atlas/interpolation/NonLinear.h" #include "atlas/linalg/sparse/SparseMatrixStorage.h" #include "atlas/util/Metadata.h" #include "atlas/util/Object.h" #include "eckit/config/Configuration.h" #include "eckit/linalg/SparseMatrix.h" #include "atlas/linalg/sparse/MakeSparseMatrixStorageEckit.h" namespace atlas { class Field; class FieldSet; class FunctionSpace; class Grid; } // namespace atlas namespace atlas { namespace test { class Access; } } // namespace atlas namespace atlas { namespace interpolation { class Method : public util::Object { public: using Config = eckit::Parametrisation; using Metadata = util::Metadata; Method(const Config&); virtual ~Method() {} /** * @brief Setup the interpolator relating two functionspaces * @param source functionspace containing source elements * @param target functionspace containing target points */ void setup(const FunctionSpace& source, const FunctionSpace& target); void setup(const Grid& source, const Grid& target); void setup(const FunctionSpace& source, const Field& target); void setup(const FunctionSpace& source, const FieldSet& target); void setup(const Grid& source, const Grid& target, const Cache&); void setup(const FunctionSpace& source, const FunctionSpace& target, const Cache&); Metadata execute(const FieldSet& source, FieldSet& target) const; Metadata execute(const Field& source, Field& target) const; /** * @brief execute_adjoint * @param source - it is either a FieldSet or a Field * @param target - it is either a FieldSet or a Field * Note that formally in an adjoint operation of this * type we should be setting the values in the target * to zero. This is not done for efficiency reasons and * because in most cases it is not necessary. */ Metadata execute_adjoint(FieldSet& source, const FieldSet& target) const; Metadata execute_adjoint(Field& source, const Field& target) const; virtual void print(std::ostream&) const = 0; virtual const FunctionSpace& source() const = 0; virtual const FunctionSpace& target() const = 0; virtual interpolation::Cache createCache() const; protected: virtual void do_execute(const FieldSet& source, FieldSet& target, Metadata&) const; virtual void do_execute(const Field& source, Field& target, Metadata&) const; virtual void do_execute_adjoint(FieldSet& source, const FieldSet& target, Metadata&) const; virtual void do_execute_adjoint(Field& source, const Field& target, Metadata&) const; using Triplet = eckit::linalg::Triplet; using Triplets = std::vector; using Matrix = atlas::linalg::SparseMatrixStorage; static void normalise(Triplets& triplets); void haloExchange(const FieldSet&, bool on_device = false) const; void haloExchange(const Field&, bool on_device = false) const; void adjointHaloExchange(const FieldSet&, bool on_device = false) const; void adjointHaloExchange(const Field&, bool on_device = false) const; // NOTE : Matrix-free or non-linear interpolation operators do not have matrices, so do not expose here friend class atlas::test::Access; friend class interpolation::MatrixCache; protected: void setMatrix(Matrix&& m, const std::string& uid = "") { if (not matrix_shared_) { matrix_shared_ = std::make_shared(); } *matrix_shared_ = std::move(m); matrix_cache_ = interpolation::MatrixCache(matrix_shared_, uid); matrix_ = &matrix_cache_.matrix(); } void setMatrix(interpolation::MatrixCache matrix_cache) { ATLAS_ASSERT(matrix_cache); matrix_cache_ = matrix_cache; matrix_ = &matrix_cache_.matrix(); matrix_shared_.reset(); } void setMatrix(eckit::linalg::SparseMatrix&& m, const std::string& uid = "") { setMatrix( linalg::make_sparse_matrix_storage(std::move(m)), uid ); } void setMatrix(std::size_t rows, std::size_t cols, const Triplets& triplets, const std::string& uid = "") { setMatrix( eckit::linalg::SparseMatrix{rows, cols, triplets}, uid); } bool matrixAllocated() const { return matrix_shared_.use_count(); } const Matrix& matrix() const { ATLAS_ASSERT(matrix_ != nullptr); return *matrix_; } const Matrix& adjoint_matrix() const; virtual void do_setup(const FunctionSpace& source, const FunctionSpace& target) = 0; virtual void do_setup(const Grid& source, const Grid& target, const Cache&) = 0; virtual void do_setup(const FunctionSpace& source, const FunctionSpace& target, const Cache&) = 0; virtual void do_setup(const FunctionSpace& source, const Field& target); virtual void do_setup(const FunctionSpace& source, const FieldSet& target); void check_compatibility(const Field& src, const Field& tgt, const Matrix& W) const; private: template void interpolate_field(const Field& src, Field& tgt, const Matrix&) const; template void interpolate_field_rank1(const Field& src, Field& tgt, const Matrix&) const; template void interpolate_field_rank2(const Field& src, Field& tgt, const Matrix&) const; template void interpolate_field_rank3(const Field& src, Field& tgt, const Matrix&) const; template void adjoint_interpolate_field(Field& src, const Field& tgt, const Matrix&) const; template void adjoint_interpolate_field_rank1(Field& src, const Field& tgt, const Matrix&) const; template void adjoint_interpolate_field_rank2(Field& src, const Field& tgt, const Matrix&) const; template void adjoint_interpolate_field_rank3(Field& src, const Field& tgt, const Matrix&) const; private: const Matrix* matrix_ = nullptr; std::shared_ptr matrix_shared_; interpolation::MatrixCache matrix_cache_; NonLinear nonLinear_; std::string linalg_backend_; bool adjoint_{false}; mutable std::unique_ptr matrix_transpose_; protected: bool allow_halo_exchange_{true}; std::vector missing_; }; } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/method/sphericalvector/0000775000175000017500000000000015160212070024322 5ustar alastairalastairatlas-0.46.0/src/atlas/interpolation/method/sphericalvector/Types.h0000664000175000017500000000144015160212070025576 0ustar alastairalastair/* * (C) Crown Copyright 2024 Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ #pragma once #include #include "atlas/interpolation/method/sphericalvector/SparseMatrix.h" namespace atlas { namespace interpolation { namespace method { namespace detail { using Real = double; using Complex = std::complex; using ComplexMatrix = SparseMatrix; using RealMatrix = SparseMatrix; using Index = ComplexMatrix::Index; using ComplexTriplet = ComplexMatrix::Triplet; using ComplexTriplets = ComplexMatrix::Triplets; using RealTriplet = RealMatrix::Triplet; using RealTriplets = RealMatrix::Triplets; } // detail } // method } // interpolation } // atlas atlas-0.46.0/src/atlas/interpolation/method/sphericalvector/SparseMatrix.h0000664000175000017500000000604315160212070027120 0ustar alastairalastair/* * (C) Crown Copyright 2024 Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ #pragma once #include #include #include #include "atlas/library/defines.h" #if ATLAS_HAVE_EIGEN ATLAS_SUPPRESS_WARNINGS_PUSH ATLAS_SUPPRESS_WARNINGS_INTEGER_SIGN_CHANGE ATLAS_SUPPRESS_WARNINGS_CODE_IS_UNREACHABLE ATLAS_SUPPRESS_WARNINGS_UNUSED_BUT_SET_VARIABLE #include ATLAS_SUPPRESS_WARNINGS_POP #endif #include "atlas/runtime/Exception.h" #include "eckit/log/CodeLocation.h" namespace atlas { namespace interpolation { namespace method { namespace detail { #if ATLAS_HAVE_EIGEN /// @brief Wrapper class for Eigen sparse matrix /// /// @details Eigen sparse matrix wrapper. Allows preprocessor disabling of class /// if Eigen library is not present. template class SparseMatrix { public: using Index = int; using EigenMatrix = Eigen::SparseMatrix; using Triplet = Eigen::Triplet; using Triplets = std::vector; using RowIter = typename EigenMatrix::InnerIterator; SparseMatrix() = default; SparseMatrix(Index nRows, Index nCols, const Triplets& triplets) : eigenMatrix_(nRows, nCols) { eigenMatrix_.setFromTriplets(triplets.begin(), triplets.end()); } Index nonZeros() const { return eigenMatrix_.nonZeros(); } Index rows() const { return eigenMatrix_.rows(); } Index cols() const { return eigenMatrix_.cols(); } RowIter rowIter(Index rowIndex) const { return RowIter(eigenMatrix_, rowIndex); } SparseMatrix adjoint() const { auto adjointMatrix = SparseMatrix{}; adjointMatrix.eigenMatrix_ = eigenMatrix_.adjoint(); return adjointMatrix; } private: EigenMatrix eigenMatrix_{}; }; #else template class SparseMatrix { public: using Index = int; class Triplet { public: template constexpr Triplet(const Args&... args) {} }; using Triplets = std::vector; class RowIter { public: template constexpr RowIter(const Args&... args) {} constexpr Index row() const { return Index{}; } constexpr Index col() const { return Index{}; } constexpr Value value() const { return Value{}; } constexpr operator bool() const { return false; } constexpr RowIter& operator++() { return *this; } }; constexpr SparseMatrix() = default; template SparseMatrix(const Args&... args) { throw_Exception("Atlas has been compiled without Eigen", Here()); } constexpr Index nonZeros() const { return Index{}; } constexpr Index rows() const { return Index{}; } constexpr Index cols() const { return Index{}; } constexpr RowIter rowIter(Index rowIndex) const { return RowIter{}; } constexpr SparseMatrix adjoint() const { return SparseMatrix{}; } }; #endif } // namespace detail } // namespace method } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/method/sphericalvector/ComplexMatrixMultiply.h0000664000175000017500000002100415160212070031024 0ustar alastairalastair/* * (C) Crown Copyright 2024 Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ #pragma once #include #include #include #include #include #include #include "atlas/array/ArrayView.h" #include "atlas/array/LocalView.h" #include "atlas/array/Range.h" #include "atlas/array/helpers/ArrayForEach.h" #include "atlas/interpolation/method/sphericalvector/Types.h" #include "atlas/parallel/omp/omp.h" namespace atlas { namespace interpolation { namespace method { namespace detail { struct TwoVectorTag {}; struct ThreeVectorTag {}; constexpr auto twoVector = TwoVectorTag{}; constexpr auto threeVector = ThreeVectorTag{}; // Permits the equivalent of static_assert(false, msg). Will be addressed in // C++26. template constexpr bool always_false_v = false; /// @brief Helper class to perform complex matrix multiplications /// /// @details Performs matrix multiplication between fields of 2-vectors or /// 3-vectors. Fields must have Rank >= 2. Here, the assumption is /// that Dim = 0 is the horizontal dimension, and Dim = (Rank - 1) is /// the vector element dimension. template class ComplexMatrixMultiply { public: ComplexMatrixMultiply() = default; /// @brief Construct object from sparse matrices. /// /// @details complexWeights is a SparseMatrix of weights. realWeights is a /// SparseMatrix containing the magnitudes of the elements of /// complexWeights. ComplexMatrixMultiply(const ComplexMatrix& complexWeights, const RealMatrix& realWeights) : complexWeightsPtr_{&complexWeights}, realWeightsPtr_{&realWeights} { if constexpr (ATLAS_BUILD_TYPE_DEBUG) { ATLAS_ASSERT(complexWeightsPtr_->rows() == realWeightsPtr_->rows()); ATLAS_ASSERT(complexWeightsPtr_->cols() == realWeightsPtr_->cols()); ATLAS_ASSERT(complexWeightsPtr_->nonZeros() == realWeightsPtr_->nonZeros()); for (auto rowIndex = Index{0}; rowIndex < complexWeightsPtr_->rows(); ++rowIndex) { for (auto [complexRowIter, realRowIter] = rowIters(rowIndex); complexRowIter; ++complexRowIter, ++realRowIter) { ATLAS_ASSERT(realRowIter); ATLAS_ASSERT(realRowIter.row() == complexRowIter.row()); ATLAS_ASSERT(realRowIter.col() == complexRowIter.col()); // tinyNum ~= 2.3e-13 for double. constexpr auto tinyNum = 1024 * std::numeric_limits::epsilon(); const auto complexMagnitude = std::abs(complexRowIter.value()); const auto realValue = std::abs(realRowIter.value()); const auto error = std::abs(complexMagnitude - realValue); const auto printError = [&]() { auto strStream = std::ostringstream{}; strStream << "Error complex components: "; strStream << std::setprecision(19) << error; return strStream.str(); }; ATLAS_ASSERT(error < tinyNum, printError()); } } } } /// @brief Apply complex matrix vector multiplication. /// /// @details Multiply weights by the elements in sourceView to give /// elements in targetView. If VectorType == TwoVectorTag, /// complexWeights are applied to the horizontal elements of /// sourceView. If VectorType == ThreeVectorTag, then realWeights /// are additionally applied to the vertical elements of sourceView. template void apply(const array::ArrayView& sourceView, array::ArrayView& targetView, VectorType) const { if constexpr (std::is_same_v) { applyTwoVector(sourceView, targetView); } else if constexpr (std::is_same_v) { applyThreeVector(sourceView, targetView); } else { static_assert(always_false_v, "Unknown vector type"); } } private: /// @brief Apply 2-vector MatMul. template void applyTwoVector(const array::ArrayView& sourceView, array::ArrayView& targetView) const { // We could probably optimise contiguous arrays using // reinterpret_cast*>(view.data()). This is fine // according to the C++ standard! atlas_omp_parallel_for(auto rowIndex = Index{0}; rowIndex < complexWeightsPtr_->rows(); ++rowIndex) { auto targetSlice = sliceColumn(targetView, rowIndex); if constexpr (InitialiseTarget) { targetSlice.assign(0.); } for (auto complexRowIter = complexWeightsPtr_->rowIter(rowIndex); complexRowIter; ++complexRowIter) { const auto colIndex = complexRowIter.col(); const auto complexWeight = complexRowIter.value(); const auto sourceSlice = sliceColumn(sourceView, colIndex); multiplyAdd(sourceSlice, targetSlice, complexWeight); } } } /// @brief Apply 3-vector MatMul. template void applyThreeVector(const array::ArrayView& sourceView, array::ArrayView& targetView) const { atlas_omp_parallel_for(auto rowIndex = Index{0}; rowIndex < complexWeightsPtr_->rows(); ++rowIndex) { auto targetSlice = sliceColumn(targetView, rowIndex); if constexpr (InitialiseTarget) { targetSlice.assign(0.); } for (auto [complexRowIter, realRowIter] = rowIters(rowIndex); complexRowIter; ++complexRowIter, ++realRowIter) { const auto colIndex = complexRowIter.col(); const auto complexWeight = complexRowIter.value(); const auto realWeight = realRowIter.value(); const auto sourceSlice = sliceColumn(sourceView, colIndex); multiplyAdd(sourceSlice, targetSlice, complexWeight, realWeight); } } } /// @brief Multiply source column by weight(s) and add to target column. template void multiplyAdd(const array::LocalView& sourceColumn, array::LocalView& targetColumn, Weights... weights) const { for (auto levelIdx = 0; levelIdx < sourceColumn.shape(0); ++levelIdx) { const auto sourceElem = sliceColumn(sourceColumn, levelIdx); auto targetElem = sliceColumn(targetColumn, levelIdx); multiplyAdd(sourceElem, targetElem, weights...); } } /// @brief Multiply source element by complex weight and add to target /// element. template void multiplyAdd(const array::LocalView& sourceElem, array::LocalView& targetElem, Complex complexWeight) const { const auto targetVector = complexWeight * Complex(sourceElem(0), sourceElem(1)); targetElem(0) += targetVector.real(); targetElem(1) += targetVector.imag(); } /// @brief Multiply source element by complex and real weights and add to /// target element. template void multiplyAdd(const array::LocalView& sourceElem, array::LocalView& targetElem, Complex complexWeight, Real realWeight) const { multiplyAdd(sourceElem, targetElem, complexWeight); targetElem(2) += realWeight * sourceElem(2); } /// @brief Return a pair of complex and real row iterators std::pair rowIters( Index rowIndex) const { return std::make_pair(complexWeightsPtr_->rowIter(rowIndex), realWeightsPtr_->rowIter(rowIndex)); } /// @brief Makes the slice arrayView.slice(index, Range::all()...). template static auto sliceColumn(View& arrayView, Index index) { constexpr auto Rank = std::decay_t::rank(); using RangeAll = decltype(array::Range::all()); const auto slicerArgs = std::tuple_cat(std::make_tuple(index), std::array{}); const auto slicer = [&](auto&&... args) { return arrayView.slice(args...); }; return std::apply(slicer, slicerArgs); } const ComplexMatrix* complexWeightsPtr_{}; const RealMatrix* realWeightsPtr_{}; }; } // namespace detail } // namespace method } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/method/sphericalvector/SphericalVector.cc0000664000175000017500000002255215160212070027734 0ustar alastairalastair/* * (C) Crown Copyright 2024 Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ #include #include #include "atlas/interpolation/method/sphericalvector/SphericalVector.h" #include "atlas/array/ArrayView.h" #include "atlas/field/Field.h" #include "atlas/field/FieldSet.h" #include "atlas/interpolation/Cache.h" #include "atlas/interpolation/Interpolation.h" #include "atlas/interpolation/method/MethodFactory.h" #include "atlas/interpolation/method/sphericalvector/ComplexMatrixMultiply.h" #include "atlas/interpolation/method/sphericalvector/Types.h" #include "atlas/linalg/sparse/MakeEckitSparseMatrix.h" #include "atlas/option/Options.h" #include "atlas/parallel/omp/omp.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Trace.h" #include "atlas/util/Constants.h" #include "atlas/util/Geometry.h" #include "atlas/util/PackVectorFields.h" #include "eckit/config/LocalConfiguration.h" namespace atlas { namespace interpolation { namespace method { namespace { MethodBuilder __builder("spherical-vector"); } using namespace detail; using WeightsMatMul = detail::ComplexMatrixMultiply; using WeightsMatMulAdjoint = detail::ComplexMatrixMultiply; SphericalVector::SphericalVector(const Config& config) : Method(config) { const auto* conf = dynamic_cast(&config); ATLAS_ASSERT(conf, "config must be derived from eckit::LocalConfiguration"); interpolationScheme_ = conf->getSubConfiguration("scheme"); adjoint_ = conf->getBool("adjoint", false); } void SphericalVector::do_setup(const Grid& source, const Grid& target, const Cache&) { ATLAS_NOTIMPLEMENTED; } void SphericalVector::do_setup(const FunctionSpace& source, const FunctionSpace& target, const Cache&) { ATLAS_NOTIMPLEMENTED; } void SphericalVector::do_setup(const FunctionSpace& source, const FunctionSpace& target) { ATLAS_TRACE("interpolation::method::SphericalVector::do_setup"); source_ = source; target_ = target; if (target_.size() == 0) { return; } interpolationScheme_.set("adjoint", false); // The temporary interpolation object should not compute the adjoint auto matrix_cache = MatrixCache(Interpolation(interpolationScheme_, source_, target_)); setMatrix(matrix_cache); if (adjoint_) { adjoint_matrix(); } // Get matrix data. const auto m = atlas::linalg::make_host_view(matrix()); const auto nRows = static_cast(m.rows()); const auto nCols = static_cast(m.cols()); const auto nNonZeros = static_cast(m.nnz()); const auto* outerIndices = m.outer(); const auto* innerIndices = m.inner(); const auto* baseWeights = m.value(); // Note: need to store copy of weights as Eigen3 sorts compressed rows by j // whereas eckit does not. auto complexTriplets = ComplexTriplets(nNonZeros); auto realTriplets = RealTriplets(nNonZeros); const auto sourceLonLatsView = array::make_view(source_.lonlat()); const auto targetLonLatsView = array::make_view(target_.lonlat()); const auto unitSphere = geometry::UnitSphere{}; atlas_omp_parallel_for(auto rowIndex = Index{0}; rowIndex < nRows; ++rowIndex) { for (auto dataIndex = outerIndices[rowIndex]; dataIndex < outerIndices[rowIndex + 1]; ++dataIndex) { const auto colIndex = static_cast(innerIndices[dataIndex]); const auto baseWeight = baseWeights[dataIndex]; const auto sourceLonLat = PointLonLat(sourceLonLatsView(colIndex, 0), sourceLonLatsView(colIndex, 1)); const auto targetLonLat = PointLonLat(targetLonLatsView(rowIndex, 0), targetLonLatsView(rowIndex, 1)); const auto alpha = unitSphere.greatCircleCourse(sourceLonLat, targetLonLat); const auto deltaAlpha = (alpha.first - alpha.second) * util::Constants::degreesToRadians(); complexTriplets[dataIndex] = ComplexTriplet{ rowIndex, colIndex, baseWeight * Complex{std::cos(deltaAlpha), std::sin(deltaAlpha)}}; realTriplets[dataIndex] = RealTriplet{rowIndex, colIndex, baseWeight}; } } complexWeights_ = ComplexMatrix(nRows, nCols, complexTriplets); realWeights_ = RealMatrix(nRows, nCols, realTriplets); if (adjoint_) { complexWeightsAdjoint_ = complexWeights_.adjoint(); realWeightsAdjoint_ = realWeights_.adjoint(); } } void SphericalVector::print(std::ostream&) const { ATLAS_NOTIMPLEMENTED; } void SphericalVector::do_execute(const FieldSet& sourceFieldSet, FieldSet& targetFieldSet, Metadata& metadata) const { ATLAS_TRACE("atlas::interpolation::method::SphericalVector::do_execute()"); ATLAS_ASSERT(sourceFieldSet.size() == targetFieldSet.size()); const auto packedSourceFieldSet = util::pack_vector_fields(sourceFieldSet); auto packedTargetFieldSet = util::pack_vector_fields(targetFieldSet); for (auto i = 0; i < packedSourceFieldSet.size(); ++i) { do_execute(packedSourceFieldSet[i], packedTargetFieldSet[i], metadata); } util::unpack_vector_fields(packedTargetFieldSet, targetFieldSet); } void SphericalVector::do_execute(const Field& sourceField, Field& targetField, Metadata&) const { ATLAS_TRACE("atlas::interpolation::method::SphericalVector::do_execute()"); const auto fieldType = sourceField.metadata().getString("type", ""); if (fieldType != "vector") { auto metadata = Metadata(); Method::do_execute(sourceField, targetField, metadata); return; } if (targetField.size() == 0) { haloExchange(sourceField); return; } Method::check_compatibility(sourceField, targetField, matrix()); haloExchange(sourceField); interpolate_vector_field(sourceField, targetField, WeightsMatMul(complexWeights_, realWeights_)); targetField.set_dirty(); } void SphericalVector::do_execute_adjoint(FieldSet& sourceFieldSet, const FieldSet& targetFieldSet, Metadata& metadata) const { ATLAS_TRACE( "atlas::interpolation::method::SphericalVector::do_execute_adjoint()"); ATLAS_ASSERT(sourceFieldSet.size() == targetFieldSet.size()); auto packedSourceFieldSet = util::pack_vector_fields(sourceFieldSet); const auto packedTargetFieldSet = util::pack_vector_fields(targetFieldSet); for (auto i = 0; i < packedSourceFieldSet.size(); ++i) { do_execute_adjoint(packedSourceFieldSet[i], packedTargetFieldSet[i], metadata); } util::unpack_vector_fields(packedSourceFieldSet, sourceFieldSet); } void SphericalVector::do_execute_adjoint(Field& sourceField, const Field& targetField, Metadata& metadata) const { ATLAS_TRACE( "atlas::interpolation::method::SphericalVector::do_execute_adjoint()"); const auto fieldType = sourceField.metadata().getString("type", ""); if (fieldType != "vector") { auto metadata = Metadata(); Method::do_execute_adjoint(sourceField, targetField, metadata); return; } if (targetField.size() == 0) { adjointHaloExchange(sourceField); return; } Method::check_compatibility(sourceField, targetField, matrix()); ATLAS_ASSERT(adjoint_, "\"adjoint\" needs to be set to \"true\" in Config."); interpolate_vector_field( targetField, sourceField, WeightsMatMulAdjoint(complexWeightsAdjoint_, realWeightsAdjoint_)); adjointHaloExchange(sourceField); } template void SphericalVector::interpolate_vector_field(const Field& sourceField, Field& targetField, const MatMul& matMul) { const auto sourceViewVariant = array::make_view_variant(sourceField); const auto sourceViewVisitor = [&](auto sourceView) { if constexpr (array::is_rank<2, 3>(sourceView) && array::is_non_const_value_type(sourceView)) { using SourceView = std::decay_t; using Value = typename SourceView::non_const_value_type; constexpr auto Rank = SourceView::rank(); auto targetView = array::make_view(targetField); switch (sourceField.variables()) { case 2: return matMul.apply(sourceView, targetView, twoVector); case 3: return matMul.apply(sourceView, targetView, threeVector); default: ATLAS_THROW_EXCEPTION("Error: no support for " + std::to_string(sourceField.variables()) + " variable vector fields.\n" + " Number of variables must be 2 or 3."); } } else { ATLAS_THROW_EXCEPTION( "Error: no support for rank = " + std::to_string(sourceField.rank()) + " and value type = " + sourceField.datatype().str() + ".\n" + "Vector field must have rank 2 or 3 with value type " "float or double"); } }; std::visit(sourceViewVisitor, sourceViewVariant); }; } // namespace method } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/method/sphericalvector/SphericalVector.h0000664000175000017500000000702715160212070027576 0ustar alastairalastair/* * (C) Crown Copyright 2024 Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ #pragma once #include "atlas/functionspace/FunctionSpace.h" #include "atlas/interpolation/method/Method.h" #include "atlas/interpolation/method/sphericalvector/Types.h" namespace atlas { namespace interpolation { namespace method { class SphericalVector : public Method { public: /// @brief Interpolation post-processor for vector field interpolation /// /// @details Takes a base interpolation config keyed to "scheme" and creates /// A set of complex intepolation weights which rotate source vector /// elements into the target elements' individual frame of reference. /// Method works by creating a great-circle arc between the source /// and target locations; the amount of rotation is determined by the /// difference the in the great-cricle course (relative to north) at /// the source and target location. /// Both source and target fields require the "type" metadata to be /// set to "vector" for this method to be invoked. Otherwise, the /// base scalar interpolation method is invoked. /// Note: This method only works with matrix-based interpolation /// schemes. /// SphericalVector(const Config& config); ~SphericalVector() override {} void print(std::ostream&) const override; const FunctionSpace& source() const override { return source_; } const FunctionSpace& target() const override { return target_; } void do_execute(const FieldSet& sourceFieldSet, FieldSet& targetFieldSet, Metadata& metadata) const override; void do_execute(const Field& sourceField, Field& targetField, Metadata& metadata) const override; void do_execute_adjoint(FieldSet& sourceFieldSet, const FieldSet& targetFieldSet, Metadata& metadata) const override; void do_execute_adjoint(Field& sourceField, const Field& targetField, Metadata& metadata) const override; private: template static void interpolate_vector_field(const Field& sourceField, Field& targetField, const MatMul& matMul); template static void interpolate_vector_field(const Field& sourceField, Field& targetField, const MatMul& matMul); template static void interpolate_vector_field(const Field& sourceField, Field& targetField, const MatMul& matMul); using Method::do_setup; void do_setup(const FunctionSpace& source, const FunctionSpace& target) override; void do_setup(const Grid& source, const Grid& target, const Cache&) override; void do_setup(const FunctionSpace& source, const FunctionSpace& target, const Cache&) override; eckit::LocalConfiguration interpolationScheme_{}; FunctionSpace source_{}; FunctionSpace target_{}; detail::ComplexMatrix complexWeights_{}; detail::RealMatrix realWeights_{}; detail::ComplexMatrix complexWeightsAdjoint_{}; detail::RealMatrix realWeightsAdjoint_{}; bool adjoint_; }; } // namespace method } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/method/knn/0000775000175000017500000000000015160212070021713 5ustar alastairalastairatlas-0.46.0/src/atlas/interpolation/method/knn/GridBox.cc0000664000175000017500000001567515160212070023576 0ustar alastairalastair/* * (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 "atlas/interpolation/method/knn/GridBox.h" #include #include #include #include "eckit/types/FloatCompare.h" #include "eckit/types/Fraction.h" #include "atlas/grid.h" #include "atlas/runtime/Exception.h" #include "atlas/util/Constants.h" #include "atlas/util/Earth.h" #include "atlas/util/GaussianLatitudes.h" #include "atlas/util/Point.h" static constexpr double GLOBE = 360; static constexpr double NORTH_POLE = 90; static constexpr double SOUTH_POLE = -90; static constexpr double TWO = 2; double normalise(double lon, double minimum) { while (lon < minimum) { lon += GLOBE; } while (lon >= minimum + GLOBE) { lon -= GLOBE; } return lon; } #define ASSERT_BOX(n, w, s, e) \ ATLAS_ASSERT(SOUTH_POLE <= s && s <= n && n <= NORTH_POLE); \ ATLAS_ASSERT(w <= e && e <= w + GLOBE) namespace atlas { namespace interpolation { namespace method { GridBox::GridBox(double north, double west, double south, double east): north_(north), west_(west), south_(south), east_(east) { ASSERT_BOX(north_, west_, south_, east_); } double GridBox::area() const { return util::Earth::area({west_, north_}, {east_, south_}); } double GridBox::diagonal() const { return util::Earth::distance({west_, north_}, {east_, south_}); } bool GridBox::intersects(GridBox& other) const { double n = std::min(north_, other.north_); double s = std::max(south_, other.south_); if (!eckit::types::is_strictly_greater(n, s)) { return false; } auto intersect = [](const GridBox& a, const GridBox& b, double& w, double& e) { double ref = normalise(b.west_, a.west_); double w_ = std::max(a.west_, ref); double e_ = std::min(a.east_, normalise(b.east_, ref)); if (eckit::types::is_strictly_greater(e_, w_)) { w = w_; e = e_; return true; } return false; }; double w = std::min(west_, other.west_); double e = w; if (west_ <= other.west_ ? intersect(*this, other, w, e) || intersect(other, *this, w, e) : intersect(other, *this, w, e) || intersect(*this, other, w, e)) { ATLAS_ASSERT(w <= e); other = {n, w, s, e}; return true; } return false; } void GridBox::print(std::ostream& out) const { out << "GridBox[north=" << north_ << ",west=" << west_ << ",south=" << south_ << ",east=" << east_ << "]"; } GridBoxes::GridBoxes(const Grid& grid, bool gaussianWeightedLatitudes) { StructuredGrid structured(grid); if (!structured || grid.projection()) { throw_NotImplemented("GridBoxes only support structured, unprojected/unrotated grids", Here()); } // Bounding box and periodicity RectangularDomain domain = structured.domain(); ATLAS_ASSERT(domain); auto north = domain.ymax(); auto south = domain.ymin(); auto west = domain.xmin(); auto east = domain.xmax(); ASSERT_BOX(north, west, south, east); bool periodic(ZonalBandDomain(structured.domain())); // Calculate grid-box parallels (latitude midpoints, or accumulated Gaussian quadrature weights) auto& y = structured.yspace(); std::vector lat; lat.reserve(y.size() + 1); GaussianGrid gaussian; if (gaussianWeightedLatitudes && (gaussian = grid)) { auto N = [&]() { auto N = gaussian.N(); ATLAS_ASSERT(N > 0); return size_t(N); }(); std::vector latitudes(N * 2); std::vector weights(N * 2); util::gaussian_quadrature_npole_spole(N, latitudes.data(), weights.data()); std::vector lat_global(N * 2 + 1); auto b = lat_global.rbegin(); auto f = lat_global.begin(); *(b++) = SOUTH_POLE; *(f++) = NORTH_POLE; double wacc = -1.; for (size_t j = 0; j < N; ++j, ++b, ++f) { wacc += TWO * weights[j]; double deg = util::Constants::radiansToDegrees() * std::asin(wacc); *b = deg; *f = -(*b); } lat_global[N] = 0.; // (equator) // Grids not covering the poles need clipping size_t j = 0; for (; j < latitudes.size(); ++j) { if (eckit::types::is_approximately_lesser_or_equal(latitudes[j], north) && eckit::types::is_approximately_lesser_or_equal(south, latitudes[j])) { lat.push_back(std::min(north, lat_global[j])); } else if (!lat.empty()) { break; } } ATLAS_ASSERT(!lat.empty()); lat.push_back(std::max(south, lat_global[j])); } else { // non-Gaussian (but structured) grids lat.push_back(north); for (auto b = y.begin(), a = b++; b != y.end(); a = b++) { lat.push_back((*b + *a) / TWO); } lat.push_back(south); } // Calculate grid-box meridians (longitude midpoints) auto& x = structured.xspace(); ATLAS_ASSERT(x.nx().size() == x.dx().size()); ATLAS_ASSERT(x.nx().size() == x.xmin().size()); auto gridSize = [&]() { auto N = grid.size(); ATLAS_ASSERT(N >= 0); return size_t(N); }(); clear(); reserve(gridSize); for (size_t j = 0; j < x.nx().size(); ++j) { eckit::Fraction dx(x.dx()[j]); eckit::Fraction xmin(x.xmin()[j]); auto n = (xmin / dx).integralPart(); if (n * dx < xmin) { n += 1; // (adjust double-fraction conversions) } // On non-periodic grids, West- and East-most grid-boxes need clipping ATLAS_ASSERT(x.nx()[j] > 0); eckit::Fraction lon0 = (n * dx) - (dx / 2); eckit::Fraction lon1 = lon0; for (idx_t i = 0; i < x.nx()[j]; ++i) { double lon0 = lon1; lon1 += dx; double n = lat[j]; double s = lat[j + 1]; double w = periodic ? lon0 : std::max(west, lon0); double e = periodic ? lon1 : std::min(east, lon1); emplace_back(GridBox(n, w, s, e)); } if (periodic) { ATLAS_ASSERT(lon1 == lon0 + GLOBE); } } ATLAS_ASSERT(size() == gridSize); } GridBoxes::GridBoxes() = default; double GridBoxes::getLongestGridBoxDiagonal() const { ATLAS_ASSERT(!empty()); double R = 0.; for (auto& box : *this) { R = std::max(R, box.diagonal()); } ATLAS_ASSERT(R > 0.); return R; } } // namespace method } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/method/knn/KNearestNeighboursBase.cc0000664000175000017500000001055215160212070026562 0ustar alastairalastair/* * (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. and Interpolation */ #include "eckit/config/Resource.h" #include "eckit/log/TraceTimer.h" #include "atlas/array.h" #include "atlas/functionspace/PointCloud.h" #include "atlas/functionspace/NodeColumns.h" #include "atlas/functionspace/CellColumns.h" #include "atlas/functionspace/StructuredColumns.h" #include "atlas/interpolation/method/knn/KNearestNeighboursBase.h" #include "atlas/library/Library.h" #include "atlas/mesh/Nodes.h" #include "atlas/mesh/actions/BuildXYZField.h" #include "atlas/runtime/Trace.h" #include "atlas/util/CoordinateEnums.h" namespace atlas { namespace interpolation { namespace method { void KNearestNeighboursBase::buildPointSearchTree(Mesh& meshSource, const mesh::Halo& _halo) { ATLAS_TRACE(); eckit::TraceTimer tim("KNearestNeighboursBase::buildPointSearchTree()"); auto lonlat = array::make_view(meshSource.nodes().lonlat()); auto halo = array::make_view(meshSource.nodes().halo()); int h = _halo.size(); static bool fastBuildKDTrees = eckit::Resource("$ATLAS_FAST_BUILD_KDTREES", true); if (fastBuildKDTrees) { pTree_.reserve(lonlat.shape(0)); } for (idx_t ip = 0; ip < lonlat.shape(0); ++ip) { if (halo(ip) <= h) { pTree_.insert(PointLonLat(lonlat(ip, LON), lonlat(ip, LAT)), ip); } } pTree_.build(); // // generate 3D point coordinates // mesh::actions::BuildXYZField("xyz")(meshSource); } namespace { template void insert_tree(util::IndexKDTree& tree, const FunctionSpace_type& functionspace) { size_t ip{0}; for (auto p : functionspace.iterate().lonlat()) { tree.insert(p, ip++); } } void insert_tree(util::IndexKDTree& tree, const functionspace::NodeColumns& functionspace) { auto lonlat = array::make_view(functionspace.lonlat()); auto halo = array::make_view(functionspace.nodes().halo()); int h = functionspace.halo().size(); for (idx_t ip = 0; ip < lonlat.shape(0); ++ip) { if (halo(ip) <= h) { tree.insert(PointLonLat(lonlat(ip, LON), lonlat(ip, LAT)), ip); } } } void insert_tree(util::IndexKDTree& tree, const functionspace::CellColumns& functionspace) { auto lonlat = array::make_view(functionspace.lonlat()); auto halo = array::make_view(functionspace.cells().halo()); int h = functionspace.halo().size(); for (idx_t ip = 0; ip < lonlat.shape(0); ++ip) { if (halo(ip) <= h) { tree.insert(PointLonLat(lonlat(ip, LON), lonlat(ip, LAT)), ip); } } } void insert_tree(util::IndexKDTree& tree, const functionspace::StructuredColumns& functionspace) { auto lonlat = array::make_view(functionspace.lonlat()); for (idx_t ip = 0; ip < lonlat.shape(0); ++ip) { tree.insert(PointLonLat(lonlat(ip, LON), lonlat(ip, LAT)), ip); } } } // namespace void KNearestNeighboursBase::buildPointSearchTree(const FunctionSpace& functionspace) { ATLAS_TRACE(); eckit::TraceTimer timer("KNearestNeighboursBase::buildPointSearchTree()"); static bool fastBuildKDTrees = eckit::Resource("$ATLAS_FAST_BUILD_KDTREES", true); if (fastBuildKDTrees) { pTree_.reserve(functionspace.size()); } if (functionspace::PointCloud fs = functionspace) { insert_tree(pTree_, fs); } else if (functionspace::NodeColumns fs = functionspace) { insert_tree(pTree_, fs); } else if (functionspace::CellColumns fs = functionspace) { insert_tree(pTree_, fs); } else if (functionspace::StructuredColumns fs = functionspace) { insert_tree(pTree_, fs); } else { ATLAS_NOTIMPLEMENTED; } pTree_.build(); } bool KNearestNeighboursBase::extractTreeFromCache(const Cache& c) { IndexKDTreeCache cache(c); if (cache) { pTree_ = cache.tree(); return true; } return false; } } // namespace method } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/method/knn/GridBoxMethod.h0000664000175000017500000000451515160212070024570 0ustar alastairalastair/* * (C) Copyright 1996- ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include "atlas/interpolation/method/knn/KNearestNeighboursBase.h" #include #include "atlas/functionspace.h" #include "atlas/interpolation/method/knn/GridBox.h" namespace atlas { namespace interpolation { namespace method { class GridBoxMethod : public KNearestNeighboursBase { public: GridBoxMethod(const Config&); virtual ~GridBoxMethod() override; protected: virtual void print(std::ostream&) const override; using KNearestNeighboursBase::do_setup; /** * @brief Create an interpolant sparse matrix relating two functionspaces, using grid-box average method * @param source functionspace containing source points * @param target functionspace containing target points */ virtual void do_setup(const FunctionSpace& source, const FunctionSpace& target) override; virtual void do_setup(const Grid& source, const Grid& target, const Cache&) override; virtual void do_setup(const FunctionSpace& source, const FunctionSpace& target, const Cache&) override; virtual const FunctionSpace& source() const override { return source_; } virtual const FunctionSpace& target() const override { return target_; } bool intersect(size_t i, const GridBox& iBox, const util::IndexKDTree::ValueList&, std::vector&) const; virtual void do_execute(const FieldSet& source, FieldSet& target, Metadata&) const override = 0; virtual void do_execute(const Field& source, Field& target, Metadata&) const override = 0; virtual Cache createCache() const override; protected: static void giveUp(const std::forward_list&); FunctionSpace source_; FunctionSpace target_; GridBoxes sourceBoxes_; GridBoxes targetBoxes_; double searchRadius_; mutable std::forward_list failures_; bool matrixFree_; bool failEarly_; bool gaussianWeightedLatitudes_; }; } // namespace method } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/method/knn/GridBoxAverage.cc0000664000175000017500000000563515160212070025064 0ustar alastairalastair/* * (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. and Interpolation */ #include "atlas/interpolation/method/knn/GridBoxAverage.h" #include #include "eckit/log/ProgressTimer.h" #include "atlas/array.h" #include "atlas/functionspace/PointCloud.h" #include "atlas/interpolation/method/MethodFactory.h" #include "atlas/runtime/Exception.h" namespace atlas { namespace interpolation { namespace method { namespace { MethodBuilder __builder("grid-box-average"); } void GridBoxAverage::do_execute(const FieldSet& source, FieldSet& target, Metadata& metadata) const { ATLAS_ASSERT(source.size() == target.size()); // Matrix-based interpolation is handled by base (Method) class // TODO: exploit sparse/dense matrix multiplication for (idx_t i = 0; i < source.size(); ++i) { if (matrixFree_) { GridBoxAverage::do_execute(source[i], target[i], metadata); } else { Method::do_execute(source[i], target[i], metadata); } } } void GridBoxAverage::do_execute(const Field& source, Field& target, Metadata& metadata) const { ATLAS_TRACE("atlas::interpolation::method::GridBoxAverage::do_execute()"); // Matrix-based interpolation is handled by base (Method) class if (!matrixFree_) { Method::do_execute(source, target, metadata); return; } // ensure GridBoxMethod::setup() functionspace::PointCloud tgt = target_; ATLAS_ASSERT(tgt); ATLAS_ASSERT(searchRadius_ > 0.); ATLAS_ASSERT(!sourceBoxes_.empty()); ATLAS_ASSERT(!targetBoxes_.empty()); // set arrays ATLAS_ASSERT(source.rank() == 1); ATLAS_ASSERT(target.rank() == 1); auto xarray = atlas::array::make_view(source); auto yarray = atlas::array::make_view(target); ATLAS_ASSERT(xarray.size() == sourceBoxes_.size()); ATLAS_ASSERT(yarray.size() == targetBoxes_.size()); yarray.assign(0.); failures_.clear(); // interpolate eckit::ProgressTimer progress("Intersecting", targetBoxes_.size(), "grid box", double(5.)); std::vector triplets; size_t i = 0; for (auto p : tgt.iterate().lonlat()) { ++progress; if (intersect(i, targetBoxes_.at(i), pTree_.closestPointsWithinRadius(p, searchRadius_), triplets)) { auto& y = yarray[i]; for (auto& t : triplets) { y += xarray[t.col()] * t.value(); } } ++i; } if (!failures_.empty()) { giveUp(failures_); } } } // namespace method } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/method/knn/KNearestNeighbours.h0000664000175000017500000000327015160212070025630 0ustar alastairalastair/* * (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/functionspace/FunctionSpace.h" #include "atlas/interpolation/method/knn/KNearestNeighboursBase.h" namespace atlas { namespace interpolation { namespace method { class KNearestNeighbours : public KNearestNeighboursBase { public: KNearestNeighbours(const Config& config); virtual ~KNearestNeighbours() override {} /** * @brief Create an interpolant sparse matrix relating two (pre-partitioned) * meshes, * using the k-nearest neighbours method * @param source functionspace containing source elements * @param target functionspace containing target points */ virtual void print(std::ostream&) const override {} virtual const FunctionSpace& source() const override { return source_; } virtual const FunctionSpace& target() const override { return target_; } private: using KNearestNeighboursBase::do_setup; virtual void do_setup(const FunctionSpace& source, const FunctionSpace& target) override; virtual void do_setup(const Grid& source, const Grid& target, const Cache&) override; virtual void do_setup(const FunctionSpace& source, const FunctionSpace& target, const Cache&) override; FunctionSpace source_; FunctionSpace target_; size_t k_; }; } // namespace method } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/method/knn/GridBoxMaximum.cc0000664000175000017500000000670115160212070025122 0ustar alastairalastair/* * (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. and Interpolation */ #include "atlas/interpolation/method/knn/GridBoxMaximum.h" #include #include #include #include "eckit/log/ProgressTimer.h" #include "eckit/types/FloatCompare.h" #include "atlas/array.h" #include "atlas/functionspace/PointCloud.h" #include "atlas/interpolation/method/MethodFactory.h" #include "atlas/runtime/Exception.h" #include "atlas/linalg/sparse/MakeEckitSparseMatrix.h" namespace atlas { namespace interpolation { namespace method { namespace { MethodBuilder __builder("grid-box-maximum"); } void GridBoxMaximum::do_execute(const FieldSet& source, FieldSet& target, Metadata& metadata) const { ATLAS_ASSERT(source.size() == target.size()); for (idx_t i = 0; i < source.size(); ++i) { do_execute(source[i], target[i], metadata); } } void GridBoxMaximum::do_execute(const Field& source, Field& target, Metadata&) const { ATLAS_TRACE("atlas::interpolation::method::GridBoxMaximum::do_execute()"); // set arrays ATLAS_ASSERT(source.rank() == 1); ATLAS_ASSERT(target.rank() == 1); auto xarray = atlas::array::make_view(source); auto yarray = atlas::array::make_view(target); ATLAS_ASSERT(xarray.size() == sourceBoxes_.size()); ATLAS_ASSERT(yarray.size() == targetBoxes_.size()); yarray.assign(0.); failures_.clear(); if (!matrixFree_) { const auto m = atlas::linalg::make_non_owning_eckit_sparse_matrix(matrix()); auto k = m.begin(); for (decltype(m.rows()) i = 0, j = 0; i < m.rows(); ++i) { double max = std::numeric_limits::lowest(); bool found = false; for (; k != m.end(i); ++k) { ATLAS_ASSERT(k.col() < size_t(xarray.shape(0))); auto value = xarray[k.col()]; if (max < value) { max = value; j = k.col(); } found = true; } ATLAS_ASSERT(found); yarray[i] = xarray[j]; } return; } // ensure GridBoxMethod::setup() functionspace::PointCloud tgt = target_; ATLAS_ASSERT(tgt); ATLAS_ASSERT(searchRadius_ > 0.); ATLAS_ASSERT(!sourceBoxes_.empty()); ATLAS_ASSERT(!targetBoxes_.empty()); // interpolate eckit::ProgressTimer progress("Intersecting", targetBoxes_.size(), "grid box", double(5.)); std::vector triplets; size_t i = 0; for (auto p : tgt.iterate().lonlat()) { ++progress; if (intersect(i, targetBoxes_.at(i), pTree_.closestPointsWithinRadius(p, searchRadius_), triplets)) { auto triplet = std::max_element(triplets.begin(), triplets.end(), [](const Triplet& a, const Triplet& b) { return !eckit::types::is_approximately_greater_or_equal(a.value(), b.value()); }); yarray[i] = xarray[triplet->col()]; } ++i; } if (!failures_.empty()) { giveUp(failures_); } } } // namespace method } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/method/knn/NearestNeighbour.h0000664000175000017500000000331015160212070025325 0ustar alastairalastair/* * (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/functionspace/FunctionSpace.h" #include "atlas/interpolation/method/knn/KNearestNeighboursBase.h" namespace atlas { namespace interpolation { namespace method { class NearestNeighbour : public KNearestNeighboursBase { public: NearestNeighbour(const Config& config): KNearestNeighboursBase(config) {} virtual ~NearestNeighbour() override {} virtual void print(std::ostream&) const override {} protected: virtual const FunctionSpace& source() const override { return source_; } virtual const FunctionSpace& target() const override { return target_; } private: using KNearestNeighboursBase::do_setup; /** * @brief Create an interpolant sparse matrix relating two (pre-partitioned) * meshes, * using nearest neighbour method * @param source functionspace containing source elements * @param target functionspace containing target points */ virtual void do_setup(const FunctionSpace& source, const FunctionSpace& target) override; virtual void do_setup(const Grid& source, const Grid& target, const Cache&) override; virtual void do_setup(const FunctionSpace& source, const FunctionSpace& target, const Cache&) override; FunctionSpace source_; FunctionSpace target_; }; } // namespace method } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/method/knn/GridBoxMaximum.h0000664000175000017500000000162115160212070024760 0ustar alastairalastair/* * (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. and Interpolation */ #pragma once #include "atlas/interpolation/method/knn/GridBoxMethod.h" namespace atlas { namespace interpolation { namespace method { class GridBoxMaximum final : public GridBoxMethod { public: using GridBoxMethod::GridBoxMethod; private: void do_execute(const FieldSet& source, FieldSet& target, Metadata&) const override; void do_execute(const Field& source, Field& target, Metadata&) const override; }; } // namespace method } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/method/knn/GridBoxAverage.h0000664000175000017500000000162115160212070024715 0ustar alastairalastair/* * (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. and Interpolation */ #pragma once #include "atlas/interpolation/method/knn/GridBoxMethod.h" namespace atlas { namespace interpolation { namespace method { class GridBoxAverage final : public GridBoxMethod { public: using GridBoxMethod::GridBoxMethod; private: void do_execute(const FieldSet& source, FieldSet& target, Metadata&) const override; void do_execute(const Field& source, Field& target, Metadata&) const override; }; } // namespace method } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/method/knn/KNearestNeighbours.cc0000664000175000017500000001162515160212070025771 0ustar alastairalastair/* * (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/interpolation/method/knn/KNearestNeighbours.h" #include #include "eckit/log/Plural.h" #include "eckit/types/FloatCompare.h" #include "atlas/array.h" #include "atlas/functionspace/NodeColumns.h" #include "atlas/grid.h" #include "atlas/interpolation/method/MethodFactory.h" #include "atlas/mesh/Nodes.h" #include "atlas/mesh/actions/BuildXYZField.h" #include "atlas/meshgenerator.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" namespace atlas { namespace interpolation { namespace method { namespace { MethodBuilder __builder("k-nearest-neighbours"); } // namespace KNearestNeighbours::KNearestNeighbours(const Method::Config& config): KNearestNeighboursBase(config) { k_ = 1; config.get("k-nearest-neighbours", k_); ATLAS_ASSERT(k_); } void KNearestNeighbours::do_setup(const Grid& source, const Grid& target, const Cache&) { if (mpi::size() > 1) { ATLAS_NOTIMPLEMENTED; } auto functionspace = [](const Grid& grid) -> FunctionSpace { Mesh mesh; if (StructuredGrid(grid)) { mesh = MeshGenerator("structured", util::Config("3d", true)).generate(grid); } else { mesh = MeshGenerator("delaunay").generate(grid); } return functionspace::NodeColumns(mesh); }; do_setup(functionspace(source), functionspace(target)); } void KNearestNeighbours::do_setup(const FunctionSpace& source, const FunctionSpace& target, const Cache& cache) { if (interpolation::MatrixCache(cache)) { setMatrix(cache); source_ = source; target_ = target; } else { do_setup(source, target); } } void KNearestNeighbours::do_setup(const FunctionSpace& source, const FunctionSpace& target) { source_ = source; target_ = target; buildPointSearchTree(source); array::ArrayView lonlat = array::make_view(target.lonlat()); size_t inp_npts = source.size(); size_t out_npts = target.size(); // return early if no output points on this partition reserve is called on // the triplets but also during the sparseMatrix constructor. This won't // work for empty matrices if (out_npts == 0) { return; } // fill the sparse matrix std::vector weights_triplets; weights_triplets.reserve(out_npts * k_); { Trace timer(Here(), "atlas::interpolation::method::KNearestNeighbour::do_setup()"); std::vector weights; Log::debug() << "Computing interpolation weights for " << out_npts << " points." << std::endl; for (size_t ip = 0; ip < out_npts; ++ip) { if (ip && (ip % 1000 == 0)) { timer.pause(); auto elapsed = timer.elapsed(); timer.resume(); auto rate = eckit::types::is_approximately_equal(elapsed, 0.) ? std::numeric_limits::infinity() : (ip / elapsed); Log::debug() << eckit::BigNum(ip) << " (at " << size_t(rate) << " points/s)... after " << elapsed << " s" << std::endl; } // find the closest input points to the output point auto nn = pTree_.closestPoints(PointLonLat{lonlat(ip, size_t(LON)), lonlat(ip, size_t(LAT))}, k_); // calculate weights (individual and total, to normalise) using distance // squared const size_t npts = nn.size(); ATLAS_ASSERT(npts); weights.resize(npts, 0); double sum = 0; for (size_t j = 0; j < npts; ++j) { const double d = nn[j].distance(); const double d2 = d * d; weights[j] = 1. / (1. + d2); sum += weights[j]; } ATLAS_ASSERT(sum > 0); // insert weights into the matrix for (size_t j = 0; j < npts; ++j) { size_t jp = nn[j].payload(); ATLAS_ASSERT(jp < inp_npts, "point found which is not covered within the halo of the source function space"); weights_triplets.emplace_back(ip, jp, weights[j] / sum); } } } // fill sparse matrix and return setMatrix(out_npts, inp_npts, weights_triplets); } } // namespace method } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/method/knn/KNearestNeighboursBase.h0000664000175000017500000000225515160212070026425 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include "atlas/interpolation/method/Method.h" #include "atlas/mesh/Halo.h" #include "atlas/mesh/Mesh.h" #include "atlas/util/KDTree.h" namespace atlas { namespace interpolation { namespace method { class KNearestNeighboursBase : public Method { public: KNearestNeighboursBase(const Config& config): Method(config) {} virtual ~KNearestNeighboursBase() override {} protected: void buildPointSearchTree(Mesh& meshSource) { buildPointSearchTree(meshSource, mesh::Halo(meshSource)); } void buildPointSearchTree(Mesh& meshSource, const mesh::Halo&); void buildPointSearchTree(const FunctionSpace&); bool extractTreeFromCache(const Cache&); util::IndexKDTree pTree_; }; } // namespace method } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/method/knn/GridBoxMethod.cc0000664000175000017500000001506315160212070024726 0ustar alastairalastair/* * (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. and Interpolation */ #include "atlas/interpolation/method/knn/GridBoxMethod.h" #include #include #include "eckit/log/Plural.h" #include "eckit/log/ProgressTimer.h" #include "eckit/types/FloatCompare.h" #include "atlas/functionspace.h" #include "atlas/grid.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Log.h" #include "atlas/runtime/Trace.h" namespace atlas { namespace interpolation { namespace method { GridBoxMethod::GridBoxMethod(const Method::Config& config): KNearestNeighboursBase(config) { config.get("matrix_free", matrixFree_ = false); config.get("fail_early", failEarly_ = true); config.get("gaussian_weighted_latitudes", gaussianWeightedLatitudes_ = true); } GridBoxMethod::~GridBoxMethod() = default; void GridBoxMethod::print(std::ostream& out) const { out << "GridBoxMethod[]"; } namespace { StructuredGrid extract_structured_grid(const FunctionSpace& fs) { if (functionspace::StructuredColumns{fs}) { return functionspace::StructuredColumns(fs).grid(); } if (functionspace::NodeColumns{fs}) { return functionspace::NodeColumns(fs).mesh().grid(); } return Grid(); } } void GridBoxMethod::do_setup(const FunctionSpace& source, const FunctionSpace& target) { if( mpi::size() > 1 ) { ATLAS_THROW_EXCEPTION("Cannot use GridBoxMethod in parallel yet."); } auto src_grid = extract_structured_grid(source); auto tgt_grid = extract_structured_grid(target); if( not src_grid ) { ATLAS_THROW_EXCEPTION("Could not extract StructuredGrid from source function space " << source.type() ); } if( not tgt_grid ) { ATLAS_THROW_EXCEPTION("Could not extract StructuredGrid from target function space " << target.type() ); } do_setup(src_grid,tgt_grid,Cache()); } bool GridBoxMethod::intersect(size_t i, const GridBox& box, const util::IndexKDTree::ValueList& closest, std::vector& triplets) const { ASSERT(!closest.empty()); triplets.clear(); triplets.reserve(closest.size()); double area = box.area(); ASSERT(area > 0.); double sumSmallAreas = 0.; for (auto& c : closest) { auto j = c.payload(); ASSERT(j >= 0); auto smallBox = sourceBoxes_.at(size_t(j)); if (box.intersects(smallBox)) { double smallArea = smallBox.area(); ASSERT(smallArea > 0.); triplets.emplace_back(i, j, smallArea / area); sumSmallAreas += smallArea; if (eckit::types::is_approximately_equal(area, sumSmallAreas, 1. /*m^2*/)) { return true; } } } if (failEarly_) { Log::error() << "Failed to intersect grid box " << i << ", " << box << std::endl; throw_Exception("Failed to intersect grid box"); } failures_.push_front(i); triplets.clear(); return false; } void GridBoxMethod::do_setup(const Grid& source, const Grid& target, const Cache& cache) { ATLAS_TRACE("GridBoxMethod::setup()"); if (mpi::size() > 1) { ATLAS_THROW_EXCEPTION("Not implemented for MPI-parallel runs"); } ATLAS_ASSERT(source); ATLAS_ASSERT(target); functionspace::PointCloud src(source); functionspace::PointCloud tgt(target); ATLAS_ASSERT(src); ATLAS_ASSERT(tgt); source_ = src; target_ = tgt; if (not matrixFree_ && interpolation::MatrixCache(cache)) { setMatrix(cache); ATLAS_ASSERT(matrix().rows() == target.size()); ATLAS_ASSERT(matrix().cols() == source.size()); return; } if (not extractTreeFromCache(cache)) { buildPointSearchTree(src); } sourceBoxes_ = GridBoxes(source, gaussianWeightedLatitudes_); targetBoxes_ = GridBoxes(target, gaussianWeightedLatitudes_); searchRadius_ = sourceBoxes_.getLongestGridBoxDiagonal() + targetBoxes_.getLongestGridBoxDiagonal(); failures_.clear(); if (matrixFree_) { return; } std::vector allTriplets; { ATLAS_TRACE("GridBoxMethod::setup: intersecting grid boxes"); constexpr double TIMED = 5.; eckit::ProgressTimer progress("Intersecting", targetBoxes_.size(), "grid box", TIMED); std::vector triplets; size_t i = 0; for (auto p : tgt.iterate().lonlat()) { ++progress; if (intersect(i, targetBoxes_.at(i), pTree_.closestPointsWithinRadius(p, searchRadius_), triplets)) { std::copy(triplets.begin(), triplets.end(), std::back_inserter(allTriplets)); } ++i; } if (!failures_.empty()) { giveUp(failures_); } } { ATLAS_TRACE("GridBoxMethod::setup: build interpolant matrix"); setMatrix(targetBoxes_.size(), sourceBoxes_.size(), allTriplets); } } void GridBoxMethod::do_setup(const FunctionSpace& source, const FunctionSpace& target, const Cache& cache) { ATLAS_TRACE("GridBoxMethod::setup()"); if (not matrixFree_ && interpolation::MatrixCache(cache)) { setMatrix(cache); source_ = source; target_ = target; ATLAS_ASSERT(matrix().rows() == target.size()); ATLAS_ASSERT(matrix().cols() == source.size()); return; } Log::warning() << "Can not create GridBoxMethod from (FunctionSpace, FunctionSpace, Cache). Use (Grid, Grid, Cache)"; ATLAS_NOTIMPLEMENTED; } void GridBoxMethod::giveUp(const std::forward_list& failures) { Log::warning() << "Failed to intersect grid boxes: "; constexpr int COUNTED = 10; int count = 0; auto sep = ""; for (const auto& f : failures) { if (count++ < COUNTED) { Log::warning() << sep << f; sep = ", "; } } Log::warning() << "... (" << eckit::Plural(count, "total failure") << std::endl; throw_Exception("Failed to intersect grid boxes"); } Cache GridBoxMethod::createCache() const { Cache cache; cache.add(interpolation::IndexKDTreeCache(pTree_)); if (not matrix().empty()) { cache.add(Method::createCache()); } return cache; } } // namespace method } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/method/knn/NearestNeighbour.cc0000664000175000017500000000777015160212070025501 0ustar alastairalastair/* * (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/interpolation/method/knn/NearestNeighbour.h" #include #include "eckit/log/Plural.h" #include "eckit/types/FloatCompare.h" #include "atlas/array.h" #include "atlas/functionspace/NodeColumns.h" #include "atlas/grid.h" #include "atlas/interpolation/method/MethodFactory.h" #include "atlas/mesh/Nodes.h" #include "atlas/mesh/actions/BuildXYZField.h" #include "atlas/meshgenerator.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" namespace atlas { namespace interpolation { namespace method { namespace { MethodBuilder __builder("nearest-neighbour"); } // namespace void NearestNeighbour::do_setup(const FunctionSpace& source, const FunctionSpace& target, const Cache& cache) { if (interpolation::MatrixCache(cache)) { setMatrix(cache); source_ = source; target_ = target; } else { do_setup(source, target); } } void NearestNeighbour::do_setup(const Grid& source, const Grid& target, const Cache&) { if (mpi::size() > 1) { ATLAS_NOTIMPLEMENTED; } auto functionspace = [](const Grid& grid) -> FunctionSpace { Mesh mesh; if (StructuredGrid(grid)) { mesh = MeshGenerator("structured", util::Config("3d", true)).generate(grid); } else { mesh = MeshGenerator("delaunay").generate(grid); } return functionspace::NodeColumns(mesh); }; do_setup(functionspace(source), functionspace(target)); } void NearestNeighbour::do_setup(const FunctionSpace& source, const FunctionSpace& target) { source_ = source; target_ = target; // build point-search tree buildPointSearchTree(source); array::ArrayView lonlat = array::make_view(target.lonlat()); size_t inp_npts = source.size(); size_t out_npts = target.size(); // return early if no output points on this partition reserve is called on // the triplets but also during the sparseMatrix constructor. This won't // work for empty matrices if (out_npts == 0) { return; } // fill the sparse matrix std::vector weights_triplets; weights_triplets.reserve(out_npts); { Trace timer(Here(), "atlas::interpolation::method::NearestNeighbour::do_setup()"); for (size_t ip = 0; ip < out_npts; ++ip) { if (ip && (ip % 1000 == 0)) { timer.pause(); auto elapsed = timer.elapsed(); timer.resume(); auto rate = eckit::types::is_approximately_equal(elapsed, 0.) ? std::numeric_limits::infinity() : (ip / elapsed); Log::debug() << eckit::BigNum(ip) << " (at " << rate << " points/s)... after " << elapsed << " s" << std::endl; } // find the closest input point to the output point auto nn = pTree_.closestPoint(PointLonLat{lonlat(ip, size_t(LON)), lonlat(ip, size_t(LAT))}); size_t jp = nn.payload(); // insert the weights into the interpolant matrix ATLAS_ASSERT(jp < inp_npts, "point found which is not covered within the halo of the source function space"); weights_triplets.emplace_back(ip, jp, 1); } } // fill sparse matrix and return setMatrix( out_npts, inp_npts, weights_triplets ); } } // namespace method } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/method/knn/GridBox.h0000664000175000017500000000404715160212070023427 0ustar alastairalastair/* * (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 namespace atlas { class Grid; } namespace atlas { namespace interpolation { namespace method { class GridBox { public: // -- Exceptions // None // -- Constructors GridBox(double north, double west, double south, double east); // -- Destructor // None // -- Convertors // None // -- Operators // None // -- Methods double area() const; double diagonal() const; bool intersects(GridBox&) const; double north() const { return north_; } double west() const { return west_; } double south() const { return south_; } double east() const { return east_; } // -- Overridden methods // None // -- Class members // None // -- Class methods // None protected: // -- Members // None // -- Methods // None // -- Overridden methods // None // -- Class members // None // -- Class methods // None // -- Friends // None private: // -- Members double north_; double west_; double south_; double east_; // -- Methods void print(std::ostream&) const; // -- Overridden methods // None // -- Class members // None // -- Class methods // None // -- Friends friend std::ostream& operator<<(std::ostream& s, const GridBox& p) { p.print(s); return s; } }; struct GridBoxes : std::vector { GridBoxes(const Grid&, bool gaussianWeightedLatitudes = true); GridBoxes(); double getLongestGridBoxDiagonal() const; }; } // namespace method } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/method/binning/0000775000175000017500000000000015160212070022551 5ustar alastairalastairatlas-0.46.0/src/atlas/interpolation/method/binning/Binning.h0000664000175000017500000000541515160212070024313 0ustar alastairalastair/* * (C) Crown Copyright 2024 Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ #pragma once #include #include "atlas/functionspace/FunctionSpace.h" #include "atlas/interpolation/Cache.h" #include "atlas/interpolation/method/Intersect.h" #include "atlas/interpolation/method/Method.h" #include "atlas/linalg/sparse/SparseMatrixStorage.h" #include "atlas/linalg/sparse/SparseMatrixView.h" #include "atlas/grid/Grid.h" #include "eckit/config/Configuration.h" namespace atlas { namespace interpolation { namespace method { class Binning : public Method { public: /// @brief Binning procedure to carry out a regridding from high /// to low resolution /// /// @details This method is part of the family of the Interpolation operations; /// it relies on the evaluation of the transpose of an interpolation matrix. /// The rigridding from high to low resolution is carried out by using /// a 'binning matrix': /// binning matrix: B = N A^T W /// area weights matrix: W /// interpolation matrix: A /// normalization factor matrix: N /// /// Setup, configuration variables: /// : method used to evaluate the 'B' matrix; /// value: 'binning' /// : method used to evaluate the 'A' matrix; /// value: 'cubedsphere-bilinear', 'structured-bilinear', ... /// : flag to control the adjoint operation /// value: 'true', 'false' /// using ValueType = double; using IndexType = int; using SparseMatrixStorage = linalg::SparseMatrixStorage; using SparseMatrixView = linalg::SparseMatrixView; Binning(const Config& config); ~Binning() override {} void print(std::ostream&) const override; const FunctionSpace& source() const override { return source_; } const FunctionSpace& target() const override { return target_; } private: using Method::do_setup; void do_setup(const FunctionSpace& source, const FunctionSpace& target) override; void do_setup(const Grid& source, const Grid& target, const Cache&) override; void do_setup(const FunctionSpace& source, const FunctionSpace& target, const Cache&) override; SparseMatrixStorage transposeAndHaloExchange(const SparseMatrixView& interpMatrix) const; std::vector getAreaWeights() const; eckit::LocalConfiguration interpAncillaryScheme_{}; FunctionSpace source_{}; FunctionSpace target_{}; }; } // namespace method } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/method/binning/Binning.cc0000664000175000017500000002260615160212070024452 0ustar alastairalastair/* * (C) Crown Copyright 2024 Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ #include "atlas/interpolation/method/binning/Binning.h" #include #include #include #include #include #include #include "atlas/functionspace/FunctionSpace.h" #include "atlas/functionspace/NodeColumns.h" #include "atlas/grid.h" #include "atlas/grid/CubedSphereGrid.h" #include "atlas/interpolation/Cache.h" #include "atlas/interpolation/Interpolation.h" #include "atlas/interpolation/method/MethodFactory.h" #include "atlas/library/config.h" #include "atlas/linalg/sparse/SparseMatrixTriplet.h" #include "atlas/mesh/actions/GetCubedSphereNodalArea.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Trace.h" #include "eckit/config/LocalConfiguration.h" namespace atlas { namespace interpolation { namespace method { namespace { MethodBuilder __builder("binning"); using TripletType = linalg::Triplet; static_assert(std::is_trivially_copyable_v); class MpiBuffer { public: MpiBuffer(): buffer_{mpi::comm().size()} {} std::size_t size(std::size_t rank) const { const auto n = buffer_.at(rank).size(); ATLAS_ASSERT(n % sizeof(TripletType) == 0, "Buffer size not a multiple of Triplet size"); return n / sizeof(TripletType); } void pushBack(std::size_t rank, const TripletType& triplet) { auto& subBuffer = buffer_.at(rank); alignas(TripletType) auto appendBuffer = std::array{}; std::memcpy(appendBuffer.data(), &triplet, sizeof(TripletType)); subBuffer.insert(subBuffer.end(), appendBuffer.begin(), appendBuffer.end()); } auto get(std::size_t rank) const { const auto& subBuffer = buffer_.at(rank); return [&subBuffer](size_t index) -> TripletType { TripletType triplet{}; std::memcpy(&triplet, subBuffer.data() + index * sizeof(TripletType), sizeof(TripletType)); return triplet; }; } MpiBuffer allToAll() const { auto recvBuffer = MpiBuffer{}; mpi::comm().allToAll(buffer_, recvBuffer.buffer_); return recvBuffer; } private: std::vector> buffer_{}; }; } // namespace Binning::Binning(const Config& config): Method(config) { const auto* conf = dynamic_cast(&config); ATLAS_ASSERT(conf, "config must be derived from eckit::LocalConfiguration"); interpAncillaryScheme_ = conf->getSubConfiguration("scheme"); } void Binning::do_setup(const Grid& source, const Grid& target, const Cache&) { ATLAS_NOTIMPLEMENTED; } void Binning::do_setup(const FunctionSpace& source, const FunctionSpace& target, const Cache&) { ATLAS_NOTIMPLEMENTED; } void Binning::do_setup(const FunctionSpace& source, const FunctionSpace& target) { ATLAS_TRACE("atlas::interpolation::method::Binning::do_setup()"); source_ = source; target_ = target; if (target_.size() == 0) { return; } const auto interp = Interpolation(interpAncillaryScheme_, target_, source_); const auto interpMatrixStorage = interpolation::MatrixCache(interp).matrix(); const auto interpMatrixView = linalg::make_host_view(interpMatrixStorage); auto transposeMatrixStorage = transposeAndHaloExchange(interpMatrixView); auto transposeMatrixView = linalg::make_host_view(transposeMatrixStorage); const IndexType transposeRows = transposeMatrixView.rows(); const IndexType transposeCols = transposeMatrixView.cols(); const auto areaWeightsVector = getAreaWeights(); auto triplets = std::vector{}; triplets.reserve(transposeMatrixView.nnz()); const auto weightedValue = [&](IndexType col, ValueType value) { return value * areaWeightsVector.at(col); }; // Area-weight and normalise rows. for (std::size_t rowIdx = 0; rowIdx < transposeRows; ++rowIdx) { ValueType rowSum = 0.; linalg::sparse_matrix_for_each_row(rowIdx, transposeMatrixView, [&](IndexType row, IndexType col, ValueType value) { rowSum += weightedValue(col, value); }); linalg::sparse_matrix_for_each_row(rowIdx, transposeMatrixView, [&](IndexType row, IndexType col, ValueType value) { triplets.emplace_back(row, col, weightedValue(col, value) / rowSum); }); } setMatrix(linalg::make_sparse_matrix_storage_from_triplets(transposeRows, transposeCols, triplets)); } void Binning::print(std::ostream&) const { ATLAS_NOTIMPLEMENTED; } Binning::SparseMatrixStorage Binning::transposeAndHaloExchange(const SparseMatrixView& interpMatrix) const { // Transpose interpolation matrix. If transposed matrix contains rows which map into ghost elements, // move the rows to the PE which owns the element. struct Views { Views(const FunctionSpace& functionSpace): partition{array::make_view(functionSpace.partition())}, remote_index{array::make_indexview(functionSpace.remote_index())}, ghost{array::make_view(functionSpace.ghost())} {} array::ArrayView partition; array::IndexView remote_index; array::ArrayView ghost; }; const Views sourceViews{source_}; const Views targetViews{target_}; // MPI send buffers. auto triplets = std::vector{}; auto sendBuffer = MpiBuffer{}; linalg::sparse_matrix_for_each(interpMatrix, [&](IndexType row, IndexType col, ValueType weight) { // Swap row and column indices for transpose. std::swap(row, col); // Skip rows of interp matrix (i.e., cols of this transposed matrix) that are made redundant by halo exchanges. // These shouldn't exist, but some interpolation methods generate them anyway. if (sourceViews.ghost(col)) { return; } if (!targetViews.ghost(row)) { // Rows is owned by target function space. triplets.emplace_back(row, col, weight); } else { // Create MPI send data buffers for non-owned rows. const int remoteRank = targetViews.partition(row); const IndexType remoteRow = static_cast(targetViews.remote_index(row)); sendBuffer.pushBack(remoteRank, {remoteRow, col, weight}); } }); const auto recvBuffer = sendBuffer.allToAll(); // Create a map from source partition and remote index to local index for ghost points only. auto sourceRemoteToLocalMap = std::map, idx_t>{}; for (idx_t localCol = 0; localCol < source_.size(); ++localCol) { if (sourceViews.ghost(localCol)) { const int remoteRank = sourceViews.partition(localCol); const idx_t remoteCol = sourceViews.remote_index(localCol); sourceRemoteToLocalMap.insert({{remoteRank, remoteCol}, localCol}); } } // Iterate over received data, creating owned rows for ghost columns for (std::size_t remoteRank = 0; remoteRank < mpi::comm().size(); ++remoteRank) { const auto subBuffer = recvBuffer.get(remoteRank); const auto subBufferSize = recvBuffer.size(remoteRank); for (std::size_t i = 0; i < subBufferSize; ++i) { const auto triplet = subBuffer(i); const IndexType row = triplet.row(); const IndexType remoteCol = triplet.col(); const ValueType weight = triplet.value(); ATLAS_ASSERT_MSG(!targetViews.ghost(row), "Row should be owned by the target functionspace"); const auto col = [&] { try { return sourceRemoteToLocalMap.at({remoteRank, remoteCol}); } catch (const std::out_of_range&) { ATLAS_THROW_EXCEPTION( "Column local index not found. Try increasing source functionspace halo size."); } }(); ATLAS_ASSERT_MSG(sourceViews.ghost(col), "Column should not be owned by the source functionspace."); triplets.emplace_back(row, col, weight); } } return linalg::make_sparse_matrix_storage_from_triplets( static_cast(target_.size()), static_cast(source_.size()), triplets); } std::vector Binning::getAreaWeights() const { const auto csGrid = CubedSphereGrid(source_.grid()); auto ncFunctionSpace = functionspace::NodeColumns(source_); if (!(csGrid && ncFunctionSpace)) { // If not a cubed sphere grid and a node columns function space, return equal weights. return std::vector(source_.size(), 1.); } auto mesh = ncFunctionSpace.mesh(); const auto nodeWeights = mesh::actions::GetCubedSphereNodalArea()(mesh); const auto nodeWeightsView = array::make_view(nodeWeights); auto weightVector = std::vector(source_.size()); for (idx_t i = 0; i < source_.size(); ++i) { weightVector[i] = nodeWeightsView(i); } return weightVector; } } // namespace method } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/method/PointIndex3.h0000664000175000017500000000500315160212070023440 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include "eckit/container/KDMapped.h" #include "eckit/container/KDMemory.h" #include "eckit/container/KDTree.h" #include "eckit/geometry/Point3.h" #include "atlas/field/Field.h" #include "atlas/mesh/Mesh.h" #include "atlas/util/CoordinateEnums.h" namespace atlas { namespace interpolation { namespace method { //---------------------------------------------------------------------------------------------------------------------- template class PointKdTree : public eckit::KDTreeMemory { public: typedef eckit::KDTreeMemory Tree; typedef typename Tree::Alloc Alloc; typedef typename Tree::NodeInfo NodeInfo; typedef typename Tree::NodeList NodeList; typedef typename Tree::PayloadType Payload; typedef typename Tree::Point Point; typedef typename Tree::Value Value; PointKdTree(): eckit::KDTreeMemory() {} PointKdTree(Alloc& alloc): Tree(alloc) {} using Tree::findInSphere; using Tree::kNearestNeighbours; using Tree::nearestNeighbour; using Tree::findInSphereBruteForce; using Tree::kNearestNeighboursBruteForce; using Tree::nearestNeighbourBruteForce; }; //---------------------------------------------------------------------------------------------------------------------- struct PointIndex3TreeTrait { typedef eckit::geometry::Point3 Point; typedef size_t Payload; }; typedef PointKdTree PointIndex3; //---------------------------------------------------------------------------------------------------------------------- struct ElemIndex3TreeTrait { typedef eckit::geometry::Point3 Point; typedef size_t Payload; }; typedef PointKdTree ElemIndex3; ElemIndex3* create_element_kdtree(const Mesh& mesh, const Field& field_centres); // TODO: remove this function, and use "create_element_kdtree(const Field&)" // instead. ElemIndex3* create_element_centre_index(const Mesh& mesh); //---------------------------------------------------------------------------------------------------------------------- } // namespace method } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/method/PointSet.h0000664000175000017500000001152615160212070023050 0ustar alastairalastair/* * (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 #include #include #include #include #include "eckit/config/Resource.h" #include "eckit/types/FloatCompare.h" #include "atlas/interpolation/method/PointIndex3.h" #include "atlas/mesh/Mesh.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Trace.h" namespace atlas { namespace interpolation { namespace method { //---------------------------------------------------------------------------------------------------------------------- class PointSet { public: // types typedef PointIndex3::Point Point; typedef PointIndex3::iterator iterator; typedef std::map DupStore_t; public: // methods PointSet(const std::vector& ipts); PointSet(atlas::Mesh& mesh); ~PointSet() { delete tree_; } DupStore_t& duplicates() { return duplicates_; } template void list_unique_points(std::vector& opts) { ATLAS_TRACE("Finding unique points"); ATLAS_ASSERT(opts.empty()); opts.reserve(npts_); for (PointIndex3::iterator i = tree_->begin(); i != tree_->end(); ++i) { Point p(i->point()); size_t ip = i->payload(); // std::cout << "point " << ip << " " << p << std::endl; size_t uidx = unique(p, ip); if (ip == uidx) { opts.push_back(POINT_T(p.data())); // std::cout << "----> UNIQ " << ip << std::endl; } else { // std::cout << "----> DUP " << ip << " -> " << uidx << // std::endl; } // ++show_progress; } } void list_unique_points(std::vector& opts) { ATLAS_TRACE("Finding unique points"); ATLAS_ASSERT(opts.empty()); opts.reserve(npts_); for (PointIndex3::iterator i = tree_->begin(); i != tree_->end(); ++i) { Point p(i->point()); size_t ip = i->payload(); // std::cout << "point " << ip << " " << p << std::endl; size_t uidx = unique(p, ip); if (ip == uidx) { opts.push_back(ip); // std::cout << "----> UNIQ " << ip << std::endl; } else { // std::cout << "----> DUP " << ip << " -> " << uidx << // std::endl; } // ++show_progress; } } size_t unique(const Point& p, size_t idx = std::numeric_limits::max()) { DupStore_t::iterator dit = duplicates_.find(idx); if (dit != duplicates_.end()) { // std::cout << " !! DUPLICATE !!" << std::endl; return dit->second; } else return this->search_unique(p, idx, 0); } iterator begin() { return tree_->begin(); } iterator end() { return tree_->end(); } size_t size() const { return npts_; } protected: // methods template void build(const V& ipts) { static bool fastBuildKDTrees = eckit::Resource("$ATLAS_FAST_BUILD_KDTREES", true); tree_ = new PointIndex3(); if (fastBuildKDTrees) { std::vector pidx; pidx.reserve(npts_); for (size_t ip = 0; ip < npts_; ++ip) { pidx.push_back(PointIndex3::Value(PointIndex3::Point(ipts[ip]), ip)); } tree_->build(pidx.begin(), pidx.end()); } else { for (size_t ip = 0; ip < npts_; ++ip) { tree_->insert(PointIndex3::Value(PointIndex3::Point(ipts[ip]), ip)); } } } size_t search_unique(const Point& p, size_t idx, uint32_t n); protected: size_t Kn(uint32_t n) { if (!n) return 2; else return 2 + std::pow(2, n) * 180; } bool duplicate(size_t idx) { return duplicates_.find(idx) != duplicates_.end(); } private: size_t npts_; PointIndex3* tree_; DupStore_t duplicates_; ///< map from duplicate idx to idx representing group /// of points }; //---------------------------------------------------------------------------------------------------------------------- } // namespace method } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/method/MethodFactory.h0000664000175000017500000000244415160212070024052 0ustar alastairalastair/* * (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 #include "atlas/interpolation/method/Method.h" #include "atlas/util/Factory.h" #include "atlas/util/Object.h" #include "eckit/config/Configuration.h" namespace atlas { class Field; class FieldSet; class FunctionSpace; class Grid; } // namespace atlas namespace atlas { namespace interpolation { struct MethodFactory : public util::Factory { public: static std::string className() { return "MethodFactory"; } static Method* build(const std::string& name, const Method::Config&); protected: virtual Method* make(const Method::Config&) = 0; using Factory::Factory; }; template struct MethodBuilder : public MethodFactory { using MethodFactory::MethodFactory; private: virtual Method* make(const Method::Config& config) { return new T(config); } }; } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/method/Method.cc0000664000175000017500000006034715160212070022666 0ustar alastairalastair/* * (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/interpolation/method/Method.h" #include "atlas/array.h" #include "atlas/field/Field.h" #include "atlas/field/FieldSet.h" #include "atlas/field/MissingValue.h" #include "atlas/functionspace/NodeColumns.h" #include "atlas/linalg/sparse.h" #include "atlas/mesh/Nodes.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Log.h" #include "atlas/runtime/Trace.h" using namespace atlas::linalg; namespace atlas { namespace interpolation { namespace { template void set_missing_values_rank1(Field& tgt, const std::vector& missing, const Value& missing_value) { auto tgt_v = array::make_view(tgt); for (auto i : missing) { tgt_v(i) = missing_value; } } template void set_missing_values_rank2(Field& tgt, const std::vector& missing, const Value& missing_value) { auto tgt_v = array::make_view(tgt); const idx_t Nj = tgt_v.shape(1); for (auto i : missing) { for (idx_t j = 0; j < Nj; ++j) { tgt_v(i, j) = missing_value; } } } template void set_missing_values_rank3(Field& tgt, const std::vector& missing, const Value& missing_value) { auto tgt_v = array::make_view(tgt); const idx_t Nj = tgt_v.shape(1); const idx_t Nk = tgt_v.shape(2); for (auto i : missing) { for (idx_t j = 0; j < Nj; ++j) { for (idx_t k = 0; k < Nk; ++k) { tgt_v(i, j, k) = missing_value; } } } } template void set_missing_values_T(Field& tgt, const std::vector& missing) { Value missing_value = tgt.metadata().get("missing_value"); if (tgt.rank() == 1) { set_missing_values_rank1(tgt, missing, missing_value); } else if (tgt.rank() == 2) { set_missing_values_rank2(tgt, missing, missing_value); } else if (tgt.rank() == 3) { set_missing_values_rank3(tgt, missing, missing_value); } else { ATLAS_NOTIMPLEMENTED; } } void set_missing_values(Field& tgt, const std::vector& missing) { if (missing.empty()) { return; } if (tgt.datatype().kind() == array::DataType::KIND_REAL64) { set_missing_values_T(tgt, missing); } else if (tgt.datatype().kind() == array::DataType::KIND_REAL32) { set_missing_values_T(tgt, missing); } else { ATLAS_NOTIMPLEMENTED; } } bool executesOnDevice(const sparse::Backend& backend) { if (backend.type() == "eckit_linalg") { return false; } else if (backend.type() == "openmp") { return false; } else if (backend.type() == "hicsparse") { return true; } else { ATLAS_NOTIMPLEMENTED; } } template atlas::array::ArrayView make_device_view_rw(atlas::Field& f) { f.syncDevice(); return atlas::array::make_device_view(f); } template atlas::array::ArrayView make_device_view_w(atlas::Field& f) { if (not f.deviceAllocated()) { f.allocateDevice(); } f.setDeviceNeedsUpdate(false); // as it will be written to here return atlas::array::make_device_view(f); } template atlas::array::ArrayView make_device_view_r(const atlas::Field& f) { f.syncDevice(); return atlas::array::make_device_view(f); } template atlas::array::ArrayView make_host_view_rw(atlas::Field& f) { f.syncHost(); return atlas::array::make_host_view(f); } template atlas::array::ArrayView make_host_view_r(const atlas::Field& f) { f.syncHost(); return atlas::array::make_host_view(f); } template atlas::array::ArrayView make_host_view_w(atlas::Field& f) { f.setHostNeedsUpdate(false); // as it will be written to return atlas::array::make_host_view(f); } template atlas::linalg::SparseMatrixView make_device_view_r(const atlas::linalg::SparseMatrixStorage& m) { if (m.deviceNeedsUpdate()) { ATLAS_TRACE("Copy interpolation matrix to device"); m.updateDevice(); } return make_device_view(m); } template atlas::linalg::SparseMatrixView make_host_view_r(const atlas::linalg::SparseMatrixStorage& m) { m.syncHost(); return make_host_view(m); } } // anonymous namespace template void Method::interpolate_field_rank1(const Field& src, Field& tgt, const Matrix& W) const { auto backend = sparse::Backend{linalg_backend_}; if (backend.type() == "hicsparse" && !std::is_same::value) { ATLAS_NOTIMPLEMENTED; // hicsparse does not support mixed double-float } if (backend.type() == "eckit_linalg" && std::is_same::value) { // Switch to OpenMP as eckit_linalg does not support float backend = sparse::backend::openmp(); } const auto on_device = executesOnDevice(backend); auto src_v = on_device ? make_device_view_r(src) : make_host_view_r(src); auto tgt_v = on_device ? make_device_view_w(tgt) : make_host_view_w(tgt); if (nonLinear_(src)) { eckit::linalg::SparseMatrix W_copy = atlas::linalg::make_eckit_sparse_matrix(W); nonLinear_->execute(W_copy, src); auto W_nl = make_sparse_matrix_storage(std::move(W_copy)); auto W_nl_v = on_device ? make_device_view_r(W_nl) : make_host_view_r(W_nl); sparse_matrix_multiply(W_nl_v, src_v, tgt_v, backend); } else { auto W_v = on_device ? make_device_view_r(W) : make_host_view_r(W); sparse_matrix_multiply(W_v, src_v, tgt_v, backend); } on_device ? tgt.setHostNeedsUpdate(true) : tgt.setDeviceNeedsUpdate(true); } template void Method::interpolate_field_rank2(const Field& src, Field& tgt, const Matrix& W) const { auto backend = sparse::Backend{linalg_backend_}; if (backend.type() == "hicsparse" && !std::is_same::value) { ATLAS_NOTIMPLEMENTED; // hicsparse does not support mixed double-float } if (backend.type() == "eckit_linalg") { // Switch to OpenMP as eckit_linalg does not support this layout backend = sparse::backend::openmp(); } if (nonLinear_(src)) { // We cannot apply the same matrix to full columns as e.g. missing values could be present in only certain parts. // Allocate temporary rank-1 fields corresponding to one horizontal level auto src_slice = Field("s", array::make_datatype(), {src.shape(0)}); auto tgt_slice = Field("t", array::make_datatype(), {tgt.shape(0)}); // Copy metadata to the source rank-1 field src_slice.metadata() = src.metadata(); auto src_v = make_host_view_r(src); auto tgt_v = make_host_view_w(tgt); auto src_slice_v = array::make_host_view(src_slice); auto tgt_slice_v = array::make_host_view(tgt_slice); for (idx_t lev = 0; lev < src_v.shape(1); ++lev) { // Copy this level to temporary rank-1 field for (idx_t i = 0; i < src.shape(0); ++i) { src_slice_v(i) = src_v(i, lev); } // Interpolate between rank-1 fields interpolate_field_rank1(src_slice, tgt_slice, W); // Copy rank-1 field to this level in the rank-2 field tgt_slice.syncHost(); for (idx_t i = 0; i < tgt.shape(0); ++i) { tgt_v(i, lev) = tgt_slice_v(i); } } tgt.setDeviceNeedsUpdate(true); tgt.setHostNeedsUpdate(false); } else { const auto on_device = executesOnDevice(backend); auto W_v = on_device ? make_device_view_r(W) : make_host_view_r(W); auto src_dv = on_device ? make_device_view_r(src) : make_host_view_r(src); auto tgt_dv = on_device ? make_device_view_w(tgt) : make_host_view_w(tgt); sparse_matrix_multiply(W_v, src_dv, tgt_dv, backend); on_device ? tgt.setHostNeedsUpdate(true) : tgt.setDeviceNeedsUpdate(true); } } template void Method::interpolate_field_rank3(const Field& src, Field& tgt, const Matrix& W) const { sparse::Backend backend{linalg_backend_}; if (backend.type() == "hicsparse") { ATLAS_NOTIMPLEMENTED; // hicsparse does not support rank-3 fields } if (backend.type() == "eckit_linalg") { // Switch to OpenMP as eckit_linalg does not support rank-3 fields backend = sparse::backend::openmp(); } auto W_v = make_host_view(W); auto src_v = make_host_view_r(src); auto tgt_v = make_host_view_w(tgt); if (not W.empty() && nonLinear_(src)) { ATLAS_ASSERT(false, "nonLinear interpolation not supported for rank-3 fields."); } sparse_matrix_multiply(W_v, src_v, tgt_v, backend); tgt.setHostNeedsUpdate(false); tgt.setDeviceNeedsUpdate(true); } template void Method::adjoint_interpolate_field_rank1(Field& src, const Field& tgt, const Matrix& W) const { auto backend = sparse::Backend{linalg_backend_}; if (backend.type() == "hicsparse" && !std::is_same::value) { ATLAS_NOTIMPLEMENTED; // hicsparse does not support mixed double-float } if (backend.type() == "eckit_linalg" && std::is_same::value) { // Switch to OpenMP as eckit_linalg does not support float backend = sparse::backend::openmp(); } const auto on_device = executesOnDevice(backend); auto src_v = on_device ? make_device_view_rw(src) : make_host_view_rw(src); auto tgt_v = on_device ? make_device_view_r(tgt) : make_host_view_r(tgt); auto W_v = on_device ? make_device_view_r(W) : make_host_view_r(W); sparse_matrix_multiply_add(W_v, tgt_v, src_v, backend); on_device ? src.setHostNeedsUpdate(true) : src.setDeviceNeedsUpdate(true); } template void Method::adjoint_interpolate_field_rank2(Field& src, const Field& tgt, const Matrix& W) const { auto backend = sparse::Backend{linalg_backend_}; if (backend.type() == "hicsparse" && !std::is_same::value) { ATLAS_NOTIMPLEMENTED; // hicsparse does not support mixed double-float } if (backend.type() == "eckit_linalg") { // Switch to OpenMP as eckit_linalg does not support this layout backend = sparse::backend::openmp(); } const auto on_device = executesOnDevice(backend); auto src_v = on_device ? make_device_view_rw(src) : make_host_view_rw(src); auto tgt_v = on_device ? make_device_view_r(tgt) : make_host_view_r(tgt); auto W_v = on_device ? make_device_view_r(W) : make_host_view_r(W); sparse_matrix_multiply_add(W_v, tgt_v, src_v, backend); on_device ? src.setHostNeedsUpdate(true) : src.setDeviceNeedsUpdate(true); } template void Method::adjoint_interpolate_field_rank3(Field& src, const Field& tgt, const Matrix& W) const { sparse::Backend backend{linalg_backend_}; if (backend.type() == "hicsparse") { ATLAS_NOTIMPLEMENTED; // hicsparse does not support rank-3 fields } if (backend.type() == "eckit_linalg") { // Switch to OpenMP as eckit_linalg does not support rank-3 fields backend = sparse::backend::openmp(); } auto src_v = make_host_view_rw(src); auto tgt_v = make_host_view_r(tgt); auto W_v = make_host_view_r(W); sparse_matrix_multiply_add(W_v, tgt_v, src_v, backend); src.setDeviceNeedsUpdate(true); } void Method::check_compatibility(const Field& src, const Field& tgt, const Matrix& W) const { ATLAS_ASSERT(src.datatype() == tgt.datatype()); ATLAS_ASSERT(src.rank() == tgt.rank()); ATLAS_ASSERT(src.levels() == tgt.levels()); ATLAS_ASSERT(src.variables() == tgt.variables()); ATLAS_ASSERT(!W.empty()); ATLAS_ASSERT(tgt.shape(0) >= static_cast(W.rows())); ATLAS_ASSERT(src.shape(0) >= static_cast(W.cols())); } template void Method::interpolate_field(const Field& src, Field& tgt, const Matrix& W) const { // do nothing if there are no observations to interpolate (W will be NULL // and would fail the compatibility check) if (tgt.shape(0) == 0) { return; } check_compatibility(src, tgt, W); if (src.rank() == 1) { interpolate_field_rank1(src, tgt, W); } else if (src.rank() == 2) { interpolate_field_rank2(src, tgt, W); } else if (src.rank() == 3) { interpolate_field_rank3(src, tgt, W); } else { ATLAS_NOTIMPLEMENTED; } } template void Method::adjoint_interpolate_field(Field& src, const Field& tgt, const Matrix& W) const { // do nothing if there are no observations to interpolate (W will be NULL // and would fail the compatibility check) if (tgt.shape(0) == 0) { return; } check_compatibility(tgt, src, W); if (src.rank() == 1) { adjoint_interpolate_field_rank1(src, tgt, W); } else if (src.rank() == 2) { adjoint_interpolate_field_rank2(src, tgt, W); } else if (src.rank() == 3) { adjoint_interpolate_field_rank3(src, tgt, W); } else { ATLAS_NOTIMPLEMENTED; } } Method::Method(const Method::Config& config) { config.get("sparse_matrix_multiply", linalg_backend_); // empty is allowed -> sparse::current_backend() std::string non_linear; if (config.get("non_linear", non_linear)) { nonLinear_ = NonLinear(non_linear, config); } config.get("adjoint", adjoint_ = false); } void Method::setup(const FunctionSpace& source, const FunctionSpace& target) { ATLAS_TRACE("atlas::interpolation::method::Method::setup(FunctionSpace, FunctionSpace)"); this->do_setup(source, target); if (adjoint_) { adjoint_matrix(); } } const Method::Matrix& Method::adjoint_matrix() const { if (not matrix_transpose_) { if (target().size() == 0) { matrix_transpose_ = std::make_unique(); // Empty matrix } else { ATLAS_ASSERT(matrix_); eckit::linalg::SparseMatrix matrix_copy = make_eckit_sparse_matrix(*matrix_); // Makes a copy! matrix_copy.transpose(); // transpose the copy in place matrix_transpose_ = std::make_unique(linalg::make_sparse_matrix_storage(std::move(matrix_copy))); // Move the copy into storage } } return *matrix_transpose_; } void Method::setup(const Grid& source, const Grid& target) { ATLAS_TRACE("atlas::interpolation::method::Method::setup(Grid, Grid)"); this->do_setup(source, target, Cache()); } void Method::setup(const FunctionSpace& source, const Field& target) { ATLAS_TRACE("atlas::interpolation::method::Method::setup(FunctionSpace, Field)"); this->do_setup(source, target); } void Method::setup(const FunctionSpace& source, const FieldSet& target) { ATLAS_TRACE("atlas::interpolation::method::Method::setup(FunctionSpace, FieldSet)"); this->do_setup(source, target); } void Method::setup(const Grid& source, const Grid& target, const Cache& cache) { ATLAS_TRACE("atlas::interpolation::method::Method::setup(Grid, Grid, Cache)"); this->do_setup(source, target, cache); } void Method::setup(const FunctionSpace& source, const FunctionSpace& target, const Cache& cache) { ATLAS_TRACE("atlas::interpolation::method::Method::setup(FunctionSpace, FunctionSpace, Cache)"); this->do_setup(source, target, cache); } Method::Metadata Method::execute(const FieldSet& source, FieldSet& target) const { ATLAS_TRACE("atlas::interpolation::method::Method::execute(FieldSet, FieldSet)"); Metadata metadata; this->do_execute(source, target, metadata); return metadata; } Method::Metadata Method::execute(const Field& source, Field& target) const { ATLAS_TRACE("atlas::interpolation::method::Method::execute(Field, Field)"); Metadata metadata; this->do_execute(source, target, metadata); return metadata; } Method::Metadata Method::execute_adjoint(FieldSet& source, const FieldSet& target) const { ATLAS_TRACE("atlas::interpolation::method::Method::execute_adjoint(FieldSet, FieldSet)"); Metadata metadata; this->do_execute_adjoint(source, target, metadata); return metadata; } Method::Metadata Method::execute_adjoint(Field& source, const Field& target) const { ATLAS_TRACE("atlas::interpolation::method::Method::execute_adjoint(Field, Field)"); Metadata metadata; this->do_execute_adjoint(source, target, metadata); return metadata; } void Method::do_setup(const FunctionSpace& /*source*/, const Field& /*target*/) { ATLAS_NOTIMPLEMENTED; } void Method::do_setup(const FunctionSpace& /*source*/, const FieldSet& /*target*/) { ATLAS_NOTIMPLEMENTED; } void Method::do_execute(const FieldSet& fieldsSource, FieldSet& fieldsTarget, Metadata& metadata) const { ATLAS_TRACE("atlas::interpolation::method::Method::do_execute()"); const idx_t N = fieldsSource.size(); ATLAS_ASSERT(N == fieldsTarget.size()); for (idx_t i = 0; i < fieldsSource.size(); ++i) { Method::do_execute(fieldsSource[i], fieldsTarget[i], metadata); } } void Method::do_execute(const Field& src, Field& tgt, Metadata&) const { ATLAS_TRACE("atlas::interpolation::method::Method::do_execute()"); if (src.hostNeedsUpdate() && src.deviceNeedsUpdate()) { throw_AssertionFailed("Inconsistent memory state flags - we will not be able to " "determine which memory space to perform the halo exchange on", Here()); } sparse::Backend backend{linalg_backend_}; const bool on_device = executesOnDevice(backend); haloExchange(src, on_device); if( matrix_ ) { // (matrix == nullptr) when a partition is empty if (src.datatype().kind() == array::DataType::KIND_REAL64) { interpolate_field(src, tgt, *matrix_); } else if (src.datatype().kind() == array::DataType::KIND_REAL32) { interpolate_field(src, tgt, *matrix_); } else { ATLAS_NOTIMPLEMENTED; } } // carry over missing value metadata if (not tgt.metadata().has("missing_value")) { field::MissingValue mv_src(src); if (mv_src) { mv_src.metadata(tgt); ATLAS_ASSERT(field::MissingValue(tgt)); } else if (not missing_.empty()) { if (not tgt.metadata().has("missing_value")) { tgt.metadata().set("missing_value", 9999.); } tgt.metadata().set("missing_value_type", "equals"); } } // set missing values on host if (not missing_.empty()) { if (on_device) { tgt.updateHost(); } set_missing_values(tgt, missing_); tgt.set_dirty(); tgt.setDeviceNeedsUpdate(true); } else { tgt.set_dirty(); on_device ? tgt.setHostNeedsUpdate(true) : tgt.setDeviceNeedsUpdate(true); } } void Method::do_execute_adjoint(FieldSet& fieldsSource, const FieldSet& fieldsTarget, Metadata& metadata) const { ATLAS_TRACE("atlas::interpolation::method::Method::do_execute_adjoint()"); const idx_t N = fieldsSource.size(); ATLAS_ASSERT(N == fieldsTarget.size()); for (idx_t i = 0; i < fieldsSource.size(); ++i) { Method::do_execute_adjoint(fieldsSource[i], fieldsTarget[i], metadata); } } void Method::do_execute_adjoint(Field& src, const Field& tgt, Metadata&) const { ATLAS_TRACE("atlas::interpolation::method::Method::do_execute_adjoint()"); if (src.hostNeedsUpdate() && src.deviceNeedsUpdate()) { throw_AssertionFailed("Inconsistent memory state flags - we will not be able to " "determine which memory space to perform the adjoint halo exchange on", Here()); } sparse::Backend backend{linalg_backend_}; if (nonLinear_(src)) { throw_NotImplemented("Adjoint interpolation only works for interpolation schemes that are linear", Here()); } if (not missing_.empty()) { throw_NotImplemented("Adjoint Interpolation does not work for fields that have missing data. ", Here()); } if (src.datatype().kind() == array::DataType::KIND_REAL64) { adjoint_interpolate_field(src, tgt, adjoint_matrix()); } else if (src.datatype().kind() == array::DataType::KIND_REAL32) { adjoint_interpolate_field(src, tgt, adjoint_matrix()); } else { ATLAS_NOTIMPLEMENTED; } src.set_dirty(); const bool on_device = executesOnDevice(backend); adjointHaloExchange(src, on_device); on_device ? src.setHostNeedsUpdate(true) : src.setDeviceNeedsUpdate(true); } void Method::normalise(Triplets& triplets) { // sum all calculated weights for normalisation double sum = 0.0; for (size_t j = 0; j < triplets.size(); ++j) { sum += triplets[j].value(); } // now normalise all weights according to the total const double invSum = 1.0 / sum; for (size_t j = 0; j < triplets.size(); ++j) { triplets[j].value() *= invSum; } } void Method::haloExchange(const FieldSet& fields, bool on_device) const { for (auto& field : fields) { haloExchange(field, on_device); } } void Method::haloExchange(const Field& field, bool on_device) const { if (field.dirty() && allow_halo_exchange_) { ATLAS_TRACE("haloExchange"); if (on_device) { if (field.deviceNeedsUpdate()) { // Prefer halo exchange on host and copy to device source().haloExchange(field); // on host field.syncDevice(); } else { source().haloExchange(field, on_device); // on device } } else { source().haloExchange(field); // on host } } } void Method::adjointHaloExchange(const FieldSet& fields, bool on_device) const { for (auto& field : fields) { adjointHaloExchange(field, on_device); } } void Method::adjointHaloExchange(const Field& field, bool on_device) const { if (field.dirty() && allow_halo_exchange_) { ATLAS_TRACE("adjointHaloExchange"); if (on_device) { if (field.deviceNeedsUpdate()) { // Prefer halo exchange on host and copy to device source().adjointHaloExchange(field); // on host field.syncDevice(); } else { source().adjointHaloExchange(field, on_device); // on device } } else { source().adjointHaloExchange(field); // on host } } } interpolation::Cache Method::createCache() const { return matrix_cache_; } } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/method/Ray.cc0000664000175000017500000000257415160212070022177 0ustar alastairalastair/* * (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/interpolation/method/Ray.h" //---------------------------------------------------------------------------------------------------------------------- namespace atlas { namespace interpolation { namespace method { Ray::Ray(const double* p) { orig = Vector3D::Map(p); dir = -orig; } Ray::Ray(const double* o, const double* d) { orig = Vector3D::Map(o); dir = Vector3D::Map(d); } Ray::Ray(const PointXYZ& p) { orig = Vector3D::Map(p.data()); dir = -orig; } Ray::Ray(const PointXYZ& o, const Vector3D& d) { orig = Vector3D::Map(o.data()); dir = d; } void Ray::print(std::ostream& s) const { s << "Ray[orig=" << orig << ",dir=" << dir << "]"; } std::ostream& operator<<(std::ostream& s, const Ray& p) { p.print(s); return s; } //---------------------------------------------------------------------------------------------------------------------- } // namespace method } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/method/PointIndex2.cc0000664000175000017500000000363615160212070023607 0ustar alastairalastair/* * (C) Crown Copyright 2021 Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ #include "eckit/config/Resource.h" #include "atlas/array/ArrayView.h" #include "atlas/array/MakeView.h" #include "atlas/interpolation/method/PointIndex2.h" #include "atlas/mesh/HybridElements.h" #include "atlas/runtime/Trace.h" #include "atlas/util/Topology.h" namespace atlas { namespace interpolation { namespace method { ElemIndex2* create_element2D_kdtree(const Mesh& mesh, const Field& field_centres) { ATLAS_TRACE(); const array::ArrayView centres = array::make_view(field_centres); const array::ArrayView flags = array::make_view(mesh.cells().flags()); auto include_element = [&](unsigned int e) { using util::Topology; return not Topology::view(flags(e)).check(Topology::INVALID); }; static bool fastBuildKDTrees = eckit::Resource("$ATLAS_FAST_BUILD_KDTREES", true); ElemIndex2* tree = new ElemIndex2(); const size_t nb_cells = centres.shape(0); if (fastBuildKDTrees) { std::vector p; p.reserve(nb_cells); for (unsigned int j = 0; j < nb_cells; ++j) { if (include_element(j)) { p.emplace_back(ElemIndex2::Point(centres(j, LON), centres(j, LAT)), ElemIndex2::Payload(j)); } } tree->build(p.begin(), p.end()); } else { for (unsigned int j = 0; j < nb_cells; ++j) { if (include_element(j)) { tree->insert( ElemIndex2::Value(ElemIndex2::Point(centres(j, LON), centres(j, LAT)), ElemIndex2::Payload(j))); } } } return tree; } } // namespace method } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/method/PointSet.cc0000664000175000017500000000654515160212070023213 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "eckit/config/Resource.h" #include "atlas/array/ArrayView.h" #include "atlas/array/MakeView.h" #include "atlas/field/Field.h" #include "atlas/interpolation/method/PointSet.h" #include "atlas/mesh/Mesh.h" #include "atlas/mesh/Nodes.h" #include "atlas/runtime/Exception.h" using namespace eckit; namespace atlas { namespace interpolation { namespace method { //---------------------------------------------------------------------------------------------------------------------- PointSet::PointSet(const std::vector& ipts): npts_(ipts.size()) { build(ipts); } PointSet::PointSet(Mesh& mesh) { mesh::Nodes& nodes = mesh.nodes(); npts_ = nodes.size(); ATLAS_ASSERT(npts_ > 0); ATLAS_ASSERT(nodes.has_field("xyz")); array::ArrayView coords = array::make_view(nodes.field("xyz")); static bool fastBuildKDTrees = eckit::Resource("$ATLAS_FAST_BUILD_KDTREES", true); tree_ = new PointIndex3(); if (fastBuildKDTrees) { std::vector pidx; pidx.reserve(npts_); for (size_t ip = 0; ip < npts_; ++ip) { pidx.emplace_back(PointIndex3::Point(coords(ip, (size_t)0), coords(ip, (size_t)1), coords(ip, (size_t)2)), ip); } tree_->build(pidx.begin(), pidx.end()); } else { for (size_t ip = 0; ip < npts_; ++ip) { tree_->insert(PointIndex3::Value( PointIndex3::Point(coords(ip, (size_t)0), coords(ip, (size_t)1), coords(ip, (size_t)2)), ip)); } } } size_t PointSet::search_unique(const Point& p, size_t idx, uint32_t n) { PointIndex3::NodeList nearest = tree_->kNearestNeighbours(p, Kn(n)); std::vector equals; equals.reserve(nearest.size()); for (size_t i = 0; i < nearest.size(); ++i) { Point np = nearest[i].value().point(); size_t nidx = nearest[i].value().payload(); // std::cout << " - " << nidx << " " << np << std::endl; if (eckit::geometry::points_equal(p, np)) { // std::cout << " EQUAL !!" << std::endl; equals.push_back(nidx); } else { break; } } if (equals.size() == nearest.size()) /* need to increase the search to find all duplicates of this point */ { return this->search_unique(p, idx, ++n); } else /* stop recursion */ { size_t ret = idx; /* if nothing found return same idx */ if (equals.size() >= 1) { /* if an equal point was found return the first one */ ret = equals[0]; } for (size_t n = 1; n < equals.size(); ++n) { duplicates_[equals[n]] = ret; } return ret; } } //---------------------------------------------------------------------------------------------------------------------- } // namespace method } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/method/Ray.h0000664000175000017500000000253415160212070022035 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include "atlas/interpolation/Vector3D.h" #include "atlas/util/Point.h" namespace atlas { namespace interpolation { namespace method { //---------------------------------------------------------------------------------------------------------------------- /// Ray trace data structure struct Ray { Vector3D orig; Vector3D dir; /// initializes ray with origin in point and direction to (0,0,0) explicit Ray(const double* p); Ray(const double* o, const double* d); explicit Ray(const PointXYZ&); Ray(const PointXYZ&, const Vector3D&); Vector3D operator()(double t) const { return orig + t * dir; } void print(std::ostream& s) const; friend std::ostream& operator<<(std::ostream& s, const Ray& p); }; //---------------------------------------------------------------------------------------------------------------------- } // namespace method } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/method/structured/0000775000175000017500000000000015160212070023331 5ustar alastairalastairatlas-0.46.0/src/atlas/interpolation/method/structured/kernels/0000775000175000017500000000000015160212070024774 5ustar alastairalastairatlas-0.46.0/src/atlas/interpolation/method/structured/kernels/QuasiCubic3DKernel.cc0000664000175000017500000000167015160212070030667 0ustar alastairalastair/* * (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. and Interpolation */ #include "QuasiCubic3DKernel.h" namespace atlas { namespace interpolation { namespace method { // Note: Following symbols should no longer be necessary from C++17 onwards constexpr std::array QuasiCubicLinearPoints::j; constexpr std::array QuasiCubicLinearPoints::jj; constexpr std::array QuasiCubicLinearPoints::jw; constexpr std::array QuasiCubicLinearPoints::i; constexpr std::array QuasiCubicLinearPoints::ii; } // namespace method } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/method/structured/kernels/QuasiCubic3DKernel.h0000664000175000017500000003710315160212070030531 0ustar alastairalastair/* * (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. and Interpolation */ #pragma once #include #include #include "atlas/array/ArrayView.h" #include "atlas/functionspace/StructuredColumns.h" #include "atlas/grid/Stencil.h" #include "atlas/grid/StencilComputer.h" #include "atlas/runtime/Exception.h" #include "atlas/util/CoordinateEnums.h" #include "atlas/util/Point.h" #include "Cubic3DLimiter.h" #include "CubicVerticalKernel.h" #include "LinearHorizontalKernel.h" #include "QuasiCubicHorizontalKernel.h" namespace atlas { namespace interpolation { namespace method { struct QuasiCubicLinearPoints { constexpr QuasiCubicLinearPoints() {} static constexpr std::array j{{1, 2}}; static constexpr std::array jj{{0, 3}}; static constexpr std::array jw{{4, 5}}; static constexpr std::array i{{1, 2}}; static constexpr std::array ii{{0, 3}}; }; class QuasiCubic3DKernel { using Limiter = Cubic3DLimiter; public: QuasiCubic3DKernel(const functionspace::StructuredColumns& fs, const util::Config& config = util::NoConfig()) { src_ = fs; ATLAS_ASSERT(src_); ATLAS_ASSERT(src_.halo() >= 2); ATLAS_ASSERT(src_.vertical().size()); quasi_cubic_horizontal_interpolation_ = QuasiCubicHorizontalKernel(src_, config); linear_horizontal_interpolation_ = LinearHorizontalKernel(src_, config); vertical_interpolation_ = CubicVerticalKernel(fs.vertical(), config); limiter_ = config.getBool("limiter", false); } private: functionspace::StructuredColumns src_; QuasiCubicHorizontalKernel quasi_cubic_horizontal_interpolation_; LinearHorizontalKernel linear_horizontal_interpolation_; CubicVerticalKernel vertical_interpolation_; bool limiter_{false}; public: static std::string className() { return "QuasiCubic3DKernel"; } static constexpr idx_t stencil_width() { return 4; } static constexpr idx_t stencil_size() { return 32; } static constexpr idx_t stencil_halo() { return static_cast(static_cast(stencil_width()) / 2. + 0.5); } public: using Stencil = grid::Stencil3D<4>; struct Weights { std::array, 4> weights_i; std::array weights_j; std::array weights_k; struct LinearWeights { std::array, 2> weights_i; std::array weights_j; } linear; }; public: struct WorkSpace { Stencil stencil; Weights weights; }; template void compute_stencil(double& x, const double y, const double z, stencil_t& stencil) const { quasi_cubic_horizontal_interpolation_.compute_stencil(x, y, stencil); vertical_interpolation_.compute_stencil(z, stencil); } template void compute_weights(double x, double y, double z, weights_t& weights) const { Stencil stencil; compute_stencil(x, y, z, stencil); compute_weights(x, y, z, stencil, weights); } template void compute_weights(const double x, const double y, const double z, const stencil_t& stencil, weights_t& weights) const { quasi_cubic_horizontal_interpolation_.compute_weights(x, y, stencil, weights); // Insert more linear weights in available slots (weights_i[0], weights_i[3], weights_j[4], weights_j[5]) { PointXY P1, P2; std::array yvec; constexpr QuasiCubicLinearPoints pts{}; // Top and bottom row x-direction for (idx_t l = 0; l < 2; ++l) { idx_t j = pts.j[l]; // index in stencil idx_t jj = pts.jj[l]; // row index in weights_i auto& weights_i = weights.weights_i[jj]; src_.compute_xy(stencil.i(pts.i[0], j), stencil.j(j), P1); src_.compute_xy(stencil.i(pts.i[1], j), stencil.j(j), P2); const double alpha = (P2.x() - x) / (P2.x() - P1.x()); weights_i[pts.ii[0]] = alpha; weights_i[pts.ii[1]] = 1. - alpha; yvec[l] = P1.y(); } // Compute weights in y-direction { auto& weights_j = weights.weights_j; const double alpha = (yvec[1] - y) / (yvec[1] - yvec[0]); weights_j[4] = alpha; weights_j[5] = 1. - alpha; } } vertical_interpolation_.compute_weights(z, stencil, weights); } template typename std::enable_if<(array_t::RANK == 2), typename array_t::value_type>::type interpolate( const stencil_t& stencil, const weights_t& weights, const array_t& input) const { using Value = typename std::remove_const::type; std::array, stencil_width()> index; const auto& wk = weights.weights_k; Value output = 0.; // Horizontally quasi-cubic part for inner levels ( k = {1,2} ) { // Inner levels, inner rows (cubic in i, cubic in j) --> 16 points const auto& wj = weights.weights_j; for (idx_t j = 1; j < 3; ++j) { // j = {1,2} const auto& wi = weights.weights_i[j]; for (idx_t i = 0; i < 4; ++i) { // i = {0,1,2,3} idx_t n = src_.index(stencil.i(i, j), stencil.j(j)); Value wij = wi[i] * wj[j]; for (idx_t k = 1; k < 3; ++k) { // k = {1,2} Value w = wij * wk[k]; output += w * input(n, stencil.k(k)); } index[j][i] = n; } } // Inner levels, outer rows: (linear in i, cubic in j) --> 8 points for (idx_t j = 0; j < 4; j += 3) { // j = {0,3} const auto& wi = weights.weights_i[j]; for (idx_t i = 1; i < 3; ++i) { // i = {1,2} idx_t n = src_.index(stencil.i(i, j), stencil.j(j)); Value wij = wi[i] * wj[j]; for (idx_t k = 1; k < 3; ++k) { // k = {1,2} Value w = wij * wk[k]; output += w * input(n, stencil.k(k)); } index[j][i] = n; } } } // Horizontally Linear part for outer levels ( k = {0,3} ) { constexpr QuasiCubicLinearPoints pts{}; // Outer levels: (linear in i, linear in j) -- > 8 points for (idx_t m = 0; m < 2; ++m) { idx_t j = pts.j[m]; // index in stencil ( j = {1,2} ) idx_t jj = pts.jj[m]; // row index in weights_i ( jj = {0,3} ) idx_t jw = pts.jw[m]; // jw = {4,5}; const auto& wi = weights.weights_i[jj]; const double wj = weights.weights_j[jw]; for (idx_t l = 0; l < 2; ++l) { idx_t i = pts.i[l]; // i = {1,2} idx_t ii = pts.ii[l]; // ii = {0,3} idx_t n = src_.index(stencil.i(i, j), stencil.j(j)); Value wij = wi[ii] * wj; for (idx_t k = 0; k < 4; k += 3) { // k = {0,3} Value w = wij * wk[k]; output += w * input(n, stencil.k(k)); } } } } if (limiter_) { Limiter::limit_scalar(output, index, stencil, input); } return output; } template struct OutputView1D { template Value& operator()(Int v) { return data_[v]; } template Value& operator[](Int v) { return data_[v]; } static constexpr int RANK{1}; OutputView1D(Value* data): data_(data) {} using value_type = Value; Value* data_; }; template OutputView1D make_outputview(Value* data) const { return OutputView1D(data); } template typename std::enable_if<(InputArray::RANK == 3), void>::type interpolate_vars(const stencil_t& stencil, const weights_t& weights, const InputArray& input, OutputArray& output, const idx_t nvar) const { using Value = typename OutputArray::value_type; std::array, stencil_width()> index; const auto& wk = weights.weights_k; const Value* _input_; for (idx_t v = 0; v < nvar; ++v) { output[v] = 0.; } // Horizontally quasi-cubic part for inner levels ( k = {1,2} ) { // Inner levels, inner rows (cubic in i, cubic in j) --> 16 points const auto& wj = weights.weights_j; for (idx_t j = 1; j < 3; ++j) { // j = {1,2} const auto& wi = weights.weights_i[j]; for (idx_t i = 0; i < 4; ++i) { // i = {0,1,2,3} idx_t n = src_.index(stencil.i(i, j), stencil.j(j)); Value wij = wi[i] * wj[j]; for (idx_t k = 1; k < 3; ++k) { // k = {1,2} Value w = wij * wk[k]; const idx_t kk = stencil.k(k); _input_ = &(input(n, kk, 0)); // Assumption that input.stride(2) == 1 for (idx_t v = 0; v < nvar; ++v) { output[v] += w * _input_[v]; } } index[j][i] = n; } } // Inner levels, outer rows: (linear in i, cubic in j) --> 8 points for (idx_t j = 0; j < 4; j += 3) { // j = {0,3} const auto& wi = weights.weights_i[j]; for (idx_t i = 1; i < 3; ++i) { // i = {1,2} idx_t n = src_.index(stencil.i(i, j), stencil.j(j)); Value wij = wi[i] * wj[j]; for (idx_t k = 1; k < 3; ++k) { // k = {1,2} Value w = wij * wk[k]; const idx_t kk = stencil.k(k); _input_ = &(input(n, kk, 0)); // Assumption that input.stride(2) == 1 for (idx_t v = 0; v < nvar; ++v) { output[v] += w * _input_[v]; } } index[j][i] = n; } } } // Horizontally Linear part for outer levels ( k = {0,3} ) { constexpr QuasiCubicLinearPoints pts{}; // Outer levels: (linear in i, linear in j) -- > 8 points for (idx_t m = 0; m < 2; ++m) { idx_t j = pts.j[m]; // index in stencil ( j = {1,2} ) idx_t jj = pts.jj[m]; // row index in weights_i ( jj = {0,3} ) idx_t jw = pts.jw[m]; // jw = {4,5}; const auto& wi = weights.weights_i[jj]; const double wj = weights.weights_j[jw]; for (idx_t l = 0; l < 2; ++l) { idx_t i = pts.i[l]; // i = {1,2} idx_t ii = pts.ii[l]; // ii = {0,3} idx_t n = src_.index(stencil.i(i, j), stencil.j(j)); Value wij = wi[ii] * wj; for (idx_t k = 0; k < 4; k += 3) { // k = {0,3} Value w = wij * wk[k]; const idx_t kk = stencil.k(k); _input_ = &(input(n, kk, 0)); // Assumption that input.stride(2) == 1 for (idx_t v = 0; v < nvar; ++v) { output[v] += w * _input_[v]; } } } } } if (limiter_) { Limiter::limit_vars(index, stencil, input, output, nvar); } } template typename std::enable_if<(InputArray::RANK == 2 && OutputArray::RANK == 1), void>::type interpolate( const stencil_t& stencil, const weights_t& weights, const InputArray& input, OutputArray& output, idx_t r) const { output(r) = interpolate(stencil, weights, input); } template typename std::enable_if<(InputArray::RANK == 2 && OutputArray::RANK == 2), void>::type interpolate( const stencil_t& stencil, const weights_t& weights, const InputArray& input, OutputArray& output, idx_t r, idx_t k) const { output(r, k) = interpolate(stencil, weights, input); } template typename std::enable_if<(InputArray::RANK == 3 && OutputArray::RANK == 3), void>::type interpolate( const stencil_t& stencil, const weights_t& weights, const InputArray& input, OutputArray& output, idx_t r, idx_t k) const { auto output_vars = make_outputview(&output(r, k, 0)); interpolate_vars(stencil, weights, input, output_vars, output.shape(2)); } template typename std::enable_if<(InputArray::RANK == 2 && OutputArray::RANK == 3), void>::type interpolate( const stencil_t&, const weights_t&, const InputArray&, OutputArray&, idx_t /*r*/, idx_t /*k*/) const { ATLAS_NOTIMPLEMENTED; } template typename std::enable_if<(InputArray::RANK == 3 && OutputArray::RANK == 1), void>::type interpolate( const stencil_t&, const weights_t&, const InputArray&, OutputArray&, idx_t /*r*/) const { ATLAS_NOTIMPLEMENTED; } template typename std::enable_if<(InputArray::RANK == 3 && OutputArray::RANK == 1), void>::type interpolate( const stencil_t&, const weights_t&, const InputArray&, OutputArray&, idx_t /*r*/, idx_t /*k*/) const { ATLAS_NOTIMPLEMENTED; } template typename std::enable_if<(InputArray::RANK == 3 && OutputArray::RANK == 2), void>::type interpolate( const stencil_t&, const weights_t&, const InputArray&, OutputArray&, idx_t /*r*/, idx_t /*k*/) const { ATLAS_NOTIMPLEMENTED; } }; // namespace method } // namespace method } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/method/structured/kernels/Cubic3DKernel.h0000664000175000017500000002257315160212070027533 0ustar alastairalastair/* * (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. and Interpolation */ #pragma once #include #include #include "atlas/array/ArrayView.h" #include "atlas/functionspace/StructuredColumns.h" #include "atlas/grid/Stencil.h" #include "atlas/grid/StencilComputer.h" #include "atlas/runtime/Exception.h" #include "atlas/util/CoordinateEnums.h" #include "atlas/util/Point.h" #include "Cubic3DLimiter.h" #include "CubicHorizontalKernel.h" #include "CubicVerticalKernel.h" namespace atlas { namespace interpolation { namespace method { class Cubic3DKernel { using Limiter = Cubic3DLimiter; public: Cubic3DKernel(const functionspace::StructuredColumns& fs, const util::Config& config = util::NoConfig()) { src_ = fs; ATLAS_ASSERT(src_); ATLAS_ASSERT(src_.halo() >= 2); ATLAS_ASSERT(src_.vertical().size()); horizontal_interpolation_ = CubicHorizontalKernel(src_, config); vertical_interpolation_ = CubicVerticalKernel(fs.vertical(), config); limiter_ = config.getBool("limiter", false); } private: functionspace::StructuredColumns src_; CubicHorizontalKernel horizontal_interpolation_; CubicVerticalKernel vertical_interpolation_; bool limiter_{false}; public: static std::string className() { return "Cubic3DKernel"; } static constexpr idx_t stencil_width() { return 4; } static constexpr idx_t stencil_size() { return stencil_width() * stencil_width() * stencil_width(); } static constexpr idx_t stencil_halo() { return static_cast(static_cast(stencil_width()) / 2. + 0.5); } public: using Stencil = grid::Stencil3D<4>; struct Weights { std::array, 4> weights_i; std::array weights_j; std::array weights_k; friend std::ostream& operator<<(std::ostream& out, const Weights& v) { ATLAS_NOTIMPLEMENTED; } }; public: struct WorkSpace { Stencil stencil; Weights weights; }; template void compute_stencil(double& x, const double y, const double z, stencil_t& stencil) const { horizontal_interpolation_.compute_stencil(x, y, stencil); vertical_interpolation_.compute_stencil(z, stencil); } template void compute_weights(double x, double y, double z, weights_t& weights) const { Stencil stencil; compute_stencil(x, y, z, stencil); compute_weights(x, y, z, stencil, weights); } template void compute_weights(const double x, const double y, const double z, const stencil_t& stencil, weights_t& weights) const { horizontal_interpolation_.compute_weights(x, y, stencil, weights); vertical_interpolation_.compute_weights(z, stencil, weights); } template typename std::enable_if<(array_t::RANK == 2), typename array_t::value_type>::type interpolate( const stencil_t& stencil, const weights_t& weights, const array_t& input) const { using Value = typename std::remove_const::type; std::array, stencil_width()> index; const auto& wj = weights.weights_j; const auto& wk = weights.weights_k; Value output = 0.; for (idx_t j = 0; j < stencil_width(); ++j) { const auto& wi = weights.weights_i[j]; for (idx_t i = 0; i < stencil_width(); ++i) { idx_t n = src_.index(stencil.i(i, j), stencil.j(j)); Value wij = wi[i] * wj[j]; for (idx_t k = 0; k < stencil_width(); ++k) { Value w = wij * wk[k]; output += w * input(n, stencil.k(k)); } index[j][i] = n; } } if (limiter_) { Limiter::limit_scalar(output, index, stencil, input); } return output; } template struct OutputView1D { template Value& operator()(Int v) { return data_[v]; } template Value& operator[](Int v) { return data_[v]; } static constexpr int RANK{1}; OutputView1D(Value* data): data_(data) {} using value_type = Value; Value* data_; }; template OutputView1D make_outputview(Value* data) const { return OutputView1D(data); } template typename std::enable_if<(InputArray::RANK == 3), void>::type interpolate_vars(const stencil_t& stencil, const weights_t& weights, const InputArray& input, OutputArray& output, const idx_t nvar) const { using Value = typename InputArray::value_type; std::array, stencil_width()> index; const auto& wj = weights.weights_j; const auto& wk = weights.weights_k; const Value* _input_; for (idx_t v = 0; v < nvar; ++v) { output[v] = 0.; } for (idx_t j = 0; j < stencil_width(); ++j) { const auto& wi = weights.weights_i[j]; for (idx_t i = 0; i < stencil_width(); ++i) { const idx_t n = src_.index(stencil.i(i, j), stencil.j(j)); const Value wij = wi[i] * wj[j]; for (idx_t k = 0; k < stencil_width(); ++k) { const Value w = wij * wk[k]; const idx_t kk = stencil.k(k); _input_ = &(input(n, kk, 0)); // Assumption that input.stride(2) == 1 for (idx_t v = 0; v < nvar; ++v) { output[v] += w * _input_[v]; } } index[j][i] = n; } } if (limiter_) { Limiter::limit_vars(index, stencil, input, output, nvar); } } template typename std::enable_if<(InputArray::RANK == 2 && OutputArray::RANK == 1), void>::type interpolate( const stencil_t& stencil, const weights_t& weights, const InputArray& input, OutputArray& output, idx_t r) const { output(r) = interpolate(stencil, weights, input); } template typename std::enable_if<(InputArray::RANK == 2 && OutputArray::RANK == 2), void>::type interpolate( const stencil_t& stencil, const weights_t& weights, const InputArray& input, OutputArray& output, idx_t r, idx_t k) const { output(r, k) = interpolate(stencil, weights, input); } template typename std::enable_if<(InputArray::RANK == 3 && OutputArray::RANK == 3), void>::type interpolate( const stencil_t& stencil, const weights_t& weights, const InputArray& input, OutputArray& output, idx_t r, idx_t k) const { auto output_vars = make_outputview(&output(r, k, 0)); interpolate_vars(stencil, weights, input, output_vars, output.shape(2)); } template typename std::enable_if<(InputArray::RANK == 2 && OutputArray::RANK == 3), void>::type interpolate( const stencil_t&, const weights_t&, const InputArray&, OutputArray&, idx_t /*r*/, idx_t /*k*/) const { ATLAS_NOTIMPLEMENTED; } template typename std::enable_if<(InputArray::RANK == 3 && OutputArray::RANK == 1), void>::type interpolate( const stencil_t&, const weights_t&, const InputArray&, OutputArray&, idx_t /*r*/) const { ATLAS_NOTIMPLEMENTED; } template typename std::enable_if<(InputArray::RANK == 3 && OutputArray::RANK == 1), void>::type interpolate( const stencil_t&, const weights_t&, const InputArray&, OutputArray&, idx_t /*r*/, idx_t /*k*/) const { ATLAS_NOTIMPLEMENTED; } template typename std::enable_if<(InputArray::RANK == 3 && OutputArray::RANK == 2), void>::type interpolate( const stencil_t&, const weights_t&, const InputArray&, OutputArray&, idx_t /*r*/, idx_t /*k*/) const { ATLAS_NOTIMPLEMENTED; } }; } // namespace method } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/method/structured/kernels/QuasiCubicHorizontalKernel.h0000664000175000017500000003752015160212070032417 0ustar alastairalastair/* * (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. and Interpolation */ #pragma once #include #include #include "CubicHorizontalLimiter.h" #include "eckit/linalg/Triplet.h" #include "atlas/array/ArrayView.h" #include "atlas/functionspace/StructuredColumns.h" #include "atlas/grid/Stencil.h" #include "atlas/grid/StencilComputer.h" #include "atlas/library/config.h" #include "atlas/runtime/Exception.h" #include "atlas/util/CoordinateEnums.h" #include "atlas/util/Point.h" namespace atlas { namespace interpolation { namespace method { class QuasiCubicHorizontalKernel { using Limiter = CubicHorizontalLimiter; using Triplet = eckit::linalg::Triplet; using Triplets = std::vector; public: QuasiCubicHorizontalKernel() = default; QuasiCubicHorizontalKernel(const functionspace::StructuredColumns& fs, const util::Config& config = util::NoConfig()) { src_ = fs; ATLAS_ASSERT(src_); ATLAS_ASSERT(src_.halo() >= 2); compute_horizontal_stencil_ = grid::ComputeHorizontalStencil(src_.grid(), stencil_width()); limiter_ = config.getBool("limiter", false); } protected: functionspace::StructuredColumns src_; grid::ComputeHorizontalStencil compute_horizontal_stencil_; bool limiter_{false}; public: static std::string className() { return "CubicHorizontalKernel"; } static constexpr idx_t stencil_width() { return 4; } static constexpr idx_t stencil_size() { return 12; } static constexpr idx_t stencil_halo() { return static_cast(static_cast(stencil_width()) / 2. + 0.5); } public: using Stencil = grid::HorizontalStencil<4>; struct Weights { std::array, 4> weights_i; std::array weights_j; }; public: struct WorkSpace { Stencil stencil; Weights weights; }; template void compute_stencil(const double x, const double y, stencil_t& stencil) const { compute_horizontal_stencil_(x, y, stencil); } template void make_valid_stencil(double& x, double y, stencil_t& stencil, bool retry = true) const { for (idx_t j = 0; j < stencil_width(); ++j) { idx_t imin = stencil.i(0, j); idx_t imax = stencil.i(stencil_width()-1, j); if (imin < src_.i_begin_halo(stencil.j(j))) { if (retry ) { x += 360.; compute_stencil(x, y, stencil); return make_valid_stencil(x, y, stencil, false); } else { Log::error() << "Stencil out of bounds" << std::endl; ATLAS_THROW_EXCEPTION("stencil out of bounds"); } } if (imax >= src_.i_end_halo(stencil.j(j))) { if (retry ) { x -= 360.; compute_stencil(x, y, stencil); return make_valid_stencil(x, y, stencil, false); } else { Log::error() << "Stencil out of bounds" << std::endl; ATLAS_THROW_EXCEPTION("Stencil out of bounds"); } } } } template void compute_weights(double x, double y, weights_t& weights) const { Stencil stencil; compute_stencil(x, y, stencil); compute_weights(x, y, stencil, weights); } template void compute_weights(const double x, const double y, const stencil_t& stencil, weights_t& weights) const { PointXY P1, P2; std::array yvec; // Compute x-direction weights LINEAR for outer rows ( j = {0,3} ) for (idx_t j = 0; j < 4; j += 3) { auto& weights_i = weights.weights_i[j]; src_.compute_xy(stencil.i(1, j), stencil.j(j), P1); src_.compute_xy(stencil.i(2, j), stencil.j(j), P2); const double alpha = (P2.x() - x) / (P2.x() - P1.x()); weights_i[1] = alpha; weights_i[2] = 1. - alpha; yvec[j] = P1.y(); } // Compute x-direction weights CUBIC for inner rows ( j = {1,2} ) for (idx_t j = 1; j < 3; ++j) { auto& weights_i = weights.weights_i[j]; src_.compute_xy(stencil.i(1, j), stencil.j(j), P1); src_.compute_xy(stencil.i(2, j), stencil.j(j), P2); const double alpha = (P2.x() - x) / (P2.x() - P1.x()); const double alpha_sqr = alpha * alpha; const double two_minus_alpha = 2. - alpha; const double one_minus_alpha_sqr = 1. - alpha_sqr; weights_i[0] = -alpha * one_minus_alpha_sqr / 6.; weights_i[1] = 0.5 * alpha * (1. + alpha) * two_minus_alpha; weights_i[2] = 0.5 * one_minus_alpha_sqr * two_minus_alpha; weights_i[3] = 1. - weights_i[0] - weights_i[1] - weights_i[2]; yvec[j] = P1.y(); } // Compute weights in y-direction const double dl12 = yvec[0] - yvec[1]; const double dl13 = yvec[0] - yvec[2]; const double dl14 = yvec[0] - yvec[3]; const double dl23 = yvec[1] - yvec[2]; const double dl24 = yvec[1] - yvec[3]; const double dl34 = yvec[2] - yvec[3]; const double dcl1 = dl12 * dl13 * dl14; const double dcl2 = -dl12 * dl23 * dl24; const double dcl3 = dl13 * dl23 * dl34; const double dl1 = y - yvec[0]; const double dl2 = y - yvec[1]; const double dl3 = y - yvec[2]; const double dl4 = y - yvec[3]; auto& weights_j = weights.weights_j; weights_j[0] = (dl2 * dl3 * dl4) / dcl1; #if defined(_CRAYC) && ATLAS_BUILD_TYPE_RELEASE // prevents FE_INVALID somehow (tested with Cray 8.7) ATLAS_ASSERT(!std::isnan(weights_j[0])); #endif weights_j[1] = (dl1 * dl3 * dl4) / dcl2; weights_j[2] = (dl1 * dl2 * dl4) / dcl3; weights_j[3] = 1. - weights_j[0] - weights_j[1] - weights_j[2]; } template typename array_t::value_type interpolate(const stencil_t& stencil, const weights_t& weights, const array_t& input) const { using Value = typename array_t::value_type; std::array, stencil_width()> index; const auto& weights_j = weights.weights_j; Value output = 0.; // LINEAR for outer rows ( j = {0,3} ) for (idx_t j = 0; j < 4; j += 3) { const auto& weights_i = weights.weights_i[j]; for (idx_t i : {1, 2}) { idx_t n = src_.index(stencil.i(i, j), stencil.j(j)); Value w = weights_i[i] * weights_j[j]; output += w * input[n]; index[j][i] = n; } } // CUBIC for inner rows ( j = {1,2} ) for (idx_t j = 1; j < 3; ++j) { const auto& weights_i = weights.weights_i[j]; for (idx_t i = 0; i < stencil_width(); ++i) { idx_t n = src_.index(stencil.i(i, j), stencil.j(j)); Value w = weights_i[i] * weights_j[j]; output += w * input[n]; index[j][i] = n; } } if (limiter_) { Limiter::limit(output, index, input); } return output; } template typename std::enable_if<(Rank == 1), void>::type interpolate(const stencil_t& stencil, const weights_t& weights, const array::ArrayView& input, array::ArrayView& output, idx_t r) const { std::array, stencil_width()> index; const auto& weights_j = weights.weights_j; output(r) = 0.; // LINEAR for outer rows ( j = {0,3} ) for (idx_t j = 0; j < 4; j += 3) { const auto& weights_i = weights.weights_i[j]; for (idx_t i = 1; i < 3; ++i) { // i = {1,2} idx_t n = src_.index(stencil.i(i, j), stencil.j(j)); Value w = weights_i[i] * weights_j[j]; output(r) += w * input[n]; index[j][i] = n; } } // CUBIC for inner rows ( j = {1,2} ) for (idx_t j = 1; j < 3; ++j) { const auto& weights_i = weights.weights_i[j]; for (idx_t i = 0; i < stencil_width(); ++i) { idx_t n = src_.index(stencil.i(i, j), stencil.j(j)); Value w = weights_i[i] * weights_j[j]; output(r) += w * input[n]; index[j][i] = n; } } if (limiter_) { Limiter::limit(index, input, output, r); } } template typename std::enable_if<(Rank == 2), void>::type interpolate(const stencil_t& stencil, const weights_t& weights, const array::ArrayView& input, array::ArrayView& output, idx_t r) const { std::array, stencil_width()> index; const auto& weights_j = weights.weights_j; const idx_t Nk = output.shape(1); for (idx_t k = 0; k < Nk; ++k) { output(r, k) = 0.; } // LINEAR for outer rows ( j = {0,3} ) for (idx_t j = 0; j < 4; j += 3) { const auto& weights_i = weights.weights_i[j]; for (idx_t i = 1; i < 3; ++i) { // i = {1,2} idx_t n = src_.index(stencil.i(i, j), stencil.j(j)); Value w = weights_i[i] * weights_j[j]; for (idx_t k = 0; k < Nk; ++k) { output(r, k) += w * input(n, k); } index[j][i] = n; } } // CUBIC for inner rows ( j = {1,2} ) for (idx_t j = 1; j < 3; ++j) { const auto& weights_i = weights.weights_i[j]; for (idx_t i = 0; i < stencil_width(); ++i) { idx_t n = src_.index(stencil.i(i, j), stencil.j(j)); Value w = weights_i[i] * weights_j[j]; for (idx_t k = 0; k < Nk; ++k) { output(r, k) += w * input(n, k); } index[j][i] = n; } } if (limiter_) { Limiter::limit(index, input, output, r); } } template typename std::enable_if<(Rank == 3), void>::type interpolate(const stencil_t& stencil, const weights_t& weights, const array::ArrayView& input, array::ArrayView& output, idx_t r) const { std::array, stencil_width()> index; const auto& weights_j = weights.weights_j; const idx_t Nk = output.shape(1); const idx_t Nl = output.shape(2); for (idx_t k = 0; k < Nk; ++k) { for (idx_t l = 0; l < Nl; ++l) { output(r, k, l) = 0.; } } // LINEAR for outer rows ( j = {0,3} ) for (idx_t j = 0; j < 4; j += 3) { const auto& weights_i = weights.weights_i[j]; for (idx_t i = 1; i < 3; ++i) { // i = {1,2} idx_t n = src_.index(stencil.i(i, j), stencil.j(j)); Value w = weights_i[i] * weights_j[j]; for (idx_t k = 0; k < Nk; ++k) { for (idx_t l = 0; l < Nl; ++l) { output(r, k, l) += w * input(n, k, l); } } index[j][i] = n; } } // CUBIC for inner rows ( j = {1,2} ) for (idx_t j = 1; j < 3; ++j) { const auto& weights_i = weights.weights_i[j]; for (idx_t i = 0; i < stencil_width(); ++i) { idx_t n = src_.index(stencil.i(i, j), stencil.j(j)); Value w = weights_i[i] * weights_j[j]; for (idx_t k = 0; k < Nk; ++k) { for (idx_t l = 0; l < Nl; ++l) { output(r, k, l) += w * input(n, k, l); } } index[j][i] = n; } } if (limiter_) { Limiter::limit(index, input, output, r); } } template typename array_t::value_type operator()(double x, double y, const array_t& input) const { Stencil stencil; compute_stencil(x, y, stencil); Weights weights; compute_weights(x, y, stencil, weights); make_valid_stencil(x, y, stencil); return interpolate(stencil, weights, input); } template typename array_t::value_type interpolate(PointXY p, const array_t& input, WorkSpace& ws) const { compute_stencil(p.x(), p.y(), ws.stencil); compute_weights(p.x(), p.y(), ws.stencil, ws.weights); make_valid_stencil(p.x(), p.y(), ws.stencil); return interpolate(ws.stencil, ws.weights, input); } // Thread private workspace Triplets compute_triplets(const idx_t row, const double x, const double y, WorkSpace& ws) const { Triplets triplets; triplets.reserve(stencil_size()); insert_triplets(row, x, y, triplets, ws); return triplets; } Triplets reserve_triplets(size_t N) { Triplets triplets; triplets.reserve(N * stencil_size()); return triplets; } Triplets allocate_triplets(size_t N) { return Triplets(N * stencil_size()); } void insert_triplets(const idx_t row, const PointXY& p, Triplets& triplets, WorkSpace& ws) const { insert_triplets(row, p.x(), p.y(), triplets, ws); } void insert_triplets(const idx_t row, double x, double y, Triplets& triplets, WorkSpace& ws) const { compute_stencil(x, y, ws.stencil); compute_weights(x, y, ws.stencil, ws.weights); make_valid_stencil(x, y, ws.stencil); const auto& wj = ws.weights.weights_j; idx_t pos = row * stencil_size(); // LINEAR for outer rows ( j = {0,3} ) for (idx_t j = 0; j < 4; j += 3) { const auto& wi = ws.weights.weights_i[j]; for (idx_t i = 1; i < 3; ++i) { // i = {1,2} idx_t col = src_.index(ws.stencil.i(i, j), ws.stencil.j(j)); double w = wi[i] * wj[j]; triplets[pos++] = Triplet(row, col, w); } } // CUBIC for inner rows ( j = {1,2} ) for (idx_t j = 1; j < 3; ++j) { const auto& wi = ws.weights.weights_i[j]; for (idx_t i = 0; i < stencil_width(); ++i) { idx_t col = src_.index(ws.stencil.i(i, j), ws.stencil.j(j)); double w = wi[i] * wj[j]; triplets[pos++] = Triplet(row, col, w); } } } }; } // namespace method } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/method/structured/kernels/LinearVerticalKernel.h0000664000175000017500000000657215160212070031224 0ustar alastairalastair/* * (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. and Interpolation */ #pragma once #include #include "atlas/grid/Stencil.h" #include "atlas/grid/StencilComputer.h" #include "atlas/grid/Vertical.h" #include "atlas/util/Point.h" namespace atlas { namespace interpolation { namespace method { class LinearVerticalKernel { grid::ComputeVerticalStencil compute_vertical_stencil_; Vertical vertical_; static constexpr idx_t stencil_width() { return 2; } static constexpr idx_t stencil_size() { return stencil_width() * stencil_width(); } idx_t first_level_; idx_t last_level_; public: LinearVerticalKernel() = default; LinearVerticalKernel(const Vertical& vertical, const eckit::Configuration&): compute_vertical_stencil_(vertical, stencil_width()), vertical_(vertical), first_level_(vertical_.k_begin()), last_level_(vertical_.k_end() - 1) {} struct Weights { std::array weights_k; }; using Stencil = grid::VerticalStencil<2>; template void compute_stencil(const double z, stencil_t& stencil) const { compute_vertical_stencil_(z, stencil); } template void compute_weights(const double z, const stencil_t& stencil, weights_t& weights) const { auto& w = weights.weights_k; std::array zvec; for (idx_t k = 0; k < stencil_width(); ++k) { zvec[k] = vertical_(stencil.k(k)); } if (stencil.k_interval() == -1) { // constant extrapolation // lev0 lev1 // + |------X // w=1 w=0 w[0] = 1.; w[1] = 0.; return; } else if (stencil.k_interval() == 1) { // constant extrapolation // lev(n-2) lev(n-1) // X---------| + // w=0 w=1 w[0] = 0.; w[1] = 1.; return; } // Linear interpolation // lev(k+0) lev(k+1) // | x | const double alpha = (zvec[1] - z) / (zvec[1] - zvec[0]); w[0] = alpha; w[1] = 1. - alpha; } template void interpolate(const stencil_t& stencil, const weights_t& weights, const array_t& input, double& output) const { output = 0.; const auto& w = weights.weights_k; for (idx_t k = 0; k < stencil_width(); ++k) { output += w[k] * input[stencil.k(k)]; } } template double operator()(const double z, const array_t& input) const { Stencil stencil; compute_vertical_stencil_(z, stencil); Weights weights; compute_weights(z, stencil, weights); double output; interpolate(stencil, weights, input, output); return output; } }; } // namespace method } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/method/structured/kernels/CubicHorizontalKernel.h0000664000175000017500000003153615160212070031415 0ustar alastairalastair/* * (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. and Interpolation */ #pragma once #include #include #include "CubicHorizontalLimiter.h" #include "eckit/linalg/Triplet.h" #include "atlas/array/ArrayView.h" #include "atlas/functionspace/StructuredColumns.h" #include "atlas/grid/Stencil.h" #include "atlas/grid/StencilComputer.h" #include "atlas/runtime/Exception.h" #include "atlas/util/CoordinateEnums.h" #include "atlas/util/Point.h" namespace atlas { namespace interpolation { namespace method { class CubicHorizontalKernel { using Limiter = CubicHorizontalLimiter; using Triplet = eckit::linalg::Triplet; using Triplets = std::vector; public: CubicHorizontalKernel() = default; CubicHorizontalKernel(const functionspace::StructuredColumns& fs, const util::Config& config = util::NoConfig()) { src_ = fs; ATLAS_ASSERT(src_); ATLAS_ASSERT(src_.halo() >= 2); compute_horizontal_stencil_ = grid::ComputeHorizontalStencil(src_.grid(), stencil_width()); limiter_ = config.getBool("limiter", false); } protected: functionspace::StructuredColumns src_; grid::ComputeHorizontalStencil compute_horizontal_stencil_; bool limiter_{false}; public: static std::string className() { return "CubicHorizontalKernel"; } static constexpr idx_t stencil_width() { return 4; } static constexpr idx_t stencil_size() { return stencil_width() * stencil_width(); } static constexpr idx_t stencil_halo() { return static_cast(static_cast(stencil_width()) / 2. + 0.5); } public: using Stencil = grid::HorizontalStencil<4>; struct Weights { std::array, 4> weights_i; std::array weights_j; }; public: struct WorkSpace { Stencil stencil; Weights weights; }; template void compute_stencil(const double x, const double y, stencil_t& stencil) const { compute_horizontal_stencil_(x, y, stencil); } template void make_valid_stencil(double& x, const double y, stencil_t& stencil, bool retry = true) const { for (idx_t j = 0; j < stencil_width(); ++j) { idx_t imin = stencil.i(0, j); idx_t imax = stencil.i(stencil_width()-1, j); if (imin < src_.i_begin_halo(stencil.j(j))) { if (retry ) { x += 360.; compute_stencil(x, y, stencil); return make_valid_stencil(x, y, stencil, false); } else { Log::error() << "Stencil out of bounds" << std::endl; ATLAS_THROW_EXCEPTION("stencil out of bounds"); } } if (imax >= src_.i_end_halo(stencil.j(j))) { if (retry ) { x -= 360.; compute_stencil(x, y, stencil); return make_valid_stencil(x, y, stencil, false); } else { Log::error() << "Stencil out of bounds" << std::endl; ATLAS_THROW_EXCEPTION("Stencil out of bounds"); } } } } template void compute_weights(const double x, const double y, weights_t& weights) const { Stencil stencil; compute_stencil(x, y, stencil); compute_weights(x, y, stencil, weights); } template void compute_weights(const double x, const double y, const stencil_t& stencil, weights_t& weights) const { PointXY P1, P2; std::array yvec; for (idx_t j = 0; j < stencil_width(); ++j) { auto& weights_i = weights.weights_i[j]; src_.compute_xy(stencil.i(1, j), stencil.j(j), P1); src_.compute_xy(stencil.i(2, j), stencil.j(j), P2); const double alpha = (P2.x() - x) / (P2.x() - P1.x()); const double alpha_sqr = alpha * alpha; const double two_minus_alpha = 2. - alpha; const double one_minus_alpha_sqr = 1. - alpha_sqr; weights_i[0] = -alpha * one_minus_alpha_sqr / 6.; weights_i[1] = 0.5 * alpha * (1. + alpha) * two_minus_alpha; weights_i[2] = 0.5 * one_minus_alpha_sqr * two_minus_alpha; weights_i[3] = 1. - weights_i[0] - weights_i[1] - weights_i[2]; yvec[j] = P1.y(); } const double dl12 = yvec[0] - yvec[1]; const double dl13 = yvec[0] - yvec[2]; const double dl14 = yvec[0] - yvec[3]; const double dl23 = yvec[1] - yvec[2]; const double dl24 = yvec[1] - yvec[3]; const double dl34 = yvec[2] - yvec[3]; const double dcl1 = dl12 * dl13 * dl14; const double dcl2 = -dl12 * dl23 * dl24; const double dcl3 = dl13 * dl23 * dl34; const double dl1 = y - yvec[0]; const double dl2 = y - yvec[1]; const double dl3 = y - yvec[2]; const double dl4 = y - yvec[3]; auto& weights_j = weights.weights_j; weights_j[0] = (dl2 * dl3 * dl4) / dcl1; #if defined(_CRAYC) || defined(__NVCOMPILER) && ATLAS_BUILD_TYPE_RELEASE // prevents FE_INVALID somehow (tested with Cray 8.7, nvhpc-22.11) ATLAS_ASSERT(!std::isnan(weights_j[0])); #endif weights_j[1] = (dl1 * dl3 * dl4) / dcl2; weights_j[2] = (dl1 * dl2 * dl4) / dcl3; weights_j[3] = 1. - weights_j[0] - weights_j[1] - weights_j[2]; } template typename array_t::value_type interpolate(const stencil_t& stencil, const weights_t& weights, const array_t& input) const { using Value = typename array_t::value_type; std::array, stencil_width()> index; const auto& weights_j = weights.weights_j; Value output = 0.; for (idx_t j = 0; j < stencil_width(); ++j) { const auto& weights_i = weights.weights_i[j]; for (idx_t i = 0; i < stencil_width(); ++i) { idx_t n = src_.index(stencil.i(i, j), stencil.j(j)); Value w = weights_i[i] * weights_j[j]; output += w * input[n]; index[j][i] = n; } } if (limiter_) { Limiter::limit(output, index, input); } return output; } template typename std::enable_if<(Rank == 1), void>::type interpolate(const stencil_t& stencil, const weights_t& weights, const array::ArrayView& input, array::ArrayView& output, idx_t r) const { std::array, stencil_width()> index; const auto& weights_j = weights.weights_j; output(r) = 0.; for (idx_t j = 0; j < stencil_width(); ++j) { const auto& weights_i = weights.weights_i[j]; for (idx_t i = 0; i < stencil_width(); ++i) { idx_t n = src_.index(stencil.i(i, j), stencil.j(j)); Value w = static_cast(weights_i[i] * weights_j[j]); output(r) += w * input[n]; index[j][i] = n; } } if (limiter_) { Limiter::limit(index, input, output, r); } } template typename std::enable_if<(Rank == 2), void>::type interpolate(const stencil_t& stencil, const weights_t& weights, const array::ArrayView& input, array::ArrayView& output, idx_t r) const { std::array, stencil_width()> index; const auto& weights_j = weights.weights_j; const idx_t Nk = output.shape(1); for (idx_t k = 0; k < Nk; ++k) { output(r, k) = 0.; } for (idx_t j = 0; j < stencil_width(); ++j) { const auto& weights_i = weights.weights_i[j]; for (idx_t i = 0; i < stencil_width(); ++i) { idx_t n = src_.index(stencil.i(i, j), stencil.j(j)); Value w = static_cast(weights_i[i] * weights_j[j]); for (idx_t k = 0; k < Nk; ++k) { output(r, k) += w * input(n, k); } index[j][i] = n; } } if (limiter_) { Limiter::limit(index, input, output, r); } } template typename std::enable_if<(Rank == 3), void>::type interpolate(const stencil_t& stencil, const weights_t& weights, const array::ArrayView& input, array::ArrayView& output, idx_t r) const { std::array, stencil_width()> index; const auto& weights_j = weights.weights_j; const idx_t Nk = output.shape(1); const idx_t Nl = output.shape(2); for (idx_t k = 0; k < Nk; ++k) { for (idx_t l = 0; l < Nl; ++l) { output(r, k, l) = 0.; } } for (idx_t j = 0; j < stencil_width(); ++j) { const auto& weights_i = weights.weights_i[j]; for (idx_t i = 0; i < stencil_width(); ++i) { idx_t n = src_.index(stencil.i(i, j), stencil.j(j)); Value w = static_cast(weights_i[i] * weights_j[j]); for (idx_t k = 0; k < Nk; ++k) { for (idx_t l = 0; l < Nl; ++l) { output(r, k, l) += w * input(n, k, l); } } index[j][i] = n; } } if (limiter_) { Limiter::limit(index, input, output, r); } } template typename array_t::value_type operator()(double x, double y, const array_t& input) const { Stencil stencil; compute_stencil(x, y, stencil); Weights weights; compute_weights(x, y, stencil, weights); make_valid_stencil(x, y, stencil); return interpolate(stencil, weights, input); } template typename array_t::value_type interpolate(PointXY p, const array_t& input, WorkSpace& ws) const { compute_stencil(p.x(), p.y(), ws.stencil); compute_weights(p.x(), p.y(), ws.stencil, ws.weights); make_valid_stencil(p.x(), p.y(), ws.stencil); return interpolate(ws.stencil, ws.weights, input); } // Thread private workspace Triplets compute_triplets(const idx_t row, const double x, const double y, WorkSpace& ws) const { Triplets triplets; triplets.reserve(stencil_size()); insert_triplets(row, x, y, triplets, ws); return triplets; } Triplets reserve_triplets(size_t N) { Triplets triplets; triplets.reserve(N * stencil_size()); return triplets; } Triplets allocate_triplets(size_t N) { return Triplets(N * stencil_size()); } void insert_triplets(const idx_t row, const PointXY& p, Triplets& triplets, WorkSpace& ws) const { insert_triplets(row, p.x(), p.y(), triplets, ws); } void insert_triplets(const idx_t row, double x, double y, Triplets& triplets, WorkSpace& ws) const { compute_stencil(x, y, ws.stencil); compute_weights(x, y, ws.stencil, ws.weights); make_valid_stencil(x, y, ws.stencil); const auto& wj = ws.weights.weights_j; idx_t pos = row * stencil_size(); for (idx_t j = 0; j < stencil_width(); ++j) { const auto& wi = ws.weights.weights_i[j]; for (idx_t i = 0; i < stencil_width(); ++i) { idx_t col = src_.index(ws.stencil.i(i, j), ws.stencil.j(j)); double w = wi[i] * wj[j]; triplets[pos++] = Triplet(row, col, w); } } } }; } // namespace method } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/method/structured/kernels/CubicHorizontalLimiter.h0000664000175000017500000001357315160212070031603 0ustar alastairalastair/* * (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. and Interpolation */ #pragma once #include #include #include namespace atlas { namespace interpolation { namespace method { class CubicHorizontalLimiter { public: CubicHorizontalLimiter() = default; template static void limit(typename array_t::value_type& output, const std::array, 4>& index, const array_t& input) { using Scalar = typename array_t::value_type; // Limit output to max/min of values in stencil marked by '*' // x x x x // x *-----* x // / P | // x *------ * x // x x x x Scalar maxval = std::numeric_limits::lowest(); Scalar minval = std::numeric_limits::max(); for (idx_t j = 1; j < 3; ++j) { for (idx_t i = 1; i < 3; ++i) { idx_t n = index[j][i]; Scalar val = input[n]; maxval = std::max(maxval, val); minval = std::min(minval, val); } } if (output < minval) { output = minval; } else if (output > maxval) { output = maxval; } } template static typename std::enable_if<(Rank == 1), void>::type limit(const std::array, 4>& index, const array::ArrayView& input, array::ArrayView& output, idx_t r) { // Limit output to max/min of values in stencil marked by '*' // x x x x // x *-----* x // / P | // x *------ * x // x x x x Value maxval = std::numeric_limits::lowest(); Value minval = std::numeric_limits::max(); for (idx_t j = 1; j < 3; ++j) { for (idx_t i = 1; i < 3; ++i) { idx_t n = index[j][i]; Value val = input[n]; maxval = std::max(maxval, val); minval = std::min(minval, val); } } if (output(r) < minval) { output(r) = minval; } else if (output(r) > maxval) { output(r) = maxval; } } template static typename std::enable_if<(Rank == 2), void>::type limit(const std::array, 4>& index, const array::ArrayView& input, array::ArrayView& output, idx_t r) { // Limit output to max/min of values in stencil marked by '*' // x x x x // x *-----* x // / P | // x *------ * x // x x x x for (idx_t k = 0; k < output.shape(1); ++k) { Value maxval = std::numeric_limits::lowest(); Value minval = std::numeric_limits::max(); for (idx_t j = 1; j < 3; ++j) { for (idx_t i = 1; i < 3; ++i) { idx_t n = index[j][i]; Value val = input(n, k); maxval = std::max(maxval, val); minval = std::min(minval, val); } } if (output(r, k) < minval) { output(r, k) = minval; } else if (output(r, k) > maxval) { output(r, k) = maxval; } } } template static typename std::enable_if<(Rank == 3), void>::type limit(const std::array, 4>& index, const array::ArrayView& input, array::ArrayView& output, idx_t r) { // Limit output to max/min of values in stencil marked by '*' // x x x x // x *-----* x // / P | // x *------ * x // x x x x for (idx_t k = 0; k < output.shape(1); ++k) { for (idx_t l = 0; l < output.shape(2); ++l) { Value maxval = std::numeric_limits::lowest(); Value minval = std::numeric_limits::max(); for (idx_t j = 1; j < 3; ++j) { for (idx_t i = 1; i < 3; ++i) { idx_t n = index[j][i]; Value val = input(n, k, l); maxval = std::max(maxval, val); minval = std::min(minval, val); } } if (output(r, k, l) < minval) { output(r, k, l) = minval; } else if (output(r, k, l) > maxval) { output(r, k, l) = maxval; } } } } }; } // namespace method } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/method/structured/kernels/LinearHorizontalKernel.h0000664000175000017500000002511415160212070031575 0ustar alastairalastair/* * (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. and Interpolation */ #pragma once #include #include #include "eckit/linalg/Triplet.h" #include "atlas/array/ArrayView.h" #include "atlas/functionspace/StructuredColumns.h" #include "atlas/grid/Stencil.h" #include "atlas/grid/StencilComputer.h" #include "atlas/runtime/Exception.h" #include "atlas/util/CoordinateEnums.h" #include "atlas/util/Point.h" namespace atlas { namespace interpolation { namespace method { class LinearHorizontalKernel { using Triplet = eckit::linalg::Triplet; using Triplets = std::vector; public: LinearHorizontalKernel() = default; LinearHorizontalKernel(const functionspace::StructuredColumns& fs, const util::Config& = util::NoConfig()) { src_ = fs; ATLAS_ASSERT(src_); compute_horizontal_stencil_ = grid::ComputeHorizontalStencil(src_.grid(), stencil_width()); } private: functionspace::StructuredColumns src_; grid::ComputeHorizontalStencil compute_horizontal_stencil_; public: static std::string className() { return "LinearHorizontalKernel"; } static constexpr idx_t stencil_width() { return 2; } static constexpr idx_t stencil_size() { return stencil_width() * stencil_width(); } static constexpr idx_t stencil_halo() { return 0; } public: using Stencil = grid::HorizontalStencil<2>; struct Weights { std::array, 2> weights_i; std::array weights_j; }; public: struct WorkSpace { Stencil stencil; Weights weights; }; template void compute_stencil(const double x, const double y, stencil_t& stencil) const { compute_horizontal_stencil_(x, y, stencil); } template void make_valid_stencil(double& x, const double y, stencil_t& stencil, bool retry = true) const { for (idx_t j = 0; j < stencil_width(); ++j) { idx_t imin = stencil.i(0, j); idx_t imax = stencil.i(stencil_width()-1, j); if (imin < src_.i_begin_halo(stencil.j(j))) { if (retry ) { x += 360.; compute_stencil(x, y, stencil); return make_valid_stencil(x, y, stencil, false); } else { Log::error() << "Stencil out of bounds" << std::endl; ATLAS_THROW_EXCEPTION("stencil out of bounds"); } } if (imax >= src_.i_end_halo(stencil.j(j))) { if (retry ) { x -= 360.; compute_stencil(x, y, stencil); return make_valid_stencil(x, y, stencil, false); } else { Log::error() << "Stencil out of bounds" << std::endl; ATLAS_THROW_EXCEPTION("Stencil out of bounds"); } } } } template void compute_weights(const double x, const double y, weights_t& weights) const { Stencil stencil; compute_stencil(x, y, stencil); compute_weights(x, y, stencil, weights); } template void compute_weights(const double x, const double y, const stencil_t& stencil, weights_t& weights) const { PointXY P1, P2; std::array yvec; // Compute weights for each constant-Y for (idx_t j = 0; j < stencil_width(); ++j) { auto& weights_i = weights.weights_i[j]; src_.compute_xy(stencil.i(0, j), stencil.j(j), P1); src_.compute_xy(stencil.i(1, j), stencil.j(j), P2); const double alpha = (P2.x() - x) / (P2.x() - P1.x()); weights_i[0] = alpha; weights_i[1] = 1. - alpha; yvec[j] = P1.y(); } // Compute weights in y-direction { auto& weights_j = weights.weights_j; const double alpha = (yvec[1] - y) / (yvec[1] - yvec[0]); weights_j[0] = alpha; weights_j[1] = 1. - alpha; } } template typename array_t::value_type interpolate(const stencil_t& stencil, const weights_t& weights, const array_t& input) const { using Value = typename array_t::value_type; const auto& weights_j = weights.weights_j; Value output = 0.; for (idx_t j = 0; j < stencil_width(); ++j) { const auto& weights_i = weights.weights_i[j]; for (idx_t i = 0; i < stencil_width(); ++i) { idx_t n = src_.index(stencil.i(i, j), stencil.j(j)); Value w = weights_i[i] * weights_j[j]; output += w * input[n]; } } return output; } template typename std::enable_if<(Rank == 1), void>::type interpolate(const stencil_t& stencil, const weights_t& weights, const array::ArrayView& input, array::ArrayView& output, idx_t r) const { const auto& weights_j = weights.weights_j; output(r) = 0.; for (idx_t j = 0; j < stencil_width(); ++j) { const auto& weights_i = weights.weights_i[j]; for (idx_t i = 0; i < stencil_width(); ++i) { idx_t n = src_.index(stencil.i(i, j), stencil.j(j)); Value w = static_cast(weights_i[i] * weights_j[j]); output(r) += w * input[n]; } } } template typename std::enable_if<(Rank == 2), void>::type interpolate(const stencil_t& stencil, const weights_t& weights, const array::ArrayView& input, array::ArrayView& output, idx_t r) const { const auto& weights_j = weights.weights_j; const idx_t Nk = output.shape(1); for (idx_t k = 0; k < Nk; ++k) { output(r, k) = 0.; } for (idx_t j = 0; j < stencil_width(); ++j) { const auto& weights_i = weights.weights_i[j]; for (idx_t i = 0; i < stencil_width(); ++i) { idx_t n = src_.index(stencil.i(i, j), stencil.j(j)); Value w = static_cast(weights_i[i] * weights_j[j]); for (idx_t k = 0; k < Nk; ++k) { output(r, k) += w * input(n, k); } } } } template typename std::enable_if<(Rank == 3), void>::type interpolate(const stencil_t& stencil, const weights_t& weights, const array::ArrayView& input, array::ArrayView& output, idx_t r) const { const auto& weights_j = weights.weights_j; const idx_t Nk = output.shape(1); const idx_t Nl = output.shape(2); for (idx_t k = 0; k < Nk; ++k) { for (idx_t l = 0; l < Nl; ++l) { output(r, k, l) = 0.; } } for (idx_t j = 0; j < stencil_width(); ++j) { const auto& weights_i = weights.weights_i[j]; for (idx_t i = 0; i < stencil_width(); ++i) { idx_t n = src_.index(stencil.i(i, j), stencil.j(j)); Value w = static_cast(weights_i[i] * weights_j[j]); for (idx_t k = 0; k < Nk; ++k) { for (idx_t l = 0; l < Nl; ++l) { output(r, k, l) += w * input(n, k, l); } } } } } template typename array_t::value_type operator()(double x, double y, const array_t& input) const { Stencil stencil; Weights weights; compute_stencil(x, y, stencil); compute_weights(x, y, stencil, weights); make_valid_stencil(x, y, stencil); return interpolate(stencil, weights, input); } template typename array_t::value_type interpolate(PointXY p, const array_t& input, WorkSpace& ws) const { compute_stencil(p.x(), p.y(), ws.stencil); compute_weights(p.x(), p.y(), ws.stencil, ws.weights); make_valid_stencil(p.x(), p.y(), ws.stencil); return interpolate(ws.stencil, ws.weights, input); } // Thread private workspace Triplets compute_triplets(const idx_t row, const double x, const double y, WorkSpace& ws) const { Triplets triplets; triplets.reserve(stencil_size()); insert_triplets(row, x, y, triplets, ws); return triplets; } Triplets reserve_triplets(size_t N) { Triplets triplets; triplets.reserve(N * stencil_size()); return triplets; } Triplets allocate_triplets(size_t N) { return Triplets(N * stencil_size()); } void insert_triplets(const idx_t row, const PointXY& p, Triplets& triplets, WorkSpace& ws) const { insert_triplets(row, p.x(), p.y(), triplets, ws); } void insert_triplets(const idx_t row, double x, double y, Triplets& triplets, WorkSpace& ws) const { compute_stencil(x, y, ws.stencil); compute_weights(x, y, ws.stencil, ws.weights); make_valid_stencil(x, y, ws.stencil); const auto& wj = ws.weights.weights_j; idx_t pos = row * stencil_size(); for (idx_t j = 0; j < stencil_width(); ++j) { const auto& wi = ws.weights.weights_i[j]; for (idx_t i = 0; i < stencil_width(); ++i) { idx_t col = src_.index(ws.stencil.i(i, j), ws.stencil.j(j)); double w = wi[i] * wj[j]; triplets[pos++] = Triplet(row, col, w); } } } }; } // namespace method } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/method/structured/kernels/CubicVerticalKernel.h0000664000175000017500000001634415160212070031035 0ustar alastairalastair/* * (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. and Interpolation */ #pragma once #include #include "atlas/grid/Stencil.h" #include "atlas/grid/StencilComputer.h" #include "atlas/grid/Vertical.h" #include "atlas/library/config.h" #include "atlas/util/Point.h" namespace atlas { namespace interpolation { namespace method { class CubicVerticalKernel { grid::ComputeVerticalStencil compute_vertical_stencil_; Vertical vertical_; static constexpr idx_t stencil_width() { return 4; } static constexpr idx_t stencil_size() { return stencil_width() * stencil_width(); } idx_t first_level_; idx_t last_level_; bool limiter_; static constexpr bool match_IFS() { return true; } public: CubicVerticalKernel() = default; CubicVerticalKernel(const Vertical& vertical, const eckit::Configuration& config = util::NoConfig()): compute_vertical_stencil_(vertical, stencil_width()), vertical_(vertical), first_level_(vertical_.k_begin()), last_level_(vertical_.k_end() - 1) { limiter_ = config.getBool("limiter", false); } struct Weights { std::array weights_k; }; using Stencil = grid::VerticalStencil<4>; template void compute_stencil(const double z, stencil_t& stencil) const { compute_vertical_stencil_(z, stencil); } template void compute_weights(const double z, const stencil_t& stencil, weights_t& weights) const { auto& w = weights.weights_k; std::array zvec; for (idx_t k = 0; k < 4; ++k) { zvec[k] = vertical_(stencil.k(k)); } // auto quadratic_interpolation = [z]( const double zvec[], double w[] ) { // double d01 = zvec[0] - zvec[1]; // double d02 = zvec[0] - zvec[2]; // double d12 = zvec[1] - zvec[2]; // double dc0 = d01 * d02; // double dc1 = -d01 * d12; // double d0 = z - zvec[0]; // double d1 = z - zvec[1]; // double d2 = z - zvec[2]; // w[0] = ( d1 * d2 ) / dc0; // w[1] = ( d0 * d2 ) / dc1; // w[2] = 1. - w[0] - w[1]; // }; auto cubic_interpolation = [z](const double zvec[], double w[]) { double d01 = zvec[0] - zvec[1]; double d02 = zvec[0] - zvec[2]; double d03 = zvec[0] - zvec[3]; double d12 = zvec[1] - zvec[2]; double d13 = zvec[1] - zvec[3]; double d23 = zvec[2] - zvec[3]; double dc0 = d01 * d02 * d03; double dc1 = -d01 * d12 * d13; double dc2 = d02 * d12 * d23; double d0 = z - zvec[0]; double d1 = z - zvec[1]; double d2 = z - zvec[2]; double d3 = z - zvec[3]; w[0] = (d1 * d2 * d3) / dc0; #if defined(_CRAYC) && ATLAS_BUILD_TYPE_RELEASE // prevents FE_INVALID somehow (tested with Cray 8.7) ATLAS_ASSERT(!std::isnan(w[0])); #endif w[1] = (d0 * d2 * d3) / dc1; w[2] = (d0 * d1 * d3) / dc2; w[3] = 1. - w[0] - w[1] - w[2]; }; if (stencil.k_interval() == 1) { // lev(k+0) lev(k+1) lev(k+2) lev(k+3) // | | x | | cubic_interpolation(zvec.data(), w.data()); return; } if (stencil.k_interval() == 0) { if (match_IFS()) { // linear interpolation // lev0 lev1 lev2 lev3 // | + | | | // w=0 w=0 const double alpha = (zvec[1] - z) / (zvec[1] - zvec[0]); w[0] = alpha; w[1] = 1. - alpha; w[2] = 0.; w[3] = 0.; } else { cubic_interpolation(zvec.data(), w.data()); } return; } if (stencil.k_interval() == 2) { if (match_IFS()) { // linear interpolation // lev(n-4) lev(n-3) lev(n-2) lev(n-1) // | | | + | // w=0 w=0 const double alpha = (zvec[3] - z) / (zvec[3] - zvec[2]); w[0] = 0.; w[1] = 0.; w[2] = alpha; w[3] = 1. - alpha; } else { cubic_interpolation(zvec.data(), w.data()); } return; } if (stencil.k_interval() == -1) { // constant extrapolation // lev0 lev1 lev2 lev3 // + |------X------X------X // w=1 w=0 w=0 w=0 w[0] = 1.; w[1] = 0.; w[2] = 0.; w[3] = 0.; return; } if (stencil.k_interval() == 3) { // constant extrapolation // lev(n-4) lev(n-3) lev(n-2) lev(n-1) // X---------X---------X---------| + // w=0 w=0 w=0 w=1 w[0] = 0.; w[1] = 0.; w[2] = 0.; w[3] = 1.; return; } ATLAS_NOTIMPLEMENTED; // should never be here } template void interpolate(const stencil_t& stencil, const weights_t& weights, const array_t& input, double& output) const { output = 0.; const auto& w = weights.weights_k; for (idx_t k = 0; k < stencil_width(); ++k) { output += w[k] * input[stencil.k(k)]; } if (limiter_) { idx_t k = stencil.k_interval(); idx_t k1, k2; if (k < 1) { k1 = 0; k2 = 1; } else if (k > 1) { k1 = 2; k2 = 3; } else { k1 = 1; k2 = 2; } double f1 = input[stencil.k(k1)]; double f2 = input[stencil.k(k2)]; double maxval = std::max(f1, f2); double minval = std::min(f1, f2); output = std::min(maxval, std::max(minval, output)); } } template double operator()(const double z, const array_t& input) const { Stencil stencil; compute_vertical_stencil_(z, stencil); Weights weights; compute_weights(z, stencil, weights); double output; interpolate(stencil, weights, input, output); return output; } }; } // namespace method } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/method/structured/kernels/Linear3DKernel.h0000664000175000017500000002136615160212070027717 0ustar alastairalastair/* * (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. and Interpolation */ #pragma once #include #include #include "atlas/array/ArrayView.h" #include "atlas/functionspace/StructuredColumns.h" #include "atlas/grid/Stencil.h" #include "atlas/grid/StencilComputer.h" #include "atlas/runtime/Exception.h" #include "atlas/util/CoordinateEnums.h" #include "atlas/util/Point.h" #include "LinearHorizontalKernel.h" #include "LinearVerticalKernel.h" namespace atlas { namespace interpolation { namespace method { class Linear3DKernel { public: Linear3DKernel(const functionspace::StructuredColumns& fs, const util::Config& config = util::NoConfig()) { src_ = fs; ATLAS_ASSERT(src_); ATLAS_ASSERT(src_.halo() >= 0); ATLAS_ASSERT(src_.vertical().size()); horizontal_interpolation_ = LinearHorizontalKernel(src_, config); vertical_interpolation_ = LinearVerticalKernel(fs.vertical(), config); } private: functionspace::StructuredColumns src_; LinearHorizontalKernel horizontal_interpolation_; LinearVerticalKernel vertical_interpolation_; public: static std::string className() { return "Linear3DKernel"; } static constexpr idx_t stencil_width() { return 2; } static constexpr idx_t stencil_size() { return stencil_width() * stencil_width() * stencil_width(); } static constexpr idx_t stencil_halo() { return 0; } public: using Stencil = grid::Stencil3D<2>; struct Weights { std::array, 2> weights_i; std::array weights_j; std::array weights_k; }; public: struct WorkSpace { Stencil stencil; Weights weights; }; template void compute_stencil(double& x, const double y, const double z, stencil_t& stencil) const { horizontal_interpolation_.compute_stencil(x, y, stencil); vertical_interpolation_.compute_stencil(z, stencil); } template void compute_weights(double x, double y, const double z, weights_t& weights) const { Stencil stencil; compute_stencil(x, y, z, stencil); compute_weights(x, y, z, stencil, weights); } template void compute_weights(const double x, const double y, const double z, const stencil_t& stencil, weights_t& weights) const { horizontal_interpolation_.compute_weights(x, y, stencil, weights); vertical_interpolation_.compute_weights(z, stencil, weights); } template typename std::enable_if<(array_t::RANK == 2), typename array_t::value_type>::type interpolate( const stencil_t& stencil, const weights_t& weights, const array_t& input) const { using Value = typename std::remove_const::type; std::array, stencil_width()> index; const auto& wj = weights.weights_j; const auto& wk = weights.weights_k; Value output = 0.; for (idx_t j = 0; j < stencil_width(); ++j) { const auto& wi = weights.weights_i[j]; for (idx_t i = 0; i < stencil_width(); ++i) { idx_t n = src_.index(stencil.i(i, j), stencil.j(j)); Value wij = wi[i] * wj[j]; for (idx_t k = 0; k < stencil_width(); ++k) { Value w = wij * wk[k]; output += w * input(n, stencil.k(k)); } index[j][i] = n; } } return output; } template struct OutputView1D { template Value& operator()(Int v) { return data_[v]; } template Value& operator[](Int v) { return data_[v]; } static constexpr int RANK{1}; OutputView1D(Value* data): data_(data) {} using value_type = Value; Value* data_; }; template OutputView1D make_outputview(Value* data) const { return OutputView1D(data); } template typename std::enable_if<(InputArray::RANK == 3), void>::type interpolate_vars(const stencil_t& stencil, const weights_t& weights, const InputArray& input, OutputArray& output, const idx_t nvar) const { using Value = typename InputArray::value_type; const auto& wj = weights.weights_j; const auto& wk = weights.weights_k; const Value* _input_; for (idx_t v = 0; v < nvar; ++v) { output[v] = 0.; } for (idx_t j = 0; j < stencil_width(); ++j) { const auto& wi = weights.weights_i[j]; for (idx_t i = 0; i < stencil_width(); ++i) { const idx_t n = src_.index(stencil.i(i, j), stencil.j(j)); const Value wij = wi[i] * wj[j]; for (idx_t k = 0; k < stencil_width(); ++k) { const Value w = wij * wk[k]; const idx_t kk = stencil.k(k); _input_ = &(input(n, kk, 0)); // Assumption that input.stride(2) == 1 for (idx_t v = 0; v < nvar; ++v) { output[v] += w * _input_[v]; } } } } } template typename std::enable_if<(InputArray::RANK == 2 && OutputArray::RANK == 1), void>::type interpolate( const stencil_t& stencil, const weights_t& weights, const InputArray& input, OutputArray& output, idx_t r) const { output(r) = interpolate(stencil, weights, input); } template typename std::enable_if<(InputArray::RANK == 2 && OutputArray::RANK == 2), void>::type interpolate( const stencil_t& stencil, const weights_t& weights, const InputArray& input, OutputArray& output, idx_t r, idx_t k) const { output(r, k) = interpolate(stencil, weights, input); } template typename std::enable_if<(InputArray::RANK == 3 && OutputArray::RANK == 3), void>::type interpolate( const stencil_t& stencil, const weights_t& weights, const InputArray& input, OutputArray& output, idx_t r, idx_t k) const { auto output_vars = make_outputview(&output(r, k, 0)); interpolate_vars(stencil, weights, input, output_vars, output.shape(2)); } template typename std::enable_if<(InputArray::RANK == 2 && OutputArray::RANK == 3), void>::type interpolate( const stencil_t&, const weights_t&, const InputArray&, OutputArray&, idx_t /*r*/, idx_t /*k*/) const { ATLAS_NOTIMPLEMENTED; } template typename std::enable_if<(InputArray::RANK == 3 && OutputArray::RANK == 1), void>::type interpolate( const stencil_t&, const weights_t&, const InputArray&, OutputArray&, idx_t /*r*/) const { ATLAS_NOTIMPLEMENTED; } template typename std::enable_if<(InputArray::RANK == 3 && OutputArray::RANK == 1), void>::type interpolate( const stencil_t&, const weights_t&, const InputArray&, OutputArray&, idx_t /*r*/, idx_t /*k*/) const { ATLAS_NOTIMPLEMENTED; } template typename std::enable_if<(InputArray::RANK == 3 && OutputArray::RANK == 2), void>::type interpolate( const stencil_t&, const weights_t&, const InputArray&, OutputArray&, idx_t /*r*/, idx_t /*k*/) const { ATLAS_NOTIMPLEMENTED; } }; } // namespace method } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/method/structured/kernels/Cubic3DLimiter.h0000664000175000017500000001037615160212070027716 0ustar alastairalastair/* * (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. and Interpolation */ #pragma once #include #include #include namespace atlas { namespace interpolation { namespace method { class Cubic3DLimiter { public: Cubic3DLimiter() = default; template static typename std::enable_if<(array_t::RANK == 2), void>::type limit_scalar( typename std::remove_const::type& output, const std::array, 4>& index, const stencil_t& stencil, const array_t& input) { using Scalar = typename std::remove_const::type; // Limit output to max/min of values in stencil marked by '*' // x x x x // x *-----* x // / P | // x *------ * x // x x x x idx_t k = stencil.k_interval(); idx_t k1, k2; if (k < 1) { k1 = stencil.k(0); k2 = stencil.k(1); } else if (k > 1) { k1 = stencil.k(2); k2 = stencil.k(3); } else { k1 = stencil.k(1); k2 = stencil.k(2); } Scalar maxval = std::numeric_limits::lowest(); Scalar minval = std::numeric_limits::max(); for (idx_t j = 1; j < 3; ++j) { for (idx_t i = 1; i < 3; ++i) { idx_t n = index[j][i]; Scalar f1 = input(n, k1); Scalar f2 = input(n, k2); maxval = std::max(maxval, f1); maxval = std::max(maxval, f2); minval = std::min(minval, f1); minval = std::min(minval, f2); } } if (output < minval) { output = minval; } else if (output > maxval) { output = maxval; } } template static typename std::enable_if<(InputArray::RANK == 3), void>::type limit_vars( const std::array, 4>& index, const stencil_t& stencil, const InputArray& input, OutputArray& output, const idx_t nvar) { // Limit output to max/min of values in stencil marked by '*' // x x x x // x *-----* x // / P | // x *------ * x // x x x x using Value = typename OutputArray::value_type; const idx_t k = stencil.k_interval(); idx_t k1, k2; if (k < 1) { k1 = stencil.k(0); k2 = stencil.k(1); } else if (k > 1) { k1 = stencil.k(2); k2 = stencil.k(3); } else { k1 = stencil.k(1); k2 = stencil.k(2); } for (idx_t v = 0; v < nvar; ++v) { Value limited = output[v]; Value maxval = std::numeric_limits::lowest(); Value minval = std::numeric_limits::max(); for (idx_t j = 1; j < 3; ++j) { for (idx_t i = 1; i < 3; ++i) { idx_t n = index[j][i]; Value f1 = input(n, k1, v); Value f2 = input(n, k2, v); maxval = std::max(maxval, f1); maxval = std::max(maxval, f2); minval = std::min(minval, f1); minval = std::min(minval, f2); } } if (limited < minval) { limited = minval; } else if (limited > maxval) { limited = maxval; } output[v] = limited; } } }; } // namespace method } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/method/structured/RegionalLinear2D.cc0000664000175000017500000005357215160212070026735 0ustar alastairalastair/* * (C) Copyright 2024 Meteorologisk Institutt * * 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. */ #include "atlas/interpolation/method/structured/RegionalLinear2D.h" #include #include "atlas/array.h" #include "atlas/interpolation/method/MethodFactory.h" #include "atlas/util/KDTree.h" #include "atlas/util/Point.h" namespace atlas { namespace interpolation { namespace method { namespace { using util::FactoryDeprecated; MethodBuilder __builder0("regional-bilinear"); MethodBuilder __builder1("regional-linear-2d", FactoryDeprecated("Please use regional-bilinear")); } void RegionalLinear2D::print(std::ostream&) const { ATLAS_NOTIMPLEMENTED; } void RegionalLinear2D::do_setup(const Grid& source, const Grid& target, const Cache&) { ATLAS_NOTIMPLEMENTED; } void RegionalLinear2D::do_setup(const FunctionSpace& source, const FunctionSpace& target, const Cache&) { ATLAS_NOTIMPLEMENTED; } void RegionalLinear2D::do_setup(const FunctionSpace& source, const FunctionSpace& target) { ATLAS_TRACE("interpolation::method::RegionalLinear2D::do_setup"); source_ = source; target_ = target; ASSERT(source_.type() == "StructuredColumns"); // Get grid parameters const functionspace::StructuredColumns sourceFs(source_); const RegularGrid sourceGrid(sourceFs.grid()); const Projection & sourceProj = sourceGrid.projection(); const size_t sourceNx = sourceGrid.nx(); const size_t sourceNy = sourceGrid.ny(); const double sourceDx = sourceGrid.dx(); const double sourceDy = std::abs(sourceGrid.y(1)-sourceGrid.y(0)); const bool reversedY = sourceGrid.y(1) < sourceGrid.y(0); // Check grid regularity in y direction for (size_t sourceJ = 0; sourceJ < sourceNy-1; ++sourceJ) { if (reversedY) { ASSERT(std::abs(sourceGrid.y(sourceJ)-sourceGrid.y(sourceJ+1)-sourceDy) < 1.0e-12*sourceDy); } else { ASSERT(std::abs(sourceGrid.y(sourceJ+1)-sourceGrid.y(sourceJ)-sourceDy) < 1.0e-12*sourceDy); } } // Source grid indices const auto sourceIndexIView = array::make_indexview(sourceFs.index_i()); const auto sourceIndexJView = array::make_indexview(sourceFs.index_j()); sourceSize_ = sourceFs.size(); // Destination grid size targetSize_ = target_.size(); // Ghost points const auto sourceGhostView = array::make_view(sourceFs.ghost()); const auto targetGhostView = array::make_view(target_.ghost()); // Define reduced grid horizontal distribution std::vector mpiTask(sourceNx*sourceNy, 0); for (size_t sourceJnode = 0; sourceJnode < sourceSize_; ++sourceJnode) { if (sourceGhostView(sourceJnode) == 0) { idx_t idx = sourceIndexIView(sourceJnode)*static_cast(sourceNy)+sourceIndexJView(sourceJnode); if (idx < 0 || idx >= mpiTask.size()) { throw_OutOfRange("mpiTask", idx, mpiTask.size(), Here()); } mpiTask[idx] = comm_.rank(); } } comm_.allReduceInPlace(mpiTask.begin(), mpiTask.end(), eckit::mpi::sum()); // Define local tree on destination grid std::vector targetPoints; std::vector targetIndices; targetPoints.reserve(targetSize_); targetIndices.reserve(targetSize_); const auto targetLonLatView = array::make_view(target_.lonlat()); for (size_t targetJnode = 0; targetJnode < targetSize_; ++targetJnode) { PointLonLat p({targetLonLatView(targetJnode, 0), targetLonLatView(targetJnode, 1)}); sourceProj.lonlat2xy(p); targetPoints.emplace_back(p[0], p[1], 0.0); targetIndices.emplace_back(targetJnode); } util::IndexKDTree targetTree; if (targetSize_ > 0) { targetTree.build(targetPoints, targetIndices); } const double radius = std::sqrt(sourceDx*sourceDx+sourceDy*sourceDy); // Delta for colocation const double eps = 1.0e-8; // RecvCounts and received points list targetRecvCounts_.resize(comm_.size()); std::fill(targetRecvCounts_.begin(), targetRecvCounts_.end(), 0); std::vector targetRecvPointsList; for (size_t sourceJ = 0; sourceJ < sourceNy; ++sourceJ) { double yMin, yMax; if (reversedY) { yMin = sourceJ < sourceNy-1 ? sourceGrid.y(sourceJ+1)-eps : -std::numeric_limits::max(); yMax = sourceJ > 0 ? sourceGrid.y(sourceJ-1)+eps : std::numeric_limits::max(); } else { yMin = sourceJ > 0 ? sourceGrid.y(sourceJ-1)-eps : -std::numeric_limits::max(); yMax = sourceJ < sourceNy-1 ? sourceGrid.y(sourceJ+1)+eps : std::numeric_limits::max(); } for (size_t sourceI = 0; sourceI < sourceNx; ++sourceI) { const double xMin = sourceI > 0 ? sourceGrid.x(sourceI-1)-eps : -std::numeric_limits::max(); const double xMax = sourceI < sourceNx-1 ? sourceGrid.x(sourceI+1)+eps : std::numeric_limits::max(); bool pointsNeeded = false; if (targetSize_ > 0) { const Point3 p(sourceGrid.x(sourceI), sourceGrid.y(sourceJ), 0.0); const auto list = targetTree.closestPointsWithinRadius(p, radius); for (const auto & item : list) { const PointXYZ targetPoint = item.point(); const size_t targetJnode = item.payload(); if (targetGhostView(targetJnode) == 0) { const bool inX = (xMin <= targetPoint[0] && targetPoint[0] <= xMax); const bool inY = (yMin <= targetPoint[1] && targetPoint[1] <= yMax); if (inX && inY) { pointsNeeded = true; break; } } } } if (pointsNeeded) { ++targetRecvCounts_[mpiTask[sourceI*sourceNy+sourceJ]]; targetRecvPointsList.push_back(sourceI*sourceNy+sourceJ); } } } // Buffer size targetRecvSize_ = targetRecvPointsList.size(); { // RecvDispls targetRecvDispls_.push_back(0); for (size_t jt = 0; jt < comm_.size()-1; ++jt) { targetRecvDispls_.push_back(targetRecvDispls_[jt]+targetRecvCounts_[jt]); } // Allgather RecvCounts eckit::mpi::Buffer targetRecvCountsBuffer(comm_.size()); comm_.allGatherv(targetRecvCounts_.begin(), targetRecvCounts_.end(), targetRecvCountsBuffer); std::vector targetRecvCountsGlb_ = std::move(targetRecvCountsBuffer.buffer); // SendCounts for (size_t jt = 0; jt < comm_.size(); ++jt) { sourceSendCounts_.push_back(targetRecvCountsGlb_[jt*comm_.size()+comm_.rank()]); } // Buffer size sourceSendSize_ = 0; for (const auto & n : sourceSendCounts_) { sourceSendSize_ += n; } // SendDispls sourceSendDispls_.push_back(0); for (size_t jt = 0; jt < comm_.size()-1; ++jt) { sourceSendDispls_.push_back(sourceSendDispls_[jt]+sourceSendCounts_[jt]); } // Ordered received points list std::vector targetRecvOffset(comm_.size(), 0); std::vector targetRecvPointsListOrdered(targetRecvSize_); for (size_t jr = 0; jr < targetRecvSize_; ++jr) { const size_t sourceI = targetRecvPointsList[jr]/sourceNy; const size_t sourceJ = targetRecvPointsList[jr]-sourceI*sourceNy; size_t jt = mpiTask[sourceI*sourceNy+sourceJ]; size_t jro = targetRecvDispls_[jt]+targetRecvOffset[jt]; targetRecvPointsListOrdered[jro] = targetRecvPointsList[jr]; ++targetRecvOffset[jt]; } std::vector sourceSentPointsList(sourceSendSize_); comm_.allToAllv(targetRecvPointsListOrdered.data(), targetRecvCounts_.data(), targetRecvDispls_.data(), sourceSentPointsList.data(), sourceSendCounts_.data(), sourceSendDispls_.data()); // Sort indices std::vector gij; for (size_t sourceJnode = 0; sourceJnode < sourceSize_; ++sourceJnode) { if (sourceGhostView(sourceJnode) == 0) { gij.push_back(sourceIndexIView(sourceJnode)*sourceNy+sourceIndexJView(sourceJnode)); } else { gij.push_back(-1); } } std::vector gidx(sourceSize_); std::iota(gidx.begin(), gidx.end(), 0); std::stable_sort(gidx.begin(), gidx.end(), [&gij](size_t i1, size_t i2) {return gij[i1] < gij[i2];}); std::vector ridx(sourceSendSize_); std::iota(ridx.begin(), ridx.end(), 0); std::stable_sort(ridx.begin(), ridx.end(), [&sourceSentPointsList](size_t i1, size_t i2) {return sourceSentPointsList[i1] < sourceSentPointsList[i2];}); // Mapping for sent points sourceSendMapping_.resize(sourceSendSize_); size_t sourceJnode = 0; for (size_t js = 0; js < sourceSendSize_; ++js) { while (gij[gidx[sourceJnode]] < sourceSentPointsList[ridx[js]]) { ++sourceJnode; ASSERT(sourceJnode < sourceSize_); } sourceSendMapping_[ridx[js]] = gidx[sourceJnode]; } // Sort indices std::vector idx(targetRecvPointsListOrdered.size()); std::iota(idx.begin(), idx.end(), 0); std::stable_sort(idx.begin(), idx.end(), [&targetRecvPointsListOrdered](size_t i1, size_t i2) {return targetRecvPointsListOrdered[i1] < targetRecvPointsListOrdered[i2];}); // Compute horizontal interpolation stencil_.resize(targetSize_); weights_.resize(targetSize_); stencilSize_.resize(targetSize_); for (size_t targetJnode = 0; targetJnode < targetSize_; ++targetJnode) { // Interpolation element default values if (targetGhostView(targetJnode) == 0) { // Destination grid indices const double targetX = targetPoints[targetJnode][0]; bool colocatedX = false; int indexI = -1; for (size_t sourceI = 0; sourceI < sourceNx-1; ++sourceI) { if (std::abs(targetX-sourceGrid.x(sourceI)) < eps) { indexI = sourceI; colocatedX = true; } if (sourceGrid.x(sourceI)+eps < targetX && targetX < sourceGrid.x(sourceI+1)-eps) { indexI = sourceI; colocatedX = false; } } if (std::abs(targetX-sourceGrid.x(sourceNx-1)) < eps) { indexI = sourceNx-1; colocatedX = true; } const double targetY = targetPoints[targetJnode][1]; bool colocatedY = false; int indexJ = -1; for (size_t sourceJ = 0; sourceJ < sourceNy-1; ++sourceJ) { if (std::abs(targetY-sourceGrid.y(sourceJ)) < eps) { indexJ = sourceJ; colocatedY = true; } if (reversedY) { if (sourceGrid.y(sourceJ+1)+eps < targetY && targetY < sourceGrid.y(sourceJ)-eps) { indexJ = sourceJ; colocatedY = false; } } else { if (sourceGrid.y(sourceJ)+eps < targetY && targetY < sourceGrid.y(sourceJ+1)-eps) { indexJ = sourceJ; colocatedY = false; } } } if (std::abs(targetY-sourceGrid.y(sourceNy-1)) < eps) { indexJ = sourceNy-1; colocatedY = true; } if (indexI == -1 || indexJ == -1) { // Point outside of the domain, using nearest neighbor if (indexI > -1) { if (!colocatedX && (std::abs(targetX-sourceGrid.x(indexI+1)) < std::abs(targetX-sourceGrid.x(indexI)))) { indexI += 1; } } else { if (std::abs(targetX-sourceGrid.x(0)) < std::abs(targetX-sourceGrid.x(sourceNx-1))) { indexI = 0; } else { indexI = sourceNx-1; } } if (indexJ > -1) { if (!colocatedY && (std::abs(targetY-sourceGrid.y(indexJ+1)) < std::abs(targetY-sourceGrid.y(indexJ)))) { indexJ += 1; } } else { if (std::abs(targetY-sourceGrid.y(0)) < std::abs(targetY-sourceGrid.y(sourceNy-1))) { indexJ = 0; } else { indexJ = sourceNy-1; } Log::info() << "WARNING: point outside of the domain" << std::endl; } // Colocated point (actually nearest neighbor) colocatedX = true; colocatedY = true; } // Bilinear interpolation factor const double alphaX = 1.0-(sourceGrid.x(indexI)+sourceDx-targetX)/sourceDx; const double alphaY = reversedY ? (sourceGrid.y(indexJ)-targetY)/sourceDy : 1.0-(sourceGrid.y(indexJ)+sourceDy-targetY)/sourceDy; // Points to find std::vector toFind = {true, !colocatedX, !colocatedY, !colocatedX && !colocatedY}; std::vector valueToFind = {indexI*sourceNy+indexJ, (indexI+1)*sourceNy+indexJ, indexI*sourceNy+(indexJ+1), (indexI+1)*sourceNy+(indexJ+1)}; std::array foundIndex; foundIndex.fill(-1); // Binary search for each point for (size_t jj = 0; jj < 4; ++jj) { if (toFind[jj]) { size_t low = 0; size_t high = targetRecvPointsListOrdered.size()-1; while (low <= high) { size_t mid = low+(high-low)/2; if (valueToFind[jj] == static_cast(targetRecvPointsListOrdered[idx[mid]])) { foundIndex[jj] = idx[mid]; break; } if (valueToFind[jj] > static_cast(targetRecvPointsListOrdered[idx[mid]])) { low = mid+1; } if (valueToFind[jj] < static_cast(targetRecvPointsListOrdered[idx[mid]])) { high = mid-1; } } ASSERT(foundIndex[jj] > -1); ASSERT(static_cast(targetRecvPointsListOrdered[foundIndex[jj]]) == valueToFind[jj]); } } // Create interpolation operations if (colocatedX && colocatedY) { // Colocated point stencil_[targetJnode][0] = foundIndex[0]; weights_[targetJnode][0] = 1.0; stencilSize_[targetJnode] = 1; } else if (colocatedY) { // Linear interpolation along x stencil_[targetJnode][0] = foundIndex[0]; weights_[targetJnode][0] = 1.0-alphaX; stencil_[targetJnode][1] = foundIndex[1]; weights_[targetJnode][1] = alphaX; stencilSize_[targetJnode] = 2; } else if (colocatedX) { // Linear interpolation along y stencil_[targetJnode][0] = foundIndex[0]; weights_[targetJnode][0] = 1.0-alphaY; stencil_[targetJnode][1] = foundIndex[2]; weights_[targetJnode][1] = alphaY; stencilSize_[targetJnode] = 2; } else { // Bilinear interpolation stencil_[targetJnode][0] = foundIndex[0]; weights_[targetJnode][0] = (1.0-alphaX)*(1.0-alphaY); stencil_[targetJnode][1] = foundIndex[1]; weights_[targetJnode][1] = alphaX*(1.0-alphaY); stencil_[targetJnode][2] = foundIndex[2]; weights_[targetJnode][2] = (1.0-alphaX)*alphaY; stencil_[targetJnode][3] = foundIndex[3]; weights_[targetJnode][3] = alphaX*alphaY; stencilSize_[targetJnode] = 4; } } else { // Ghost point stencilSize_[targetJnode] = 0; } } } } void RegionalLinear2D::do_execute(const FieldSet& sourceFieldSet, FieldSet& targetFieldSet, Metadata& metadata) const { ATLAS_TRACE("atlas::interpolation::method::RegionalLinear2D::do_execute()"); ATLAS_ASSERT(sourceFieldSet.size() == targetFieldSet.size()); for (auto i = 0; i < sourceFieldSet.size(); ++i) { do_execute(sourceFieldSet[i], targetFieldSet[i], metadata); } } void RegionalLinear2D::do_execute(const Field& sourceField, Field& targetField, Metadata&) const { ATLAS_TRACE("atlas::interpolation::method::RegionalLinear2D::do_execute()"); // Check number of levels ASSERT(sourceField.levels() == targetField.levels()); const size_t nz = sourceField.levels() > 0 ? sourceField.levels() : 1; const size_t ndim = sourceField.levels() > 0 ? 2 : 1; // Scale counts and displs for all levels std::vector sourceSendCounts3D(comm_.size()); std::vector sourceSendDispls3D(comm_.size()); std::vector targetRecvCounts3D(comm_.size()); std::vector targetRecvDispls3D(comm_.size()); for (size_t jt = 0; jt < comm_.size(); ++jt) { sourceSendCounts3D[jt] = sourceSendCounts_[jt]*nz; sourceSendDispls3D[jt] = sourceSendDispls_[jt]*nz; targetRecvCounts3D[jt] = targetRecvCounts_[jt]*nz; targetRecvDispls3D[jt] = targetRecvDispls_[jt]*nz; } // Halo exchange haloExchange(sourceField); // Serialize std::vector sourceSendVec(sourceSendSize_*nz); if (ndim == 1) { const auto sourceView = array::make_view(sourceField); for (size_t js = 0; js < sourceSendSize_; ++js) { size_t sourceJnode = sourceSendMapping_[js]; sourceSendVec[js] = sourceView(sourceJnode); } } else if (ndim == 2) { const auto sourceView = array::make_view(sourceField); for (size_t js = 0; js < sourceSendSize_; ++js) { for (size_t k = 0; k < nz; ++k) { size_t sourceJnode = sourceSendMapping_[js]; sourceSendVec[js*nz+k] = sourceView(sourceJnode, k); } } } // Communication std::vector targetRecvVec(targetRecvSize_*nz); comm_.allToAllv(sourceSendVec.data(), sourceSendCounts3D.data(), sourceSendDispls3D.data(), targetRecvVec.data(), targetRecvCounts3D.data(), targetRecvDispls3D.data()); // Interpolation if (ndim == 1) { auto targetView = array::make_view(targetField); targetView.assign(0.0); for (size_t targetJnode = 0; targetJnode < targetSize_; ++targetJnode) { for (size_t jj = 0; jj < stencilSize_[targetJnode]; ++jj) { targetView(targetJnode) += weights_[targetJnode][jj] *targetRecvVec[stencil_[targetJnode][jj]]; } } } else if (ndim == 2) { auto targetView = array::make_view(targetField); targetView.assign(0.0); for (size_t targetJnode = 0; targetJnode < targetSize_; ++targetJnode) { for (size_t jj = 0; jj < stencilSize_[targetJnode]; ++jj) { for (size_t k = 0; k < nz; ++k) { targetView(targetJnode, k) += weights_[targetJnode][jj] *targetRecvVec[stencil_[targetJnode][jj]*nz+k]; } } } } // Set target field dirty targetField.set_dirty(); } void RegionalLinear2D::do_execute_adjoint(FieldSet& sourceFieldSet, const FieldSet& targetFieldSet, Metadata& metadata) const { ATLAS_TRACE( "atlas::interpolation::method::RegionalLinear2D::do_execute_adjoint()"); ATLAS_ASSERT(sourceFieldSet.size() == targetFieldSet.size()); for (auto i = 0; i < sourceFieldSet.size(); ++i) { do_execute_adjoint(sourceFieldSet[i], targetFieldSet[i], metadata); } } void RegionalLinear2D::do_execute_adjoint(Field& sourceField, const Field& targetField, Metadata& metadata) const { ATLAS_TRACE( "atlas::interpolation::method::RegionalLinear2D::do_execute_adjoint()"); if (targetField.size() == 0) { return; } // Check number of levels ASSERT(sourceField.levels() == targetField.levels()); const size_t nz = sourceField.levels() > 0 ? sourceField.levels() : 1; const size_t ndim = sourceField.levels() > 0 ? 2 : 1; // Scale counts and displs for all levels std::vector sourceSendCounts3D(comm_.size()); std::vector sourceSendDispls3D(comm_.size()); std::vector targetRecvCounts3D(comm_.size()); std::vector targetRecvDispls3D(comm_.size()); for (size_t jt = 0; jt < comm_.size(); ++jt) { sourceSendCounts3D[jt] = sourceSendCounts_[jt]*nz; sourceSendDispls3D[jt] = sourceSendDispls_[jt]*nz; targetRecvCounts3D[jt] = targetRecvCounts_[jt]*nz; targetRecvDispls3D[jt] = targetRecvDispls_[jt]*nz; } // Copy destination field Field targetTmpField = targetField.clone(); // Interpolation adjoint std::vector targetRecvVec(targetRecvSize_*nz, 0.0); if (ndim == 1) { const auto targetView = array::make_view(targetTmpField); for (size_t targetJnode = 0; targetJnode < targetSize_; ++targetJnode) { for (size_t jj = 0; jj < stencilSize_[targetJnode]; ++jj) { targetRecvVec[stencil_[targetJnode][jj]] += weights_[targetJnode][jj] *targetView(targetJnode); } } } else if (ndim == 2) { const auto targetView = array::make_view(targetTmpField); for (size_t targetJnode = 0; targetJnode < targetSize_; ++targetJnode) { for (size_t jj = 0; jj < stencilSize_[targetJnode]; ++jj) { for (size_t k = 0; k < nz; ++k) { targetRecvVec[stencil_[targetJnode][jj]*nz+k] += weights_[targetJnode][jj] *targetView(targetJnode, k); } } } } // Communication std::vector sourceSendVec(sourceSendSize_*nz); comm_.allToAllv(targetRecvVec.data(), targetRecvCounts3D.data(), targetRecvDispls3D.data(), sourceSendVec.data(), sourceSendCounts3D.data(), sourceSendDispls3D.data()); // Deserialize if (ndim == 1) { auto sourceView = array::make_view(sourceField); sourceView.assign(0.0); for (size_t js = 0; js < sourceSendSize_; ++js) { size_t sourceJnode = sourceSendMapping_[js]; sourceView(sourceJnode) += sourceSendVec[js]; } } else if (ndim == 2) { auto sourceView = array::make_view(sourceField); sourceView.assign(0.0); for (size_t js = 0; js < sourceSendSize_; ++js) { size_t sourceJnode = sourceSendMapping_[js]; for (size_t k = 0; k < nz; ++k) { sourceView(sourceJnode, k) += sourceSendVec[js*nz+k]; } } } // Adjoint halo exchange adjointHaloExchange(sourceField); } } // namespace method } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/method/structured/Cubic2D.h0000664000175000017500000000137015160212070024716 0ustar alastairalastair/* * (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. and Interpolation */ #pragma once #include "StructuredInterpolation2D.h" #include "kernels/CubicHorizontalKernel.h" namespace atlas { namespace interpolation { namespace method { class Cubic2D : public StructuredInterpolation2D { public: Cubic2D(const Config&); }; } // namespace method } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/method/structured/Linear3D.cc0000664000175000017500000000221415160212070025240 0ustar alastairalastair/* * (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. and Interpolation */ #include "Linear3D.h" #include "atlas/interpolation/method/MethodFactory.h" namespace atlas { namespace interpolation { namespace method { namespace { using util::FactoryDeprecated; MethodBuilder __builder1("structured-trilinear"); MethodBuilder __builder2("structured-linear3D", FactoryDeprecated("Please use structured-trilinear")); MethodBuilder __builder3("linear3D", FactoryDeprecated("Please use structured-trilinear")); MethodBuilder __builder4("trilinear", FactoryDeprecated("Please use structured-trilinear")); } // namespace Linear3D::Linear3D(const Config& config): StructuredInterpolation3D(config) {} } // namespace method } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/method/structured/Linear2D.cc0000664000175000017500000000221715160212070025242 0ustar alastairalastair/* * (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. and Interpolation */ #include "Linear2D.h" #include "atlas/interpolation/method/MethodFactory.h" namespace atlas { namespace interpolation { namespace method { namespace { using util::FactoryDeprecated; MethodBuilder __builder1("structured-bilinear"); MethodBuilder __builder2("structured-linear2D", FactoryDeprecated("Please use structured-bilinear")); MethodBuilder __builder3("linear2D", FactoryDeprecated("Please use structured-bilinear")); MethodBuilder __builder4("bilinear", FactoryDeprecated("Please use structured-bilinear")); } // namespace Linear2D::Linear2D(const Config& config): StructuredInterpolation2D(config) {} } // namespace method } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/method/structured/Cubic2D.cc0000664000175000017500000000220015160212070025045 0ustar alastairalastair/* * (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. and Interpolation */ #include "Cubic2D.h" #include "atlas/interpolation/method/MethodFactory.h" namespace atlas { namespace interpolation { namespace method { namespace { using util::FactoryDeprecated; MethodBuilder __builder1("structured-bicubic"); MethodBuilder __builder2("structured-cubic2D", FactoryDeprecated("Please use structured-bicubic")); MethodBuilder __builder3("cubic2D", FactoryDeprecated("Please use structured-bicubic")); MethodBuilder __builder4("bicubic", FactoryDeprecated("Please use structured-bicubic")); } // namespace Cubic2D::Cubic2D(const Config& config): StructuredInterpolation2D(config) {} } // namespace method } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/method/structured/Linear2D.h0000664000175000017500000000137415160212070025107 0ustar alastairalastair/* * (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. and Interpolation */ #pragma once #include "StructuredInterpolation2D.h" #include "kernels/LinearHorizontalKernel.h" namespace atlas { namespace interpolation { namespace method { class Linear2D : public StructuredInterpolation2D { public: Linear2D(const Config&); }; } // namespace method } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/method/structured/Cubic3D.cc0000664000175000017500000000217715160212070025063 0ustar alastairalastair/* * (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. and Interpolation */ #include "Cubic3D.h" #include "atlas/interpolation/method/MethodFactory.h" namespace atlas { namespace interpolation { namespace method { namespace { using util::FactoryDeprecated; MethodBuilder __builder1("structured-tricubic"); MethodBuilder __builder2("structured-bicubic3D", FactoryDeprecated("Please use structured-tricubic")); MethodBuilder __builder3("cubic3D", FactoryDeprecated("Please use structured-tricubic")); MethodBuilder __builder4("tricubic", FactoryDeprecated("Please use structured-tricubic")); } // namespace Cubic3D::Cubic3D(const Config& config): StructuredInterpolation3D(config) {} } // namespace method } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/method/structured/QuasiCubic3D.cc0000664000175000017500000000231015160212070026053 0ustar alastairalastair/* * (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. and Interpolation */ #include "QuasiCubic3D.h" #include "atlas/interpolation/method/MethodFactory.h" namespace atlas { namespace interpolation { namespace method { namespace { using util::FactoryDeprecated; MethodBuilder __builder1("structured-triquasicubic"); MethodBuilder __builder2("structured-quasicubic3D", FactoryDeprecated("Please use structured-triquasicubic")); MethodBuilder __builder3("quasicubic3D", FactoryDeprecated("Please use structured-triquasicubic")); MethodBuilder __builder4("triquasicubic", FactoryDeprecated("Please use structured-triquasicubic")); } // namespace QuasiCubic3D::QuasiCubic3D(const Config& config): StructuredInterpolation3D(config) {} } // namespace method } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/method/structured/QuasiCubic3D.h0000664000175000017500000000137415160212070025726 0ustar alastairalastair/* * (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. and Interpolation */ #pragma once #include "StructuredInterpolation3D.h" #include "kernels/QuasiCubic3DKernel.h" namespace atlas { namespace interpolation { namespace method { class QuasiCubic3D : public StructuredInterpolation3D { public: QuasiCubic3D(const Config&); }; } // namespace method } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/method/structured/StructuredInterpolation2D.tcc0000664000175000017500000005550415160212070031137 0ustar alastairalastair/* * (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. and Interpolation */ #pragma once #include "StructuredInterpolation2D.h" #include #include #include #include #include #include #include #include #include "eckit/filesystem/LocalPathName.h" #include "atlas/array/ArrayView.h" #include "atlas/field/Field.h" #include "atlas/field/FieldSet.h" #include "atlas/functionspace/NodeColumns.h" #include "atlas/functionspace/PointCloud.h" #include "atlas/functionspace/StructuredColumns.h" #include "atlas/grid/Grid.h" #include "atlas/grid/StructuredGrid.h" #include "atlas/mesh/Nodes.h" #include "atlas/parallel/mpi/mpi.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/CoordinateEnums.h" #include "atlas/util/NormaliseLongitude.h" #include "atlas/util/Point.h" // With nvidia/22.1, following error is encountered during interpolation. // NVC++-S-0000-Internal compiler error. BAD sptr in var_refsym 0 // It is observed to have been fixed with nvidia/22.11 // Disabling OpenMP in this routine for nvidia < 22.11 seems to fix things #if defined(__NVCOMPILER) #if (__NVCOMPILER_MAJOR__*100) + __NVCOMPILER_MINOR__ < 2211 #warning "Disabled OpenMP for StructuredInterpolation2D due to internal compiler error" #undef atlas_omp_parallel #define atlas_omp_parallel #undef atlas_omp_for #define atlas_omp_for for #endif #endif namespace atlas { namespace interpolation { namespace method { namespace structured2d { namespace { inline std::string search_replace(const std::string& in, const std::string& search, const std::string& replace) { std::string out = in; int pos = out.find(search); while (pos != std::string::npos) { out.erase(pos, search.length()); out.insert(pos, replace); pos = out.find(search, pos + replace.length()); } return out; } inline std::string left_padded_str(int x, int max = 0) { auto digits = [](long x) -> long { return std::floor(std::log10(std::max(1l, x))) + 1l; }; if (max) { std::ostringstream ss; ss << std::setw(digits(max)) << std::setfill('0') << x; return ss.str(); } return std::to_string(x); } inline std::string filename( const std::string& path ){ return search_replace(path, "%p", left_padded_str(mpi::rank(),mpi::size())); } template inline std::string to_str (const std::vector& points, LonLat lonlat) { std::ostringstream out; out << "[\n"; for( size_t j=0; j inline void output_target_points(const std::string& path, const std::vector& points, LonLat lonlat) { std::ofstream f(filename(path)); f << to_str(points,lonlat); } template inline void handle_failed_points(const Interpolation& interpolation, const std::vector& failed_points, LonLat lonlat ) { const auto src_fs = functionspace::StructuredColumns( interpolation.source() ); size_t num_failed_points{0}; mpi::comm().allReduce(failed_points.size(), num_failed_points, eckit::mpi::sum()); if( num_failed_points > 0 ) { std::string halo_str = std::to_string(src_fs.halo()); output_source_partition_polygon("atlas_source_partition_polygons_halo_0_p%p.json",src_fs,0); output_source_partition_polygon("atlas_source_partition_polygons_halo_" + halo_str + "_p%p.json",src_fs,src_fs.halo()); output_target_points("atlas_target_failed_points_p%p.json", failed_points, lonlat); idx_t my_rank = mpi::rank(); for( idx_t p=0; p"); f << src_fs_config.json(); } std::string p_range = "{"+left_padded_str(0,mpi::size()-1)+".."+left_padded_str(mpi::size()-1,mpi::size()-1)+"}"; Log::info() << "Dumped files to " << eckit::LocalPathName::cwd() << ": \n" << " atlas_target_failed_points_p" << p_range << ".json\n" << " atlas_source_partition_polygons_halo_0_p" << p_range << ".json\n" << " atlas_source_partition_polygons_halo_0.json\n" << " atlas_source_partition_polygons_halo_" << src_fs.halo() << "_p" << p_range << ".json\n" << " atlas_source_partition_polygons_halo_" << src_fs.halo() << ".json\n" << " atlas_interpolation_info.json\n" << std::endl; std::ostringstream err; err << "StructuredInterpolation2D<" << interpolation.kernel().className() << "> failed for " << num_failed_points << " points with source halo="< double StructuredInterpolation2D::convert_units_multiplier( const Field& field ) { std::string units = field.metadata().getString( "units", "degrees" ); if ( units == "degrees" ) { return 1.; } if ( units == "radians" ) { return 180. / M_PI; } ATLAS_NOTIMPLEMENTED; } template StructuredInterpolation2D::StructuredInterpolation2D( const Method::Config& config ) : Method( config ), verbose_{false}, limiter_{false}, matrix_free_{false} { config.get( "verbose", verbose_ ); config.get( "limiter", limiter_ ); config.get( "matrix_free", matrix_free_ ); if (limiter_ && not matrix_free_) { ATLAS_THROW_EXCEPTION("Cannot apply configuration 'limiter=true' and 'matrix_free=false' together"); } } template void StructuredInterpolation2D::do_setup( const Grid& source, const Grid& target, const Cache& ) { ATLAS_TRACE( "StructuredInterpolation2D<" + Kernel::className() + ">::do_setup(Grid source, Grid target)" ); if ( mpi::size() > 1 ) { ATLAS_NOTIMPLEMENTED; } ATLAS_ASSERT( StructuredGrid( source ) ); FunctionSpace source_fs = functionspace::StructuredColumns( source, option::halo( std::max( kernel_->stencil_halo(), 1 ) ) ); // guarantee "1" halo for pole treatment! FunctionSpace target_fs = functionspace::PointCloud( target ); do_setup( source_fs, target_fs ); } template void StructuredInterpolation2D::do_setup( const FunctionSpace& source, const FunctionSpace& target, const Cache& cache) { ATLAS_TRACE( "StructuredInterpolation2D<" + Kernel::className() + ">::do_setup(FunctionSpace source, FunctionSpace target)" ); if (! matrix_free_ && interpolation::MatrixCache(cache)) { setMatrix(cache); source_ = source; target_ = target; ATLAS_ASSERT(matrix().rows() == target.size()); ATLAS_ASSERT(matrix().cols() == source.size()); return; } else { do_setup(source, target); } } template void StructuredInterpolation2D::do_setup( const FunctionSpace& source, const FunctionSpace& target ) { ATLAS_TRACE( "StructuredInterpolation2D<" + Kernel::className() + ">::do_setup(FS source, FS target)" ); source_ = source; target_ = target; if ( functionspace::NodeColumns tgt = target ) { target_lonlat_ = tgt.mesh().nodes().lonlat(); target_ghost_ = tgt.mesh().nodes().ghost(); } else if ( functionspace::PointCloud tgt = target ) { target_lonlat_ = tgt.lonlat(); target_ghost_ = tgt.ghost(); } else if ( functionspace::StructuredColumns tgt = target ) { target_lonlat_ = tgt.xy(); target_ghost_ = tgt.ghost(); } else { throw_NotImplemented( "Only interpolation to functionspaces NodeColumns, PointCloud or StructuredColumns are implemented", Here() ); } setup( source ); } template void StructuredInterpolation2D::do_setup( const FunctionSpace& source, const Field& target ) { ATLAS_TRACE( "StructuredInterpolation<" + Kernel::className() + ">::do_setup(FunctionSpace source, Field target)" ); source_ = source; if ( target.functionspace() ) { target_ = target.functionspace(); } target_lonlat_ = target; setup( source ); } template void StructuredInterpolation2D::do_setup( const FunctionSpace& source, const FieldSet& target ) { ATLAS_TRACE( "StructuredInterpolation<" + Kernel::className() + ">::do_setup(FunctionSpace source,FieldSet target)" ); source_ = source; ATLAS_ASSERT( target.size() >= 2 ); if ( target[0].functionspace() ) { target_ = target[0].functionspace(); } target_lonlat_fields_ = target; setup( source ); } template void StructuredInterpolation2D::print( std::ostream& ) const { ATLAS_NOTIMPLEMENTED; } template void StructuredInterpolation2D::setup( const FunctionSpace& source ) { using namespace structured2d; kernel_.reset( new Kernel( source, util::Config( "limiter", limiter_ ) ) ); if ( functionspace::StructuredColumns( source ).halo() < 1 ) { throw_Exception( "The source functionspace must have (halo >= 1) for pole treatment" ); } out_npts_ = 0; if( target_lonlat_ ) { convert_units_ = convert_units_multiplier(target_lonlat_); out_npts_ = target_lonlat_.shape( 0 ); } else if( target_lonlat_fields_.empty() ) { convert_units_ = convert_units_multiplier(target_lonlat_fields_[LON]); out_npts_ = target_lonlat_fields_[0].shape(0); } if ( not matrix_free_ ) { ATLAS_TRACE( "Precomputing interpolation matrix" ); std::vector failed_points; auto triplets = kernel_->allocate_triplets( out_npts_ ); using WorkSpace = typename Kernel::WorkSpace; auto interpolate_point = [&]( idx_t n, PointLonLat&& p, WorkSpace& workspace ) -> int { try { kernel_->insert_triplets( n, p, triplets, workspace ); return 0; } catch(const eckit::Exception& e) {} if (verbose_) { Log::error() << "Could not interpolate point " << n << " :\t" << p << std::endl; } return 1; }; auto interpolate_omp = [&failed_points,interpolate_point]( idx_t out_npts, auto lonlat, auto ghost) { atlas_omp_parallel { WorkSpace workspace; atlas_omp_for( idx_t n = 0; n < out_npts; ++n ) { if( not ghost(n) ) { if (interpolate_point(n, lonlat(n), workspace) != 0) { atlas_omp_critical { failed_points.emplace_back(n); } } } } } }; if ( target_lonlat_ ) { auto lonlat_view = array::make_view( target_lonlat_ ); auto lonlat = [lonlat_view, convert_units = convert_units_] (idx_t n) { return PointLonLat{lonlat_view(n,LON) * convert_units, lonlat_view(n,LAT) * convert_units}; }; if( out_npts_ != 0 ) { if ( target_ghost_ ) { auto ghost = array::make_view( target_ghost_ ); interpolate_omp(out_npts_, lonlat, ghost); } else { auto no_ghost = [](idx_t n) { return false; }; interpolate_omp(out_npts_, lonlat, no_ghost); } } handle_failed_points(*this, failed_points, lonlat); } else if ( not target_lonlat_fields_.empty() ) { const auto lon = array::make_view( target_lonlat_fields_[LON] ); const auto lat = array::make_view( target_lonlat_fields_[LAT] ); auto lonlat = [lon, lat, convert_units = convert_units_] (idx_t n) { return PointLonLat{lon(n) * convert_units, lat(n) * convert_units}; }; if( out_npts_ != 0) { if ( target_ghost_ ) { auto ghost = array::make_view( target_ghost_ ); interpolate_omp(out_npts_, lonlat, ghost); } else { auto no_ghost = [](idx_t n) { return false; }; interpolate_omp(out_npts_, lonlat, no_ghost); } } handle_failed_points(*this, failed_points, lonlat); } else { ATLAS_NOTIMPLEMENTED; } // fill sparse matrix if( failed_points.empty() && out_npts_) { idx_t inp_npts = source.size(); setMatrix(out_npts_, inp_npts, triplets); } } } template void StructuredInterpolation2D::do_execute( const Field& src_field, Field& tgt_field, Metadata& metadata ) const { FieldSet tgt( tgt_field ); do_execute( FieldSet( src_field ), tgt, metadata ); } template void StructuredInterpolation2D::do_execute( const FieldSet& src_fields, FieldSet& tgt_fields, Metadata& metadata ) const { if ( not matrix_free_ ) { Method::do_execute( src_fields, tgt_fields, metadata ); return; } ATLAS_TRACE( "StructuredInterpolation<" + Kernel::className() + ">::do_execute()" ); const idx_t N = src_fields.size(); ATLAS_ASSERT( N == tgt_fields.size() ); if ( N == 0 ) return; haloExchange( src_fields ); array::DataType datatype = src_fields[0].datatype(); int rank = src_fields[0].rank(); for ( idx_t i = 0; i < N; ++i ) { ATLAS_ASSERT( src_fields[i].datatype() == datatype ); ATLAS_ASSERT( src_fields[i].rank() == rank ); ATLAS_ASSERT( tgt_fields[i].datatype() == datatype ); ATLAS_ASSERT( tgt_fields[i].rank() == rank ); } if ( datatype.kind() == array::DataType::KIND_REAL64 && rank == 1 ) { execute_impl( *kernel_, src_fields, tgt_fields ); } else if ( datatype.kind() == array::DataType::KIND_REAL32 && rank == 1 ) { execute_impl( *kernel_, src_fields, tgt_fields ); } else if ( datatype.kind() == array::DataType::KIND_REAL64 && rank == 2 ) { execute_impl( *kernel_, src_fields, tgt_fields ); } else if ( datatype.kind() == array::DataType::KIND_REAL32 && rank == 2 ) { execute_impl( *kernel_, src_fields, tgt_fields ); } else if ( datatype.kind() == array::DataType::KIND_REAL64 && rank == 3 ) { execute_impl( *kernel_, src_fields, tgt_fields ); } else if ( datatype.kind() == array::DataType::KIND_REAL32 && rank == 3 ) { execute_impl( *kernel_, src_fields, tgt_fields ); } else { ATLAS_NOTIMPLEMENTED; } tgt_fields.set_dirty(); } template template void StructuredInterpolation2D::execute_impl( const Kernel& kernel, const FieldSet& src_fields, FieldSet& tgt_fields ) const { using namespace structured2d; const idx_t N = src_fields.size(); std::vector > src_view; std::vector > tgt_view; src_view.reserve( N ); tgt_view.reserve( N ); for ( idx_t i = 0; i < N; ++i ) { src_view.emplace_back( array::make_view( src_fields[i] ) ); tgt_view.emplace_back( array::make_view( tgt_fields[i] ) ); } using WorkSpace = typename Kernel::WorkSpace; auto interpolate_point = [&]( idx_t n, PointLonLat&& p, WorkSpace& workspace ) -> int { try { kernel.compute_stencil( p.lon(), p.lat(), workspace.stencil ); kernel.compute_weights( p.lon(), p.lat(), workspace.stencil, workspace.weights ); kernel.make_valid_stencil( p.lon(), p.lat(), workspace.stencil ); for ( idx_t i = 0; i < N; ++i ) { kernel.interpolate( workspace.stencil, workspace.weights, src_view[i], tgt_view[i], n ); } return 0; } catch(const eckit::Exception& e) {} if (verbose_) { Log::error() << "Could not interpolate point " << n << " :\t" << p << std::endl; } return 1; }; std::vector failed_points; auto interpolate_omp = [&failed_points,interpolate_point]( idx_t out_npts, auto lonlat, auto ghost) { atlas_omp_parallel { WorkSpace workspace; atlas_omp_for( idx_t n = 0; n < out_npts; ++n ) { if( not ghost(n) ) { if (interpolate_point(n, lonlat(n), workspace) != 0) { atlas_omp_critical { failed_points.emplace_back(n); } } } } } }; if ( target_lonlat_ ) { auto lonlat_view = array::make_view( target_lonlat_ ); auto lonlat = [lonlat_view, convert_units = convert_units_] (idx_t n) { return PointLonLat{lonlat_view(n,LON) * convert_units, lonlat_view(n,LAT) * convert_units}; }; if( out_npts_ != 0 ) { if ( target_ghost_ ) { auto ghost = array::make_view( target_ghost_ ); interpolate_omp(out_npts_, lonlat, ghost); } else { auto no_ghost = [](idx_t n) { return false; }; interpolate_omp(out_npts_, lonlat, no_ghost); } } handle_failed_points(*this, failed_points, lonlat); } else if ( not target_lonlat_fields_.empty() ) { const auto lon = array::make_view( target_lonlat_fields_[LON] ); const auto lat = array::make_view( target_lonlat_fields_[LAT] ); auto lonlat = [lon, lat, convert_units = convert_units_] (idx_t n) { return PointLonLat{lon(n) * convert_units, lat(n) * convert_units}; }; if( out_npts_ != 0) { if ( target_ghost_ ) { auto ghost = array::make_view( target_ghost_ ); interpolate_omp(out_npts_, lonlat, ghost); } else { auto no_ghost = [](idx_t n) { return false; }; interpolate_omp(out_npts_, lonlat, no_ghost); } } handle_failed_points(*this, failed_points, lonlat); } else { ATLAS_NOTIMPLEMENTED; } } } // namespace method } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/method/structured/StructuredInterpolation3D.h0000664000175000017500000000471615160212070030615 0ustar alastairalastair/* * (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/interpolation/method/Method.h" #include #include "atlas/field/Field.h" #include "atlas/field/FieldSet.h" #include "atlas/functionspace/FunctionSpace.h" #include "atlas/grid/Vertical.h" namespace atlas { namespace interpolation { namespace method { /** * @class StructuredInterpolation3D * * Three-dimensional interpolation making use of Structure of grid. */ template class StructuredInterpolation3D : public Method { public: StructuredInterpolation3D(const Config& config); virtual ~StructuredInterpolation3D() override {} virtual void print(std::ostream&) const override; protected: void setup(const FunctionSpace& source); virtual const FunctionSpace& source() const override { return source_; } virtual const FunctionSpace& target() const override { return target_; } private: virtual void do_setup(const Grid& source, const Grid& target, const Cache&) override; virtual void do_setup(const FunctionSpace& source, const FunctionSpace& target, const Cache&) override; virtual void do_setup(const FunctionSpace& source, const FunctionSpace& target) override; virtual void do_setup(const FunctionSpace& source, const Field& target) override; virtual void do_setup(const FunctionSpace& source, const FieldSet& target) override; virtual void do_execute(const Field& src, Field& tgt, Metadata&) const override; virtual void do_execute(const FieldSet& src, FieldSet& tgt, Metadata&) const override; template void execute_impl(const Kernel& kernel, const FieldSet& src, FieldSet& tgt) const; static double convert_units_multiplier(const Field& field); protected: Field target_ghost_; Field target_lonlat_; Field target_vertical_; Field target_3d_; FieldSet target_xyz_; FunctionSpace source_; FunctionSpace target_; bool matrix_free_; bool limiter_; std::unique_ptr kernel_; }; } // namespace method } // namespace interpolation } // namespace atlas #include "StructuredInterpolation3D.tcc" atlas-0.46.0/src/atlas/interpolation/method/structured/QuasiCubic2D.cc0000664000175000017500000000231315160212070026055 0ustar alastairalastair/* * (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. and Interpolation */ #include "QuasiCubic2D.h" #include "atlas/interpolation/method/MethodFactory.h" namespace atlas { namespace interpolation { namespace method { namespace { using util::FactoryDeprecated; MethodBuilder __builder1("structured-biquasicubic"); MethodBuilder __builder2("structured-quasicubic2D", FactoryDeprecated("Please use structured-biquasicubic")); MethodBuilder __builder3("quasicubic2D", FactoryDeprecated("Please use structured-biquasicubic")); MethodBuilder __builder4("biquasicubic", FactoryDeprecated("Please use structured-biquasicubic")); } // namespace QuasiCubic2D::QuasiCubic2D(const Config& config): StructuredInterpolation2D(config) {} } // namespace method } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/method/structured/RegionalLinear2D.h0000664000175000017500000000454715160212070026575 0ustar alastairalastair/* * (C) Copyright 2024 Meteorologisk Institutt * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ #pragma once #include "atlas/interpolation/method/Method.h" #include #include "atlas/field.h" #include "atlas/functionspace.h" #include "atlas/grid/Grid.h" #include "eckit/config/Configuration.h" #include "eckit/mpi/Comm.h" namespace atlas { namespace interpolation { namespace method { class RegionalLinear2D : public Method { public: /// @brief Regional linear interpolation /// /// @details /// RegionalLinear2D(const Config& config) : Method(config), comm_(eckit::mpi::comm()) {} ~RegionalLinear2D() override {} void print(std::ostream&) const override; const FunctionSpace& source() const override { return source_; } const FunctionSpace& target() const override { return target_; } void do_execute(const FieldSet& sourceFieldSet, FieldSet& targetFieldSet, Metadata& metadata) const override; void do_execute(const Field& sourceField, Field& targetField, Metadata& metadata) const override; void do_execute_adjoint(FieldSet& sourceFieldSet, const FieldSet& targetFieldSet, Metadata& metadata) const override; void do_execute_adjoint(Field& sourceField, const Field& targetField, Metadata& metadata) const override; private: using Method::do_setup; void do_setup(const FunctionSpace& source, const FunctionSpace& target) override; void do_setup(const Grid& source, const Grid& target, const Cache&) override; void do_setup(const FunctionSpace& source, const FunctionSpace& target, const Cache&) override; FunctionSpace source_{}; FunctionSpace target_{}; const eckit::mpi::Comm & comm_; size_t sourceSize_; std::vector mpiTask_; size_t targetSize_; size_t sourceSendSize_; size_t targetRecvSize_; std::vector sourceSendCounts_; std::vector sourceSendDispls_; std::vector targetRecvCounts_; std::vector targetRecvDispls_; std::vector sourceSendMapping_; std::vector> stencil_; std::vector> weights_; std::vector stencilSize_; }; } // namespace method } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/method/structured/Cubic3D.h0000664000175000017500000000135015160212070024715 0ustar alastairalastair/* * (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. and Interpolation */ #pragma once #include "StructuredInterpolation3D.h" #include "kernels/Cubic3DKernel.h" namespace atlas { namespace interpolation { namespace method { class Cubic3D : public StructuredInterpolation3D { public: Cubic3D(const Config&); }; } // namespace method } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/method/structured/QuasiCubic2D.h0000664000175000017500000000141415160212070025720 0ustar alastairalastair/* * (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. and Interpolation */ #pragma once #include "StructuredInterpolation2D.h" #include "kernels/QuasiCubicHorizontalKernel.h" namespace atlas { namespace interpolation { namespace method { class QuasiCubic2D : public StructuredInterpolation2D { public: QuasiCubic2D(const Config&); }; } // namespace method } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/method/structured/StructuredInterpolation2D.h0000664000175000017500000000511215160212070030603 0ustar alastairalastair/* * (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/interpolation/method/Method.h" #include #include "atlas/field/Field.h" #include "atlas/field/FieldSet.h" #include "atlas/functionspace/FunctionSpace.h" namespace atlas { namespace interpolation { namespace method { /** * @class StructuredInterpolation2D * * Horizontal interpolation making use of Structure of grid * Multiple (vertical) levels can be interpolated as well but * assumes that input and output levels are the same. */ template class StructuredInterpolation2D : public Method { public: StructuredInterpolation2D(const Config& config); ~StructuredInterpolation2D() override {} void print(std::ostream&) const override; const Kernel& kernel() const { return *kernel_; } const FunctionSpace& source() const override { return source_; } const FunctionSpace& target() const override { return target_; } protected: void setup(const FunctionSpace& source); private: virtual void do_setup(const Grid& source, const Grid& target, const Cache&) override; virtual void do_setup(const FunctionSpace& source, const FunctionSpace& target, const Cache&) override; virtual void do_setup(const FunctionSpace& source, const FunctionSpace& target) override; virtual void do_setup(const FunctionSpace& source, const Field& target) override; virtual void do_setup(const FunctionSpace& source, const FieldSet& target) override; virtual void do_execute(const Field& src, Field& tgt, Metadata&) const override; virtual void do_execute(const FieldSet& src, FieldSet& tgt, Metadata&) const override; template void execute_impl(const Kernel& kernel, const FieldSet& src, FieldSet& tgt) const; static double convert_units_multiplier(const Field& field); protected: Field target_lonlat_; Field target_ghost_; FieldSet target_lonlat_fields_; FunctionSpace source_; FunctionSpace target_; bool verbose_; bool limiter_; bool matrix_free_; double convert_units_; idx_t out_npts_; std::unique_ptr kernel_; }; } // namespace method } // namespace interpolation } // namespace atlas #include "StructuredInterpolation2D.tcc" atlas-0.46.0/src/atlas/interpolation/method/structured/Linear3D.h0000664000175000017500000000135415160212070025106 0ustar alastairalastair/* * (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. and Interpolation */ #pragma once #include "StructuredInterpolation3D.h" #include "kernels/Linear3DKernel.h" namespace atlas { namespace interpolation { namespace method { class Linear3D : public StructuredInterpolation3D { public: Linear3D(const Config&); }; } // namespace method } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/method/structured/StructuredInterpolation3D.tcc0000664000175000017500000003113615160212070031133 0ustar alastairalastair/* * (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. and Interpolation */ #pragma once #include "StructuredInterpolation3D.h" #include "atlas/array/ArrayView.h" #include "atlas/field/Field.h" #include "atlas/field/FieldSet.h" #include "atlas/functionspace/NodeColumns.h" #include "atlas/functionspace/PointCloud.h" #include "atlas/functionspace/StructuredColumns.h" #include "atlas/grid/StructuredGrid.h" #include "atlas/mesh/Nodes.h" #include "atlas/parallel/mpi/mpi.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/CoordinateEnums.h" #include "atlas/util/Point.h" namespace atlas { namespace interpolation { namespace method { template double StructuredInterpolation3D::convert_units_multiplier( const Field& field ) { std::string units = field.metadata().getString( "units", "degrees" ); if ( units == "degrees" ) { return 1.; } if ( units == "radians" ) { return 180. / M_PI; } ATLAS_NOTIMPLEMENTED; } template StructuredInterpolation3D::StructuredInterpolation3D( const Method::Config& config ) : Method( config ), matrix_free_{false}, limiter_{false} { config.get( "matrix_free", matrix_free_ ); config.get( "limiter", limiter_ ); if ( not matrix_free_ ) { throw_NotImplemented( "Matrix-free StructuredInterpolation3D not implemented", Here() ); } } template void StructuredInterpolation3D::do_setup( const Grid& source, const Grid& target, const Cache& ) { if ( mpi::size() > 1 ) { ATLAS_NOTIMPLEMENTED; } ATLAS_ASSERT( StructuredGrid( source ) ); FunctionSpace source_fs = functionspace::StructuredColumns( source, option::halo( kernel_->stencil_halo() ) ); FunctionSpace target_fs = functionspace::PointCloud( target ); do_setup( source_fs, target_fs ); } template void StructuredInterpolation3D::do_setup( const FunctionSpace& source, const FunctionSpace& target, const Cache& cache) { ATLAS_TRACE( "StructuredInterpolation3D<" + Kernel::className() + ">::do_setup(FunctionSpace source, FunctionSpace target, const Cache)" ); if (! matrix_free_ && interpolation::MatrixCache(cache)) { setMatrix(cache); source_ = source; target_ = target; ATLAS_ASSERT(matrix().rows() == target.size()); ATLAS_ASSERT(matrix().cols() == source.size()); return; } else { do_setup( source, target ); } } template void StructuredInterpolation3D::do_setup( const FunctionSpace& source, const FunctionSpace& target ) { ATLAS_TRACE( "StructuredInterpolation<" + Kernel::className() + ">::do_setup()" ); source_ = source; target_ = target; if ( functionspace::PointCloud tgt = target ) { target_lonlat_ = tgt.lonlat(); target_vertical_ = tgt.vertical(); target_ghost_ = tgt.ghost(); } else { ATLAS_NOTIMPLEMENTED; } setup( source ); } template void StructuredInterpolation3D::do_setup( const FunctionSpace& source, const Field& target ) { ATLAS_TRACE( "StructuredInterpolation<" + Kernel::className() + ">::do_setup(FunctionSpace source, Field target)" ); source_ = source; if ( target.functionspace() ) { target_ = target.functionspace(); } target_3d_ = target; setup( source ); } template void StructuredInterpolation3D::do_setup( const FunctionSpace& source, const FieldSet& target ) { ATLAS_TRACE( "StructuredInterpolation<" + Kernel::className() + ">::do_setup(FunctionSpace source,FieldSet target)" ); source_ = source; ATLAS_ASSERT( target.size() >= 3 ); if ( target[0].functionspace() ) { target_ = target[0].functionspace(); } target_xyz_ = target; setup( source ); } template void StructuredInterpolation3D::print( std::ostream& ) const { ATLAS_NOTIMPLEMENTED; } template void StructuredInterpolation3D::setup( const FunctionSpace& source ) { kernel_.reset( new Kernel( source, util::Config( "limiter", limiter_ ) ) ); } template void StructuredInterpolation3D::do_execute( const Field& src_field, Field& tgt_field, Metadata& metadata ) const { FieldSet tgt( tgt_field ); do_execute( FieldSet( src_field ), tgt, metadata ); } template void StructuredInterpolation3D::do_execute( const FieldSet& src_fields, FieldSet& tgt_fields, Metadata& metadata ) const { if ( not matrix_free_ ) { Method::do_execute( src_fields, tgt_fields, metadata ); return; } const idx_t N = src_fields.size(); ATLAS_ASSERT( N == tgt_fields.size() ); if ( N == 0 ) return; haloExchange( src_fields ); ATLAS_TRACE( "StructuredInterpolation<" + Kernel::className() + ">::do_execute()" ); array::DataType datatype = src_fields[0].datatype(); int rank = src_fields[0].rank(); ATLAS_ASSERT( rank > 1 ); for ( idx_t i = 0; i < N; ++i ) { ATLAS_ASSERT( src_fields[i].datatype() == datatype ); ATLAS_ASSERT( src_fields[i].rank() == rank ); ATLAS_ASSERT( tgt_fields[i].datatype() == datatype ); } if ( datatype.kind() == array::DataType::KIND_REAL64 && rank == 2 ) { execute_impl( *kernel_, src_fields, tgt_fields ); } if ( datatype.kind() == array::DataType::KIND_REAL32 && rank == 2 ) { execute_impl( *kernel_, src_fields, tgt_fields ); } if ( datatype.kind() == array::DataType::KIND_REAL64 && rank == 3 ) { execute_impl( *kernel_, src_fields, tgt_fields ); } if ( datatype.kind() == array::DataType::KIND_REAL32 && rank == 3 ) { execute_impl( *kernel_, src_fields, tgt_fields ); } tgt_fields.set_dirty(); } template template void StructuredInterpolation3D::execute_impl( const Kernel& kernel, const FieldSet& src_fields, FieldSet& tgt_fields ) const { const idx_t N = src_fields.size(); auto make_src_view = [&]( const FieldSet& src_fields ) { std::vector > src_view; src_view.reserve( N ); for ( idx_t i = 0; i < N; ++i ) { src_view.emplace_back( array::make_view( src_fields[i] ) ); } return src_view; }; // Assertions ATLAS_ASSERT( tgt_fields.size() == src_fields.size() ); idx_t tgt_rank = -1; for ( auto& f : tgt_fields ) { if ( tgt_rank == -1 ) tgt_rank = f.rank(); if ( f.rank() != tgt_rank ) { throw_Exception( "target fields don't all have the same rank!", Here() ); } } if ( functionspace::PointCloud( target() ) && tgt_rank == 1 ) { const idx_t out_npts = target_lonlat_.shape( 0 ); const auto ghost = array::make_view( target_ghost_ ); const auto lonlat = array::make_view( target_lonlat_ ); const auto vertical = array::make_view( target_vertical_ ); auto src_view = make_src_view( src_fields ); constexpr int TargetRank = 1; std::vector > tgt_view; tgt_view.reserve( N ); for ( idx_t i = 0; i < N; ++i ) { tgt_view.emplace_back( array::make_view( tgt_fields[i] ) ); } const double convert_units = convert_units_multiplier( target_lonlat_ ); atlas_omp_parallel { typename Kernel::Stencil stencil; typename Kernel::Weights weights; atlas_omp_for( idx_t n = 0; n < out_npts; ++n ) { if ( not ghost( n ) ) { double x = lonlat( n, LON ) * convert_units; double y = lonlat( n, LAT ) * convert_units; double z = vertical( n ); kernel.compute_stencil( x, y, z, stencil ); kernel.compute_weights( x, y, z, stencil, weights ); for ( idx_t i = 0; i < N; ++i ) { kernel.interpolate( stencil, weights, src_view[i], tgt_view[i], n ); } } } } } else if ( target_3d_ && tgt_rank == Rank ) { const idx_t out_npts = target_3d_.shape( 0 ); const idx_t out_nlev = target_3d_.shape( 1 ); const auto coords = array::make_view( target_3d_ ); const auto src_view = make_src_view( src_fields ); constexpr int TargetRank = Rank; std::vector > tgt_view; tgt_view.reserve( N ); for ( idx_t i = 0; i < N; ++i ) { tgt_view.emplace_back( array::make_view( tgt_fields[i] ) ); if ( Rank == 3 && ( src_fields[i].stride( Rank - 1 ) != 1 || tgt_fields[i].stride( TargetRank - 1 ) != 1 ) ) { throw_Exception( "Something will go seriously wrong if we continue from here as " "the implementation assumes stride=1 for fastest moving index (variables).", Here() ); } } const double convert_units = convert_units_multiplier( target_3d_ ); atlas_omp_parallel { typename Kernel::Stencil stencil; typename Kernel::Weights weights; atlas_omp_for( idx_t n = 0; n < out_npts; ++n ) { for ( idx_t k = 0; k < out_nlev; ++k ) { double x = coords( n, k, LON ) * convert_units; double y = coords( n, k, LAT ) * convert_units; double z = coords( n, k, ZZ ); kernel.compute_stencil( x, y, z, stencil ); kernel.compute_weights( x, y, z, stencil, weights ); for ( idx_t i = 0; i < N; ++i ) { kernel.interpolate( stencil, weights, src_view[i], tgt_view[i], n, k ); } } } } } else if ( not target_xyz_.empty() && tgt_rank == Rank ) { const idx_t out_npts = target_xyz_[0].shape( 0 ); const idx_t out_nlev = target_xyz_[0].shape( 1 ); const auto xcoords = array::make_view( target_xyz_[LON] ); const auto ycoords = array::make_view( target_xyz_[LAT] ); const auto zcoords = array::make_view( target_xyz_[ZZ] ); const auto src_view = make_src_view( src_fields ); constexpr int TargetRank = Rank; std::vector > tgt_view; tgt_view.reserve( N ); for ( idx_t i = 0; i < N; ++i ) { tgt_view.emplace_back( array::make_view( tgt_fields[i] ) ); if ( Rank == 3 && ( src_fields[i].stride( Rank - 1 ) != 1 || tgt_fields[i].stride( TargetRank - 1 ) != 1 ) ) { throw_Exception( "Something will go seriously wrong if we continue from here as " "the implementation assumes stride=1 for fastest moving index (variables).", Here() ); } } const double convert_units = convert_units_multiplier( target_xyz_[LON] ); atlas_omp_parallel { typename Kernel::Stencil stencil; typename Kernel::Weights weights; atlas_omp_for( idx_t n = 0; n < out_npts; ++n ) { for ( idx_t k = 0; k < out_nlev; ++k ) { double x = xcoords( n, k ) * convert_units; double y = ycoords( n, k ) * convert_units; double z = zcoords( n, k ); kernel.compute_stencil( x, y, z, stencil ); kernel.compute_weights( x, y, z, stencil, weights ); for ( idx_t i = 0; i < N; ++i ) { kernel.interpolate( stencil, weights, src_view[i], tgt_view[i], n, k ); } } } } } else { ATLAS_NOTIMPLEMENTED; } } } // namespace method } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/method/Intersect.h0000664000175000017500000000245015160212070023237 0ustar alastairalastair/* * (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 namespace atlas { namespace interpolation { namespace method { //---------------------------------------------------------------------------------------------------------------------- /// Intersection data structure struct Intersect { double u; double v; double t; Intersect(); operator bool() const { return success_; } Intersect& success() { success_ = true; return *this; } Intersect& fail() { success_ = false; return *this; } void print(std::ostream& s) const; friend std::ostream& operator<<(std::ostream& s, const Intersect& p) { p.print(s); return s; } private: bool success_; }; //---------------------------------------------------------------------------------------------------------------------- } // namespace method } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/method/Intersect.cc0000664000175000017500000000145215160212070023376 0ustar alastairalastair/* * (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/interpolation/method/Intersect.h" #include namespace atlas { namespace interpolation { namespace method { Intersect::Intersect(): u(0.), v(0.), t(0.), success_(false) {} void Intersect::print(std::ostream& s) const { s << "Intersect[u=" << u << ",v=" << v << ",t=" << t << ",success=" << success_ << "]"; } } // namespace method } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/method/MethodFactory.cc0000664000175000017500000000471215160212070024210 0ustar alastairalastair/* * (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 "MethodFactory.h" // for static linking #include "binning/Binning.h" #include "cubedsphere/CubedSphereBilinear.h" #include "knn/GridBoxAverage.h" #include "knn/GridBoxMaximum.h" #include "knn/KNearestNeighbours.h" #include "knn/NearestNeighbour.h" #include "sphericalvector/SphericalVector.h" #include "structured/Cubic2D.h" #include "structured/Cubic3D.h" #include "structured/Linear2D.h" #include "structured/Linear3D.h" #include "structured/QuasiCubic2D.h" #include "structured/QuasiCubic3D.h" #include "structured/RegionalLinear2D.h" #include "unstructured/FiniteElement.h" #include "unstructured/ConservativeSphericalPolygonInterpolation.h" #include "unstructured/UnstructuredBilinearLonLat.h" #include "unstructured/SphericalMeanValue.h" namespace atlas { namespace interpolation { namespace { void force_link() { static struct Link { Link() { MethodBuilder(); MethodBuilder(); MethodBuilder(); MethodBuilder(); MethodBuilder(); MethodBuilder(); MethodBuilder(); MethodBuilder(); MethodBuilder(); MethodBuilder(); MethodBuilder(); MethodBuilder(); MethodBuilder(); MethodBuilder(); MethodBuilder(); MethodBuilder(); MethodBuilder(); MethodBuilder(); } } link; } } // namespace Method* MethodFactory::build(const std::string& name, const Method::Config& config) { force_link(); auto factory = get(name); return factory->make(config); } } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/NonLinear.h0000664000175000017500000000417015160212070021705 0ustar alastairalastair/* * (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/interpolation/nonlinear/NonLinear.h" #include "atlas/library/config.h" #include "atlas/util/ObjectHandle.h" namespace atlas { class Field; namespace util { class Config; } } // namespace atlas namespace atlas { namespace interpolation { /** * @brief NonLinear class applies non-linear corrections to an interpolation matrix */ struct NonLinear : DOXYGEN_HIDE(public util::ObjectHandle) { using Spec = util::Config; using Config = nonlinear::NonLinear::Config; using Matrix = nonlinear::NonLinear::Matrix; /** * @brief ctor */ using Handle::Handle; NonLinear(); NonLinear(const Config&); NonLinear(const std::string& type, const Config&); /** * @brief bool operator * @return if NonLinear object has been setup */ using Handle::operator bool; // (ensure this exists) /** * @brief if NonLinear applies to field * @param [in] f field * @return if NonLinear applies to field */ bool operator()(const Field& f) const; /** * @brief Apply non-linear corrections to interpolation matrix * @param [inout] W interpolation matrix * @param [in] f field * @return if W was modified */ bool execute(Matrix& W, const Field& f) const; /** * @brief Apply non-linear corrections to interpolation matrix * @param [inout] W interpolation matrix * @param [in] a array containing possibly missing values * @param [in] c configuration containing missing value diagnostic information * @return if W was modified */ bool execute(Matrix& W, const array::Array& a, const Config& c) const; }; } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/Cache.cc0000664000175000017500000001071415160212070021162 0ustar alastairalastair/* * (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/interpolation/Cache.h" #include "atlas/interpolation/Interpolation.h" namespace atlas { namespace interpolation { InterpolationCacheEntry::~InterpolationCacheEntry() = default; Cache::Cache(std::shared_ptr cache) { auto& new_cache = cache_[cache->type()]; new_cache = cache; } Cache::Cache(const Cache& other) { add(other); } Cache::Cache(const Cache& other, const std::string& filter): Cache(other) { std::shared_ptr filtered; for (auto& entry : cache_) { if (entry.first == filter) { filtered = entry.second; } } cache_.clear(); if (filtered) { cache_[filtered->type()] = filtered; } } Cache::Cache(const Interpolation& interpolation): Cache(interpolation.createCache()) {} Cache::~Cache() = default; Cache& Cache::operator=(const Cache& other) { cache_.clear(); add(other); return *this; } Cache& Cache::operator=(Cache&& other) { cache_.clear(); add(other); other.cache_.clear(); return *this; } void Cache::add(const Cache& other) { for (auto& entry : other.cache_) { auto& new_cache = cache_[entry.first]; new_cache = entry.second; } } MatrixCacheEntry::~MatrixCacheEntry() = default; class MatrixCacheEntryOwned : public MatrixCacheEntry { public: MatrixCacheEntryOwned(Matrix&& matrix): MatrixCacheEntry(&matrix_) { const_cast(matrix_) = std::move(matrix); } private: const Matrix matrix_; }; class MatrixCacheEntryShared : public MatrixCacheEntry { public: MatrixCacheEntryShared(std::shared_ptr matrix, const std::string& uid = ""): MatrixCacheEntry(matrix.get(), uid), matrix_{matrix} {} private: const std::shared_ptr matrix_; }; MatrixCache::MatrixCache(const Cache& c): Cache(c, MatrixCacheEntry::static_type()), matrix_{dynamic_cast(c.get(MatrixCacheEntry::static_type()))} {} MatrixCache::MatrixCache(Matrix&& m): MatrixCache(std::make_shared(std::move(m))) {} MatrixCache::MatrixCache(std::shared_ptr m, const std::string& uid): MatrixCache(std::make_shared(m, uid)) {} MatrixCache::MatrixCache(const Matrix* m): MatrixCache(std::make_shared(m)) {} MatrixCache::MatrixCache(const Interpolation& interpolation): MatrixCache(Cache(interpolation)) {} MatrixCache::operator bool() const { return matrix_ && !matrix().empty(); } const MatrixCache::Matrix& MatrixCache::matrix() const { ATLAS_ASSERT(matrix_); return matrix_->matrix(); } const std::string& MatrixCache::uid() const { ATLAS_ASSERT(matrix_); return matrix_->uid(); } size_t MatrixCache::footprint() const { if (matrix_) { return matrix_->footprint(); } return 0; } MatrixCache::MatrixCache(std::shared_ptr entry): Cache(entry), matrix_{dynamic_cast(entry.get())} {} IndexKDTreeCacheEntry::~IndexKDTreeCacheEntry() = default; const IndexKDTreeCacheEntry::IndexKDTree& IndexKDTreeCacheEntry::tree() const { ATLAS_ASSERT(tree_); return tree_; } IndexKDTreeCache::IndexKDTreeCache(const Cache& c): Cache(c, IndexKDTreeCacheEntry::static_type()), tree_{dynamic_cast(c.get(IndexKDTreeCacheEntry::static_type()))} {} IndexKDTreeCache::IndexKDTreeCache(const IndexKDTree& tree): Cache(std::make_shared(tree)) { tree_ = dynamic_cast(get(IndexKDTreeCacheEntry::static_type())); } IndexKDTreeCache::IndexKDTreeCache(const Interpolation& interpolation): IndexKDTreeCache(Cache(interpolation)) {} IndexKDTreeCache::operator bool() const { return tree_; //&& !tree().empty(); } const IndexKDTreeCache::IndexKDTree& IndexKDTreeCache::tree() const { ATLAS_ASSERT(tree_); return tree_->tree(); } size_t IndexKDTreeCache::footprint() const { if (tree_) { return tree_->footprint(); } return 0; } } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/Interpolation.h0000664000175000017500000001100015160212070022635 0ustar alastairalastair/* * (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/interpolation/method/Method.h" #include "atlas/library/config.h" #include "atlas/util/ObjectHandle.h" #include "atlas/interpolation/Cache.h" namespace eckit { class Parametrisation; } namespace atlas { class Field; class FieldSet; class FunctionSpace; class Grid; namespace interpolation { class Method; } } // namespace atlas namespace atlas { class Interpolation : DOXYGEN_HIDE(public util::ObjectHandle) { public: using Config = eckit::Parametrisation; using Cache = interpolation::Cache; using Metadata = interpolation::Method::Metadata; using Handle::Handle; Interpolation() = default; // Setup Interpolation from source to target function space Interpolation(const Config&, const FunctionSpace& source, const FunctionSpace& target) noexcept(false); // Setup Interpolation from source to coordinates given in a field with multiple components Interpolation(const Config&, const FunctionSpace& source, const Field& target) noexcept(false); // Setup Interpolation from source to coordinates given by separate fields for each component Interpolation(const Config&, const FunctionSpace& source, const FieldSet& target) noexcept(false); // Setup Interpolation from source grid to target grid Interpolation(const Config&, const Grid& source, const Grid& target) noexcept(false); Metadata execute(const FieldSet& source, FieldSet& target) const; Metadata execute(const Field& source, Field& target) const; Metadata execute_adjoint(FieldSet& source, const FieldSet& target) const; Metadata execute_adjoint(Field& source, const Field& target) const; void print(std::ostream& out) const; const FunctionSpace& source() const; const FunctionSpace& target() const; Cache createCache() const; Interpolation(const Config&, const Grid& source, const Grid& target, const Cache&) noexcept(false); Interpolation(const Config&, const FunctionSpace& source, const FunctionSpace& target, const Cache&) noexcept(false); friend std::ostream& operator<<(std::ostream& out, const Interpolation& i) { i.print(out); return out; } }; #ifndef DOXYGEN_SHOULD_SKIP_THIS namespace functionspace { class FunctionSpaceImpl; } namespace field { class FieldImpl; class FieldSetImpl; } // namespace field extern "C" { Interpolation::Implementation* atlas__Interpolation__new(const eckit::Parametrisation* config, const functionspace::FunctionSpaceImpl* source, const functionspace::FunctionSpaceImpl* target); Interpolation::Implementation* atlas__Interpolation__new_tgt_field(const eckit::Parametrisation* config, const functionspace::FunctionSpaceImpl* source, const field::FieldImpl* target); Interpolation::Implementation* atlas__Interpolation__new_tgt_fieldset(const eckit::Parametrisation* config, const functionspace::FunctionSpaceImpl* source, const field::FieldSetImpl* target); void atlas__Interpolation__delete(Interpolation::Implementation* This); void atlas__Interpolation__execute_field(Interpolation::Implementation* This, const field::FieldImpl* source, field::FieldImpl* target); void atlas__Interpolation__execute_fieldset(Interpolation::Implementation* This, const field::FieldSetImpl* source, field::FieldSetImpl* target); void atlas__Interpolation__execute_adjoint_field(Interpolation::Implementation* This, field::FieldImpl* source, const field::FieldImpl* target); void atlas__Interpolation__execute_adjoint_fieldset(Interpolation::Implementation* This, field::FieldSetImpl* source, const field::FieldSetImpl* target); } #endif } // namespace atlas atlas-0.46.0/src/atlas/interpolation/NonLinear.cc0000664000175000017500000000323015160212070022037 0ustar alastairalastair/* * (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 "atlas/interpolation/NonLinear.h" #include "atlas/interpolation/nonlinear/NonLinear.h" #include "atlas/runtime/Exception.h" #include "atlas/util/Config.h" namespace atlas { namespace interpolation { namespace { std::string config_type(const eckit::Parametrisation& config) { std::string value; ATLAS_ASSERT_MSG(config.get("type", value), ""); return value; } } // namespace NonLinear::NonLinear(): Handle(nullptr) {} NonLinear::NonLinear(const NonLinear::Config& config): Handle(nonlinear::NonLinearFactory::build(config_type(config), config)) {} NonLinear::NonLinear(const std::string& type, const NonLinear::Config& config): Handle(nonlinear::NonLinearFactory::build(type, config)) {} bool NonLinear::operator()(const Field& f) const { return operator bool() && get()->applicable(f); } bool NonLinear::execute(NonLinear::Matrix& W, const Field& f) const { ATLAS_ASSERT_MSG(operator bool(), "NonLinear: ObjectHandle not setup"); return get()->execute(W, f); } bool NonLinear::execute(NonLinear::Matrix& W, const array::Array& a, const NonLinear::Config& c) const { ATLAS_ASSERT_MSG(operator bool(), "NonLinear: ObjectHandle not setup"); return get()->execute(W, a, c); } } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/Cache.h0000664000175000017500000001152515160212070021025 0ustar alastairalastair/* * (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 #include "eckit/filesystem/PathName.h" #include "eckit/io/Buffer.h" #include "atlas/linalg/sparse.h" #include "atlas/runtime/Exception.h" #include "atlas/util/KDTree.h" //----------------------------------------------------------------------------- // Forward declarations namespace atlas { class Interpolation; } // namespace atlas //----------------------------------------------------------------------------- namespace atlas { namespace interpolation { //----------------------------------------------------------------------------- class InterpolationCacheEntry { public: virtual ~InterpolationCacheEntry(); virtual size_t footprint() const = 0; virtual std::string type() const = 0; }; //----------------------------------------------------------------------------- class Cache { public: Cache() = default; Cache(const Cache& other); Cache(const Cache& other, const std::string& filter); Cache(const Interpolation&); Cache& operator=(const Cache& other); Cache& operator=(Cache&& other); operator bool() const { return not cache_.empty(); } virtual ~Cache(); size_t footprint() const { size_t footprint{0}; for (auto& entry : cache_) { footprint += entry.second->footprint(); } return footprint; } void add(const Cache&); protected: Cache(std::shared_ptr cache); public: const InterpolationCacheEntry* get(const std::string& type) const { auto it = cache_.find(type); if (it != cache_.end()) { return it->second.get(); } return nullptr; } private: std::map> cache_; }; //----------------------------------------------------------------------------- class MatrixCacheEntry : public InterpolationCacheEntry { public: using Matrix = atlas::linalg::SparseMatrixStorage; ~MatrixCacheEntry() override; MatrixCacheEntry(const Matrix* matrix, const std::string& uid = ""): matrix_{matrix}, uid_(uid) { ATLAS_ASSERT(matrix_ != nullptr); } const Matrix& matrix() const { return *matrix_; } const std::string& uid() const { return uid_; } size_t footprint() const override { return matrix_->footprint(); } operator bool() const { return not matrix_->empty(); } static std::string static_type() { return "Matrix"; } std::string type() const override { return static_type(); } private: const Matrix* matrix_; const std::string uid_; }; //----------------------------------------------------------------------------- class MatrixCache final : public Cache { public: using Matrix = MatrixCacheEntry::Matrix; public: MatrixCache() = default; MatrixCache(const Cache& c); MatrixCache(Matrix&& m); MatrixCache(std::shared_ptr m, const std::string& uid = ""); MatrixCache(const Matrix* m); MatrixCache(const Interpolation&); operator bool() const; const Matrix& matrix() const; const std::string& uid() const; size_t footprint() const; private: MatrixCache(std::shared_ptr entry); const MatrixCacheEntry* matrix_{nullptr}; }; //---------------------------------------------------------------------------------------------------------------------- class IndexKDTreeCacheEntry : public InterpolationCacheEntry { public: using IndexKDTree = util::IndexKDTree; IndexKDTreeCacheEntry(const IndexKDTree& tree): tree_{tree} { ATLAS_ASSERT(tree_); } virtual ~IndexKDTreeCacheEntry() override; const IndexKDTree& tree() const; size_t footprint() const override { return tree().footprint(); } operator bool() const { return bool(tree_); } static std::string static_type() { return "IndexKDTree"; } std::string type() const override { return static_type(); } private: const IndexKDTree tree_; }; class IndexKDTreeCache final : public Cache { public: using IndexKDTree = IndexKDTreeCacheEntry::IndexKDTree; public: IndexKDTreeCache() = default; IndexKDTreeCache(const Cache& c); IndexKDTreeCache(const IndexKDTreeCache& c) : IndexKDTreeCache(Cache(c)) {} IndexKDTreeCache(const IndexKDTree&); IndexKDTreeCache(const Interpolation&); operator bool() const; const IndexKDTree& tree() const; size_t footprint() const; private: const IndexKDTreeCacheEntry* tree_{nullptr}; }; } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/element/0000775000175000017500000000000015160212070021276 5ustar alastairalastairatlas-0.46.0/src/atlas/interpolation/element/Quad3D.cc0000664000175000017500000000557115160212070022676 0ustar alastairalastair/* * (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 "atlas/interpolation/element/Quad3D.h" #include "atlas/interpolation/element/Triag3D.h" #include "atlas/interpolation/method/Ray.h" #include "atlas/runtime/Log.h" namespace atlas { namespace interpolation { namespace element { //---------------------------------------------------------------------------------------------------------------------- method::Intersect Quad3D::intersects(const method::Ray& r, double edgeEpsilon, double epsilon) const { method::Intersect isect; // intersection is false Triag3D T013(v00, v10, v01); isect = T013.intersects(r, edgeEpsilon, epsilon); if (isect) { return isect; } Triag3D T231(v11, v01, v10); isect = T231.intersects(r, edgeEpsilon, epsilon); if (isect) { isect.u = 1 - isect.u; isect.v = 1 - isect.v; return isect; } return isect.fail(); } bool Quad3D::validate() const { // normal for sub-triangle T231 Vector3D E23 = v01 - v11; Vector3D E21 = v10 - v11; Vector3D N231 = E23.cross(E21); // normal for sub-triangle T013 Vector3D E01 = v10 - v00; Vector3D E03 = v01 - v00; Vector3D N013 = E01.cross(E03); // normal for sub-triangle T120 Vector3D E12 = -E21; Vector3D E10 = -E01; Vector3D N120 = E12.cross(E10); // normal for sub-triangle T302 Vector3D E30 = -E03; Vector3D E32 = -E23; Vector3D N302 = E30.cross(E32); // all normals must point same way double dot02 = N231.dot(N013); double dot23 = N013.dot(N120); double dot31 = N120.dot(N302); double dot10 = N302.dot(N231); // all normals must point same way bool is_inside = ((dot02 >= 0. && dot23 >= 0. && dot31 >= 0. && dot10 >= 0.) || (dot02 <= 0. && dot23 <= 0. && dot31 <= 0. && dot10 <= 0.)); return is_inside; } double Quad3D::area() const { Triag3D T013(v00, v10, v01); Triag3D T231(v11, v01, v10); return T013.area() + T231.area(); } void Quad3D::print(std::ostream& s) const { auto printVector3D = [&s](const Vector3D& v) { s << "[" << v[0] << "," << v[1] << "," << v[2] << "]"; }; s << "Quad3D["; printVector3D(v00); s << ", "; printVector3D(v10); s << ", "; printVector3D(v11); s << ", "; printVector3D(v01); s << "]"; } //---------------------------------------------------------------------------------------------------------------------- } // namespace element } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/element/Quad3D.h0000664000175000017500000000420515160212070022531 0ustar alastairalastair/* * (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/interpolation/Vector3D.h" #include "atlas/interpolation/method/Intersect.h" #include "atlas/runtime/Exception.h" #include "atlas/util/Point.h" namespace atlas { namespace interpolation { namespace method { struct Ray; } namespace element { //---------------------------------------------------------------------------------------------------------------------- class Quad3D { public: Quad3D(const double* x0, const double* x1, const double* x2, const double* x3): v00(x0), v10(x1), v11(x2), v01(x3) {} Quad3D(const PointXYZ& x0, const PointXYZ& x1, const PointXYZ& x2, const PointXYZ& x3): Quad3D(x0.data(), x1.data(), x2.data(), x3.data()) {} method::Intersect intersects(const method::Ray& r, double edgeEpsilon = 5 * std::numeric_limits::epsilon(), double epsilon = 5 * std::numeric_limits::epsilon()) const; bool validate() const; double area() const; void print(std::ostream&) const; friend std::ostream& operator<<(std::ostream& s, const Quad3D& p) { p.print(s); return s; } const Vector3D& p(int i) { if (i == 0) return v00; if (i == 1) return v10; if (i == 2) return v11; if (i == 3) return v01; throw_OutOfRange("Quad3D::p(i)", i, 4, Here()); } private: // members Vector3D v00; // aka v0 Vector3D v10; // aka v1 Vector3D v11; // aka v2 Vector3D v01; // aka v3 }; //---------------------------------------------------------------------------------------------------------------------- } // namespace element } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/element/Triag2D.h0000664000175000017500000000422115160212070022702 0ustar alastairalastair/* * (C) Crown Copyright 2021 Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ #pragma once #include #include #include "atlas/interpolation/Vector2D.h" #include "atlas/interpolation/method/Intersect.h" #include "atlas/runtime/Exception.h" #include "atlas/util/Point.h" namespace atlas { namespace interpolation { namespace element { static constexpr double BAD_WEIGHT_VALUE = -1.; //---------------------------------------------------------------------------------------------------------------------- class Triag2D { public: Triag2D(const double* x0, const double* x1, const double* x2): v00(x0), v10(x1), v11(x2) {} Triag2D(const PointXY& x0, const PointXY& x1, const PointXY& x2): Triag2D(x0.data(), x1.data(), x2.data()) {} Triag2D(const Vector2D& x0, const Vector2D& x1, const Vector2D& x2): Triag2D(x0.data(), x1.data(), x2.data()) {} method::Intersect intersects(const PointXY& r, double edgeEpsilon = 5 * std::numeric_limits::epsilon(), double epsilon = 5 * std::numeric_limits::epsilon()) const; bool validate() const; double area() const; void print(std::ostream&) const; friend std::ostream& operator<<(std::ostream& s, const Triag2D& p) { p.print(s); return s; } const Vector2D& p(int i) { if (i == 0) return v00; if (i == 1) return v10; if (i == 2) return v11; throw_OutOfRange("Triag2D::p(i)", i, 3, Here()); } private: // members Vector2D v00; // aka v0 Vector2D v10; // aka v1 Vector2D v11; // aka v2 static double cross2d(const Vector2D& a, const Vector2D& b) { return a.x() * b.y() - a.y() * b.x(); } bool inTriangle(const Vector2D& p, double tolerance = 5 * std::numeric_limits::epsilon()) const; }; //---------------------------------------------------------------------------------------------------------------------- } // namespace element } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/element/Triag3D.h0000664000175000017500000000431715160212070022711 0ustar alastairalastair/* * (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/interpolation/Vector3D.h" #include "atlas/interpolation/method/Intersect.h" #include "atlas/runtime/Exception.h" #include "atlas/util/Point.h" namespace atlas { namespace interpolation { namespace method { struct Ray; } namespace element { //---------------------------------------------------------------------------------------------------------------------- /// Triangle structure /// Implements @link /// http://www.scratchapixel.com/lessons/3d-basic-lessons/lesson-9-ray-triangle-intersection/m-ller-trumbore-algorithm class Triag3D { public: // types Triag3D(const double* x0, const double* x1, const double* x2): v0(x0), v1(x1), v2(x2) {} Triag3D(const PointXYZ& x0, const PointXYZ& x1, const PointXYZ& x2): Triag3D(x0.data(), x1.data(), x2.data()) {} Triag3D(const Vector3D& x0, const Vector3D& x1, const Vector3D& x2): Triag3D(x0.data(), x1.data(), x2.data()) {} method::Intersect intersects(const method::Ray& r, double edgeEpsilon = 5 * std::numeric_limits::epsilon(), double epsilon = 5 * std::numeric_limits::epsilon()) const; double area() const; void print(std::ostream& s) const; friend std::ostream& operator<<(std::ostream& s, const Triag3D& p) { p.print(s); return s; } const Vector3D& p(int i) { if (i == 0) return v0; if (i == 1) return v1; if (i == 2) return v2; throw_OutOfRange("Triag3D::p(i)", i, 3, Here()); } private: // members Vector3D v0; Vector3D v1; Vector3D v2; }; //---------------------------------------------------------------------------------------------------------------------- } // namespace element } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/element/Quad2D.h0000664000175000017500000000507315160212070022534 0ustar alastairalastair/* * (C) Crown Copyright 2021 Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ #pragma once #include #include #include "atlas/interpolation/Vector2D.h" #include "atlas/interpolation/method/Intersect.h" #include "atlas/runtime/Exception.h" #include "atlas/util/Point.h" namespace atlas { namespace interpolation { namespace method { struct Ray; } namespace element { //---------------------------------------------------------------------------------------------------------------------- class Quad2D { public: Quad2D(const double* x0, const double* x1, const double* x2, const double* x3): v00(x0), v10(x1), v11(x2), v01(x3) {} Quad2D(const Point2& x0, const Point2& x1, const Point2& x2, const Point2& x3): Quad2D(x0.data(), x1.data(), x2.data(), x3.data()) {} Quad2D(const Vector2D& x0, const Vector2D& x1, const Vector2D& x2, const Vector2D& x3): Quad2D(x0.data(), x1.data(), x2.data(), x3.data()) {} method::Intersect intersects(const Point2& r, double edgeEpsilon = 5 * std::numeric_limits::epsilon(), double epsilon = 5 * std::numeric_limits::epsilon()) const; method::Intersect localRemap(const Point2& r, double edgeEpsilon = 5 * std::numeric_limits::epsilon(), double epsilon = 5 * std::numeric_limits::epsilon()) const; bool validate() const; double area() const; const Vector2D& p(int i) { if (i == 0) { return v00; } if (i == 1) { return v10; } if (i == 2) { return v11; } if (i == 3) { return v01; } throw_OutOfRange("Quad2D::p(i)", i, 4, Here()); } void print(std::ostream&) const; friend std::ostream& operator<<(std::ostream& s, const Quad2D& p) { p.print(s); return s; } private: // members Vector2D v00; // aka v0 Vector2D v10; // aka v1 Vector2D v11; // aka v2 Vector2D v01; // aka v3 bool inQuadrilateral(const Vector2D& p, double tolerance = 5 * std::numeric_limits::epsilon()) const; static double cross2d(const Vector2D& a, const Vector2D& b) { return a.x() * b.y() - a.y() * b.x(); } }; //---------------------------------------------------------------------------------------------------------------------- } // namespace element } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/element/Quad2D.cc0000664000175000017500000001422715160212070022673 0ustar alastairalastair/* * (C) Crown Copyright 2021 Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ #include #include #include "atlas/interpolation/Vector2D.h" #include "atlas/interpolation/element/Quad2D.h" #include "atlas/interpolation/element/Triag2D.h" #include "atlas/runtime/Log.h" namespace atlas { namespace interpolation { namespace element { //---------------------------------------------------------------------------------------------------------------------- method::Intersect Quad2D::intersects(const Point2& r, double edgeEpsilon, double epsilon) const { method::Intersect isect; // intersection is false /* Split quadrilateral into two triangles, points are labelled counter-clockwise. * v01-----------v11 * / " . `. * / " . `. * / " .`. * v00-------------------v10 * */ Triag2D T013(v00, v10, v01); isect = T013.intersects(r, edgeEpsilon, epsilon); if (isect) { return isect; } Triag2D T231(v11, v01, v10); isect = T231.intersects(r, edgeEpsilon, epsilon); if (isect) { isect.u = 1 - isect.u; isect.v = 1 - isect.v; return isect; } return isect.fail(); } method::Intersect Quad2D::localRemap(const Point2& p, double edgeEpsilon, double epsilon) const { method::Intersect isect; // get area of quad. double quadArea = area(); // Epsilon is compared against areas. Scale value accordingly. double areaEpsilon = epsilon * quadArea; // work out if point is within the polygon if (!inQuadrilateral({p.x(), p.y()}, areaEpsilon)) { return isect.fail(); } auto solve_weight = [&](double a, double b, double c, double& weight) -> bool { auto checkWeight = [](double weight, double tol) -> bool { return ((weight > -tol) && (weight < (1. + tol))); }; // Quadratic equation ax^2 + bx + c = 0. if (std::abs(a) >= areaEpsilon) { // Solve numerically stable form of quadratic formula: // x1 = (-b - sign(b) sqrt(b^2 - 4ac)) / 2a // x2 = c / ax1 // Kahan's algorithm for accurate difference of products. const auto prodDiff = [](double a, double b, double c, double d) { // return ab - cd const double w = d * c; const double e = std::fma(-d, c, w); const double f = std::fma(a, b, -w); return f + e; }; double discriminant = prodDiff(b, b, 4. * a, c); if (discriminant > -areaEpsilon * 2. * quadArea) { // Solution is real. double sqrtDiscriminant = std::sqrt(std::max(0., discriminant)); // "Classic" solution to quadratic formula with no cancelation // on numerator. weight = (-b - std::copysign(sqrtDiscriminant, b)) / (2. * a); if (checkWeight(weight, edgeEpsilon)) { return true; } // Use Vieta's formula x1 * x2 = c / a; weight = c / (a * weight); return checkWeight(weight, edgeEpsilon); } } else if (std::abs(b) >= areaEpsilon) { // Linear case bx + c = 0. weight = -c / b; return checkWeight(weight, edgeEpsilon); } // No real solutions to equation. return false; }; // solve for u and v where: // w1 = ( 1 - u ) * ( 1 - v ) // w2 = u * ( 1 - v ) // w3 = u * v // w4 = ( 1 - u ) * v Vector2D point(p.x(), p.y()); Vector2D vA = v00 - point; Vector2D vB = v10 - v00; Vector2D vC = v01 - v00; Vector2D vD = v00 - v10 - v01 + v11; // solve for v double a = cross2d(vC, vD); double b = cross2d(vC, vB) + cross2d(vA, vD); double c = cross2d(vA, vB); if (!solve_weight(a, b, c, isect.v)) { return isect.fail(); } // solve for u a = cross2d(vB, vD); b = cross2d(vB, vC) + cross2d(vA, vD); c = cross2d(vA, vC); if (!solve_weight(a, b, c, isect.u)) { return isect.fail(); } return isect.success(); } bool Quad2D::validate() const { // normal for sub-triangle T231 Vector2D E23 = v01 - v11; Vector2D E21 = v10 - v11; double N231 = cross2d(E23, E21); // normal for sub-triangle T013 Vector2D E01 = v10 - v00; Vector2D E03 = v01 - v00; double N013 = cross2d(E01, E03); // normal for sub-triangle T120 Vector2D E12 = -E21; Vector2D E10 = -E01; double N120 = cross2d(E12, E10); // normal for sub-triangle T302 Vector2D E30 = -E03; Vector2D E32 = -E23; double N302 = cross2d(E30, E32); // all normals must point same way double dot02 = N231 * N013; double dot23 = N013 * N120; double dot31 = N120 * N302; double dot10 = N302 * N231; // all normals must point same way bool is_inside = ((dot02 >= 0. && dot23 >= 0. && dot31 >= 0. && dot10 >= 0.) || (dot02 <= 0. && dot23 <= 0. && dot31 <= 0. && dot10 <= 0.)); return is_inside; } double Quad2D::area() const { return std::abs(0.5 * cross2d((v01 - v00), (v11 - v00))) + std::abs(0.5 * cross2d((v10 - v11), (v01 - v11))); } bool Quad2D::inQuadrilateral(const Vector2D& p, double tolerance) const { // point p must be on the inside of all quad edges to be inside the quad. return cross2d(p - v00, p - v10) > -tolerance && cross2d(p - v10, p - v11) > -tolerance && cross2d(p - v11, p - v01) > -tolerance && cross2d(p - v01, p - v00) > -tolerance; } void Quad2D::print(std::ostream& s) const { auto printVector2D = [&s](const Vector2D& v) { s << "[" << v[0] << "," << v[1] << "]"; }; s << "Quad2D["; printVector2D(v00); s << ", "; printVector2D(v10); s << ", "; printVector2D(v11); s << ", "; printVector2D(v01); s << "]"; } //---------------------------------------------------------------------------------------------------------------------- } // namespace element } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/element/Triag3D.cc0000664000175000017500000000637215160212070023052 0ustar alastairalastair/* * (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 "atlas/interpolation/element/Triag3D.h" #include "atlas/interpolation/method/Intersect.h" #include "atlas/interpolation/method/Ray.h" //---------------------------------------------------------------------------------------------------------------------- namespace atlas { namespace interpolation { namespace element { method::Intersect Triag3D::intersects(const method::Ray& r, double edgeEpsilon, double epsilon) const { method::Intersect isect; Vector3D edge1 = v1 - v0; Vector3D edge2 = v2 - v0; Vector3D pvec = r.dir.cross(edge2); // ray is parallel to triangle (check?) const double det = edge1.dot(pvec); if (fabs(det) < epsilon) { return isect.fail(); } const double invDet = 1. / det; Vector3D tvec = r.orig - v0; Vector3D qvec = tvec.cross(edge1); isect.u = tvec.dot(pvec) * invDet; isect.v = r.dir.dot(qvec) * invDet; isect.t = edge2.dot(qvec) * invDet; const double w = 1 - (isect.u + isect.v); if (w < 0) { // check if far outside of triangle, in respect to diagonal edge if (w < -edgeEpsilon) { return isect.fail(); } // snap to diagonal edge // Note: we may still be to the left of vertical edge or below the // horizontal edge isect.u += 0.5 * w; isect.v += 0.5 * w; } if (isect.u < 0) { // check if far outside of triangle, in respect to vertical edge if ((isect.u < -edgeEpsilon) || (isect.v < -edgeEpsilon) || (isect.v > 1 + edgeEpsilon)) { return isect.fail(); } // snap to lower/upper left corners isect.u = 0; if (isect.v < 0) { isect.v = 0; } else if (isect.v > 1) { isect.v = 1; } } if (isect.v < 0) { // check if far outside of triangle, in respect to horizontal edge if ((isect.v < -edgeEpsilon) || (isect.u < -edgeEpsilon) || (isect.u > 1 + edgeEpsilon)) { return isect.fail(); } // snap to lower left/right corners isect.v = 0; if (isect.u < 0) { isect.u = 0; } else if (isect.u > 1) { isect.u = 1; } } return isect.success(); } double Triag3D::area() const { Vector3D edge1 = v1 - v0; Vector3D edge2 = v2 - v0; Vector3D cross = edge1.cross(edge2); return 0.5 * cross.norm(); } void Triag3D::print(std::ostream& s) const { s << "Triag3D[" << "v0=(" << v0[0] << ", " << v0[1] << ", " << v0[2] << "), v1=(" << v1[0] << ", " << v1[1] << ", " << v1[2] << "), v2=(" << v2[0] << ", " << v2[1] << ", " << v2[2] << ")]"; } //---------------------------------------------------------------------------------------------------------------------- } // namespace element } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/element/Triag2D.cc0000664000175000017500000000617415160212070023051 0ustar alastairalastair/* * (C) Crown Copyright 2021 Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ #include #include #include "atlas/interpolation/Vector2D.h" #include "atlas/interpolation/element/Triag2D.h" #include "atlas/runtime/Log.h" namespace atlas { namespace interpolation { namespace element { //---------------------------------------------------------------------------------------------------------------------- method::Intersect Triag2D::intersects(const PointXY& r, double edgeEpsilon, double epsilon) const { method::Intersect isect; // intersection is false /* * ,v11 * e2 , ^ `. * , ^ r `. * , ^ `. * v00-------------------v10 * e1 */ Vector2D rvec{r.data()}; if (!inTriangle(rvec, epsilon * area())) { return isect.fail(); } Vector2D e1{v10 - v00}; Vector2D e2{v11 - v00}; Vector2D pvec{rvec - v00}; // solve u e1 + v e2 = pvec for u and v double invDet = 1. / (e1.x() * e2.y() - e2.x() * e1.y()); isect.u = (pvec.x() * e2.y() - e2.x() * pvec.y()) * invDet; isect.v = (e1.x() * pvec.y() - pvec.x() * e1.y()) * invDet; // make sure weights are valid if (isect.u > -edgeEpsilon || isect.u < 1. + edgeEpsilon || isect.v > -edgeEpsilon || isect.v < 1. + edgeEpsilon) { return isect.success(); } else { return isect.fail(); } } bool Triag2D::validate() const { // normal for sub-triangle T012 Vector2D E01 = v10 - v00; Vector2D E02 = v11 - v00; double N012 = cross2d(E01, E02); // normal for sub-triangle T120 Vector2D E12 = v11 - v10; Vector2D E10 = v00 - v10; double N120 = cross2d(E12, E10); // normal for sub-triangle T201 Vector2D E20 = -E02; Vector2D E21 = -E12; double N201 = cross2d(E20, E21); // all normals must point same way double dot1 = N120 * N012; double dot2 = N201 * N120; double dot3 = N201 * N012; // all normals must point same way bool is_inside = ((dot1 >= 0. && dot2 >= 0. && dot3 >= 0.) || (dot1 <= 0. && dot2 <= 0. && dot3 <= 0.)); return is_inside; } double Triag2D::area() const { return std::abs(0.5 * cross2d((v10 - v00), (v11 - v00))); } bool Triag2D::inTriangle(const Vector2D& p, double tolerance) const { // point p must be on the inside of all triangle edges to be inside the triangle. return cross2d(p - v00, p - v10) > -tolerance && cross2d(p - v10, p - v11) > -tolerance && cross2d(p - v11, p - v00) > -tolerance; } void Triag2D::print(std::ostream& s) const { auto printVector2D = [&s](const Vector2D& v) { s << "[" << v[0] << "," << v[1] << "]"; }; s << "Triag2D["; printVector2D(v00); s << ", "; printVector2D(v11); s << ", "; printVector2D(v10); s << "]"; } //---------------------------------------------------------------------------------------------------------------------- } // namespace element } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/AssembleGlobalMatrix.cc0000664000175000017500000003255715160212070024231 0ustar alastairalastair/* * (C) Copyright 2025- 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 "AssembleGlobalMatrix.h" #include #include "eckit/linalg/types.h" #include "atlas/array.h" #include "atlas/linalg/sparse/SparseMatrixToTriplets.h" #include "atlas/functionspace/StructuredColumns.h" #include "atlas/functionspace/NodeColumns.h" namespace atlas::interpolation { linalg::SparseMatrixStorage assemble_global_matrix(const Interpolation& interpolation, int mpi_root) { auto& mpi_comm = mpi::comm(); auto compute_max_global_index = [&mpi_comm,mpi_root](const FunctionSpace& fs) { auto global_index = array::make_view(fs.global_index()); auto ghost = array::make_view(fs.ghost()); gidx_t max_gidx{0}; for( size_t i = 0; i < global_index.size(); ++i) { if (not ghost(i)) { max_gidx = std::max(max_gidx, global_index(i)); } } #if ATLAS_ECKIT_VERSION_AT_LEAST(1, 22, 2) // eckit::mpi::Comm::reduceInPlace() was only added in eckit 1.22.2 mpi_comm.reduceInPlace(max_gidx, eckit::mpi::max(), mpi_root); #else mpi_comm.allReduceInPlace(max_gidx, eckit::mpi::max()); (void)mpi_root; // to silence unused lambda capture warning #endif return max_gidx; }; auto src_fs = interpolation.source(); auto tgt_fs = interpolation.target(); // compute max column and row indices gidx_t tgt_max_gidx = compute_max_global_index(tgt_fs); gidx_t src_max_gidx = compute_max_global_index(src_fs); return assemble_global_matrix(tgt_max_gidx, src_max_gidx, interpolation, mpi_root); } linalg::SparseMatrixStorage assemble_global_matrix(size_t nrows, size_t ncols, const Interpolation& interpolation, int mpi_root) { auto src_fs = interpolation.source(); auto tgt_fs = interpolation.target(); auto& mpi_comm = mpi::comm(); auto mpi_size = mpi_comm.size(); auto mpi_rank = mpi_comm.rank(); std::vector global_cols; std::vector global_rows; std::vector global_vals; // Compute global_cols, global_rows, global_vals { std::vector local_cols; std::vector local_rows; std::vector local_vals; // Compute local_cols, local_rows, local_vals { interpolation::MatrixCache interpolation_cache = interpolation.createCache(); auto local_matrix_storage = interpolation_cache.matrix(); auto local_matrix = atlas::linalg::make_host_view(local_matrix_storage); const auto src_ridx = array::make_indexview(src_fs.remote_index()); const auto src_global_index = array::make_view(src_fs.global_index()); const auto tgt_global_index = array::make_view(tgt_fs.global_index()); const auto src_part = array::make_view(src_fs.partition()); const auto tgt_ghost = array::make_view(tgt_fs.ghost()); eckit::mpi::Buffer recv_global_idx_buf(mpi_size); { std::vector send_global_idx(src_global_index.size()); for (int i = 0; i < send_global_idx.size(); ++i) { send_global_idx[i] = src_global_index[i]; } mpi_comm.allGatherv(send_global_idx.begin(), send_global_idx.end(), recv_global_idx_buf); } auto recv_global_idx = [&recv_global_idx_buf](int part, int ridx) { return recv_global_idx_buf.buffer[ recv_global_idx_buf.displs[part] + ridx ]; }; auto src_gidx = [&](auto idx) { return recv_global_idx(src_part(idx), src_ridx(idx)); }; local_cols.reserve(local_matrix.nnz()); local_rows.reserve(local_matrix.nnz()); local_vals.reserve(local_matrix.nnz()); const auto* outer = local_matrix.outer(); const auto* inner = local_matrix.inner(); const auto* value = local_matrix.value(); const size_t nrows = local_matrix.rows(); for(idx_t r = 0; r < nrows; ++r) { if( not tgt_ghost(r) ) { idx_t row = tgt_global_index(r); for (idx_t c = outer[r]; c < outer[r + 1]; ++c) { auto src_idx = inner[c]; auto col = src_gidx(src_idx); auto val = value[c]; local_rows.emplace_back(row); local_cols.emplace_back(col); local_vals.emplace_back(val); } } } } // Gather local to global on mpi_root size_t local_nnz = local_vals.size(); std::vector local_nnz_per_rank(mpi_size); mpi_comm.gather(local_nnz, local_nnz_per_rank, mpi_root); constexpr int mpi_tag = 0; if (mpi_rank == mpi_root) { size_t global_nnz = std::accumulate(local_nnz_per_rank.begin(), local_nnz_per_rank.end(), 0); global_cols.resize(global_nnz); global_rows.resize(global_nnz); global_vals.resize(global_nnz); size_t pos = 0; size_t pos_root = 0; for (int jproc = 0; jproc < mpi_size; ++jproc) { if (jproc == mpi_root) { pos += (jproc == 0) ? 0 : local_nnz_per_rank[jproc - 1]; pos_root = pos; continue; } pos += (jproc == 0 ? 0 : local_nnz_per_rank[jproc - 1]); mpi_comm.receive(global_cols.data() + pos, local_nnz_per_rank[jproc], jproc, mpi_tag); mpi_comm.receive(global_rows.data() + pos, local_nnz_per_rank[jproc], jproc, mpi_tag); mpi_comm.receive(global_vals.data() + pos, local_nnz_per_rank[jproc], jproc, mpi_tag); } for (int i = 0; i < local_nnz; ++i) { global_cols[i + pos_root] = local_cols[i]; global_rows[i + pos_root] = local_rows[i]; global_vals[i + pos_root] = local_vals[i]; } } else { mpi_comm.send(local_cols.data(), local_cols.size(), mpi_root, mpi_tag); mpi_comm.send(local_rows.data(), local_rows.size(), mpi_root, mpi_tag); mpi_comm.send(local_vals.data(), local_vals.size(), mpi_root, mpi_tag); } } linalg::SparseMatrixStorage global_matrix; if (mpi_rank == mpi_root) { size_t index_base = 1; bool is_sorted = false; global_matrix = atlas::linalg::make_sparse_matrix_storage_from_rows_columns_values(nrows, ncols, global_rows, global_cols, global_vals, index_base, is_sorted); } return global_matrix; } template void distribute_global_matrix(const linalg::SparseMatrixView& global_matrix, const array::Array& partition, std::vector& rows, std::vector& cols, std::vector& vals, int mpi_root) { ATLAS_TRACE("distribute_global_matrix_lowlevel"); const auto tgt_part_glb = array::make_view(partition); auto& mpi_comm = mpi::comm(); auto mpi_size = mpi_comm.size(); auto mpi_rank = mpi_comm.rank(); // compute how many nnz-entries each task gets size_t nnz_loc = 0; int mpi_tag = 0; std::vector nnz_per_task(mpi_size); const auto outer = global_matrix.outer(); const auto inner = global_matrix.inner(); const auto value = global_matrix.value(); if (mpi_rank == mpi_root) { ATLAS_ASSERT(global_matrix.rows() <= tgt_part_glb.size()); for(std::size_t r = 0; r < global_matrix.rows(); ++r) { if (tgt_part_glb(r) >= mpi_size) { ATLAS_DEBUG_VAR(r); ATLAS_DEBUG_VAR(mpi_size); ATLAS_DEBUG_VAR(tgt_part_glb(r)); ATLAS_ASSERT(tgt_part_glb(r) < nnz_per_task.size()); } nnz_per_task[tgt_part_glb(r)] += outer[r+1] - outer[r]; } for (int jproc = 0; jproc < mpi::comm().size(); ++jproc) { if (jproc != mpi_root) { mpi::comm().send(nnz_per_task.data() + jproc, 1, jproc, mpi_tag); } } nnz_loc = nnz_per_task[mpi_root]; } else { mpi_comm.receive(&nnz_loc, 1, mpi_root, mpi_tag); } rows.resize(nnz_loc); cols.resize(nnz_loc); vals.resize(nnz_loc); if (mpi_rank == mpi_root) { std::vector> send_rows(mpi_size); std::vector> send_cols(mpi_size); std::vector> send_vals(mpi_size); for(std::size_t jproc=0; jproc < mpi_size; ++jproc) { send_rows[jproc].reserve(nnz_per_task[jproc]); send_cols[jproc].reserve(nnz_per_task[jproc]); send_vals[jproc].reserve(nnz_per_task[jproc]); } for(std::size_t r = 0; r < global_matrix.rows(); ++r) { int jproc = tgt_part_glb(r); for (auto c = outer[r]; c < outer[r + 1]; ++c) { auto col = inner[c]; send_rows[jproc].emplace_back(r); send_cols[jproc].emplace_back(col); send_vals[jproc].emplace_back(value[c]); } } for(std::size_t jproc = 0; jproc < mpi_size; ++jproc) { if (jproc != mpi_root) { mpi_comm.send(send_rows[jproc].data(), send_rows[jproc].size(), jproc, mpi_tag); mpi_comm.send(send_cols[jproc].data(), send_cols[jproc].size(), jproc, mpi_tag); mpi_comm.send(send_vals[jproc].data(), send_vals[jproc].size(), jproc, mpi_tag); } else { rows = send_rows[jproc]; cols = send_cols[jproc]; vals = send_vals[jproc]; } } } else { mpi_comm.receive(rows.data(), nnz_loc, mpi_root, mpi_tag); mpi_comm.receive(cols.data(), nnz_loc, mpi_root, mpi_tag); mpi_comm.receive(vals.data(), nnz_loc, mpi_root, mpi_tag); } } linalg::SparseMatrixStorage distribute_global_matrix(const FunctionSpace& src_fs, const FunctionSpace& tgt_fs, const linalg::SparseMatrixStorage& gmatrix, int mpi_root) { ATLAS_TRACE("distribute_global_matrix"); Field field_tgt_part_glb = tgt_fs.createField(tgt_fs.partition(), option::global(mpi_root)); // This should not be needed, but there's a problem with the NodeColumns functionspace for ORCA grids leading to uninitialized partitions if (functionspace::NodeColumns(tgt_fs)) { if (auto grid = functionspace::NodeColumns(tgt_fs).mesh().grid()) { if (grid.type() == "ORCA") { array::make_view(field_tgt_part_glb).assign(0); } } } ATLAS_TRACE_SCOPE("gather partition") { tgt_fs.gather(tgt_fs.partition(), field_tgt_part_glb); } using Index = eckit::linalg::Index; using Value = eckit::linalg::Scalar; std::vector rows, cols; std::vector vals; distribute_global_matrix(atlas::linalg::make_host_view(gmatrix), field_tgt_part_glb, rows, cols, vals, mpi_root); // map global index to local index std::unordered_map to_local_rows; std::unordered_map to_local_cols; ATLAS_TRACE_SCOPE("convert to local indexing") { auto tgt_gidx_exchanged = tgt_fs.createField(tgt_fs.global_index()); tgt_gidx_exchanged.array().copy(tgt_fs.global_index()); tgt_fs.haloExchange(tgt_gidx_exchanged); const auto tgt_global_index = array::make_view(tgt_gidx_exchanged); const auto tgt_ghost = array::make_view(tgt_fs.ghost()); auto src_gidx_exchanged = src_fs.createField(src_fs.global_index()); src_gidx_exchanged.array().copy(src_fs.global_index()); src_fs.haloExchange(src_gidx_exchanged); const auto src_global_index = array::make_view(src_gidx_exchanged); const auto src_ghost = array::make_view(src_fs.ghost()); for (idx_t r = 0; r < tgt_global_index.size(); ++r) { auto gr = tgt_global_index(r); if (tgt_ghost(r) && to_local_rows.find(gr) != to_local_rows.end()) { continue; } to_local_rows[gr] = r; } for (idx_t c = 0; c < src_global_index.size(); ++c) { auto gc = src_global_index(c); if (src_ghost(c) && to_local_cols.find(gc) != to_local_cols.end()) { continue; } to_local_cols[gc] = c; } for (int r = 0; r < rows.size(); ++r) { rows[r] = to_local_rows[rows[r] + 1]; cols[r] = to_local_cols[cols[r] + 1]; } } linalg::SparseMatrixStorage matrix; constexpr int index_base = 0; constexpr bool is_sorted = false; matrix = linalg::make_sparse_matrix_storage_from_rows_columns_values(tgt_fs.size(), src_fs.size(), rows, cols, vals, index_base, is_sorted); return matrix; } } //end namespace atlas-0.46.0/src/atlas/interpolation/Vector3D.h0000664000175000017500000000612615160212070021454 0ustar alastairalastair/* * (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/library/config.h" #if ATLAS_HAVE_EIGEN #define EIGEN_NO_AUTOMATIC_RESIZING //#define EIGEN_DONT_ALIGN //#define EIGEN_DONT_VECTORIZE ATLAS_SUPPRESS_WARNINGS_PUSH ATLAS_SUPPRESS_WARNINGS_INTEGER_SIGN_CHANGE ATLAS_SUPPRESS_WARNINGS_CODE_IS_UNREACHABLE #include #include ATLAS_SUPPRESS_WARNINGS_POP #else #include #include #endif namespace atlas { namespace interpolation { //---------------------------------------------------------------------------------------------------------------------- #if ATLAS_HAVE_EIGEN typedef Eigen::Vector3d Vector3D; #else class Vector3D { public: Vector3D(const double* d) { xyz_[0] = d[0]; xyz_[1] = d[1]; xyz_[2] = d[2]; } Vector3D(double x, double y, double z) { xyz_[0] = x; xyz_[1] = y; xyz_[2] = z; } Vector3D() { // Warning, data_ is uninitialised } static Vector3D Map(const double* data) { return Vector3D(data); } // Operators double operator[](size_t i) const { return xyz_[i]; } // Vector3D operator*(const Vector3D &) const; Vector3D operator-(const Vector3D& other) const { return Vector3D(x() - other.x(), y() - other.y(), z() - other.z()); } Vector3D operator+(const Vector3D& other) const { return Vector3D(x() + other.x(), y() + other.y(), z() + other.z()); } Vector3D operator-() const { return Vector3D(-x(), -y(), -z()); } Vector3D operator/(double a) const { return Vector3D(x() / a, y() / a, z() / a); } Vector3D operator*(double a) const { return Vector3D(x() * a, y() * a, z() * a); } double norm() const { return std::sqrt(squaredNorm()); } double squaredNorm() const { return x() * x() + y() * y() + z() * z(); } double dot(const Vector3D& other) const { return x() * other.x() + y() * other.y() + z() * other.z(); } Vector3D cross(const Vector3D& other) const { return Vector3D(y() * other.z() - z() * other.y(), z() * other.x() - x() * other.z(), x() * other.y() - y() * other.x()); } void print(std::ostream& s) const; friend std::ostream& operator<<(std::ostream& s, const Vector3D& p) { p.print(s); return s; } double* data() { return xyz_; } const double* data() const { return xyz_; } private: double x() const { return xyz_[0]; } double y() const { return xyz_[1]; } double z() const { return xyz_[2]; } double xyz_[3]; }; Vector3D operator*(double, const Vector3D&); #endif //---------------------------------------------------------------------------------------------------------------------- } // namespace interpolation } // namespace atlas atlas-0.46.0/src/atlas/interpolation/Vector3D.cc0000664000175000017500000000123415160212070021605 0ustar alastairalastair/* * (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 "Vector3D.h" #if !ATLAS_HAVE_EIGEN #include namespace atlas { namespace interpolation { void Vector3D::print(std::ostream& s) const { s << "[" << x() << "," << y() << "," << z() << "]"; } } // namespace interpolation } // namespace atlas #endif atlas-0.46.0/src/atlas/domain.h0000664000175000017500000000072515160212070016402 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ /// @author Willem Deconinck #pragma once #include "atlas/domain/Domain.h" atlas-0.46.0/src/atlas/trans/0000775000175000017500000000000015160212070016105 5ustar alastairalastairatlas-0.46.0/src/atlas/trans/local/0000775000175000017500000000000015160212070017177 5ustar alastairalastairatlas-0.46.0/src/atlas/trans/local/LegendrePolynomials.h0000664000175000017500000000550215160212070023326 0ustar alastairalastair/* * (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 namespace atlas { namespace trans { //----------------------------------------------------------------------------- // Routine to compute the Legendre polynomials in serial according to Belousov // (using correction by Swarztrauber) // // Reference: // S.L. Belousov, Tables of normalized associated Legendre Polynomials, Pergamon // Press (1962) // P.N. Swarztrauber, On computing the points and weights for Gauss-Legendre // quadrature, // SIAM J. Sci. Comput. Vol. 24 (3) pp. 945-954 (2002) // // Author of Fortran version: // Mats Hamrud, Philippe Courtier, Nils Wedi *ECMWF* // // Ported to C++ by: // Andreas Mueller *ECMWF* // void compute_zfn(const int trc, double zfn[]); // Workspace to avoid repeated allocations struct LegendrePolynomialsWorkspace { LegendrePolynomialsWorkspace(int trc) { vsin.reserve(trc+1); vcos.reserve(trc+1); } std::vector vsin; std::vector vcos; }; void compute_legendre_polynomials_lat(const int trc, // truncation (in) const double lat, // latitude in radians (in) double legpol[], // legendre polynomials double zfn[], LegendrePolynomialsWorkspace& w); // workspace to avoid allocations void compute_legendre_polynomials( const int trc, // truncation (in) const int nlats, // number of latitudes const double lats[], // latitudes in radians (in) double legendre_sym[], // values of associated Legendre functions, symmetric part double legendre_asym[], // values of associated Legendre functions, asymmetric part size_t leg_start_sym[], // start indices for different zonal wave numbers, symmetric part size_t leg_start_asym[]); // start indices for different zonal wave numbers, asymmetric part void compute_legendre_polynomials_all(const int trc, // truncation (in) const int nlats, // number of latitudes const double lats[], // latitudes in radians (in) double legendre[]); // legendre polynomials for all latitudes // -------------------------------------------------------------------------------------------------------------------- } // namespace trans } // namespace atlas atlas-0.46.0/src/atlas/trans/local/LegendreCacheCreatorLocal.h0000664000175000017500000000265315160212070024322 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include "atlas/grid/Grid.h" #include "atlas/trans/LegendreCacheCreator.h" #include "atlas/util/Config.h" //----------------------------------------------------------------------------- namespace atlas { namespace trans { //----------------------------------------------------------------------------- class LegendreCacheCreatorLocal : public trans::LegendreCacheCreatorImpl { public: LegendreCacheCreatorLocal(const Grid&, int truncation, const eckit::Configuration& = util::NoConfig()); virtual ~LegendreCacheCreatorLocal() override; virtual bool supported() const override; virtual std::string uid() const override; virtual void create(const std::string& path) const override; virtual Cache create() const override; size_t estimate() const override; private: const Grid grid_; const int truncation_; const util::Config config_; mutable std::string unique_identifier_; }; // ------------------------------------------------------------------ } // namespace trans } // namespace atlas atlas-0.46.0/src/atlas/trans/local/VorDivToUVLocal.h0000664000175000017500000000401015160212070022305 0ustar alastairalastair/* * (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/trans/VorDivToUV.h" //----------------------------------------------------------------------------- // Forward declarations namespace atlas { class FunctionSpace; } //----------------------------------------------------------------------------- namespace atlas { namespace trans { //----------------------------------------------------------------------------- class VorDivToUVLocal : public trans::VorDivToUVImpl { public: VorDivToUVLocal(const FunctionSpace&, const eckit::Configuration& = util::NoConfig()); VorDivToUVLocal(int truncation, const eckit::Configuration& = util::NoConfig()); virtual ~VorDivToUVLocal(); virtual int truncation() const override { return truncation_; } // pure virtual interface // -- IFS style API -- // These fields have special interpretation required. You need to know what // you're doing. // See IFS trans library. /*! * @brief Compute spectral wind (U/V) from spectral vorticity/divergence * * U = u*cos(lat) * V = v*cos(lat) * * @param nb_fields [in] Number of fields * @param vorticity [in] Spectral vorticity * @param divergence [in] Spectral divergence * @param U [out] Spectral wind U = u*cos(lat) * @param V [out] Spectral wind V = v*cos(lat) */ virtual void execute(const int nb_coeff, const int nb_fields, const double vorticity[], const double divergence[], double U[], double V[], const eckit::Configuration& = util::NoConfig()) const override; private: int truncation_; }; // ------------------------------------------------------------------ } // namespace trans } // namespace atlas atlas-0.46.0/src/atlas/trans/local/TransLocal.h0000664000175000017500000002711115160212070021414 0ustar alastairalastair/* * (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/array.h" #include "atlas/functionspace/Spectral.h" #include "atlas/grid/Grid.h" #include "atlas/linalg/dense/Backend.h" #include "atlas/trans/detail/TransImpl.h" //----------------------------------------------------------------------------- // Forward declarations namespace atlas { class Field; class FieldSet; class StructuredGrid; } // namespace atlas namespace atlas::linalg { class FFT; } //----------------------------------------------------------------------------- namespace atlas { namespace trans { namespace detail { struct FFT_Data; } class LegendreCacheCreatorLocal; int fourier_truncation(const int truncation, // truncation const int nx, // number of longitudes const int nxmax, // maximum nx const int ndgl, // number of latitudes const double lat, // latitude in radian const bool regular); // regular grid //----------------------------------------------------------------------------- /// @class TransLocal /// /// Local spherical harmonics transformations to any grid /// Optimisations are present for structured grids /// For global grids, please consider using TransIFS instead. /// /// @todo: /// - support multiple fields /// - support atlas::Field and atlas::FieldSet based on function spaces /// /// @note: Direct transforms are not implemented and cannot be unless /// the grid is global. There are no plans to support this at the moment. /// /// @note: The matrix_multiply (GEMM) implementation can be configured within the Configuration argument in the constructor /// using "matrix_multiply" key or if not given, it will use the atlas::linalg::dense::current_backend(), /// evaluated at invocation time. To reset the current_backend at any time: /// atlas::linalg::dense::current_backend( type ); /// Possible values for type or "matrix_multiply" are e.g. /// - "eckit_linalg" which uses the configured default backend of eckit::linalg::LinearAlgebra /// - "generic" : "generic" backend for eckit::linalg::LinearAlgebra /// - "lapack" : "lapack" backend for eckit::linalg::LinearAlgebra /// - "openmp" : "openmp" backend for eckit::linalg::LinearAlgebra, or "generic" if "openmp" is not available. /// - "eigen" : "eigen" backend for eckit::linalg::LinearAlgebra class TransLocal : public trans::TransImpl { public: TransLocal(const Grid&, const long truncation, const eckit::Configuration& = util::NoConfig()); TransLocal(const Grid&, const Domain&, const long truncation, const eckit::Configuration& = util::NoConfig()); TransLocal(const Cache&, const Grid&, const long truncation, const eckit::Configuration& = util::NoConfig()); TransLocal(const Cache&, const Grid&, const Domain&, const long truncation, const eckit::Configuration& = util::NoConfig()); virtual ~TransLocal() override; virtual int truncation() const override { return truncation_; } virtual size_t nb_spectral_coefficients() const override { return (truncation_ + 1) * (truncation_ + 2); } virtual size_t nb_spectral_coefficients_global() const override { return (truncation_ + 1) * (truncation_ + 2); } virtual const Grid& grid() const override { return grid_; } virtual const functionspace::Spectral& spectral() const override; virtual void invtrans(const Field& spfield, Field& gpfield, const eckit::Configuration& = util::NoConfig()) const override; virtual void invtrans(const FieldSet& spfields, FieldSet& gpfields, const eckit::Configuration& = util::NoConfig()) const override; virtual void invtrans_grad(const Field& spfield, Field& gradfield, const eckit::Configuration& = util::NoConfig()) const override; virtual void invtrans_grad(const FieldSet& spfields, FieldSet& gradfields, const eckit::Configuration& = util::NoConfig()) const override; virtual void invtrans_vordiv2wind(const Field& spvor, const Field& spdiv, Field& gpwind, const eckit::Configuration& = util::NoConfig()) const override; virtual void invtrans_adj(const Field& gpfield, Field& spfield, const eckit::Configuration& = util::NoConfig()) const override; virtual void invtrans_adj(const FieldSet& gpfields, FieldSet& spfields, const eckit::Configuration& = util::NoConfig()) const override; virtual void invtrans_grad_adj(const Field& gradfield, Field& spfield, const eckit::Configuration& = util::NoConfig()) const override; virtual void invtrans_grad_adj(const FieldSet& spfields, FieldSet& gradfields, const eckit::Configuration& = util::NoConfig()) const override; virtual void invtrans_vordiv2wind_adj(const Field& gpwind, Field& spvor, Field& spdiv, const eckit::Configuration& = util::NoConfig()) const override; // -- IFS style API -- virtual void invtrans(const int nb_scalar_fields, const double scalar_spectra[], const int nb_vordiv_fields, const double vorticity_spectra[], const double divergence_spectra[], double gp_fields[], const eckit::Configuration& = util::NoConfig()) const override; virtual void invtrans(const int nb_scalar_fields, const double scalar_spectra[], double gp_fields[], const eckit::Configuration& = util::NoConfig()) const override; virtual void invtrans(const int nb_vordiv_fields, const double vorticity_spectra[], const double divergence_spectra[], double gp_fields[], const eckit::Configuration& = util::NoConfig()) const override; virtual void invtrans_adj(const int nb_scalar_fields, const double gp_fields[], const int nb_vordiv_fields, double vorticity_spectra[], double divergence_spectra[], double scalar_spectra[], const eckit::Configuration& = util::NoConfig()) const override; virtual void invtrans_adj(const int nb_scalar_fields, const double gp_fields[], double scalar_spectra[], const eckit::Configuration& = util::NoConfig()) const override; virtual void invtrans_adj(const int nb_vordiv_fields, const double gp_fields[], double vorticity_spectra[], double divergence_spectra[], const eckit::Configuration& = util::NoConfig()) const override; // -- NOT SUPPORTED -- // virtual void dirtrans(const Field& gpfield, Field& spfield, const eckit::Configuration& = util::NoConfig()) const override; virtual void dirtrans(const FieldSet& gpfields, FieldSet& spfields, const eckit::Configuration& = util::NoConfig()) const override; virtual void dirtrans_wind2vordiv(const Field& gpwind, Field& spvor, Field& spdiv, const eckit::Configuration& = util::NoConfig()) const override; virtual void dirtrans_adj(const Field& spfield, Field& gpfield, const eckit::Configuration& = util::NoConfig()) const override; virtual void dirtrans_adj(const FieldSet& spfields, FieldSet& gpfields, const eckit::Configuration& = util::NoConfig()) const override; virtual void dirtrans_wind2vordiv_adj(const Field& spvor, const Field& spdiv, Field& gpwind, const eckit::Configuration& = util::NoConfig()) const override; virtual void dirtrans(const int nb_fields, const double scalar_fields[], double scalar_spectra[], const eckit::Configuration& = util::NoConfig()) const override; virtual void dirtrans(const int nb_fields, const double wind_fields[], double vorticity_spectra[], double divergence_spectra[], const eckit::Configuration& = util::NoConfig()) const override; private: inline constexpr int posMethod(const int jfld, const int imag, const int jlat, const int jm, const int nb_fields, const int nlats) const { return imag + 2 * (jm + (truncation_ + 1) * (jlat + nlats * jfld)); } void invtrans_legendre(const int truncation, const int nlats, const int nb_fields, const int nb_vordiv_fields, const double scalar_spectra[], double scl_fourier[], const eckit::Configuration& config) const; void invtrans_fourier_regular(const int nlats, const int nlons, const int nb_fields, double scl_fourier[], double gp_fields[], const eckit::Configuration& config) const; void invtrans_fourier_reduced(const int nlats, const StructuredGrid& g, const int nb_fields, double scl_fourier[], double gp_fields[], const eckit::Configuration& config) const; void invtrans_unstructured_precomp(const int truncation, const int nb_scalar_fields, const int nb_vordiv_fields, const double scalar_spectra[], double gp_fields[], const eckit::Configuration& = util::NoConfig()) const; void invtrans_unstructured(const int truncation, const int nb_fields, const int nb_vordiv_fields, const double scalar_spectra[], double gp_fields[], const eckit::Configuration& config) const; void invtrans_uv(const int truncation, const int nb_scalar_fields, const int nb_vordiv_fields, const double scalar_spectra[], double gp_fields[], const eckit::Configuration& = util::NoConfig()) const; bool warning(const eckit::Configuration& = util::NoConfig()) const; friend class LegendreCacheCreatorLocal; private: mutable functionspace::Spectral spectral_; Grid grid_; Grid gridGlobal_; bool useFFT_; bool dgemmMethod1_; bool unstruct_precomp_; bool no_symmetry_; int truncation_; idx_t nlatsNH_; idx_t nlatsSH_; idx_t nlatsLeg_; idx_t nlatsLegReduced_; idx_t nlatsLegDomain_; std::vector jlonMin_; idx_t jlatMin_; idx_t jlatMinLeg_; int nlonsMaxGlobal_; std::vector nlonsGlobal_; std::vector nlat0_; idx_t nlatsGlobal_; bool precompute_; double* legendre_; double* legendre_sym_; double* legendre_asym_; double* fourier_; double* fouriertp_; std::vector legendre_sym_begin_; std::vector legendre_asym_begin_; Cache cache_; Cache export_legendre_; const void* legendre_cache_{nullptr}; size_t legendre_cachesize_{0}; const void* fft_cache_{nullptr}; size_t fft_cachesize_{0}; std::unique_ptr fft_data_; std::unique_ptr fft_; std::string linalg_backend_; std::string fft_backend_; int warning_ = 0; }; //----------------------------------------------------------------------------- } // namespace trans } // namespace atlas atlas-0.46.0/src/atlas/trans/local/LegendreCacheCreatorLocal.cc0000664000175000017500000001136115160212070024454 0ustar alastairalastair/* * (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/trans/local/LegendreCacheCreatorLocal.h" #include #include #include #include "eckit/types/FloatCompare.h" #include "eckit/utils/MD5.h" #include "atlas/grid.h" #include "atlas/option.h" #include "atlas/runtime/Exception.h" #include "atlas/trans/Trans.h" #include "atlas/trans/local/TransLocal.h" namespace atlas { namespace trans { namespace { static LegendreCacheCreatorBuilder builder("local"); } namespace { std::string truncate(const std::string& str) { const int trunc = std::min(10ul, str.size()); return str.substr(0, trunc); } std::string hash(const Grid& grid) { eckit::MD5 h; StructuredGrid structured(grid); if (structured && not grid.projection()) { for (auto& y : structured.y()) { h.add(std::lround(y * 1.e8)); } } else { grid.hash(h); } return truncate(h.digest()); } std::string hash(const eckit::Configuration& config) { eckit::MD5 h; // Add options and other unique keys h << "flt" << config.getBool("flt", false); return truncate(h.digest()); } } // namespace std::string LegendreCacheCreatorLocal::uid() const { if (unique_identifier_.empty()) { std::ostringstream stream; auto give_up = [&]() { // We cannot make more assumptions on reusability for different grids stream << "grid-" << hash(grid_); }; stream << "local-T" << truncation_ << "-"; StructuredGrid structured(grid_); if (grid_.projection()) { give_up(); } else if (GaussianGrid(grid_)) { // Same cache for any global Gaussian grid stream << "GaussianN" << GaussianGrid(grid_).N(); } else if (RegularLonLatGrid(grid_)) { // Same cache for any global regular grid auto g = RegularLonLatGrid(grid_); const double dy_2 = 90. / double(g.ny()); bool shifted_lat = eckit::types::is_approximately_equal(g.y().front(), 90. - dy_2) && eckit::types::is_approximately_equal(g.y().back(), -90. + dy_2); bool standard_lat = eckit::types::is_approximately_equal(g.y().front(), 90.) && eckit::types::is_approximately_equal(g.y().back(), -90.); if (standard_lat) { stream << "L" << "-ny" << g.ny(); } else if (shifted_lat) { stream << "S" << "-ny" << g.ny(); } else { // I don't think we get here, but just in case, give up give_up(); } } else if (RegularGrid(grid_) && structured.yspace().type() == "linear") { RectangularDomain domain(grid_.domain()); ATLAS_ASSERT(domain); stream << "Regional"; stream << "-south" << domain.ymin(); stream << "-north" << domain.ymax(); stream << "-ny" << structured.ny(); } else { // It gets too complicated, so let's not be smart give_up(); } stream << "-OPT" << hash(config_); unique_identifier_ = stream.str(); } return unique_identifier_; } LegendreCacheCreatorLocal::~LegendreCacheCreatorLocal() = default; LegendreCacheCreatorLocal::LegendreCacheCreatorLocal(const Grid& grid, int truncation, const eckit::Configuration& config): grid_(grid), truncation_(truncation), config_(config) {} bool LegendreCacheCreatorLocal::supported() const { if (not StructuredGrid(grid_)) { return false; } if (grid_.projection()) { return false; } return true; } void LegendreCacheCreatorLocal::create(const std::string& path) const { Trans tmp(grid_, truncation_, config_ | option::type("local") | option::write_legendre(path)); } Cache LegendreCacheCreatorLocal::create() const { util::Config export_legendre("export_legendre", true); Trans tmp(grid_, truncation_, config_ | option::type("local") | export_legendre); auto impl = dynamic_cast(tmp.get()); return impl->export_legendre_; } size_t LegendreCacheCreatorLocal::estimate() const { return size_t(truncation_ * truncation_ * truncation_) / 2 * sizeof(double); } } // namespace trans } // namespace atlas atlas-0.46.0/src/atlas/trans/local/VorDivToUVLocal.cc0000664000175000017500000002105515160212070022453 0ustar alastairalastair/* * (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/trans/local/VorDivToUVLocal.h" #include // for std::sqrt #include "atlas/functionspace/Spectral.h" #include "atlas/runtime/Log.h" #include "atlas/runtime/Trace.h" #include "atlas/util/Earth.h" using atlas::FunctionSpace; using atlas::functionspace::Spectral; namespace atlas { namespace trans { namespace { static VorDivToUVBuilder builder("local"); } // -------------------------------------------------------------------------------------------------------------------- // Routine to copy spectral data into internal storage form of IFS trans // Ported to C++ by: Andreas Mueller *ECMWF* void prfi1b(const int truncation, const int km, // zonal wavenumber const int nb_fields, // number of fields const double rspec[], // spectral data double pia[]) // spectral components in data layout of trans library { int ilcm = truncation + 1 - km, ioff = (2 * truncation - km + 3) * km, nlei1 = truncation + 4 + (truncation + 4 + 1) % 2; for (int j = 1; j <= ilcm; j++) { int inm = ioff + (ilcm - j) * 2; for (int jfld = 0; jfld < nb_fields; jfld++) { int ir = 2 * jfld, ii = ir + 1; pia[ir * nlei1 + j + 1] = rspec[inm * nb_fields + jfld]; pia[ii * nlei1 + j + 1] = rspec[(inm + 1) * nb_fields + jfld]; } } for (int jfld = 0; jfld < 2 * nb_fields; jfld++) { pia[jfld * nlei1] = 0.; pia[jfld * nlei1 + 1] = 0.; pia[jfld * nlei1 + ilcm + 2] = 0.; } } // -------------------------------------------------------------------------------------------------------------------- // Routine to compute spectral velocities (*cos(latitude)) out of spectral // vorticity and divergence // Reference: // ECMWF Research Department documentation of the IFS // Temperton, 1991, MWR 119 p1303 // Ported to C++ by: Andreas Mueller *ECMWF* void vd2uv(const int truncation, // truncation const int nb_vordiv_fields, // number of vorticity and divergence fields const double vorticity_spectra[], // spectral data of vorticity const double divergence_spectra[], // spectral data of divergence double U[], // spectral data of U double V[], // spectral data of V const eckit::Configuration& config) { std::vector repsnm((truncation + 1) * (truncation + 6) / 2); int idx = 0; int nlei1 = truncation + 4 + (truncation + 4 + 1) % 2; double ra = util::Earth::radius(); std::vector rlapin(truncation + 3); { //ATLAS_TRACE( "general setup" ); // repsnm: epsilon from eq.(2.12) and (2.13) in [Temperton 1991] for (int jm = 0; jm <= truncation; ++jm) { for (int jn = jm; jn <= truncation + 2; ++jn, ++idx) { repsnm[idx] = std::sqrt((jn * jn - jm * jm) / (4. * jn * jn - 1.)); } } repsnm[0] = 0.; // rlapin: constant factor from eq.(2.2) and (2.3) in [Temperton 1991] for (int jn = 1; jn <= truncation + 2; ++jn) { rlapin[jn] = -ra * ra / (jn * (jn + 1.)); } rlapin[0] = 0.; } // inverse the order of repsnm and rlapin for improved accuracy std::vector zepsnm(truncation + 6); std::vector zlapin(truncation + 6); std::vector zn(truncation + 6); for (int km = 0; km <= truncation; ++km) { { //ATLAS_TRACE( "current wavenumber setup" ); for (int jn = km - 1; jn <= truncation + 2; ++jn) { int ij = truncation + 3 - jn; if (jn >= 0) { zlapin[ij] = rlapin[jn]; if (jn < km) { zepsnm[ij] = 0.; } else { zepsnm[ij] = repsnm[jn + (2 * truncation - km + 5) * km / 2]; } } else { zlapin[ij] = 0.; zepsnm[ij] = 0.; } zn[ij] = jn; } zn[0] = truncation + 3; } // copy spectral data into internal trans storage: std::vector rvor(2 * nb_vordiv_fields * nlei1); std::vector rdiv(2 * nb_vordiv_fields * nlei1); std::vector ru(2 * nb_vordiv_fields * nlei1); std::vector rv(2 * nb_vordiv_fields * nlei1); { //ATLAS_TRACE( "copy data to internal storage" ); prfi1b(truncation, km, nb_vordiv_fields, vorticity_spectra, rvor.data()); prfi1b(truncation, km, nb_vordiv_fields, divergence_spectra, rdiv.data()); } { //ATLAS_TRACE( "actual computation" ); // compute eq.(2.12) and (2.13) in [Temperton 1991]: if (km == 0) { for (int jfld = 0; jfld < nb_vordiv_fields; ++jfld) { int ir = 2 * jfld * nlei1 - 1; for (int ji = 2; ji < truncation + 4 - km; ++ji) { double psiM1 = zn[ji + 1] * zepsnm[ji] * zlapin[ji + 1]; double psiP1 = zn[ji - 2] * zepsnm[ji - 1] * zlapin[ji - 1]; ru[ir + ji] = +psiM1 * rvor[ir + ji + 1] - psiP1 * rvor[ir + ji - 1]; rv[ir + ji] = -psiM1 * rdiv[ir + ji + 1] + psiP1 * rdiv[ir + ji - 1]; } } } else { for (int jfld = 0; jfld < nb_vordiv_fields; ++jfld) { int ir = 2 * jfld * nlei1 - 1, ii = ir + nlei1; for (int ji = 2; ji < truncation + 4 - km; ++ji) { double chiIm = km * zlapin[ji]; double psiM1 = zn[ji + 1] * zepsnm[ji] * zlapin[ji + 1]; double psiP1 = zn[ji - 2] * zepsnm[ji - 1] * zlapin[ji - 1]; ru[ir + ji] = -chiIm * rdiv[ii + ji] + psiM1 * rvor[ir + ji + 1] - psiP1 * rvor[ir + ji - 1]; ru[ii + ji] = +chiIm * rdiv[ir + ji] + psiM1 * rvor[ii + ji + 1] - psiP1 * rvor[ii + ji - 1]; rv[ir + ji] = -chiIm * rvor[ii + ji] - psiM1 * rdiv[ir + ji + 1] + psiP1 * rdiv[ir + ji - 1]; rv[ii + ji] = +chiIm * rvor[ir + ji] - psiM1 * rdiv[ii + ji + 1] + psiP1 * rdiv[ii + ji - 1]; } } } } { //ATLAS_TRACE( "copy data back to external storage" ); // copy data from internal storage back to external spectral data: int ilcm = truncation - km; int ioff = (2 * truncation - km + 3) * km; // ioff: start index of zonal wavenumber km in spectral data double za_r = 1. / util::Earth::radius(); for (int j = 0; j <= ilcm; ++j) { // ilcm-j = total wavenumber int inm = ioff + (ilcm - j) * 2; for (int jfld = 0; jfld < nb_vordiv_fields; ++jfld) { int ir = 2 * jfld * nlei1, ii = ir + nlei1; int idx = inm * nb_vordiv_fields + jfld; // real part: U[idx] = ru[ir + j + 2] * za_r; V[idx] = rv[ir + j + 2] * za_r; idx += nb_vordiv_fields; // imaginary part: U[idx] = ru[ii + j + 2] * za_r; V[idx] = rv[ii + j + 2] * za_r; } } } } } void VorDivToUVLocal::execute(const int nb_coeff, const int nb_fields, const double vorticity[], const double divergence[], double U[], double V[], const eckit::Configuration& config) const { vd2uv(truncation_, nb_fields, vorticity, divergence, U, V, config); } VorDivToUVLocal::VorDivToUVLocal(const int truncation, const eckit::Configuration& config): truncation_(truncation) {} VorDivToUVLocal::VorDivToUVLocal(const FunctionSpace& fs, const eckit::Configuration& config): truncation_(Spectral(fs).truncation()) {} VorDivToUVLocal::~VorDivToUVLocal() = default; } // namespace trans } // namespace atlas atlas-0.46.0/src/atlas/trans/local/LegendrePolynomials.cc0000664000175000017500000002523315160212070023467 0ustar alastairalastair/* * (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 "atlas/array.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/trans/local/LegendrePolynomials.h" namespace atlas { namespace trans { //----------------------------------------------------------------------------- void compute_zfn(const int trc, double zfn[]) { auto idxzfn = [&](int jn, int jk) { return jk + (trc + 1) * jn; }; int iodd = 0; // Compute coefficients for Taylor series in Belousov (19) and (21) // Belousov, Swarztrauber use zfn[0]=std::sqrt(2.) // IFS normalisation chosen to be 0.5*Integral(Pnm**2) = 1 zfn[idxzfn(0, 0)] = 2.; for (int jn = 1; jn <= trc; ++jn) { double zfnn = zfn[idxzfn(0, 0)]; for (int jgl = 1; jgl <= jn; ++jgl) { zfnn *= std::sqrt(1. - 0.25 / (jgl * jgl)); } iodd = jn % 2; zfn[idxzfn(jn, jn)] = zfnn; for (int jgl = 2; jgl <= jn - iodd; jgl += 2) { double zfjn = ((jgl - 1.) * (2. * jn - jgl + 2.)); // new factor numerator double zfjd = (jgl * (2. * jn - jgl + 1.)); // new factor denominator zfn[idxzfn(jn, jn - jgl)] = zfn[idxzfn(jn, jn - jgl + 2)] * zfjn / zfjd; } } } void compute_legendre_polynomials_lat(const int trc, // truncation (in) const double lat, // latitude in radians (in) double legpol[], // legendre polynomials double zfn[], LegendrePolynomialsWorkspace& w // workspace to avoid allocations ) { auto idxmn = [&](int jm, int jn) { return (2 * trc + 3 - jm) * jm / 2 + jn - jm; }; auto idxzfn = [&](int jn, int jk) { return jk + (trc + 1) * jn; }; { //ATLAS_TRACE( "compute Legendre polynomials" ); // -------------------- // 1. First two columns // -------------------- double zdlx1 = (M_PI_2 - lat); // theta double zdlx = std::cos(zdlx1); // cos(theta) volatile double zdlsita = std::sqrt(1. - zdlx * zdlx); // sin(theta) (this is how trans library does it) legpol[idxmn(0, 0)] = 1.; w.vsin.resize(trc+1); w.vcos.resize(trc+1); for (int j = 1; j <= trc; j++) { w.vsin[j] = std::sin(j * zdlx1); w.vcos[j] = std::cos(j * zdlx1); } double zdl1sita = 0.; // if we are less than 1 meter from the pole, if (std::abs(zdlsita) <= std::sqrt(std::numeric_limits::epsilon())) { zdlx = 1.; zdlsita = 0.; } else { zdl1sita = 1. / zdlsita; } // ordinary Legendre polynomials from series expansion // --------------------------------------------------- // even N for (int jn = 2; jn <= trc; jn += 2) { double zdlk = 0.5 * zfn[idxzfn(jn, 0)]; double zdlldn = 0.0; double zdsq = 1. / std::sqrt(jn * (jn + 1.)); // represented by only even k for (int jk = 2; jk <= jn; jk += 2) { // normalised ordinary Legendre polynomial == \overbar{P_n}^0 zdlk = zdlk + zfn[idxzfn(jn, jk)] * w.vcos[jk]; // normalised associated Legendre polynomial == \overbar{P_n}^1 zdlldn = zdlldn + zdsq * zfn[idxzfn(jn, jk)] * jk * w.vsin[jk]; } legpol[idxmn(0, jn)] = zdlk; legpol[idxmn(1, jn)] = zdlldn; } // odd N for (int jn = 1; jn <= trc; jn += 2) { zfn[idxzfn(jn, 0)] = 0.; double zdlk = 0.; double zdlldn = 0.0; double zdsq = 1. / std::sqrt(jn * (jn + 1.)); // represented by only even k for (int jk = 1; jk <= jn; jk += 2) { // normalised ordinary Legendre polynomial == \overbar{P_n}^0 zdlk = zdlk + zfn[idxzfn(jn, jk)] * w.vcos[jk]; // normalised associated Legendre polynomial == \overbar{P_n}^1 zdlldn = zdlldn + zdsq * zfn[idxzfn(jn, jk)] * jk * w.vsin[jk]; } legpol[idxmn(0, jn)] = zdlk; legpol[idxmn(1, jn)] = zdlldn; } // -------------------------------------------------------------- // 2. Diagonal (the terms 0,0 and 1,1 have already been computed) // Belousov, equation (23) // -------------------------------------------------------------- double zdls = zdl1sita * std::numeric_limits::min(); for (int jn = 2; jn <= trc; ++jn) { double sq = std::sqrt((2. * jn + 1.) / (2. * jn)); legpol[idxmn(jn, jn)] = legpol[idxmn(jn - 1, jn - 1)] * zdlsita * sq; if (std::abs(legpol[idxmn(jn, jn)]) < zdls) { legpol[idxmn(jn, jn)] = 0.0; } } // --------------------------------------------- // 3. General recurrence (Belousov, equation 17) // --------------------------------------------- for (int jn = 3; jn <= trc; ++jn) { for (int jm = 2; jm < jn; ++jm) { double cn = ((2. * jn + 1.) * (jn + jm - 3.) * (jn + jm - 1.)); // numerator of c in Belousov double cd = ((2. * jn - 3.) * (jn + jm - 2.) * (jn + jm)); // denominator of c in Belousov double dn = ((2. * jn + 1.) * (jn - jm + 1.) * (jn + jm - 1.)); // numerator of d in Belousov double dd = ((2. * jn - 1.) * (jn + jm - 2.) * (jn + jm)); // denominator of d in Belousov double en = ((2. * jn + 1.) * (jn - jm)); // numerator of e in Belousov double ed = ((2. * jn - 1.) * (jn + jm)); // denominator of e in Belousov legpol[idxmn(jm, jn)] = std::sqrt(cn / cd) * legpol[idxmn(jm - 2, jn - 2)] - std::sqrt(dn / dd) * legpol[idxmn(jm - 2, jn - 1)] * zdlx + std::sqrt(en / ed) * legpol[idxmn(jm, jn - 1)] * zdlx; } } } } void compute_legendre_polynomials( const int truncation, // truncation (in) const int nlats, // number of latitudes const double lats[], // latitudes in radians (in) double leg_sym[], // values of associated Legendre functions, symmetric part double leg_asym[], // values of associated Legendre functions, asymmetric part size_t leg_start_sym[], // start indices for different zonal wave numbers, symmetric part size_t leg_start_asym[]) // start indices for different zonal wave numbers, asymmetric part { size_t trc = static_cast(truncation); size_t legendre_size = (trc + 2) * (trc + 1) / 2; std::vector legpol(legendre_size); std::vector zfn((trc + 1) * (trc + 1)); auto idxmn = [&](size_t jm, size_t jn) { return (2 * trc + 3 - jm) * jm / 2 + jn - jm; }; compute_zfn(truncation, zfn.data()); LegendrePolynomialsWorkspace w{truncation}; // Loop over latitudes: for (size_t jlat = 0; jlat < size_t(nlats); ++jlat) { // compute legendre polynomials for current latitude: compute_legendre_polynomials_lat(truncation, lats[jlat], legpol.data(), zfn.data(), w); // split polynomials into symmetric and antisymmetric parts: { //ATLAS_TRACE( "add to global arrays" ); for (size_t jm = 0; jm <= trc; jm++) { size_t is1 = 0, ia1 = 0; for (size_t jn = jm; jn <= trc; jn++) { (jn - jm) % 2 ? ia1++ : is1++; } size_t is2 = 0, ia2 = 0; // the choice between the following two code lines determines whether // total wavenumbers are summed in an ascending or descending order. // The trans library in IFS uses descending order because it should // be more accurate (higher wavenumbers have smaller contributions). // This also needs to be changed when splitting the spectral data in // TransLocal::invtrans_uv! //for ( int jn = jm; jn <= trc; jn++ ) { for (long ljn = long(trc), ljm = long(jm); ljn >= ljm; ljn--) { size_t jn = size_t(ljn); if ((jn - jm) % 2 == 0) { size_t is = leg_start_sym[jm] + is1 * jlat + is2++; leg_sym[is] = legpol[idxmn(jm, jn)]; } else { size_t ia = leg_start_asym[jm] + ia1 * jlat + ia2++; leg_asym[ia] = legpol[idxmn(jm, jn)]; } } } } } } void compute_legendre_polynomials_all(const int truncation, // truncation (in) const int nlats, // number of latitudes const double lats[], // latitudes in radians (in) double legendre[]) // legendre polynomials for all latitudes { size_t trc = static_cast(truncation); size_t legendre_size = (trc + 2) * (trc + 1) / 2; std::vector legpol(legendre_size); std::vector zfn((trc + 1) * (trc + 1)); auto idxmn = [&](size_t jm, size_t jn) { return (2 * trc + 3 - jm) * jm / 2 + jn - jm; }; auto idxmnl = [&](size_t jm, size_t jn, size_t jlat) { return (2 * trc + 3 - jm) * jm / 2 * nlats + jlat * (trc - jm + 1) + jn - jm; }; compute_zfn(truncation, zfn.data()); LegendrePolynomialsWorkspace w{truncation}; // Loop over latitudes: for (size_t jlat = 0; jlat < nlats; ++jlat) { // compute legendre polynomials for current latitude: compute_legendre_polynomials_lat(truncation, lats[jlat], legpol.data(), zfn.data(), w); for (size_t jm = 0; jm <= trc; ++jm) { for (size_t jn = jm; jn <= trc; ++jn) { legendre[idxmnl(jm, jn, jlat)] = legpol[idxmn(jm, jn)]; } } } } // namespace trans // -------------------------------------------------------------------------------------------------------------------- } // namespace trans } // namespace atlas atlas-0.46.0/src/atlas/trans/local/TransLocal.cc0000664000175000017500000022032515160212070021554 0ustar alastairalastair/* * (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/trans/local/TransLocal.h" #include #include #include #include "eckit/config/YAMLConfiguration.h" #include "eckit/eckit.h" #include "eckit/io/DataHandle.h" #include "eckit/linalg/LinearAlgebra.h" #include "eckit/log/Bytes.h" #include "eckit/log/JSON.h" #include "eckit/types/FloatCompare.h" #include "atlas/array.h" #include "atlas/field.h" #include "atlas/grid/Iterator.h" #include "atlas/grid/StructuredGrid.h" #include "atlas/library.h" #include "atlas/linalg/dense.h" #include "atlas/linalg/fft.h" #include "atlas/option.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Log.h" #include "atlas/trans/Trans.h" #include "atlas/trans/VorDivToUV.h" #include "atlas/trans/detail/TransFactory.h" #include "atlas/trans/local/LegendrePolynomials.h" #include "atlas/util/Constants.h" #include "atlas/library/defines.h" #if ATLAS_HAVE_FFTW #include "fftw3.h" #endif // move latitudes at the poles to the following latitude: // (otherwise we would divide by zero when computing u,v from U,V) static constexpr double latPole = 89.9999999; // (latPole=89.9999999 seems to produce the best accuracy. Moving it further away // or closer to the pole both increase the errors!) namespace atlas { namespace trans { namespace { static TransBuilderGrid builder("local", "local"); } // namespace namespace { class TransParameters { public: TransParameters(const eckit::Configuration& config): config_(config) {} ~TransParameters() = default; /* * For the future */ // bool scalar_derivatives() const { return config_.getBool( "scalar_derivatives", false ); } // bool wind_EW_derivatives() const { return config_.getBool( "wind_EW_derivatives", false ); } // bool vorticity_divergence_fields() const { return config_.getBool( "vorticity_divergence_fields", false ); } // std::string read_legendre() const { return config_.getString( "read_legendre", "" ); } // bool global() const { return config_.getBool( "global", false ); } // std::string read_fft() const { return config_.getString( "read_fft", "" ); } std::string write_legendre() const { return config_.getString("write_legendre", ""); } std::string write_fft() const { return config_.getString("write_fft", ""); } bool export_legendre() const { return config_.getBool("export_legendre", false); } int warning() const { return config_.getInt("warning", 1); } std::string fft() const { static const std::set supported = {"OFF","FFTW","pocketfft"}; std::string defaultLinalgFFTBackend = atlas::Library::instance().linalgFFTBackend(); if (defaultLinalgFFTBackend.empty()) { defaultLinalgFFTBackend = "FFTW"; } std::string value = config_.getString("fft", defaultLinalgFFTBackend); if (supported.find(value) == supported.end()) { ATLAS_THROW_EXCEPTION("FFT backend \"" << value << "\" is not one of the supported : OFF, FFTW, pocketfft"); } if (not ATLAS_HAVE_FFTW && value == "FFTW") { value = "pocketfft"; } return value; } std::string matrix_multiply() const { return config_.getString("matrix_multiply", ""); } private: const eckit::Configuration& config_; }; struct ReadCache { ReadCache(const void* cache) { begin = reinterpret_cast(cache); pos = 0; } template T* read(size_t size) { const T* v = reinterpret_cast(begin + pos); pos += size * sizeof(T); return const_cast(v); } const char* begin; size_t pos; }; struct WriteCache { WriteCache(const eckit::PathName& file_path): dh_(file_path.fileHandle(/*overwrite = */ true)) { if (file_path.exists()) { std::stringstream err; err << "Cannot open cache file " << file_path << " for writing as it already exists. Remove first."; throw_Exception(err.str(), Here()); } dh_->openForWrite(0); pos = 0; } ~WriteCache() { dh_->close(); } template void write(const T* v, long size) { dh_->write(v, size * sizeof(T)); pos += size * sizeof(T); } //void write( long v ) { // dh_->write( &v , sizeof(long) ); // pos += sizeof(long); //} //void write( const Grid& grid ) { // std::stringstream s; // eckit::JSON json(s); // json << grid.spec(); // std::string grid_spec( s.str() ); // long size = grid_spec.size(); // write( size ); // dh_->write( grid_spec.c_str(), grid_spec.size() ); // pos += grid_spec.size(); //} std::unique_ptr dh_; size_t pos; }; } // namespace // -------------------------------------------------------------------------------------------------------------------- // Helper functions // -------------------------------------------------------------------------------------------------------------------- namespace { // anonymous size_t legendre_size(const size_t truncation) { return (truncation + 2) * (truncation + 1) / 2; } //int nlats_northernHemisphere( const int nlats ) { // return ceil( nlats / 2. ); // // using ceil here should make it possible to have odd number of latitudes (with the centre latitude being the equator) //} size_t num_n(const int truncation, const int m, const bool symmetric) { int len = (truncation - m + (symmetric ? 2 : 1)) / 2; ATLAS_ASSERT(len >= 0); return size_t(len); } [[noreturn]] void throw_AllocationFailed(size_t bytes, const eckit::CodeLocation& loc) { std::stringstream ss; ss << "AllocationFailed: Could not allocate " << eckit::Bytes(bytes); throw_Exception(ss.str(), loc); } void alloc_aligned(double*& ptr, size_t n) { const size_t alignment = 256; size_t bytes = sizeof(double) * n; int err = posix_memalign((void**)&ptr, alignment, bytes); if (err) { throw_AllocationFailed(bytes, Here()); } } void free_aligned(double*& ptr) { free(ptr); ptr = nullptr; } void alloc_aligned(double*& ptr, size_t n, const char* msg) { ATLAS_ASSERT(msg); Log::debug() << "TransLocal: allocating '" << msg << "': " << eckit::Bytes(sizeof(double) * n) << std::endl; alloc_aligned(ptr, n); } void free_aligned(double*& ptr, const char* msg) { ATLAS_ASSERT(msg); Log::debug() << "TransLocal: deallocating '" << msg << "'" << std::endl; free_aligned(ptr); } void alloc_aligned(std::complex*& ptr, size_t n) { const size_t alignment = 256; size_t bytes = sizeof(std::complex) * n; int err = posix_memalign((void**)&ptr, alignment, bytes); if (err) { throw_AllocationFailed(bytes, Here()); } } void free_aligned(std::complex*& ptr) { free(ptr); ptr = nullptr; } size_t add_padding(size_t n) { return size_t(std::ceil(n / 8.)) * 8; } std::string detect_linalg_backend(const std::string& linalg_backend_) { linalg::dense::Backend linalg_backend = linalg::dense::Backend{linalg_backend_}; if (linalg_backend.type() == linalg::dense::backend::eckit_linalg::type()) { std::string backend; linalg_backend.get("backend", backend); if (backend.empty() || backend == "default") { #if ATLAS_ECKIT_HAVE_ECKIT_585 return eckit::linalg::LinearAlgebraDense::backend().name(); #else return eckit::linalg::LinearAlgebra::backend().name(); #endif } return backend; } return linalg_backend.type(); }; bool using_eckit_default_backend(const std::string& linalg_backend_) { linalg::dense::Backend linalg_backend = linalg::dense::Backend{linalg_backend_}; if (linalg_backend.type() == linalg::dense::backend::eckit_linalg::type()) { std::string backend; linalg_backend.get("backend", backend); if (backend.empty() || backend == "default") { return true; } } return false; }; } // namespace int fourier_truncation(const int truncation, // truncation const int nx, // number of longitudes const int /*nxmax*/, // maximum nx const int ndgl, // number of latitudes const double lat, // latitude in radian const bool fullgrid) { // regular grid int trc = truncation; int trclin = ndgl - 1; int trcquad = ndgl * 2 / 3 - 1; if (truncation >= trclin || fullgrid) { // linear trc = (nx - 1) / 2; } else if (truncation >= trcquad) { // quadratic double weight = 3 * (trclin - truncation) / ndgl; double sqcos = std::pow(std::cos(lat), 2); trc = static_cast((nx - 1) / (2 + weight * sqcos)); } else { // cubic double sqcos = std::pow(std::cos(lat), 2); trc = static_cast((nx - 1) / (2 + sqcos) - 1); } trc = std::min(truncation, trc); return trc; } namespace detail { struct FFT_Data { std::complex* in{nullptr}; double* out{nullptr}; }; } // namespace detail // -------------------------------------------------------------------------------------------------------------------- // Class TransLocal // -------------------------------------------------------------------------------------------------------------------- bool TransLocal::warning(const eckit::Configuration& config) const { int warning = warning_; config.get("warning", warning); return (warning > 0 && grid_.size() >= warning); } TransLocal::TransLocal(const Cache& cache, const Grid& grid, const Domain& domain, const long truncation, const eckit::Configuration& config): grid_(grid, domain), truncation_(static_cast(truncation)), precompute_(config.getBool("precompute", true)), cache_(cache), legendre_cache_(cache.legendre().data()), legendre_cachesize_(cache.legendre().size()), fft_cache_(cache.fft().data()), fft_cachesize_(cache.fft().size()), fft_data_(new detail::FFT_Data), linalg_backend_(TransParameters{config}.matrix_multiply()), fft_backend_(TransParameters{config}.fft()), warning_(TransParameters{config}.warning()) { ATLAS_TRACE("TransLocal constructor"); if (mpi::size() > 1) { ATLAS_THROW_EXCEPTION("TransLocal is not implemented for more than 1 MPI task."); } double fft_threshold = 0.0; // fraction of latitudes of the full grid down to which FFT is used. // This threshold needs to be adjusted depending on the dgemm and FFT performance of the machine // on which this code is running! idx_t nlats = 0; idx_t nlonsMax = 0; idx_t neqtr = 0; useFFT_ = (fft_backend_ != "OFF"); if (useFFT_) { if (fft_backend_ == "FFTW") { fft_.reset(new linalg::FFTW()); } else if(fft_backend_ == "pocketfft") { fft_.reset(new linalg::pocketfft()); } else { ATLAS_THROW_EXCEPTION("Unrecognised FFT configuration"); } } unstruct_precomp_ = (config.has("precompute") ? precompute_ : false); no_symmetry_ = false; nlatsNH_ = 0; nlatsSH_ = 0; nlatsLeg_ = 0; nlatsLegDomain_ = 0; nlatsLegReduced_ = 0; bool useGlobalLeg = true; bool no_nest = false; if (StructuredGrid(grid_) && not grid_.projection()) { StructuredGrid g(grid_); nlats = g.ny(); nlonsMax = g.nxmax(); // check location of domain relative to the equator: for (idx_t j = 0; j < nlats; ++j) { // assumptions: latitudes in g.y(j) are monotone and decreasing // no assumption on whether we have 0, 1 or 2 latitudes at the equator double lat = g.y(j); (eckit::types::is_approximately_equal(lat, 0.) ? neqtr : (lat < 0 ? nlatsSH_ : nlatsNH_))++; } if (neqtr > 0) { nlatsNH_++; nlatsSH_++; } if (nlatsNH_ >= nlatsSH_) { nlatsLegDomain_ = nlatsNH_; } else { nlatsLegDomain_ = nlatsSH_; } gridGlobal_ = grid; if (not gridGlobal_.domain().global()) { // The grid is not a nest of a global grid if (RegularGrid(grid_)) { no_nest = true; no_symmetry_ = true; useFFT_ = false; fft_backend_ = "OFF"; nlatsNH_ = nlats; nlatsSH_ = 0; nlatsLegDomain_ = nlatsNH_; gridGlobal_ = grid_; useGlobalLeg = false; } else { // non-nested reduced grids are not supported std::ostringstream log; log << "Transforms to non-regular regional grids is not supported, unless it defined as a cropping of " "a global grid." << std::endl; log << " Input grid: " << grid.spec() << std::endl; log << " Applied domain: " << domain << std::endl; log << " Regional grid after Domain applied (the output grid): " << grid_.spec() << std::endl; throw_NotImplemented(log.str(), Here()); } } StructuredGrid gs_global(gridGlobal_); ATLAS_ASSERT(gs_global); // assert structured grid StructuredGrid gsLeg = (useGlobalLeg ? gs_global : g); nlonsMaxGlobal_ = gs_global.nxmax(); jlonMin_.resize(1); jlonMin_[0] = 0; jlatMin_ = 0; nlatsGlobal_ = gs_global.ny(); if (grid_.domain().global()) { Log::debug() << "Global grid with " << nlats << " latitudes." << std::endl; } else { Log::debug() << "Grid has " << nlats << " latitudes. Global grid has " << nlatsGlobal_ << std::endl; } if (useGlobalLeg) { nlatsLeg_ = (nlatsGlobal_ + 1) / 2; } else { nlatsLeg_ = nlatsLegDomain_; nlatsLegReduced_ = nlatsLeg_; } for (int jlat = 0; jlat < nlatsGlobal_; jlat++) { if (gs_global.y(jlat) > g.y(0)) { //Log::info() << gs_global.y( jlat ) << ">" << g.y( 0 ) << " "; jlatMin_++; }; } //Log::info() << std::endl; int jlatMinLeg_ = jlatMin_; if (nlatsNH_ < nlatsSH_) { jlatMinLeg_ += nlatsNH_ - nlatsSH_; }; if (jlatMin_ >= (nlatsGlobal_ + 1) / 2) { jlatMinLeg_ -= 2 * (jlatMin_ - (nlatsGlobal_ + 1) / 2); if (nlatsGlobal_ % 2 == 1) { jlatMinLeg_--; } }; if (useGlobalLeg) { nlatsLegReduced_ = jlatMinLeg_ + nlatsLegDomain_; } // reduce truncation towards the pole for reduced meshes: nlat0_.resize(truncation_ + 1); if (no_nest) { for (int j = 0; j <= truncation_; j++) { nlat0_[j] = 0; } } else { int nmen0 = -1; for (int jlat = 0; jlat < nlatsGlobal_ / 2; jlat++) { double lat = gs_global.y(jlat) * util::Constants::degreesToRadians(); int nmen = fourier_truncation(truncation_, gs_global.nx(jlat), gs_global.nxmax(), nlatsGlobal_, lat, RegularGrid(gs_global)); nmen = std::max(nmen0, nmen); int ndgluj = nlatsLeg_ - std::min(nlatsLeg_, nlatsLeg_ + jlatMinLeg_ - jlat); if (useGlobalLeg) { ndgluj = std::max(jlatMinLeg_, jlat); } for (int j = nmen0 + 1; j <= nmen; j++) { nlat0_[j] = ndgluj; } nmen0 = nmen; } for (int j = nmen0 + 1; j <= truncation_; j++) { nlat0_[j] = nlatsLeg_; } } /*Log::info() << "nlats=" << g.ny() << " nlatsGlobal=" << gs_global.ny() << " jlatMin=" << jlatMin_ << " jlatMinLeg=" << jlatMinLeg_ << " nlatsGlobal/2-nlatsLeg=" << nlatsGlobal_ / 2 - nlatsLeg_ << " nlatsLeg_=" << nlatsLeg_ << " nlatsLegDomain_=" << nlatsLegDomain_ << std::endl;*/ // compute longitudinal location of domain within global grid for using FFT: auto wrapAngle = [&](double angle) { double result = std::fmod(angle, 360.); if (result < 0.) { result += 360.; } return result; }; if (useFFT_) { double lonmin = wrapAngle(g.x(0, 0)); if (nlonsMax < fft_threshold * nlonsMaxGlobal_) { useFFT_ = false; fft_backend_ = "OFF"; } else { // need to use FFT with cropped grid if (RegularGrid(gridGlobal_)) { for (idx_t jlon = 0; jlon < nlonsMaxGlobal_; ++jlon) { if (gs_global.x(jlon, 0) < lonmin) { jlonMin_[0]++; } } } else { nlonsGlobal_.resize(nlats); jlonMin_.resize(nlats); for (idx_t jlat = 0; jlat < nlats; jlat++) { double lonmin = wrapAngle(g.x(0, jlat)); nlonsGlobal_[jlat] = gs_global.nx(jlat + jlatMin_); jlonMin_[jlat] = 0; for (idx_t jlon = 0; jlon < nlonsGlobal_[jlat]; ++jlon) { if (gs_global.x(jlon, jlat + jlatMin_) < lonmin) { jlonMin_[jlat]++; } } } } } } //Log::info() << "nlats=" << g.ny() << " nlatsGlobal=" << gs_global.ny() << std::endl; std::vector lats(nlatsLeg_); std::vector lons(nlonsMax); if (nlatsNH_ >= nlatsSH_ || useGlobalLeg) { for (idx_t j = 0; j < nlatsLeg_; ++j) { double lat = gsLeg.y(j); if (lat > latPole) { lat = latPole; } if (lat < -latPole) { lat = -latPole; } lats[j] = lat * util::Constants::degreesToRadians(); } } else { for (idx_t j = nlats - 1, idx = 0; idx < nlatsLeg_; --j, ++idx) { double lat = gsLeg.y(j); if (lat > latPole) { lat = latPole; } if (lat < -latPole) { lat = -latPole; } lats[idx] = -lat * util::Constants::degreesToRadians(); } } for (idx_t j = 0; j < nlonsMax; ++j) { lons[j] = g.x(j, 0) * util::Constants::degreesToRadians(); } /*Log::info() << "lats: "; for ( int j = 0; j < nlatsLeg_; j++ ) { Log::info() << lats[j] << " "; } Log::info() << std::endl;*/ Log::debug() << "TransLocal set up with:\n" << " - grid: " << grid.name() << '\n' << " - truncation: " << truncation << '\n'; if (not domain.global()) { Log::debug() << " - domain: " << domain << '\n'; } if (GlobalDomain(domain)) { if (GlobalDomain(domain).west() != 0.) { Log::debug() << " - global domain with modified west: " << GlobalDomain(domain).west() << '\n'; } } Log::debug() << " - fft: " << fft_backend_ << '\n'; Log::debug() << " - linalg_backend: "; if (using_eckit_default_backend(linalg_backend_)) { Log::debug() << "eckit_linalg default (currently \"" << detect_linalg_backend(linalg_backend_) << "\" but could be changed after setup, check invtrans debug output)" << '\n'; } else { Log::debug() << detect_linalg_backend(linalg_backend_) << '\n'; } Log::debug() << " - legendre_cache: " << std::boolalpha << bool(legendre_cache_) << std::endl; // precomputations for Legendre polynomials: { const auto nlatsLeg = size_t(nlatsLeg_); size_t size_sym = 0; size_t size_asym = 0; legendre_sym_begin_.resize(truncation_ + 3); legendre_asym_begin_.resize(truncation_ + 3); legendre_sym_begin_[0] = 0; legendre_asym_begin_[0] = 0; for (idx_t jm = 0; jm <= truncation_ + 1; jm++) { size_sym += add_padding(num_n(truncation_ + 1, jm, /*symmetric*/ true ) * nlatsLeg); size_asym += add_padding(num_n(truncation_ + 1, jm, /*antisymmetric*/ false) * nlatsLeg); legendre_sym_begin_[jm + 1] = size_sym; legendre_asym_begin_[jm + 1] = size_asym; } if (legendre_cache_) { ReadCache legendre(legendre_cache_); legendre_sym_ = legendre.read(size_sym); legendre_asym_ = legendre.read(size_asym); ATLAS_ASSERT(legendre.pos == legendre_cachesize_); // TODO: check this is all aligned... } else { if (TransParameters(config).export_legendre()) { ATLAS_ASSERT(not cache_.legendre()); size_t bytes = sizeof(double) * (size_sym + size_asym); Log::debug() << "TransLocal: allocating LegendreCache: " << eckit::Bytes(bytes) << std::endl; export_legendre_ = LegendreCache(bytes); legendre_cachesize_ = export_legendre_.legendre().size(); legendre_cache_ = export_legendre_.legendre().data(); ReadCache legendre(legendre_cache_); legendre_sym_ = legendre.read(size_sym); legendre_asym_ = legendre.read(size_asym); } else { alloc_aligned(legendre_sym_, size_sym, "Legendre coeffs symmetric"); alloc_aligned(legendre_asym_, size_asym, "Legendre coeffs asymmetric"); } ATLAS_TRACE_SCOPE("Legendre precomputations (structured)") { compute_legendre_polynomials(truncation_ + 1, nlatsLeg_, lats.data(), legendre_sym_, legendre_asym_, legendre_sym_begin_.data(), legendre_asym_begin_.data()); } std::string file_path = TransParameters(config).write_legendre(); if (file_path.size()) { ATLAS_TRACE("Write LegendreCache to file"); Log::debug() << "Writing Legendre cache file ..." << std::endl; Log::debug() << " path: " << file_path << std::endl; WriteCache legendre(file_path); legendre.write(legendre_sym_, size_sym); legendre.write(legendre_asym_, size_asym); Log::debug() << " size: " << eckit::Bytes(legendre.pos) << std::endl; } } } // precomputations for Fourier transformations: if (useFFT_) { ATLAS_TRACE("Fourier precomputations ("+fft_->type()+")"); int num_complex = (nlonsMaxGlobal_ / 2) + 1; int howmany = RegularGrid(gridGlobal_) ? nlats : 1; alloc_aligned(fft_data_->in, howmany * num_complex); alloc_aligned(fft_data_->out, howmany * nlonsMaxGlobal_); #if ATLAS_HAVE_FFTW if (fft_cache_) { Log::debug() << "Import FFTW wisdom from cache" << std::endl; fftw_import_wisdom_from_string(static_cast(fft_cache_)); } // std::string wisdomString( "" ); // std::ifstream read( "wisdom.bin" ); // if ( read.is_open() ) { // std::getline( read, wisdomString ); // while ( read ) { // std::string line; // std::getline( read, line ); // wisdomString += line; // } // } // read.close(); // if ( wisdomString.length() > 0 ) { fftw_import_wisdom_from_string( &wisdomString[0u] ); } #endif if (RegularGrid(gridGlobal_)) { fft_->plan_inverse_c2r_many(nlats, nlonsMaxGlobal_, fft_data_->in, fft_data_->out); } else { for (int j = 0; j < nlatsLegDomain_; j++) { int nlonsGlobalj = gs_global.nx(jlatMinLeg_ + j); fft_->plan_inverse_c2r(nlonsGlobalj, fft_data_->in, fft_data_->out); } } #if ATLAS_HAVE_FFTW std::string file_path = TransParameters(config).write_fft(); if (file_path.size()) { Log::debug() << "Write FFTW wisdom to file " << file_path << std::endl; //bool success = fftw_export_wisdom_to_filename( "wisdom.bin" ); //ASSERT( success ); //std::ofstream write( file_path ); //write << FFTW_Wisdom(); FILE* file_fftw = fopen(file_path.c_str(), "wb"); fftw_export_wisdom_to_file(file_fftw); fclose(file_fftw); } // std::string newWisdom( fftw_export_wisdom_to_string() ); // if ( 1.1 * wisdomString.length() < newWisdom.length() ) { // std::ofstream write( "wisdom.bin" ); // write << newWisdom; // write.close(); // } #else // std::string file_path = TransParameters(config).write_fft(); // if (file_path.size()) { // std::ofstream write(file_path); // write << "No cache available, as FFTW is not enabled" << std::endl; // write.close(); // } #endif } else { // not using FFT Log::warning() << "WARNING: Spectral transform results may contain aliasing errors. This will be addressed soon." << std::endl; alloc_aligned(fourier_, 2 * (truncation_ + 1) * nlonsMax, "Fourier coeffs"); { ATLAS_TRACE("Fourier precomputations (NoFFT)"); int idx = 0; for (int jm = 0; jm < truncation_ + 1; jm++) { double factor = 1.; if (jm > 0) { factor = 2.; } for (int jlon = 0; jlon < nlonsMax; jlon++) { fourier_[idx++] = +std::cos(jm * lons[jlon]) * factor; // real part } for (int jlon = 0; jlon < nlonsMax; jlon++) { fourier_[idx++] = -std::sin(jm * lons[jlon]) * factor; // imaginary part } } } } } else { // unstructured grid if (unstruct_precomp_) { ATLAS_TRACE("Legendre precomputations (unstructured)"); if (warning()) { Log::warning() << "WARNING: Precomputations for spectral transforms could take a long time as there's no structure" " to take advantage of!!!" << std::endl << "The precomputed values consume at least " << eckit::Bytes(sizeof(double) * legendre_size(truncation_) * grid_.size()) << " (" << eckit::Bytes(sizeof(double) * legendre_size(truncation_)) << " for each of " << grid_.size() << " grid points )" << std::endl << "Furthermore, results may contain aliasing errors." << std::endl; } std::vector lats(grid_.size()); alloc_aligned(legendre_, legendre_size(truncation_) * grid_.size(), "Legendre coeffs."); int j(0); for (PointLonLat p : grid_.lonlat()) { lats[j++] = p.lat() * util::Constants::degreesToRadians(); } compute_legendre_polynomials_all(truncation_, grid_.size(), lats.data(), legendre_); } if (TransParameters(config).write_legendre().size()) { throw_NotImplemented( "Caching for unstructured grids or structured grids with projections not yet implemented", Here()); } } } // -------------------------------------------------------------------------------------------------------------------- TransLocal::TransLocal(const Grid& grid, const long truncation, const eckit::Configuration& config): TransLocal(Cache(), grid, grid.domain(), truncation, config) {} TransLocal::TransLocal(const Grid& grid, const Domain& domain, const long truncation, const eckit::Configuration& config): TransLocal(Cache(), grid, domain, truncation, config) {} TransLocal::TransLocal(const Cache& cache, const Grid& grid, const long truncation, const eckit::Configuration& config): TransLocal(cache, grid, grid.domain(), truncation, config) {} // -------------------------------------------------------------------------------------------------------------------- TransLocal::~TransLocal() { if (StructuredGrid(grid_) && not grid_.projection()) { if (not legendre_cache_) { free_aligned(legendre_sym_, "symmetric"); free_aligned(legendre_asym_, "asymmetric"); } if (useFFT_) { free_aligned(fft_data_->in); free_aligned(fft_data_->out); } else { free_aligned(fourier_, "Fourier coeffs."); } } else { if (unstruct_precomp_) { free_aligned(legendre_, "Legendre coeffs."); } } } // -------------------------------------------------------------------------------------------------------------------- const functionspace::Spectral& TransLocal::spectral() const { if (not spectral_) { spectral_ = functionspace::Spectral( truncation() ); } return spectral_; } // -------------------------------------------------------------------------------------------------------------------- void TransLocal::invtrans(const Field& spfield, Field& gpfield, const eckit::Configuration& config) const { // VERY PRELIMINARY IMPLEMENTATION WITHOUT ANY GUARANTEES int nb_scalar_fields = 1; ATLAS_ASSERT(spfield.rank() == 1, "Only rank-1 fields supported at the moment"); ATLAS_ASSERT(gpfield.rank() == 1, "Only rank-1 fields supported at the moment"); const auto scalar_spectra = array::make_view(spfield); auto gp_fields = array::make_view(gpfield); if (gp_fields.shape(0) < grid().size()) { // Hopefully the halo (if present) is appended ATLAS_DEBUG_VAR(gp_fields.shape(0)); ATLAS_DEBUG_VAR(grid().size()); ATLAS_ASSERT(gp_fields.shape(0) < grid().size()); } invtrans(nb_scalar_fields, scalar_spectra.data(), gp_fields.data(), config); } // -------------------------------------------------------------------------------------------------------------------- void TransLocal::invtrans(const FieldSet& spfields, FieldSet& gpfields, const eckit::Configuration& config) const { // VERY PRELIMINARY IMPLEMENTATION WITHOUT ANY GUARANTEES ATLAS_ASSERT(spfields.size() == gpfields.size()); for (idx_t f = 0; f < spfields.size(); ++f) { invtrans(spfields[f], gpfields[f], config); } } // -------------------------------------------------------------------------------------------------------------------- void TransLocal::invtrans_grad(const Field& /*spfield*/, Field& /*gradfield*/, const eckit::Configuration&) const { ATLAS_NOTIMPLEMENTED; } // -------------------------------------------------------------------------------------------------------------------- void TransLocal::invtrans_grad(const FieldSet& /*spfields*/, FieldSet& /*gradfields*/, const eckit::Configuration&) const { ATLAS_NOTIMPLEMENTED; } // -------------------------------------------------------------------------------------------------------------------- void gp_transpose(const int nb_size, const int nb_fields, const double gp_tmp[], double gp_fields[]) { for (int jgp = 0; jgp < nb_size; jgp++) { for (int jfld = 0; jfld < nb_fields; jfld++) { gp_fields[jfld * nb_size + jgp] = gp_tmp[jgp * nb_fields + jfld]; } } } // -------------------------------------------------------------------------------------------------------------------- void TransLocal::invtrans_vordiv2wind(const Field& spvor, const Field& spdiv, Field& gpwind, const eckit::Configuration& config) const { // VERY PRELIMINARY IMPLEMENTATION WITHOUT ANY GUARANTEES ATLAS_ASSERT(spvor.rank() == 1, "Only rank-1 fields supported at the moment"); ATLAS_ASSERT(spdiv.rank() == 1, "Only rank-1 fields supported at the moment"); int nb_vordiv_fields = 1; const auto vorticity_spectra = array::make_view(spvor); const auto divergence_spectra = array::make_view(spdiv); auto gp_fields = array::make_view(gpwind); size_t spectral_data_size = 2 * legendre_size(truncation_) * nb_vordiv_fields; ATLAS_ASSERT(vorticity_spectra.size() == spectral_data_size); ATLAS_ASSERT(divergence_spectra.size() == spectral_data_size); if (gp_fields.shape(1) == grid().size() && gp_fields.shape(0) == 2) { invtrans(nb_vordiv_fields, vorticity_spectra.data(), divergence_spectra.data(), gp_fields.data(), config); } else if (gp_fields.shape(0) == grid().size() && gp_fields.shape(1) == 2) { array::ArrayT gpwind_t(gp_fields.shape(1), gp_fields.shape(0)); auto gp_fields_t = array::make_view(gpwind_t); invtrans(nb_vordiv_fields, vorticity_spectra.data(), divergence_spectra.data(), gp_fields_t.data(), config); gp_transpose(grid().size(), 2, gp_fields_t.data(), gp_fields.data()); } else { ATLAS_NOTIMPLEMENTED; } } void TransLocal::invtrans_adj(const Field& gpfield, Field& spfield, const eckit::Configuration& config) const { ATLAS_NOTIMPLEMENTED; } // -------------------------------------------------------------------------------------------------------------------- void TransLocal::invtrans_adj(const FieldSet& gpfields, FieldSet& spfields, const eckit::Configuration& config) const { ATLAS_NOTIMPLEMENTED; } // -------------------------------------------------------------------------------------------------------------------- void TransLocal::invtrans_grad_adj(const Field& /*gradfield*/, Field& /*spfield*/, const eckit::Configuration&) const { ATLAS_NOTIMPLEMENTED; } // -------------------------------------------------------------------------------------------------------------------- void TransLocal::invtrans_grad_adj(const FieldSet& /*gradfields*/, FieldSet& /*spfields*/, const eckit::Configuration&) const { ATLAS_NOTIMPLEMENTED; } // -------------------------------------------------------------------------------------------------------------------- void TransLocal::invtrans_vordiv2wind_adj(const Field& gpwind, Field& spvor, Field& spdiv, const eckit::Configuration& config) const { ATLAS_NOTIMPLEMENTED; } // -------------------------------------------------------------------------------------------------------------------- void TransLocal::invtrans(const int nb_scalar_fields, const double scalar_spectra[], double gp_fields[], const eckit::Configuration& config) const { invtrans_uv(truncation_, nb_scalar_fields, 0, scalar_spectra, gp_fields, config); } // -------------------------------------------------------------------------------------------------------------------- void TransLocal::invtrans_legendre(const int truncation, const int nlats, const int nb_fields, const int /*nb_vordiv_fields*/, const double scalar_spectra[], double scl_fourier[], const eckit::Configuration&) const { // Legendre transform: { Log::debug() << "TransLocal::invtrans_legendre: Legendre GEMM with \"" << detect_linalg_backend(linalg_backend_) << "\" using " << nlatsLegReduced_ - nlat0_[0] << " latitudes out of " << nlatsGlobal_ / 2 << std::endl; linalg::dense::Backend linalg_backend{linalg_backend_}; ATLAS_TRACE("Inverse Legendre Transform (GEMM)"); for (int jm = 0; jm <= truncation_; jm++) { size_t size_sym = num_n(truncation_ + 1, jm, true); size_t size_asym = num_n(truncation_ + 1, jm, false); const int n_imag = (jm ? 2 : 1); int size_fourier = nb_fields * n_imag * (nlatsLegReduced_ - nlat0_[jm]); if (size_fourier > 0) { auto posFourier = [&](int jfld, int imag, int jlat, int jm, int nlatsH) { return jfld + nb_fields * (imag + n_imag * (nlatsLegReduced_ - nlat0_[jm] - nlatsH + jlat)); }; // THESE ARE REALLOCATED FOR EACH jm ??? // THEY COULD ALLOCATED ONCE BEFORE jm LOOP WITH A MAXIMUM SIZE? double* scalar_sym; double* scalar_asym; double* scl_fourier_sym; double* scl_fourier_asym; alloc_aligned(scalar_sym, n_imag * nb_fields * size_sym); alloc_aligned(scalar_asym, n_imag * nb_fields * size_asym); alloc_aligned(scl_fourier_sym, size_fourier); alloc_aligned(scl_fourier_asym, size_fourier); { //ATLAS_TRACE( "Legendre split" ); idx_t idx = 0, is = 0, ia = 0, ioff = (2 * truncation + 3 - jm) * jm / 2 * nb_fields * 2; // the choice between the following two code lines determines whether // total wavenumbers are summed in an ascending or descending order. // The trans library in IFS uses descending order because it should // be more accurate (higher wavenumbers have smaller contributions). // This also needs to be changed when splitting the spectral data in // compute_legendre_polynomials! //for ( int jn = jm; jn <= truncation_ + 1; jn++ ) { for (int jn = truncation_ + 1; jn >= jm; jn--) { for (int imag = 0; imag < n_imag; imag++) { for (int jfld = 0; jfld < nb_fields; jfld++) { idx = jfld + nb_fields * (imag + 2 * (jn - jm)); if (jn <= truncation && jm < truncation) { if ((jn - jm) % 2 == 0) { scalar_sym[is++] = scalar_spectra[idx + ioff]; } else { scalar_asym[ia++] = scalar_spectra[idx + ioff]; } } else { if ((jn - jm) % 2 == 0) { scalar_sym[is++] = 0.; } else { scalar_asym[ia++] = 0.; } } } } } ATLAS_ASSERT(size_t(ia) == n_imag * nb_fields * size_asym && size_t(is) == n_imag * nb_fields * size_sym); } if (nlatsLegReduced_ - nlat0_[jm] > 0) { ATLAS_TRACE("matrix_multiply (" + std::string(linalg_backend) + ")"); { linalg::Matrix A(scalar_sym, nb_fields * n_imag, size_sym); linalg::Matrix B(legendre_sym_ + legendre_sym_begin_[jm] + nlat0_[jm] * size_sym, size_sym, nlatsLegReduced_ - nlat0_[jm]); linalg::Matrix C(scl_fourier_sym, nb_fields * n_imag, nlatsLegReduced_ - nlat0_[jm]); linalg::matrix_multiply(A, B, C, linalg_backend); /*Log::info() << "sym: "; for ( int j = 0; j < size_sym * ( nlatsLegReduced_ - nlat0_[jm] ); j++ ) { Log::info() << legendre_sym_[j + legendre_sym_begin_[jm] + nlat0_[jm] * size_sym] << " "; } Log::info() << std::endl;*/ } if (size_asym > 0) { linalg::Matrix A(scalar_asym, nb_fields * n_imag, size_asym); linalg::Matrix B(legendre_asym_ + legendre_asym_begin_[jm] + nlat0_[jm] * size_asym, size_asym, nlatsLegReduced_ - nlat0_[jm]); linalg::Matrix C(scl_fourier_asym, nb_fields * n_imag, nlatsLegReduced_ - nlat0_[jm]); linalg::matrix_multiply(A, B, C, linalg_backend); /*Log::info() << "asym: "; for ( int j = 0; j < size_asym * ( nlatsLegReduced_ - nlat0_[jm] ); j++ ) { Log::info() << legendre_asym_[j + legendre_asym_begin_[jm] + nlat0_[jm] * size_asym] << " "; } Log::info() << std::endl;*/ } } { //ATLAS_TRACE( "merge spheres" ); // northern hemisphere: for (int jlat = 0; jlat < nlatsNH_; jlat++) { if (nlatsLegReduced_ - nlat0_[jm] - nlatsNH_ + jlat >= 0) { for (int imag = 0; imag < n_imag; imag++) { for (int jfld = 0; jfld < nb_fields; jfld++) { int idx = posFourier(jfld, imag, jlat, jm, nlatsNH_); scl_fourier[posMethod(jfld, imag, jlat, jm, nb_fields, nlats)] = scl_fourier_sym[idx] + scl_fourier_asym[idx]; } } } else { for (int imag = 0; imag < n_imag; imag++) { for (int jfld = 0; jfld < nb_fields; jfld++) { scl_fourier[posMethod(jfld, imag, jlat, jm, nb_fields, nlats)] = 0.; } } } /*for ( int imag = 0; imag < n_imag; imag++ ) { for ( int jfld = 0; jfld < nb_fields; jfld++ ) { if ( scl_fourier[posMethod( jfld, imag, jlat, jm, nb_fields, nlats )] > 0. ) { Log::info() << "jm=" << jm << " jlat=" << jlat << " nlatsLeg_=" << nlatsLeg_ << " nlat0=" << nlat0_[jm] << " nlatsNH=" << nlatsNH_ << std::endl; } } }*/ } // southern hemisphere: for (int jlat = 0; jlat < nlatsSH_; jlat++) { int jslat = nlats - jlat - 1; if (nlatsLegReduced_ - nlat0_[jm] - nlatsSH_ + jlat >= 0) { for (int imag = 0; imag < n_imag; imag++) { for (int jfld = 0; jfld < nb_fields; jfld++) { int idx = posFourier(jfld, imag, jlat, jm, nlatsSH_); scl_fourier[posMethod(jfld, imag, jslat, jm, nb_fields, nlats)] = scl_fourier_sym[idx] - scl_fourier_asym[idx]; } } } else { for (int imag = 0; imag < n_imag; imag++) { for (int jfld = 0; jfld < nb_fields; jfld++) { scl_fourier[posMethod(jfld, imag, jslat, jm, nb_fields, nlats)] = 0.; } } } } } free_aligned(scalar_sym); free_aligned(scalar_asym); free_aligned(scl_fourier_sym); free_aligned(scl_fourier_asym); } else { for (int jlat = 0; jlat < nlats; jlat++) { for (int imag = 0; imag < n_imag; imag++) { for (int jfld = 0; jfld < nb_fields; jfld++) { scl_fourier[posMethod(jfld, imag, jlat, jm, nb_fields, nlats)] = 0.; } } } } } } } // -------------------------------------------------------------------------------------------------------------------- void TransLocal::invtrans_fourier_regular(const int nlats, const int nlons, const int nb_fields, double scl_fourier[], double gp_fields[], const eckit::Configuration&) const { // Fourier transformation: if (useFFT_) { int num_complex = (nlonsMaxGlobal_ / 2) + 1; { ATLAS_TRACE("Inverse Fourier Transform ("+fft_->type()+", RegularGrid)"); for (int jfld = 0; jfld < nb_fields; jfld++) { int idx = 0; for (int jlat = 0; jlat < nlats; jlat++) { fft_data_->in[idx++] = std::complex{scl_fourier[posMethod(jfld, 0, jlat, 0, nb_fields, nlats)], 0.}; for (int jm = 1; jm < num_complex; jm++, idx++) { auto& complex_in = reinterpret_cast&>(fft_data_->in[idx]); for (int imag = 0; imag < 2; imag++) { if (jm <= truncation_) { complex_in[imag] = scl_fourier[posMethod(jfld, imag, jlat, jm, nb_fields, nlats)]; } else { complex_in[imag] = 0.; } } } } fft_->inverse_c2r_many(nlats, nlonsMaxGlobal_, fft_data_->in, fft_data_->out); for (int jlat = 0; jlat < nlats; jlat++) { for (int jlon = 0; jlon < nlons; jlon++) { int j = jlon + jlonMin_[0]; if (j >= nlonsMaxGlobal_) { j -= nlonsMaxGlobal_; } gp_fields[jlon + nlons * (jlat + nlats * jfld)] = fft_data_->out[j + nlonsMaxGlobal_ * jlat]; } } } } } else { linalg::dense::Backend linalg_backend{linalg_backend_}; // dgemm-method 1 { ATLAS_TRACE("Inverse Fourier Transform (NoFFT,matrix_multiply=" + detect_linalg_backend(linalg_backend_) + ")"); linalg::Matrix A(fourier_, nlons, (truncation_ + 1) * 2); linalg::Matrix B(scl_fourier, (truncation_ + 1) * 2, nb_fields * nlats); linalg::Matrix C(gp_fields, nlons, nb_fields * nlats); linalg::matrix_multiply(A, B, C, linalg_backend); } } } // -------------------------------------------------------------------------------------------------------------------- void TransLocal::invtrans_fourier_reduced(const int nlats, const StructuredGrid& g, const int nb_fields, double scl_fourier[], double gp_fields[], const eckit::Configuration&) const { // Fourier transformation: if (useFFT_) { ATLAS_TRACE("Inverse Fourier Transform ("+fft_->type()+", ReducedGrid)"); int jgp = 0; for (int jfld = 0; jfld < nb_fields; jfld++) { for (int jlat = 0; jlat < nlats; jlat++) { int idx = 0; int num_complex = (nlonsGlobal_[jlat] / 2) + 1; fft_data_->in[idx++] = std::complex{scl_fourier[posMethod(jfld, 0, jlat, 0, nb_fields, nlats)], 0.}; for (int jm = 1; jm < num_complex; jm++, idx++) { auto& complex_in = reinterpret_cast&>(fft_data_->in[idx]); for (int imag = 0; imag < 2; imag++) { if (jm <= truncation_) { complex_in[imag] = scl_fourier[posMethod(jfld, imag, jlat, jm, nb_fields, nlats)]; } else { complex_in[imag] = 0.; } } } fft_->inverse_c2r(nlonsGlobal_[jlat], fft_data_->in, fft_data_->out); for (int jlon = 0; jlon < g.nx(jlat); jlon++) { int j = jlon + jlonMin_[jlat]; if (j >= nlonsGlobal_[jlat]) { j -= nlonsGlobal_[jlat]; } ATLAS_ASSERT(j < nlonsMaxGlobal_); gp_fields[jgp++] = fft_data_->out[j]; } } } } else { throw_NotImplemented( "Using dgemm in Fourier transform for reduced grids is extremely slow.", Here()); } } // -------------------------------------------------------------------------------------------------------------------- void TransLocal::invtrans_unstructured_precomp(const int truncation, const int nb_fields, const int nb_vordiv_fields, const double scalar_spectra[], double gp_fields[], const eckit::Configuration&) const { ATLAS_TRACE("invtrans_uv unstructured"); const int nlats = grid_.size(); const int size_fourier = nb_fields * 2; double* scl_fourier; double* scl_fourier_tp; double* fouriertp; double* gp_opt; alloc_aligned(scl_fourier, size_fourier * (truncation)*nlats); alloc_aligned(scl_fourier_tp, size_fourier * (truncation)); alloc_aligned(fouriertp, 2 * (truncation)); alloc_aligned(gp_opt, nb_fields); linalg::dense::Backend linalg_backend{linalg_backend_}; { ATLAS_TRACE("Inverse Legendre Transform (GEMM," + std::string(linalg_backend) + ")"); for (int jm = 0; jm < truncation; jm++) { const int noff = (2 * truncation + 3 - jm) * jm / 2, ns = truncation - jm + 1; linalg::Matrix A( eckit::linalg::Matrix(const_cast(scalar_spectra) + nb_fields * 2 * noff, nb_fields * 2, ns)); linalg::Matrix B(legendre_ + noff * nlats, ns, nlats); linalg::Matrix C(scl_fourier + jm * size_fourier * nlats, nb_fields * 2, nlats); linalg::matrix_multiply(A, B, C, linalg_backend); } } // loop over all points: { ATLAS_TRACE("Inverse Fourier Transform (NoFFT," + std::string(linalg_backend) + ")"); int ip = 0; for (const PointLonLat p : grid_.lonlat()) { const double lon = p.lon() * util::Constants::degreesToRadians(); const double lat = p.lat() * util::Constants::degreesToRadians(); { //ATLAS_TRACE( "opt transposition in Fourier" ); for (int jm = 0; jm < truncation; jm++) { int idx = nb_fields * 2 * (ip + nlats * jm); for (int imag = 0; imag < 2; imag++) { for (int jfld = 0; jfld < nb_fields; jfld++) { const int pos_tp = imag + 2 * (jm + (truncation) * (jfld)); //int pos = jfld + nb_fields * ( imag + 2 * ( jm ) ); scl_fourier_tp[pos_tp] = scl_fourier[idx++]; // = scl_fourier[pos] } } } } // Fourier transformation: { //ATLAS_TRACE( "opt compute fouriertp" ); int idx = 0; fouriertp[idx++] = 1.; // real part fouriertp[idx++] = 0.; // imaginary part for (int jm = 1; jm < truncation; jm++) { fouriertp[idx++] = +2. * std::cos(jm * lon); // real part fouriertp[idx++] = -2. * std::sin(jm * lon); // imaginary part } } { //ATLAS_TRACE( "opt Fourier dgemm" ); linalg::Matrix A(fouriertp, 1, (truncation)*2); linalg::Matrix B(scl_fourier_tp, (truncation)*2, nb_fields); linalg::Matrix C(gp_opt, 1, nb_fields); linalg::matrix_multiply(A, B, C, linalg_backend); for (int j = 0; j < nb_fields; j++) { gp_fields[ip + j * grid_.size()] = gp_opt[j]; } } // Computing u,v from U,V: { if (nb_vordiv_fields > 0) { //ATLAS_TRACE( " u,v from U,V" ); double coslat = std::cos(lat); for (int j = 0; j < 2 * nb_vordiv_fields && j < nb_fields; j++) { gp_fields[ip + j * grid_.size()] /= coslat; } } } ++ip; } } free_aligned(scl_fourier); free_aligned(scl_fourier_tp); free_aligned(fouriertp); free_aligned(gp_opt); } // -------------------------------------------------------------------------------------------------------------------- void TransLocal::invtrans_unstructured(const int truncation, const int nb_fields, const int nb_vordiv_fields, const double scalar_spectra[], double gp_fields[], const eckit::Configuration& config) const { ATLAS_TRACE("invtrans_unstructured"); if (warning(config)) { Log::warning() << "WARNING: Spectral transforms could take a long time (unstructured grid approach). Results " "may contain aliasing errors." << std::endl; } linalg::dense::Backend linalg_backend{linalg_backend_}; double* zfn; alloc_aligned(zfn, (truncation + 1) * (truncation + 1)); compute_zfn(truncation, zfn); int size_fourier = nb_fields * 2; double* legendre; double* scl_fourier; double* scl_fourier_tp; double* fouriertp; double* gp_opt; alloc_aligned(legendre, legendre_size(truncation + 1)); alloc_aligned(scl_fourier, size_fourier * (truncation + 1)); alloc_aligned(scl_fourier_tp, size_fourier * (truncation + 1)); alloc_aligned(fouriertp, 2 * (truncation + 1)); alloc_aligned(gp_opt, nb_fields); // loop over all points: int ip = 0; LegendrePolynomialsWorkspace w{truncation}; for (const PointLonLat p : grid_.lonlat()) { const double lon = p.lon() * util::Constants::degreesToRadians(); const double lat = p.lat() * util::Constants::degreesToRadians(); compute_legendre_polynomials_lat(truncation, lat, legendre, zfn, w); // Legendre transform: { //ATLAS_TRACE( "opt Legendre dgemm" ); ATLAS_TRACE("Legendre matrix_multiply (" + std::string(linalg_backend) + ")"); for (int jm = 0; jm <= truncation; jm++) { const int noff = (2 * truncation + 3 - jm) * jm / 2, ns = truncation - jm + 1; linalg::Matrix A(eckit::linalg::Matrix(const_cast(scalar_spectra) + nb_fields * 2 * noff, nb_fields * 2, ns)); linalg::Matrix B(legendre + noff, ns, 1); linalg::Matrix C(scl_fourier + jm * size_fourier, nb_fields * 2, 1); linalg::matrix_multiply(A, B, C, linalg_backend); } } { //ATLAS_TRACE( "opt transposition in Fourier" ); int idx = 0; for (int jm = 0; jm < truncation + 1; jm++) { for (int imag = 0; imag < 2; imag++) { for (int jfld = 0; jfld < nb_fields; jfld++) { const int pos_tp = imag + 2 * (jm + (truncation + 1) * (jfld)); //int pos = jfld + nb_fields * ( imag + 2 * ( jm ) ); scl_fourier_tp[pos_tp] = scl_fourier[idx++]; // = scl_fourier[pos] } } } } // Fourier transformation: int idx = 0; fouriertp[idx++] = 1.; // real part fouriertp[idx++] = 0.; // imaginary part for (int jm = 1; jm < truncation + 1; jm++) { fouriertp[idx++] = +2. * std::cos(jm * lon); // real part fouriertp[idx++] = -2. * std::sin(jm * lon); // imaginary part } { ATLAS_TRACE("Fourier matrix_multiply (" + std::string(linalg_backend) + ")"); linalg::Matrix A(fouriertp, 1, (truncation + 1) * 2); linalg::Matrix B(scl_fourier_tp, (truncation + 1) * 2, nb_fields); linalg::Matrix C(gp_opt, 1, nb_fields); linalg::matrix_multiply(A, B, C, linalg_backend); for (int j = 0; j < nb_fields; j++) { gp_fields[ip + j * grid_.size()] = gp_opt[j]; } } // Computing u,v from U,V: { if (nb_vordiv_fields > 0) { //ATLAS_TRACE( "u,v from U,V" ); const double coslat = std::cos(lat); for (int j = 0; j < 2 * nb_vordiv_fields && j < nb_fields; j++) { gp_fields[ip + j * grid_.size()] /= coslat; } } } ++ip; } free_aligned(legendre); free_aligned(scl_fourier); free_aligned(scl_fourier_tp); free_aligned(fouriertp); free_aligned(gp_opt); free_aligned(zfn); } //----------------------------------------------------------------------------- // Routine to compute the spectral transform by using a Local Fourier transformation // for a grid (same latitude for all longitudes, allows to compute Legendre functions // once for all longitudes). U and v components are divided by cos(latitude) for // nb_vordiv_fields > 0. // // Legendre polynomials are computed up to truncation_+1 to be accurate for vorticity and // divergence computation. The parameter truncation is the truncation used in storing the // spectral data scalar_spectra and can be different from truncation_. If truncation is // larger than truncation_+1 the transform will behave as if the spectral data was truncated // to truncation_+1. // // Author: // Andreas Mueller *ECMWF* // void TransLocal::invtrans_uv(const int truncation, const int nb_scalar_fields, const int nb_vordiv_fields, const double scalar_spectra[], double gp_fields[], const eckit::Configuration& config) const { if (nb_scalar_fields > 0) { int nb_fields = nb_scalar_fields; // Transform if (StructuredGrid(grid_) && not grid_.projection()) { auto g = StructuredGrid(grid_); ATLAS_TRACE("invtrans_uv structured"); int nlats = g.ny(); int nlons = g.nxmax(); int size_fourier_max = nb_fields * 2 * nlats; double* scl_fourier; alloc_aligned(scl_fourier, size_fourier_max * (truncation_ + 1)); // ATLAS-159 workaround begin for (int i = 0; i < size_fourier_max * (truncation_ + 1); ++i) { scl_fourier[i] = 0.; } // ATLAS-159 workaround end // Legendre transformation: invtrans_legendre(truncation, nlats, nb_scalar_fields, nb_vordiv_fields, scalar_spectra, scl_fourier, config); // Fourier transformation: if (RegularGrid(gridGlobal_)) { invtrans_fourier_regular(nlats, nlons, nb_fields, scl_fourier, gp_fields, config); } else { invtrans_fourier_reduced(nlats, g, nb_fields, scl_fourier, gp_fields, config); } // Computing u,v from U,V: { if (nb_vordiv_fields > 0) { ATLAS_TRACE("compute u,v from U,V"); std::vector coslatinvs(nlats); for (idx_t j = 0; j < nlats; ++j) { double lat = g.y(j); if (lat > latPole) { lat = latPole; } if (lat < -latPole) { lat = -latPole; } double coslat = std::cos(lat * util::Constants::degreesToRadians()); coslatinvs[j] = 1. / coslat; //Log::info() << "lat=" << g.y( j ) << " coslat=" << coslat << std::endl; } int idx = 0; for (idx_t jfld = 0; jfld < 2 * nb_vordiv_fields && jfld < nb_fields; jfld++) { for (idx_t jlat = 0; jlat < g.ny(); jlat++) { for (idx_t jlon = 0; jlon < g.nx(jlat); jlon++) { gp_fields[idx] *= coslatinvs[jlat]; idx++; } } } } } free_aligned(scl_fourier); } else { if (unstruct_precomp_) { invtrans_unstructured_precomp(truncation, nb_scalar_fields, nb_vordiv_fields, scalar_spectra, gp_fields, config); } else { invtrans_unstructured(truncation, nb_scalar_fields, nb_vordiv_fields, scalar_spectra, gp_fields, config); } } } } // -------------------------------------------------------------------------------------------------------------------- void TransLocal::invtrans(const int nb_vordiv_fields, const double vorticity_spectra[], const double divergence_spectra[], double gp_fields[], const eckit::Configuration& config) const { invtrans(0, nullptr, nb_vordiv_fields, vorticity_spectra, divergence_spectra, gp_fields, config); } // -------------------------------------------------------------------------------------------------------------------- void extend_truncation(const int old_truncation, const int nb_fields, const double old_spectra[], double new_spectra[]) { int new_truncation = old_truncation + 1; int old_size = 2 * legendre_size(old_truncation) * nb_fields; int new_size = 2 * legendre_size(new_truncation) * nb_fields; int k = 0, k_old = 0; for (int m = 0; m <= new_truncation; m++) { // zonal wavenumber for (int n = m; n <= new_truncation; n++) { // total wavenumber for (int imag = 0; imag < 2; imag++) { // imaginary/real part for (int jfld = 0; jfld < nb_fields; jfld++) { // field if (m == new_truncation || n == new_truncation) { new_spectra[k++] = 0.; } else { new_spectra[k++] = old_spectra[k_old++]; } } } } } ATLAS_ASSERT(k==new_size); ATLAS_ASSERT(k_old==old_size); } // -------------------------------------------------------------------------------------------------------------------- void TransLocal::invtrans(const int nb_scalar_fields, const double scalar_spectra[], const int nb_vordiv_fields, const double vorticity_spectra[], const double divergence_spectra[], double gp_fields[], const eckit::Configuration& config) const { int nb_gp = grid_.size(); if (nb_vordiv_fields > 0) { // collect all spectral data into one array "all_spectra": ATLAS_TRACE("TransLocal::invtrans"); int nb_vordiv_spec_ext = 2 * legendre_size(truncation_ + 1) * nb_vordiv_fields; std::vector U_ext; std::vector V_ext; std::vector scalar_ext; if (nb_vordiv_fields > 0) { std::vector vorticity_spectra_extended(nb_vordiv_spec_ext); std::vector divergence_spectra_extended(nb_vordiv_spec_ext); U_ext.resize(nb_vordiv_spec_ext); V_ext.resize(nb_vordiv_spec_ext); { ATLAS_TRACE("extend vordiv"); // increase truncation in vorticity_spectra and divergence_spectra: extend_truncation(truncation_, nb_vordiv_fields, vorticity_spectra, vorticity_spectra_extended.data()); extend_truncation(truncation_, nb_vordiv_fields, divergence_spectra, divergence_spectra_extended.data()); } { ATLAS_TRACE("vordiv to UV"); // call vd2uv to compute u and v in spectral space trans::VorDivToUV vordiv_to_UV_ext(truncation_ + 1, option::type("local")); vordiv_to_UV_ext.execute(nb_vordiv_spec_ext, nb_vordiv_fields, vorticity_spectra_extended.data(), divergence_spectra_extended.data(), U_ext.data(), V_ext.data()); } } if (nb_scalar_fields > 0) { int nb_scalar_ext = 2 * legendre_size(truncation_ + 1) * nb_scalar_fields; scalar_ext.resize(nb_scalar_ext); extend_truncation(truncation_, nb_scalar_fields, scalar_spectra, scalar_ext.data()); } int nb_all_fields = 2 * nb_vordiv_fields + nb_scalar_fields; int nb_all_size = 2 * legendre_size(truncation_ + 1) * nb_all_fields; std::vector all_spectra(nb_all_size); int k = 0, i = 0, j = 0, l = 0; { ATLAS_TRACE("merge all spectra"); for (int m = 0; m <= truncation_ + 1; m++) { // zonal wavenumber for (int n = m; n <= truncation_ + 1; n++) { // total wavenumber for (int imag = 0; imag < 2; imag++) { // imaginary/real part for (int jfld = 0; jfld < nb_vordiv_fields; jfld++) { // vorticity fields all_spectra[k++] = U_ext[i++]; } for (int jfld = 0; jfld < nb_vordiv_fields; jfld++) { // divergence fields all_spectra[k++] = V_ext[j++]; } for (int jfld = 0; jfld < nb_scalar_fields; jfld++) { // scalar fields all_spectra[k++] = scalar_ext[l++]; } } } } } int nb_vordiv_size = 2 * legendre_size(truncation_ + 1) * nb_vordiv_fields; int nb_scalar_size = 2 * legendre_size(truncation_ + 1) * nb_scalar_fields; ATLAS_ASSERT(k == nb_all_size); ATLAS_ASSERT(i == nb_vordiv_size); ATLAS_ASSERT(j == nb_vordiv_size); ATLAS_ASSERT(l == nb_scalar_size); invtrans_uv(truncation_ + 1, nb_all_fields, nb_vordiv_fields, all_spectra.data(), gp_fields, config); } else { if (nb_scalar_fields > 0) { invtrans_uv(truncation_, nb_scalar_fields, 0, scalar_spectra, gp_fields + 2 * nb_gp * nb_vordiv_fields, config); } } } void TransLocal::invtrans_adj(const int nb_scalar_fields, const double gp_fields[], double scalar_spectra[], const eckit::Configuration& config) const { ATLAS_NOTIMPLEMENTED; // Not implemented and not planned. // Use the TransIFS implementation instead. } void TransLocal::invtrans_adj(const int nb_vordiv_fields, const double gp_fields[], double vorticity_spectra[], double divergence_spectra[], const eckit::Configuration& config) const { ATLAS_NOTIMPLEMENTED; // Not implemented and not planned. // Use the TransIFS implementation instead. } void TransLocal::invtrans_adj(const int nb_scalar_fields, const double gp_fields[], const int nb_vordiv_fields, double vorticity_spectra[], double divergence_spectra[], double scalar_spectra[], const eckit::Configuration& config) const { ATLAS_NOTIMPLEMENTED; // Not implemented and not planned. // Use the TransIFS implementation instead. } // -------------------------------------------------------------------------------------------------------------------- void TransLocal::dirtrans(const Field& gpfield, Field& spfield, const eckit::Configuration& config) const { ATLAS_NOTIMPLEMENTED; // Not implemented and not planned. // Use the TransIFS implementation instead. } // -------------------------------------------------------------------------------------------------------------------- void TransLocal::dirtrans(const FieldSet& gpfields, FieldSet& spfields, const eckit::Configuration& config) const { ATLAS_NOTIMPLEMENTED; // Not implemented and not planned. // Use the TransIFS implementation instead. } // -------------------------------------------------------------------------------------------------------------------- void TransLocal::dirtrans_wind2vordiv(const Field& gpwind, Field& spvor, Field& spdiv, const eckit::Configuration& config) const { ATLAS_NOTIMPLEMENTED; // Not implemented and not planned. // Use the TransIFS implementation instead. } void TransLocal::dirtrans_adj(const Field& spfield, Field& gpfield, const eckit::Configuration& config) const { ATLAS_NOTIMPLEMENTED; // Not implemented and not planned. // Use the TransIFS implementation instead. } void TransLocal::dirtrans_adj(const FieldSet& spfields, FieldSet& gpfields, const eckit::Configuration& config) const { ATLAS_NOTIMPLEMENTED; // Not implemented and not planned. // Use the TransIFS implementation instead. } void TransLocal::dirtrans_wind2vordiv_adj(const Field& spvor, const Field& spdiv, Field& gpwind, const eckit::Configuration& config) const { ATLAS_NOTIMPLEMENTED; // Not implemented and not planned. // Use the TransIFS implementation instead. } // -------------------------------------------------------------------------------------------------------------------- void TransLocal::dirtrans(const int nb_fields, const double scalar_fields[], double scalar_spectra[], const eckit::Configuration&) const { ATLAS_NOTIMPLEMENTED; // Not implemented and not planned. // Use the TransIFS implementation instead. } // -------------------------------------------------------------------------------------------------------------------- void TransLocal::dirtrans(const int nb_fields, const double wind_fields[], double vorticity_spectra[], double divergence_spectra[], const eckit::Configuration&) const { ATLAS_NOTIMPLEMENTED; // Not implemented and not planned. // Use the TransIFS implementation instead. } // -------------------------------------------------------------------------------------------------------------------- } // namespace trans } // namespace atlas atlas-0.46.0/src/atlas/trans/Cache.cc0000664000175000017500000000701215160212070017417 0ustar alastairalastair/* * (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/trans/Cache.h" #include "pluto/pluto.h" #include "eckit/io/DataHandle.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Log.h" #include "atlas/runtime/Trace.h" #include "atlas/trans/Trans.h" namespace atlas { namespace trans { TransCacheFileEntry::TransCacheFileEntry(const eckit::PathName& path) { ATLAS_TRACE(); Log::debug() << "Loading cache from file " << path << std::endl; std::unique_ptr dh(path.fileHandle()); size_ = path.size(); data_ = pluto::host_resource()->allocate(size_, 256); dh->openForRead(); dh->read(data_, size_); dh->close(); } TransCacheFileEntry::~TransCacheFileEntry() { pluto::host_resource()->deallocate(data_, size_, 256); } TransCacheMemoryEntry::TransCacheMemoryEntry(const void* data, size_t size): data_(data), size_(size) { ATLAS_ASSERT(data_); ATLAS_ASSERT(size_); } LegendreFFTCache::LegendreFFTCache(const void* legendre_address, size_t legendre_size, const void* fft_address, size_t fft_size): Cache(std::make_shared(legendre_address, legendre_size), std::make_shared(fft_address, fft_size)) {} static std::shared_ptr read_legendre_cache(const eckit::PathName& legendre_path) { ATLAS_TRACE(); return std::make_shared(legendre_path); } static std::shared_ptr read_fft_cache(const eckit::PathName& fft_path) { ATLAS_TRACE(); return std::make_shared(fft_path); } LegendreFFTCache::LegendreFFTCache(const eckit::PathName& legendre_path, const eckit::PathName& fft_path): Cache(read_legendre_cache(legendre_path), read_fft_cache(fft_path)) {} LegendreCache::LegendreCache(const eckit::PathName& path): Cache(std::shared_ptr(new TransCacheFileEntry(path))) {} LegendreCache::LegendreCache(size_t size): Cache(std::make_shared(size)) {} LegendreCache::LegendreCache(const void* address, size_t size): Cache(std::make_shared(address, size)) {} Cache::Cache(const std::shared_ptr& legendre): trans_(nullptr), legendre_(legendre), fft_(new EmptyCacheEntry()) {} Cache::Cache(const std::shared_ptr& legendre, const std::shared_ptr& fft): trans_(nullptr), legendre_(legendre), fft_(fft) {} Cache::Cache(const TransImpl* trans): trans_(trans), legendre_(new EmptyCacheEntry()), fft_(new EmptyCacheEntry()) {} Cache::Cache(): trans_(nullptr), legendre_(new EmptyCacheEntry()), fft_(new EmptyCacheEntry()) {} Cache::operator bool() const { return trans_ || bool(legendre()); } Cache::~Cache() = default; TransCache::TransCache(const Trans& trans): Cache(trans.get()) {} TransCacheOwnedMemoryEntry::TransCacheOwnedMemoryEntry(size_t size): size_(size) { if (size_) { data_ = pluto::host_resource()->allocate(size_, 256); } } TransCacheOwnedMemoryEntry::~TransCacheOwnedMemoryEntry() { if (size_) { pluto::host_resource()->deallocate(data_, size_, 256); } } } // namespace trans } // namespace atlas atlas-0.46.0/src/atlas/trans/LegendreCacheCreator.cc0000664000175000017500000001111715160212070022406 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ // file deepcode ignore CppMemoryLeak: static pointers for global registry are OK and will be cleaned up at end #include "eckit/thread/AutoLock.h" #include "eckit/thread/Mutex.h" #include "atlas/grid/Grid.h" #include "atlas/library/defines.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Log.h" #include "atlas/trans/LegendreCacheCreator.h" // For factory registration only: #if ATLAS_HAVE_TRANS #include "atlas/trans/ifs/LegendreCacheCreatorIFS.h" #endif #include "atlas/trans/local/LegendreCacheCreatorLocal.h" namespace atlas { namespace trans { LegendreCacheCreatorImpl::~LegendreCacheCreatorImpl() = default; namespace { static eckit::Mutex* local_mutex = nullptr; static std::map* m = nullptr; static pthread_once_t once = PTHREAD_ONCE_INIT; static void init() { local_mutex = new eckit::Mutex(); m = new std::map(); } template void load_builder() { LegendreCacheCreatorBuilder("tmp"); } struct force_link { force_link() { #if ATLAS_HAVE_TRANS load_builder(); #endif load_builder(); } }; static LegendreCacheCreatorFactory& factory(const std::string& name) { ATLAS_ASSERT(m); std::map::const_iterator j = m->find(name); if (j == m->end()) { Log::error() << "No LegendreCacheCreatorFactory for [" << name << "]" << std::endl; Log::error() << "TransFactories are:" << std::endl; for (j = m->begin(); j != m->end(); ++j) { Log::error() << " " << (*j).first << std::endl; } throw_Exception(std::string("No LegendreCacheCreatorFactory called ") + name); } return *j->second; } } // namespace LegendreCacheCreatorFactory::LegendreCacheCreatorFactory(const std::string& name): name_(name) { pthread_once(&once, init); eckit::AutoLock lock(local_mutex); ATLAS_ASSERT(m); ATLAS_ASSERT(m->find(name) == m->end()); (*m)[name] = this; } LegendreCacheCreatorFactory::~LegendreCacheCreatorFactory() { eckit::AutoLock lock(local_mutex); ATLAS_ASSERT(m); m->erase(name_); } bool LegendreCacheCreatorFactory::has(const std::string& name) { pthread_once(&once, init); eckit::AutoLock lock(local_mutex); static force_link static_linking; ATLAS_ASSERT(m); return (m->find(name) != m->end()); } void LegendreCacheCreatorFactory::list(std::ostream& out) { pthread_once(&once, init); eckit::AutoLock lock(local_mutex); static force_link static_linking; ATLAS_ASSERT(m); const char* sep = ""; for (std::map::const_iterator j = m->begin(); j != m->end(); ++j) { out << sep << (*j).first; sep = ", "; } } LegendreCacheCreator::Implementation* LegendreCacheCreatorFactory::build(const Grid& grid, int truncation, const eckit::Configuration& config) { pthread_once(&once, init); eckit::AutoLock lock(local_mutex); static force_link static_linking; util::Config options = Trans::config(); options.set(eckit::LocalConfiguration(config)); std::string name = options.getString("type"); Log::debug() << "Looking for LegendreCacheCreatorFactory [" << name << "]" << std::endl; return factory(name).make(grid, truncation, options); } LegendreCacheCreator::LegendreCacheCreator(const Grid& grid, int truncation, const eckit::Configuration& config): Handle(LegendreCacheCreatorFactory::build(grid, truncation, config)) {} bool LegendreCacheCreator::supported() const { return get()->supported(); } std::string LegendreCacheCreator::uid() const { return get()->uid(); } void LegendreCacheCreator::create(const std::string& path) const { get()->create(path); } Cache LegendreCacheCreator::create() const { return get()->create(); } size_t LegendreCacheCreator::estimate() const { return get()->estimate(); } } // namespace trans } // namespace atlas atlas-0.46.0/src/atlas/trans/ifs/0000775000175000017500000000000015160212070016666 5ustar alastairalastairatlas-0.46.0/src/atlas/trans/ifs/TransIFSStructuredColumns.cc0000664000175000017500000000350515160212070024257 0ustar alastairalastair/* * (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/trans/ifs/TransIFSStructuredColumns.h" #include "atlas/functionspace/Spectral.h" #include "atlas/functionspace/StructuredColumns.h" #include "atlas/trans/detail/TransFactory.h" namespace atlas { namespace trans { TransIFSStructuredColumns::TransIFSStructuredColumns(const functionspace::StructuredColumns& gp, const functionspace::Spectral& sp, const eckit::Configuration& config): TransIFSStructuredColumns(Cache(), gp, sp, config) {} TransIFSStructuredColumns::TransIFSStructuredColumns(const Cache& cache, const functionspace::StructuredColumns& gp, const functionspace::Spectral& sp, const eckit::Configuration& config): TransIFS(cache, gp.grid(), sp.truncation(), config) { assertCompatibleDistributions(gp, sp); spectral_ = sp; } TransIFSStructuredColumns::~TransIFSStructuredColumns() = default; namespace { static TransBuilderFunctionSpace builder_ifs("ifs(StructuredColumns,Spectral)", "ifs"); // Deprecated, use below static TransBuilderFunctionSpace builder_ectrans("ectrans(StructuredColumns,Spectral)", "ectrans"); } // namespace } // namespace trans } // namespace atlas atlas-0.46.0/src/atlas/trans/ifs/VorDivToUVIFS.h0000664000175000017500000000400015160212070021362 0ustar alastairalastair/* * (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/trans/VorDivToUV.h" //----------------------------------------------------------------------------- // Forward declarations namespace atlas { class FunctionSpace; } //----------------------------------------------------------------------------- namespace atlas { namespace trans { //----------------------------------------------------------------------------- class VorDivToUVIFS : public trans::VorDivToUVImpl { public: VorDivToUVIFS(const FunctionSpace&, const eckit::Configuration& = util::NoConfig()); VorDivToUVIFS(int truncation, const eckit::Configuration& = util::NoConfig()); virtual ~VorDivToUVIFS(); virtual int truncation() const override { return truncation_; } // pure virtual interface // -- IFS style API -- // These fields have special interpretation required. You need to know what // you're doing. // See IFS trans library. /*! * @brief Compute spectral wind (U/V) from spectral vorticity/divergence * * U = u*cos(lat) * V = v*cos(lat) * * @param nb_fields [in] Number of fields * @param vorticity [in] Spectral vorticity * @param divergence [in] Spectral divergence * @param U [out] Spectral wind U = u*cos(lat) * @param V [out] Spectral wind V = v*cos(lat) */ virtual void execute(const int nb_coeff, const int nb_fields, const double vorticity[], const double divergence[], double U[], double V[], const eckit::Configuration& = util::NoConfig()) const override; private: int truncation_; }; // ------------------------------------------------------------------ } // namespace trans } // namespace atlas atlas-0.46.0/src/atlas/trans/ifs/TransIFSStructuredColumns.h0000664000175000017500000000273515160212070024125 0ustar alastairalastair/* * (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/trans/ifs/TransIFS.h" //----------------------------------------------------------------------------- // Forward declarations namespace atlas { namespace functionspace { class StructuredColumns; class Spectral; } // namespace functionspace } // namespace atlas //----------------------------------------------------------------------------- namespace atlas { namespace trans { //----------------------------------------------------------------------------- class TransIFSStructuredColumns : public trans::TransIFS { public: TransIFSStructuredColumns(const functionspace::StructuredColumns&, const functionspace::Spectral&, const eckit::Configuration& = util::Config()); TransIFSStructuredColumns(const Cache&, const functionspace::StructuredColumns&, const functionspace::Spectral&, const eckit::Configuration& = util::Config()); virtual ~TransIFSStructuredColumns(); }; //----------------------------------------------------------------------------- } // namespace trans } // namespace atlas atlas-0.46.0/src/atlas/trans/ifs/TransIFSNodeColumns.cc0000664000175000017500000000304415160212070022776 0ustar alastairalastair/* * (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/trans/ifs/TransIFSNodeColumns.h" #include "atlas/functionspace/NodeColumns.h" #include "atlas/functionspace/Spectral.h" #include "atlas/trans/detail/TransFactory.h" namespace atlas { namespace trans { TransIFSNodeColumns::TransIFSNodeColumns(const functionspace::NodeColumns& gp, const functionspace::Spectral& sp, const eckit::Configuration& config): TransIFSNodeColumns(Cache(), gp, sp, config) {} TransIFSNodeColumns::TransIFSNodeColumns(const Cache& cache, const functionspace::NodeColumns& gp, const functionspace::Spectral& sp, const eckit::Configuration& config): TransIFS(cache, gp.mesh().grid(), sp.truncation(), config) { assertCompatibleDistributions(gp, sp); spectral_ = sp; } TransIFSNodeColumns::~TransIFSNodeColumns() = default; namespace { static TransBuilderFunctionSpace builder_ifs("ifs(NodeColumns,Spectral)", "ifs"); // Deprecated, use below static TransBuilderFunctionSpace builder_ectrans("ectrans(NodeColumns,Spectral)", "ectrans"); } // namespace } // namespace trans } // namespace atlas atlas-0.46.0/src/atlas/trans/ifs/TransIFS.cc0000664000175000017500000032020615160212070020631 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "eckit/log/JSON.h" #include "atlas/library/config.h" #if ATLAS_HAVE_ECTRANS #include "ectrans/transi.h" #else #include "transi/trans.h" #endif #include "atlas/array.h" #include "atlas/field/FieldSet.h" #include "atlas/functionspace/NodeColumns.h" #include "atlas/functionspace/Spectral.h" #include "atlas/functionspace/StructuredColumns.h" #include "atlas/mesh/IsGhostNode.h" #include "atlas/mesh/Nodes.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Log.h" #include "atlas/trans/Trans.h" #include "atlas/trans/detail/TransFactory.h" #include "atlas/trans/ifs/TransIFS.h" using Topology = atlas::mesh::Nodes::Topology; using atlas::Field; using atlas::FunctionSpace; using atlas::array::ArrayView; using atlas::array::LocalView; using atlas::array::make_shape; using atlas::array::make_view; using atlas::functionspace::NodeColumns; using atlas::functionspace::Spectral; using atlas::functionspace::StructuredColumns; using atlas::mesh::IsGhostNode; namespace atlas { namespace trans { namespace { static TransBuilderGrid builder_ifs("ifs", "ifs"); // deprecated static TransBuilderGrid builder_ectrans("ectrans", "ectrans"); } // namespace class TransParameters { public: TransParameters(const TransIFS& trans, const eckit::Configuration& config): trans_(trans), config_(config) {} ~TransParameters() = default; bool scalar_derivatives() const { return config_.getBool("scalar_derivatives", false); } bool wind_EW_derivatives() const { return config_.getBool("wind_EW_derivatives", false); } bool vorticity_divergence_fields() const { return config_.getBool("vorticity_divergence_fields", false); } bool split_y() const { return config_.getBool("split_y", true); } int fft() const { static const std::map string_to_FFT = {{"FFT992", TRANS_FFT992}, {"FFTW", TRANS_FFTW}}; return string_to_FFT.at(config_.getString("fft", "FFTW")); } bool flt() const { return config_.getBool("flt", false); } std::string read_legendre() const { return config_.getString("read_legendre", ""); } std::string write_legendre() const { return config_.getString("write_legendre", ""); } bool global() const { return config_.getBool("global", false); } int nproma() const { return config_.getInt("nproma", trans_->ngptot); } int ngpblks() const { int _ngptot = trans_->ngptot; int _nproma = nproma(); ATLAS_ASSERT(_ngptot % _nproma == 0); // assert _ngptot is divisable by nproma return _ngptot / _nproma; } private: const Trans_t* trans_; const eckit::Configuration& config_; }; namespace { std::string fieldset_functionspace(const FieldSet& fields) { std::string functionspace("undefined"); for (idx_t jfld = 0; jfld < fields.size(); ++jfld) { if (functionspace == "undefined") { functionspace = fields[jfld].functionspace().type(); } if (fields[jfld].functionspace().type() != functionspace) { throw_Exception(": fielset has fields with different functionspaces", Here()); } } return functionspace; } void assert_spectral_functionspace(const FieldSet& fields) { for (idx_t jfld = 0; jfld < fields.size(); ++jfld) { ATLAS_ASSERT(functionspace::Spectral(fields[jfld].functionspace())); } } void trans_check(const int code, const char* msg, const eckit::CodeLocation& location) { if (code != TRANS_SUCCESS) { std::stringstream errmsg; errmsg << "atlas::trans ERROR: " << msg << " failed: \n"; errmsg << ::trans_error_msg(code); throw_Exception(errmsg.str(), location); } } #define TRANS_CHECK(CALL) trans_check(CALL, #CALL, Here()) static int compute_nfld(const Field& f) { int nfld = 1; for (int i = 1; i < f.rank(); ++i) { nfld *= f.shape(i); } return nfld; } static int compute_nfld(const FieldSet& f) { int nfld = 0; for (int i = 0; i < f.size(); ++i) { nfld += compute_nfld(f[i]); } return nfld; } // -------------------------------------------------------------------------------------------- } // namespace void TransIFS::dirtrans(const Field& gpfield, Field& spfield, const eckit::Configuration& config) const { ATLAS_ASSERT(functionspace::Spectral(spfield.functionspace())); if (functionspace::StructuredColumns(gpfield.functionspace())) { __dirtrans(functionspace::StructuredColumns(gpfield.functionspace()), gpfield, functionspace::Spectral(spfield.functionspace()), spfield, config); } else if (functionspace::NodeColumns(gpfield.functionspace())) { __dirtrans(functionspace::NodeColumns(gpfield.functionspace()), gpfield, functionspace::Spectral(spfield.functionspace()), spfield, config); } else { ATLAS_NOTIMPLEMENTED; } } void TransIFS::dirtrans(const FieldSet& gpfields, FieldSet& spfields, const eckit::Configuration& config) const { ATLAS_TRACE(); assert_spectral_functionspace(spfields); std::string functionspace(fieldset_functionspace(gpfields)); if (functionspace == StructuredColumns::type()) { __dirtrans(StructuredColumns(gpfields[0].functionspace()), gpfields, Spectral(spfields[0].functionspace()), spfields, config); } else if (functionspace == NodeColumns::type()) { __dirtrans(NodeColumns(gpfields[0].functionspace()), gpfields, Spectral(spfields[0].functionspace()), spfields, config); } else { ATLAS_NOTIMPLEMENTED; } } void TransIFS::dirtrans_wind2vordiv(const Field& gpwind, Field& spvor, Field& spdiv, const eckit::Configuration& config) const { ATLAS_ASSERT(Spectral(spvor.functionspace())); ATLAS_ASSERT(Spectral(spdiv.functionspace())); if (StructuredColumns(gpwind.functionspace())) { __dirtrans_wind2vordiv(StructuredColumns(gpwind.functionspace()), gpwind, Spectral(spvor.functionspace()), spvor, spdiv, config); } else if (NodeColumns(gpwind.functionspace())) { __dirtrans_wind2vordiv(NodeColumns(gpwind.functionspace()), gpwind, Spectral(spvor.functionspace()), spvor, spdiv, config); } else { ATLAS_NOTIMPLEMENTED; } } void TransIFS::dirtrans_adj(const Field& spfield, Field& gpfield, const eckit::Configuration& config) const { ATLAS_ASSERT(functionspace::Spectral(spfield.functionspace())); if (functionspace::StructuredColumns(gpfield.functionspace())) { __dirtrans_adj(functionspace::Spectral(spfield.functionspace()), spfield, functionspace::StructuredColumns(gpfield.functionspace()), gpfield, config); } else if (functionspace::NodeColumns(gpfield.functionspace())) { __dirtrans_adj(functionspace::Spectral(spfield.functionspace()), spfield, functionspace::NodeColumns(gpfield.functionspace()), gpfield, config); } else { ATLAS_NOTIMPLEMENTED; } } void TransIFS::dirtrans_adj(const FieldSet& spfields, FieldSet& gpfields, const eckit::Configuration& config) const { ATLAS_TRACE(); assert_spectral_functionspace(spfields); std::string functionspace(fieldset_functionspace(gpfields)); if (functionspace == StructuredColumns::type()) { __dirtrans_adj(Spectral(spfields[0].functionspace()), spfields, StructuredColumns(gpfields[0].functionspace()), gpfields, config); } else if (functionspace == NodeColumns::type()) { // __dirtrans_adj(NodeColumns(gpfields[0].functionspace()), gpfields, Spectral(spfields[0].functionspace()), spfields, // config); } else { ATLAS_NOTIMPLEMENTED; } } void TransIFS::dirtrans_wind2vordiv_adj(const Field& spvor, const Field& spdiv, Field& gpwind, const eckit::Configuration& config) const { ATLAS_ASSERT(Spectral(spvor.functionspace())); ATLAS_ASSERT(Spectral(spdiv.functionspace())); if (StructuredColumns(gpwind.functionspace())) { __dirtrans_wind2vordiv_adj(Spectral(spvor.functionspace()), spvor, spdiv, StructuredColumns(gpwind.functionspace()), gpwind, config); } else if (NodeColumns(gpwind.functionspace())) { // __dirtrans_wind2vordiv_adj(Spectral(spvor.functionspace()), spvor, spdiv, // NodeColumns(gpwind.functionspace()), gpwind, // config); } else { ATLAS_NOTIMPLEMENTED; } } void TransIFS::invtrans(const Field& spfield, Field& gpfield, const eckit::Configuration& config) const { ATLAS_TRACE(); ATLAS_ASSERT(Spectral(spfield.functionspace())); if (StructuredColumns(gpfield.functionspace())) { __invtrans(Spectral(spfield.functionspace()), spfield, StructuredColumns(gpfield.functionspace()), gpfield, config); } else if (NodeColumns(gpfield.functionspace())) { __invtrans(Spectral(spfield.functionspace()), spfield, NodeColumns(gpfield.functionspace()), gpfield, config); } else { ATLAS_NOTIMPLEMENTED; } } void TransIFS::invtrans(const FieldSet& spfields, FieldSet& gpfields, const eckit::Configuration& config) const { ATLAS_TRACE(); assert_spectral_functionspace(spfields); std::string functionspace(fieldset_functionspace(gpfields)); if (functionspace == StructuredColumns::type()) { __invtrans(Spectral(spfields[0].functionspace()), spfields, StructuredColumns(gpfields[0].functionspace()), gpfields, config); } else if (functionspace == NodeColumns::type()) { __invtrans(Spectral(spfields[0].functionspace()), spfields, NodeColumns(gpfields[0].functionspace()), gpfields, config); } else { ATLAS_NOTIMPLEMENTED; } } void TransIFS::invtrans_adj(const Field& gpfield, Field& spfield, const eckit::Configuration& config) const { ATLAS_ASSERT(Spectral(spfield.functionspace())); if (StructuredColumns(gpfield.functionspace())) { __invtrans_adj(Spectral(spfield.functionspace()), spfield, StructuredColumns(gpfield.functionspace()), gpfield, config); } else if (NodeColumns(gpfield.functionspace())) { __invtrans_adj(Spectral(spfield.functionspace()), spfield, NodeColumns(gpfield.functionspace()), gpfield, config); } else { ATLAS_NOTIMPLEMENTED; } } void TransIFS::invtrans_adj(const FieldSet& gpfields, FieldSet& spfields, const eckit::Configuration& config) const { assert_spectral_functionspace(spfields); std::string functionspace(fieldset_functionspace(gpfields)); if (functionspace == StructuredColumns::type()) { __invtrans_adj(Spectral(spfields[0].functionspace()), spfields, StructuredColumns(gpfields[0].functionspace()), gpfields, config); } else if (functionspace == NodeColumns::type()) { __invtrans_adj(Spectral(spfields[0].functionspace()), spfields, NodeColumns(gpfields[0].functionspace()), gpfields, config); } else { ATLAS_NOTIMPLEMENTED; } } // -------------------------------------------------------------------------------------------- void TransIFS::invtrans_grad(const Field& spfield, Field& gradfield, const eckit::Configuration& config) const { ATLAS_ASSERT(Spectral(spfield.functionspace())); ATLAS_ASSERT(NodeColumns(gradfield.functionspace())); if (StructuredColumns(gradfield.functionspace())) { __invtrans_grad(Spectral(spfield.functionspace()), spfield, StructuredColumns(gradfield.functionspace()), gradfield, config); } else if (NodeColumns(gradfield.functionspace())) { __invtrans_grad(Spectral(spfield.functionspace()), spfield, NodeColumns(gradfield.functionspace()), gradfield, config); } else { ATLAS_NOTIMPLEMENTED; } } void TransIFS::invtrans_grad(const FieldSet& spfields, FieldSet& gradfields, const eckit::Configuration& config) const { assert_spectral_functionspace(spfields); std::string functionspace(fieldset_functionspace(gradfields)); if (functionspace == StructuredColumns::type()) { __invtrans_grad(Spectral(spfields[0].functionspace()), spfields, StructuredColumns(gradfields[0].functionspace()), gradfields, config); } else if (functionspace == NodeColumns::type()) { __invtrans_grad(Spectral(spfields[0].functionspace()), spfields, NodeColumns(gradfields[0].functionspace()), gradfields, config); } else { ATLAS_NOTIMPLEMENTED; } } // -------------------------------------------------------------------------------------------- void TransIFS::invtrans_grad_adj(const Field& gradfield, Field& spfield, const eckit::Configuration& config) const { ATLAS_ASSERT(Spectral(spfield.functionspace())); ATLAS_ASSERT(NodeColumns(gradfield.functionspace())); if (StructuredColumns(gradfield.functionspace())) { __invtrans_grad_adj(Spectral(spfield.functionspace()), spfield, StructuredColumns(gradfield.functionspace()), gradfield, config); } else if (NodeColumns(gradfield.functionspace())) { __invtrans_grad_adj(Spectral(spfield.functionspace()), spfield, NodeColumns(gradfield.functionspace()), gradfield, config); } else { ATLAS_NOTIMPLEMENTED; } } void TransIFS::invtrans_grad_adj(const FieldSet& gradfields, FieldSet& spfields, const eckit::Configuration& config) const { assert_spectral_functionspace(spfields); std::string functionspace(fieldset_functionspace(gradfields)); if (functionspace == StructuredColumns::type()) { __invtrans_grad_adj(Spectral(spfields[0].functionspace()), spfields, StructuredColumns(gradfields[0].functionspace()), gradfields, config); } else if (functionspace == NodeColumns::type()) { __invtrans_grad_adj(Spectral(spfields[0].functionspace()), spfields, NodeColumns(gradfields[0].functionspace()), gradfields, config); } else { ATLAS_NOTIMPLEMENTED; } } void TransIFS::invtrans_vordiv2wind(const Field& spvor, const Field& spdiv, Field& gpwind, const eckit::Configuration& config) const { ATLAS_ASSERT(Spectral(spvor.functionspace())); ATLAS_ASSERT(Spectral(spdiv.functionspace())); if (StructuredColumns(gpwind.functionspace())) { __invtrans_vordiv2wind(Spectral(spvor.functionspace()), spvor, spdiv, StructuredColumns(gpwind.functionspace()), gpwind, config); } else if (NodeColumns(gpwind.functionspace())) { __invtrans_vordiv2wind(Spectral(spvor.functionspace()), spvor, spdiv, NodeColumns(gpwind.functionspace()), gpwind, config); } else { ATLAS_NOTIMPLEMENTED; } } void TransIFS::invtrans_vordiv2wind_adj(const Field& gpwind, Field& spvor, Field& spdiv, const eckit::Configuration& config) const { ATLAS_ASSERT(Spectral(spvor.functionspace())); ATLAS_ASSERT(Spectral(spdiv.functionspace())); if (StructuredColumns(gpwind.functionspace())) { __invtrans_vordiv2wind_adj(Spectral(spvor.functionspace()), spvor, spdiv, StructuredColumns(gpwind.functionspace()), gpwind, config); } else if (NodeColumns(gpwind.functionspace())) { __invtrans_vordiv2wind_adj(Spectral(spvor.functionspace()), spvor, spdiv, NodeColumns(gpwind.functionspace()), gpwind, config); } else { ATLAS_NOTIMPLEMENTED; } } // -------------------------------------------------------------------------------------------- void TransIFS::invtrans(const int nb_scalar_fields, const double scalar_spectra[], const int nb_vordiv_fields, const double vorticity_spectra[], const double divergence_spectra[], double gp_fields[], const eckit::Configuration& config) const { ATLAS_TRACE("TransIFS::invtrans"); TransParameters params(*this, config); struct ::InvTrans_t args = new_invtrans(trans_.get()); args.nscalar = nb_scalar_fields; args.rspscalar = scalar_spectra; args.nvordiv = nb_vordiv_fields; args.rspvor = vorticity_spectra; args.rspdiv = divergence_spectra; args.rgp = gp_fields; args.lglobal = params.global(); args.lscalarders = params.scalar_derivatives(); args.luvder_EW = params.wind_EW_derivatives(); args.lvordivgp = params.vorticity_divergence_fields(); args.nproma = params.nproma(); args.ngpblks = params.ngpblks(); TRANS_CHECK(::trans_invtrans(&args)); } void TransIFS::invtrans_adj(const int nb_scalar_fields, const double gp_fields[], const int nb_vordiv_fields, double vorticity_spectra[], double divergence_spectra[], double scalar_spectra[], const eckit::Configuration& config) const { #if ATLAS_HAVE_ECTRANS || defined(TRANS_HAVE_INVTRANS_ADJ) ATLAS_TRACE("TransIFS::invtrans_adj"); TransParameters params(*this, config); struct ::InvTransAdj_t args = new_invtrans_adj(trans_.get()); args.nscalar = nb_scalar_fields; args.rspscalar = scalar_spectra; args.nvordiv = nb_vordiv_fields; args.rspvor = vorticity_spectra; args.rspdiv = divergence_spectra; args.rgp = gp_fields; args.lglobal = params.global(); args.lscalarders = params.scalar_derivatives(); args.luvder_EW = params.wind_EW_derivatives(); args.lvordivgp = params.vorticity_divergence_fields(); args.nproma = params.nproma(); args.ngpblks = params.ngpblks(); TRANS_CHECK(::trans_invtrans_adj(&args)); #else ATLAS_NOTIMPLEMENTED; #endif } /////////////////////////////////////////////////////////////////////////////// void TransIFS::invtrans(const int nb_scalar_fields, const double scalar_spectra[], double gp_fields[], const eckit::Configuration& config) const { return invtrans(nb_scalar_fields, scalar_spectra, 0, nullptr, nullptr, gp_fields, config); } /////////////////////////////////////////////////////////////////////////////// void TransIFS::invtrans(const int nb_vordiv_fields, const double vorticity_spectra[], const double divergence_spectra[], double gp_fields[], const eckit::Configuration& config) const { return invtrans(0, nullptr, nb_vordiv_fields, vorticity_spectra, divergence_spectra, gp_fields, config); } /////////////////////////////////////////////////////////////////////////////// void TransIFS::invtrans_adj(const int nb_scalar_fields, const double gp_fields[], double scalar_spectra[], const eckit::Configuration& config) const { return invtrans_adj(nb_scalar_fields, gp_fields, 0, nullptr, nullptr, scalar_spectra, config); } /////////////////////////////////////////////////////////////////////////////// void TransIFS::invtrans_adj(const int nb_vordiv_fields, const double gp_fields[], double divergence_spectra[], double vorticity_spectra[], const eckit::Configuration& config) const { return invtrans_adj(0, gp_fields, nb_vordiv_fields, vorticity_spectra, divergence_spectra, nullptr, config); } void TransIFS::dirtrans(const int nb_fields, const double scalar_fields[], double scalar_spectra[], const eckit::Configuration& config) const { ATLAS_TRACE(); TransParameters params(*this, config); struct ::DirTrans_t args = new_dirtrans(trans_.get()); args.nscalar = nb_fields; args.rgp = scalar_fields; args.rspscalar = scalar_spectra; args.lglobal = params.global(); args.nproma = params.nproma(); args.ngpblks = params.ngpblks(); TRANS_CHECK(::trans_dirtrans(&args)); } /////////////////////////////////////////////////////////////////////////////// void TransIFS::dirtrans(const int nb_fields, const double wind_fields[], double vorticity_spectra[], double divergence_spectra[], const eckit::Configuration& config) const { ATLAS_TRACE(); TransParameters params(*this, config); struct ::DirTrans_t args = new_dirtrans(trans_.get()); args.nvordiv = nb_fields; args.rspvor = vorticity_spectra; args.rspdiv = divergence_spectra; args.rgp = wind_fields; args.lglobal = params.global(); args.nproma = params.nproma(); args.ngpblks = params.ngpblks(); TRANS_CHECK(::trans_dirtrans(&args)); } } // namespace trans // anonymous namespace namespace { struct PackNodeColumns { LocalView& rgpview_; IsGhostNode is_ghost; size_t f; PackNodeColumns(LocalView& rgpview, const NodeColumns& fs): rgpview_(rgpview), is_ghost(fs.nodes()), f(0) {} void operator()(const Field& field, idx_t components = 0) { switch (field.rank()) { case 1: pack_1(field, components); break; case 2: pack_2(field, components); break; case 3: pack_3(field, components); break; default: ATLAS_DEBUG_VAR(field.rank()); ATLAS_NOTIMPLEMENTED; //break; } } void pack_1(const Field& field, idx_t) { auto gpfield = make_view(field); idx_t n = 0; for (idx_t jnode = 0; jnode < gpfield.shape(0); ++jnode) { if (!is_ghost(jnode)) { rgpview_(f, n) = gpfield(jnode); ++n; } } ++f; } void pack_2(const Field& field, idx_t) { auto gpfield = make_view(field); const idx_t nvars = gpfield.shape(1); for (idx_t jvar = 0; jvar < nvars; ++jvar) { idx_t n = 0; for (idx_t jnode = 0; jnode < gpfield.shape(0); ++jnode) { if (!is_ghost(jnode)) { rgpview_(f, n) = gpfield(jnode, jvar); ++n; } } ++f; } } void pack_3(const Field& field, idx_t components) { auto gpfield = make_view(field); 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; for (idx_t jnode = 0; jnode < gpfield.shape(0); ++jnode) { if (!is_ghost(jnode)) { rgpview_(f, n) = gpfield(jnode, jlev, jcomp); ++n; } } ++f; } } } }; struct PackStructuredColumns { LocalView& rgpview_; size_t f; PackStructuredColumns(LocalView& rgpview): rgpview_(rgpview), f(0) {} void operator()(const StructuredColumns& sc, const Field& field, idx_t components = 0) { switch (field.rank()) { case 1: pack_1(sc, field, components); break; case 2: pack_2(sc, field, components); break; case 3: pack_3(sc, field, components); break; default: ATLAS_DEBUG_VAR(field.rank()); ATLAS_NOTIMPLEMENTED; //break; } } void pack_1(const StructuredColumns& sc, const Field& field, idx_t) { auto gpfield = make_view(field); for (idx_t jnode = 0; jnode < sc.sizeOwned(); ++jnode) { rgpview_(f, jnode) = gpfield(jnode); } ++f; } void pack_2(const StructuredColumns& sc, const Field& field, idx_t) { auto gpfield = make_view(field); const idx_t nvars = gpfield.shape(1); for (idx_t jvar = 0; jvar < nvars; ++jvar) { for (idx_t jnode = 0; jnode < sc.sizeOwned(); ++jnode) { rgpview_(f, jnode) = gpfield(jnode, jvar); } ++f; } } void pack_3(const StructuredColumns& sc, const Field& field, idx_t components) { auto gpfield = make_view(field); if (not components) { components = gpfield.shape(2); } for (idx_t jcomp = 0; jcomp < components; ++jcomp) { const idx_t nvars = gpfield.shape(1); for (idx_t jvar = 0; jvar < nvars; ++jvar) { for (idx_t jnode = 0; jnode < sc.sizeOwned(); ++jnode) { rgpview_(f, jnode) = gpfield(jnode, jvar, jcomp); } ++f; } } } }; struct PackSpectral { LocalView& rspecview_; size_t f; PackSpectral(LocalView& rspecview): rspecview_(rspecview), f(0) {} void operator()(const Field& field) { switch (field.rank()) { case 1: pack_1(field); break; case 2: pack_2(field); break; default: ATLAS_DEBUG_VAR(field.rank()); ATLAS_NOTIMPLEMENTED; //break; } } void pack_1(const Field& field) { auto spfield = make_view(field); for (idx_t jwave = 0; jwave < spfield.shape(0); ++jwave) { rspecview_(jwave, f) = spfield(jwave); } ++f; } void pack_2(const Field& field) { auto spfield = make_view(field); const idx_t nvars = spfield.shape(1); for (idx_t jvar = 0; jvar < nvars; ++jvar) { for (idx_t jwave = 0; jwave < spfield.shape(0); ++jwave) { rspecview_(jwave, f) = spfield(jwave, jvar); } ++f; } } }; struct UnpackNodeColumns { const LocalView& rgpview_; IsGhostNode is_ghost; size_t f; UnpackNodeColumns(const LocalView& rgpview, const NodeColumns& fs): rgpview_(rgpview), is_ghost(fs.nodes()), f(0) {} void operator()(Field& field, int components = 0) { switch (field.rank()) { case 1: unpack_1(field, components); break; case 2: unpack_2(field, components); break; case 3: unpack_3(field, components); break; default: ATLAS_DEBUG_VAR(field.rank()); ATLAS_NOTIMPLEMENTED; //break; } } void unpack_1(Field& field, idx_t) { auto gpfield = make_view(field); idx_t n(0); for (idx_t jnode = 0; jnode < gpfield.shape(0); ++jnode) { if (!is_ghost(jnode)) { gpfield(jnode) = rgpview_(f, n); ++n; } } ++f; } void unpack_2(Field& field, idx_t) { auto gpfield = make_view(field); const idx_t nvars = gpfield.shape(1); for (idx_t jvar = 0; jvar < nvars; ++jvar) { idx_t n = 0; for (idx_t jnode = 0; jnode < gpfield.shape(0); ++jnode) { if (!is_ghost(jnode)) { gpfield(jnode, jvar) = rgpview_(f, n); ++n; } } ++f; } } void unpack_3(Field& field, idx_t components) { auto gpfield = make_view(field); 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; for (idx_t jnode = 0; jnode < gpfield.shape(0); ++jnode) { if (!is_ghost(jnode)) { gpfield(jnode, jlev, jcomp) = rgpview_(f, n); ++n; } } ++f; } } } }; struct UnpackStructuredColumns { const LocalView& rgpview_; size_t f; UnpackStructuredColumns(const LocalView& rgpview): rgpview_(rgpview), f(0) {} void operator()(const StructuredColumns& sc, Field& field, int components = 0) { switch (field.rank()) { case 1: unpack_1(sc, field, components); break; case 2: unpack_2(sc, field, components); break; case 3: unpack_3(sc, field, components); break; default: ATLAS_DEBUG_VAR(field.rank()); ATLAS_NOTIMPLEMENTED; //break; } } void unpack_1(const StructuredColumns& sc, Field& field, idx_t) { auto gpfield = make_view(field); for (idx_t jnode = 0; jnode < sc.sizeOwned(); ++jnode) { gpfield(jnode) = rgpview_(f, jnode); } ++f; } void unpack_2(const StructuredColumns& sc, Field& field, idx_t) { auto gpfield = make_view(field); const idx_t nvars = gpfield.shape(1); for (idx_t jvar = 0; jvar < nvars; ++jvar) { for (idx_t jnode = 0; jnode < sc.sizeOwned(); ++jnode) { gpfield(jnode, jvar) = rgpview_(f, jnode); } ++f; } } void unpack_3(const StructuredColumns& sc, Field& field, idx_t components) { auto gpfield = make_view(field); 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) { for (idx_t jnode = 0; jnode < sc.sizeOwned(); ++jnode) { gpfield(jnode, jlev, jcomp) = rgpview_(f, jnode); } ++f; } } } }; struct UnpackSpectral { const LocalView& rspecview_; size_t f; UnpackSpectral(const LocalView& rspecview): rspecview_(rspecview), f(0) {} void operator()(Field& field) { switch (field.rank()) { case 1: unpack_1(field); break; case 2: unpack_2(field); break; default: ATLAS_DEBUG_VAR(field.rank()); ATLAS_NOTIMPLEMENTED; //break; } } void unpack_1(Field& field) { ArrayView spfield = make_view(field); for (idx_t jwave = 0; jwave < spfield.shape(0); ++jwave) { spfield(jwave) = rspecview_(jwave, f); } ++f; } void unpack_2(Field& field) { ArrayView spfield = make_view(field); const idx_t nvars = spfield.shape(1); for (idx_t jvar = 0; jvar < nvars; ++jvar) { for (idx_t jwave = 0; jwave < spfield.shape(0); ++jwave) { spfield(jwave, jvar) = rspecview_(jwave, f); } ++f; } } }; } // end anonymous namespace } // namespace atlas namespace atlas { namespace trans { void TransIFS::assertCompatibleDistributions(const FunctionSpace& gp, const FunctionSpace& /*sp*/) const { std::string gp_dist = gp.distribution(); if (gp_dist != "ectrans" && // distribution computed by TransPartitioner gp_dist != "serial" && // serial distribution always works gp_dist != "custom") { // trust user that he knows what he is doing throw_Exception(gp.type() + " functionspace has unsupported distribution (" + gp_dist + ") " "to do spectral transforms. Please " "partition grid with TransPartitioner", Here()); } } TransIFS::TransIFS(const Cache& cache, const Grid& grid, const long truncation, const eckit::Configuration& config): grid_(grid), cache_(cache.legendre().data()), cachesize_(cache.legendre().size()) { ATLAS_ASSERT(grid.domain().global()); ATLAS_ASSERT(not grid.projection()); ctor(grid, truncation, config); } TransIFS::TransIFS(const Grid& grid, const long truncation, const eckit::Configuration& config): TransIFS(Cache(), grid, truncation, config) {} TransIFS::TransIFS(const Grid& grid, const eckit::Configuration& config): TransIFS(grid, /*grid-only*/ -1, config) {} int atlas::trans::TransIFS::handle() const { return trans_->handle; } int atlas::trans::TransIFS::ndgl() const { return trans_->ndgl; } int atlas::trans::TransIFS::nsmax() const { return trans_->nsmax; } int atlas::trans::TransIFS::ngptot() const { return trans_->ngptot; } int atlas::trans::TransIFS::ngptotg() const { return trans_->ngptotg; } int atlas::trans::TransIFS::ngptotmx() const { return trans_->ngptotmx; } int atlas::trans::TransIFS::nspec() const { return trans_->nspec; } int atlas::trans::TransIFS::nspec2() const { return trans_->nspec2; } int atlas::trans::TransIFS::nspec2g() const { return trans_->nspec2g; } int atlas::trans::TransIFS::nspec2mx() const { return trans_->nspec2mx; } int atlas::trans::TransIFS::n_regions_NS() const { return trans_->n_regions_NS; } int atlas::trans::TransIFS::n_regions_EW() const { return trans_->n_regions_EW; } int atlas::trans::TransIFS::nump() const { return trans_->nump; } int atlas::trans::TransIFS::nproc() const { return trans_->nproc; } int atlas::trans::TransIFS::myproc(int proc0) const { return trans_->myproc - 1 + proc0; } const int* atlas::trans::TransIFS::nloen(int& size) const { size = trans_->ndgl; ATLAS_ASSERT(trans_->nloen != nullptr); return trans_->nloen; } const int* atlas::trans::TransIFS::n_regions(int& size) const { size = trans_->n_regions_NS; ATLAS_ASSERT(trans_->n_regions != nullptr); return trans_->n_regions; } const int* atlas::trans::TransIFS::nfrstlat(int& size) const { size = trans_->n_regions_NS; 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) { ::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) { ::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) { ::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) { ::trans_inquire(trans_.get(), "nonl"); } return trans_->nonl; } const int* atlas::trans::TransIFS::nmyms(int& size) const { size = trans_->nump; 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) { ::trans_inquire(trans_.get(), "nasm0"); } return trans_->nasm0; } const int* atlas::trans::TransIFS::nvalue(int& size) const { size = trans_->nspec2; if (trans_->nvalue == nullptr) { ::trans_inquire(trans_.get(), "nvalue"); } return trans_->nvalue; } array::LocalView atlas::trans::TransIFS::nvalue() const { 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) { ::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) { ::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) { ::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) { ::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) { ::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) { ::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) { ::trans_inquire(trans_.get(), "nfrstlat"); } return array::LocalView(trans_->nfrstlat, array::make_shape(trans_->n_regions_NS)); } array::LocalView atlas::trans::TransIFS::n_regions() const { ATLAS_ASSERT(trans_->n_regions != nullptr); return array::LocalView(trans_->n_regions, array::make_shape(trans_->n_regions_NS)); } array::LocalView atlas::trans::TransIFS::nloen() const { ATLAS_ASSERT(trans_->nloen != nullptr); return array::LocalView(trans_->nloen, array::make_shape(trans_->ndgl)); } TransIFS::TransIFS(const Grid& grid, const Domain& domain, const long truncation, const eckit::Configuration& config): TransIFS(Cache(), grid, truncation, config) { ATLAS_ASSERT(domain.global()); } TransIFS::TransIFS(const Cache& cache, const Grid& grid, const Domain& domain, const long truncation, const eckit::Configuration& config): TransIFS(cache, grid, truncation, config) { ATLAS_ASSERT(domain.global()); } TransIFS::~TransIFS() = default; int atlas::trans::TransIFS::truncation() const { return std::max(0, trans_->nsmax); } size_t TransIFS::nb_spectral_coefficients() const { return trans_->nspec2; } size_t TransIFS::nb_spectral_coefficients_global() const { return trans_->nspec2g; } const functionspace::Spectral& TransIFS::spectral() const { if (not spectral_) { spectral_ = functionspace::Spectral( truncation() ); } return spectral_; } void TransIFS::ctor(const Grid& grid, long truncation, const eckit::Configuration& config) { trans_ = std::shared_ptr<::Trans_t>(new ::Trans_t, [](::Trans_t* p) { ::trans_delete(p); delete p; }); if (auto gg = GaussianGrid(grid)) { ctor_rgg(gg.ny(), gg.nx().data(), truncation, config); return; } if (auto ll = RegularLonLatGrid(grid)) { if (ll.standard() || ll.shifted()) { ctor_lonlat(ll.nx(), ll.ny(), truncation, config); return; } } throw_NotImplemented("Grid type not supported for Spectral Transforms", Here()); } 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) { nloen[jlat] = pl[jlat]; } // Set MPI communicator to the same as Atlas. Must be set prior to MPL_INIT in ectrans/fiat. #if ATLAS_ECTRANS_VERSION_AT_LEAST(1, 8, 0) TRANS_CHECK(::trans_set_mpi_comm(mpi::comm().communicator())); #endif TRANS_CHECK(::trans_new(trans_.get())); TRANS_CHECK(::trans_use_mpi(mpi::size() > 1)); TRANS_CHECK(::trans_set_resol(trans_.get(), nlat, nloen.data())); 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::size() == 1) { eckit::PathName file(p.read_legendre()); if (not file.exists()) { std::stringstream msg; msg << "File " << file << " doesn't exist"; throw_CantOpenFile(msg.str(), Here()); } TRANS_CHECK(::trans_set_read(trans_.get(), file.asString().c_str())); } if (p.write_legendre().size() && mpi::size() == 1) { eckit::PathName file(p.write_legendre()); TRANS_CHECK(::trans_set_write(trans_.get(), file.asString().c_str())); } trans_->fft = p.fft(); trans_->lsplit = p.split_y(); trans_->flt = p.flt(); ATLAS_TRACE_SCOPE("trans_setup") { TRANS_CHECK(::trans_setup(trans_.get())); } } void TransIFS::ctor_lonlat(const long nlon, const long nlat, long truncation, const eckit::Configuration& config) { TransParameters p(*this, config); #if ATLAS_ECTRANS_VERSION_AT_LEAST(1, 8, 0) TRANS_CHECK(::trans_set_mpi_comm(mpi::comm().communicator())); #endif TRANS_CHECK(::trans_new(trans_.get())); TRANS_CHECK(::trans_use_mpi(mpi::size() > 1)); TRANS_CHECK(::trans_set_resol_lonlat(trans_.get(), nlon, nlat)); 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::size() == 1) { eckit::PathName file(p.read_legendre()); if (not file.exists()) { std::stringstream msg; msg << "File " << file << " doesn't exist"; throw_CantOpenFile(msg.str(), Here()); } TRANS_CHECK(::trans_set_read(trans_.get(), file.asString().c_str())); } if (p.write_legendre().size() && mpi::size() == 1) { eckit::PathName file(p.write_legendre()); TRANS_CHECK(::trans_set_write(trans_.get(), file.asString().c_str())); } trans_->fft = p.fft(); trans_->lsplit = p.split_y(); trans_->flt = p.flt(); TRANS_CHECK(::trans_setup(trans_.get())); } // -------------------------------------------------------------------------------------------- void TransIFS::__dirtrans(const StructuredColumns& gp, const Field& gpfield, const Spectral& sp, Field& spfield, const eckit::Configuration&) const { ATLAS_ASSERT(gpfield.functionspace() == 0 || functionspace::StructuredColumns(gpfield.functionspace())); ATLAS_ASSERT(spfield.functionspace() == 0 || functionspace::Spectral(spfield.functionspace())); assertCompatibleDistributions(gp, sp); if (compute_nfld(gpfield) != compute_nfld(spfield)) { throw_Exception("dirtrans: different number of gridpoint fields than spectral fields", Here()); } if ((int)gpfield.shape(0) < ngptot()) { throw_Exception("dirtrans: slowest moving index must be >= ngptot", Here()); } const int nfld = compute_nfld(gpfield); // Arrays Trans expects std::vector rgp(nfld * ngptot()); std::vector rsp(nspec2() * nfld); auto rgpview = LocalView(rgp.data(), make_shape(nfld, ngptot())); auto rspview = LocalView(rsp.data(), make_shape(nspec2(), nfld)); // Pack gridpoints { PackStructuredColumns pack(rgpview); pack(gp, gpfield); } // Do transform { struct ::DirTrans_t transform = ::new_dirtrans(trans_.get()); transform.nscalar = nfld; transform.rgp = rgp.data(); transform.rspscalar = rsp.data(); TRANS_CHECK(::trans_dirtrans(&transform)); } // Unpack spectral { UnpackSpectral unpack(rspview); unpack(spfield); } } // -------------------------------------------------------------------------------------------- void TransIFS::__dirtrans(const functionspace::NodeColumns& gp, const Field& gpfield, const Spectral& sp, Field& spfield, const eckit::Configuration& config) const { FieldSet gpfields; gpfields.add(gpfield); FieldSet spfields; spfields.add(spfield); __dirtrans(gp, gpfields, sp, spfields, config); } // -------------------------------------------------------------------------------------------- void TransIFS::__dirtrans(const StructuredColumns& gp, const FieldSet& gpfields, const Spectral& sp, FieldSet& spfields, const eckit::Configuration&) const { assertCompatibleDistributions(gp, sp); // Count total number of fields and do sanity checks const idx_t nfld = compute_nfld(gpfields); for (idx_t jfld = 0; jfld < gpfields.size(); ++jfld) { const Field& f = gpfields[jfld]; ATLAS_ASSERT(f.functionspace() == 0 || functionspace::StructuredColumns(f.functionspace())); } const int trans_sp_nfld = compute_nfld(spfields); if (nfld != trans_sp_nfld) { throw_Exception("dirtrans: different number of gridpoint fields than spectral fields", Here()); } // Arrays Trans expects std::vector rgp(nfld * ngptot()); std::vector rsp(nspec2() * nfld); auto rgpview = LocalView(rgp.data(), make_shape(nfld, ngptot())); auto rspview = LocalView(rsp.data(), make_shape(nspec2(), nfld)); // Pack gridpoints { PackStructuredColumns pack(rgpview); for (idx_t jfld = 0; jfld < gpfields.size(); ++jfld) { pack(gp, gpfields[jfld]); } } // Do transform { struct ::DirTrans_t transform = ::new_dirtrans(trans_.get()); transform.nscalar = int(nfld); transform.rgp = rgp.data(); transform.rspscalar = rsp.data(); TRANS_CHECK(::trans_dirtrans(&transform)); } // Unpack the spectral fields { UnpackSpectral unpack(rspview); for (idx_t jfld = 0; jfld < spfields.size(); ++jfld) { unpack(spfields[jfld]); } } } // -------------------------------------------------------------------------------------------- void TransIFS::__dirtrans(const functionspace::NodeColumns& gp, const FieldSet& gpfields, const Spectral& sp, FieldSet& spfields, const eckit::Configuration&) const { assertCompatibleDistributions(gp, sp); // Count total number of fields and do sanity checks const int nfld = compute_nfld(gpfields); const int trans_sp_nfld = compute_nfld(spfields); if (nfld != trans_sp_nfld) { throw_Exception("dirtrans: different number of gridpoint fields than spectral fields", Here()); } // Arrays Trans expects std::vector rgp(nfld * ngptot()); std::vector rsp(nspec2() * nfld); auto rgpview = LocalView(rgp.data(), make_shape(nfld, ngptot())); auto rspview = LocalView(rsp.data(), make_shape(nspec2(), nfld)); // Pack gridpoints { PackNodeColumns pack(rgpview, gp); for (idx_t jfld = 0; jfld < gpfields.size(); ++jfld) { pack(gpfields[jfld]); } } // Do transform { struct ::DirTrans_t transform = ::new_dirtrans(trans_.get()); transform.nscalar = nfld; transform.rgp = rgp.data(); transform.rspscalar = rsp.data(); TRANS_CHECK(::trans_dirtrans(&transform)); } // Unpack the spectral fields { UnpackSpectral unpack(rspview); for (idx_t jfld = 0; jfld < spfields.size(); ++jfld) { unpack(spfields[jfld]); } } } // -------------------------------------------------------------------------------------------- void TransIFS::__dirtrans_wind2vordiv(const functionspace::StructuredColumns& gp, const Field& gpwind, const Spectral& sp, Field& spvor, Field& spdiv, const eckit::Configuration&) const { assertCompatibleDistributions(gp, sp); // Count total number of fields and do sanity checks const size_t nfld = compute_nfld(spvor); if (spdiv.shape(0) != spvor.shape(0)) { throw_Exception("dirtrans:vorticity not compatible with divergence for 1st dimension", Here()); } if (spdiv.shape(1) != spvor.shape(1)) { throw_Exception("dirtrans:vorticity not compatible with divergence for 2st dimension", Here()); } const size_t nwindfld = compute_nfld(gpwind); 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; msg << "dirtrans: Spectral vorticity and divergence have wrong dimension: " "nspec2 " << spdiv.shape(0) << " should be " << nspec2(); throw_Exception(msg.str(), Here()); } if (spvor.size() == 0) { throw_Exception("dirtrans: spectral vorticity field is empty."); } if (spdiv.size() == 0) { throw_Exception("dirtrans: spectral divergence field is empty."); } // Arrays Trans expects std::vector rgp(2 * nfld * ngptot()); std::vector rspvor(nspec2() * nfld); std::vector rspdiv(nspec2() * nfld); auto rgpview = LocalView(rgp.data(), make_shape(2 * nfld, ngptot())); auto rspvorview = LocalView(rspvor.data(), make_shape(nspec2(), nfld)); auto rspdivview = LocalView(rspdiv.data(), make_shape(nspec2(), nfld)); // Pack gridpoints { PackStructuredColumns pack(rgpview); int wind_components = 2; pack(gpwind.functionspace(), gpwind, wind_components); } // Do transform { struct ::DirTrans_t transform = ::new_dirtrans(trans_.get()); transform.nvordiv = int(nfld); transform.rgp = rgp.data(); transform.rspvor = rspvor.data(); transform.rspdiv = rspdiv.data(); ATLAS_ASSERT(transform.rspvor); ATLAS_ASSERT(transform.rspdiv); TRANS_CHECK(::trans_dirtrans(&transform)); } // Pack spectral fields UnpackSpectral unpack_vor(rspvorview); unpack_vor(spvor); UnpackSpectral unpack_div(rspdivview); unpack_div(spdiv); } // ----------------------------------------------------------------------------------------------- void TransIFS::__dirtrans_wind2vordiv(const functionspace::NodeColumns& gp, const Field& gpwind, const Spectral& sp, Field& spvor, Field& spdiv, const eckit::Configuration&) const { assertCompatibleDistributions(gp, sp); // Count total number of fields and do sanity checks const size_t nfld = compute_nfld(spvor); if (spdiv.shape(0) != spvor.shape(0)) { throw_Exception("dirtrans: vorticity not compatible with divergence for 1st dimension.", Here()); } if (spdiv.shape(1) != spvor.shape(1)) { throw_Exception("dirtrans: vorticity not compatible with divergence for 2nd dimension.", Here()); } const size_t nwindfld = compute_nfld(gpwind); 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; msg << "dirtrans: Spectral vorticity and divergence have wrong dimension: " "nspec2 " << spdiv.shape(0) << " should be " << nspec2(); throw_Exception(msg.str(), Here()); } if (spvor.size() == 0) { throw_Exception("dirtrans: spectral vorticity field is empty."); } if (spdiv.size() == 0) { throw_Exception("dirtrans: spectral divergence field is empty."); } // Arrays Trans expects std::vector rgp(2 * nfld * ngptot()); std::vector rspvor(nspec2() * nfld); std::vector rspdiv(nspec2() * nfld); auto rgpview = LocalView(rgp.data(), make_shape(2 * nfld, ngptot())); auto rspvorview = LocalView(rspvor.data(), make_shape(nspec2(), nfld)); auto rspdivview = LocalView(rspdiv.data(), make_shape(nspec2(), nfld)); // Pack gridpoints { PackNodeColumns pack(rgpview, gp); int wind_components = 2; pack(gpwind, wind_components); } // Do transform { struct ::DirTrans_t transform = ::new_dirtrans(trans_.get()); transform.nvordiv = int(nfld); transform.rgp = rgp.data(); transform.rspvor = rspvor.data(); transform.rspdiv = rspdiv.data(); ATLAS_ASSERT(transform.rspvor); ATLAS_ASSERT(transform.rspdiv); TRANS_CHECK(::trans_dirtrans(&transform)); } // Pack spectral fields UnpackSpectral unpack_vor(rspvorview); unpack_vor(spvor); UnpackSpectral unpack_div(rspdivview); unpack_div(spdiv); } // -------------------------------------------------------------------------------------------- void TransIFS::__dirtrans_adj(const Spectral& sp, const FieldSet& spfields, const StructuredColumns& gp, FieldSet& gpfields, const eckit::Configuration&) const { #if ATLAS_HAVE_ECTRANS assertCompatibleDistributions(gp, sp); // Count total number of fields and do sanity checks const idx_t nfld = compute_nfld(gpfields); for (idx_t jfld = 0; jfld < gpfields.size(); ++jfld) { const Field& f = gpfields[jfld]; ATLAS_ASSERT(f.functionspace() == 0 || functionspace::StructuredColumns(f.functionspace())); } const int trans_sp_nfld = compute_nfld(spfields); if (nfld != trans_sp_nfld) { throw_Exception("dirtrans_adj: different number of gridpoint fields than spectral fields", Here()); } // Arrays Trans expects std::vector rgp(nfld * ngptot()); std::vector rsp(nspec2() * nfld); auto rgpview = LocalView(rgp.data(), make_shape(nfld, ngptot())); auto rspview = LocalView(rsp.data(), make_shape(nspec2(), nfld)); // Pack spectral fields { PackSpectral pack(rspview); for (idx_t jfld = 0; jfld < spfields.size(); ++jfld) { pack(spfields[jfld]); } } // Do transform { struct ::DirTransAdj_t transform = ::new_dirtrans_adj(trans_.get()); transform.nscalar = int(nfld); transform.rgp = rgp.data(); transform.rspscalar = rsp.data(); TRANS_CHECK(::trans_dirtrans_adj(&transform)); } // Unpack the gridpoint fields { UnpackStructuredColumns unpack(rgpview); for (idx_t jfld = 0; jfld < gpfields.size(); ++jfld) { unpack(gp, gpfields[jfld]); } } #else ATLAS_NOTIMPLEMENTED; #endif } // -------------------------------------------------------------------------------------------- void TransIFS::__dirtrans_adj( const Spectral& sp, const Field& spfield, const NodeColumns& gp, Field& gpfield, const eckit::Configuration& ) const { #if ATLAS_HAVE_ECTRANS ATLAS_ASSERT(gpfield.functionspace() == 0 || functionspace::NodeColumns(gpfield.functionspace())); ATLAS_ASSERT(spfield.functionspace() == 0 || functionspace::Spectral(spfield.functionspace())); assertCompatibleDistributions(gp, sp); if (compute_nfld(gpfield) != compute_nfld(spfield)) { throw_Exception("dirtrans: different number of gridpoint fields than spectral fields", Here()); } if ((int)gpfield.shape(0) < ngptot()) { throw_Exception("dirtrans: slowest moving index must be >= ngptot", Here()); } const int nfld = compute_nfld(gpfield); // Arrays Trans expects std::vector rgp(nfld * ngptot()); std::vector rsp(nspec2() * nfld); auto rgpview = LocalView(rgp.data(), make_shape(nfld, ngptot())); auto rspview = LocalView(rsp.data(), make_shape(nspec2(), nfld)); // Pack spectral fields { PackSpectral pack(rspview); pack(spfield); } // Do transform { struct ::DirTransAdj_t transform = ::new_dirtrans_adj(trans_.get()); transform.nscalar = nfld; transform.rgp = rgp.data(); transform.rspscalar = rsp.data(); TRANS_CHECK(::trans_dirtrans_adj(&transform)); } // Unpack gridpoint fields { UnpackNodeColumns unpack(rgpview, gp); unpack(gpfield); } #else ATLAS_NOTIMPLEMENTED; #endif } // -------------------------------------------------------------------------------------------- void TransIFS::__dirtrans_adj( const Spectral& sp, const Field& spfield, const StructuredColumns& gp, Field& gpfield, const eckit::Configuration& ) const { #if ATLAS_HAVE_ECTRANS ATLAS_ASSERT(gpfield.functionspace() == 0 || functionspace::StructuredColumns(gpfield.functionspace())); ATLAS_ASSERT(spfield.functionspace() == 0 || functionspace::Spectral(spfield.functionspace())); assertCompatibleDistributions(gp, sp); if (compute_nfld(gpfield) != compute_nfld(spfield)) { throw_Exception("dirtrans: different number of gridpoint fields than spectral fields", Here()); } if ((int)gpfield.shape(0) < ngptot()) { throw_Exception("dirtrans: slowest moving index must be >= ngptot", Here()); } const int nfld = compute_nfld(gpfield); // Arrays Trans expects std::vector rgp(nfld * ngptot()); std::vector rsp(nspec2() * nfld); auto rgpview = LocalView(rgp.data(), make_shape(nfld, ngptot())); auto rspview = LocalView(rsp.data(), make_shape(nspec2(), nfld)); // Pack spectral fields { PackSpectral pack(rspview); pack(spfield); } // Do transform { struct ::DirTransAdj_t transform = ::new_dirtrans_adj(trans_.get()); transform.nscalar = nfld; transform.rgp = rgp.data(); transform.rspscalar = rsp.data(); TRANS_CHECK(::trans_dirtrans_adj(&transform)); } // Unpack gridpoint fields { UnpackStructuredColumns unpack(rgpview); unpack(gp, gpfield); } #else ATLAS_NOTIMPLEMENTED; #endif } // -------------------------------------------------------------------------------------------- void TransIFS::__dirtrans_adj(const Spectral& sp, const FieldSet& spfields, const NodeColumns& gp, FieldSet& gpfields, const eckit::Configuration&) const { #if ATLAS_HAVE_ECTRANS assertCompatibleDistributions(gp, sp); // Count total number of fields and do sanity checks const idx_t nfld = compute_nfld(gpfields); for (idx_t jfld = 0; jfld < gpfields.size(); ++jfld) { const Field& f = gpfields[jfld]; ATLAS_ASSERT(f.functionspace() == 0 || functionspace::NodeColumns(f.functionspace())); } const int trans_sp_nfld = compute_nfld(spfields); if (nfld != trans_sp_nfld) { throw_Exception("dirtrans_adj: different number of gridpoint fields than spectral fields", Here()); } // Arrays Trans expects std::vector rgp(nfld * ngptot()); std::vector rsp(nspec2() * nfld); auto rgpview = LocalView(rgp.data(), make_shape(nfld, ngptot())); auto rspview = LocalView(rsp.data(), make_shape(nspec2(), nfld)); // Pack spectral fields { PackSpectral pack(rspview); for (idx_t jfld = 0; jfld < spfields.size(); ++jfld) { pack(spfields[jfld]); } } // Do transform { struct ::DirTransAdj_t transform = ::new_dirtrans_adj(trans_.get()); transform.nscalar = int(nfld); transform.rgp = rgp.data(); transform.rspscalar = rsp.data(); TRANS_CHECK(::trans_dirtrans_adj(&transform)); } // Unpack the gridpoint fields { UnpackNodeColumns unpack(rgpview, gp); for (idx_t jfld = 0; jfld < gpfields.size(); ++jfld) { unpack(gpfields[jfld]); } } #else ATLAS_NOTIMPLEMENTED; #endif } // -------------------------------------------------------------------------------------------- void TransIFS::__dirtrans_wind2vordiv_adj(const Spectral& sp, const Field& spvor, const Field& spdiv, const functionspace::StructuredColumns& gp, Field& gpwind, const eckit::Configuration&) const { #if ATLAS_HAVE_ECTRANS assertCompatibleDistributions(gp, sp); // Count total number of fields and do sanity checks const size_t nfld = compute_nfld(spvor); if (spdiv.shape(0) != spvor.shape(0)) { throw_Exception("dirtrans_adj:vorticity not compatible with divergence for 1st dimension", Here()); } if (spdiv.shape(1) != spvor.shape(1)) { throw_Exception("dirtrans_adj:vorticity not compatible with divergence for 2st dimension", Here()); } const size_t nwindfld = compute_nfld(gpwind); if (nwindfld != 2 * nfld && nwindfld != 3 * nfld) { throw_Exception("dirtrans_adj:wind field is not compatible with vorticity, divergence.", Here()); } if (spdiv.shape(0) != nspec2()) { std::stringstream msg; msg << "dirtrans_adj: Spectral vorticity and divergence have wrong dimension: " "nspec2 " << spdiv.shape(0) << " should be " << nspec2(); throw_Exception(msg.str(), Here()); } if (spvor.size() == 0) { throw_Exception("dirtrans_adj: spectral vorticity field is empty."); } if (spdiv.size() == 0) { throw_Exception("dirtrans_adj: spectral divergence field is empty."); } // Arrays Trans expects std::vector rgp(2 * nfld * ngptot()); std::vector rspvor(nspec2() * nfld); std::vector rspdiv(nspec2() * nfld); auto rgpview = LocalView(rgp.data(), make_shape(2 * nfld, ngptot())); auto rspvorview = LocalView(rspvor.data(), make_shape(nspec2(), nfld)); auto rspdivview = LocalView(rspdiv.data(), make_shape(nspec2(), nfld)); // Pack spectral fields PackSpectral pack_vor(rspvorview); pack_vor(spvor); PackSpectral pack_div(rspdivview); pack_div(spdiv); // Do transform { struct ::DirTransAdj_t transform = ::new_dirtrans_adj(trans_.get()); transform.nvordiv = int(nfld); transform.rgp = rgp.data(); transform.rspvor = rspvor.data(); transform.rspdiv = rspdiv.data(); ATLAS_ASSERT(transform.rspvor); ATLAS_ASSERT(transform.rspdiv); TRANS_CHECK(::trans_dirtrans_adj(&transform)); } // Unpack the gridpoint fields { UnpackStructuredColumns unpack(rgpview); int wind_components = 2; unpack(gp, gpwind, wind_components); } #else ATLAS_NOTIMPLEMENTED; #endif } // -------------------------------------------------------------------------------------------- void TransIFS::__dirtrans_wind2vordiv_adj(const Spectral& sp, const Field& spvor, const Field& spdiv, const functionspace::NodeColumns& gp, Field& gpwind, const eckit::Configuration&) const { #if ATLAS_HAVE_ECTRANS assertCompatibleDistributions(gp, sp); // Count total number of fields and do sanity checks const size_t nfld = compute_nfld(spvor); if (spdiv.shape(0) != spvor.shape(0)) { throw_Exception("dirtrans_adj:vorticity not compatible with divergence for 1st dimension", Here()); } if (spdiv.shape(1) != spvor.shape(1)) { throw_Exception("dirtrans_adj:vorticity not compatible with divergence for 2st dimension", Here()); } const size_t nwindfld = compute_nfld(gpwind); if (nwindfld != 2 * nfld && nwindfld != 3 * nfld) { throw_Exception("dirtrans_adj:wind field is not compatible with vorticity, divergence.", Here()); } if (spdiv.shape(0) != nspec2()) { std::stringstream msg; msg << "dirtrans_adj: Spectral vorticity and divergence have wrong dimension: " "nspec2 " << spdiv.shape(0) << " should be " << nspec2(); throw_Exception(msg.str(), Here()); } if (spvor.size() == 0) { throw_Exception("dirtrans_adj: spectral vorticity field is empty."); } if (spdiv.size() == 0) { throw_Exception("dirtrans_adj: spectral divergence field is empty."); } // Arrays Trans expects std::vector rgp(2 * nfld * ngptot()); std::vector rspvor(nspec2() * nfld); std::vector rspdiv(nspec2() * nfld); auto rgpview = LocalView(rgp.data(), make_shape(2 * nfld, ngptot())); auto rspvorview = LocalView(rspvor.data(), make_shape(nspec2(), nfld)); auto rspdivview = LocalView(rspdiv.data(), make_shape(nspec2(), nfld)); // Pack spectral fields PackSpectral pack_vor(rspvorview); pack_vor(spvor); PackSpectral pack_div(rspdivview); pack_div(spdiv); // Do transform { struct ::DirTransAdj_t transform = ::new_dirtrans_adj(trans_.get()); transform.nvordiv = int(nfld); transform.rgp = rgp.data(); transform.rspvor = rspvor.data(); transform.rspdiv = rspdiv.data(); ATLAS_ASSERT(transform.rspvor); ATLAS_ASSERT(transform.rspdiv); TRANS_CHECK(::trans_dirtrans_adj(&transform)); } // Unpack the gridpoint fields { UnpackNodeColumns unpack(rgpview, gp); int wind_components = 2; unpack(gpwind, wind_components); } #else ATLAS_NOTIMPLEMENTED; #endif } // -------------------------------------------------------------------------------------------- void TransIFS::__invtrans(const functionspace::Spectral& sp, const Field& spfield, const functionspace::StructuredColumns& gp, Field& gpfield, const eckit::Configuration& config) const { assertCompatibleDistributions(gp, sp); ATLAS_ASSERT(gpfield.functionspace() == 0 || functionspace::StructuredColumns(gpfield.functionspace())); ATLAS_ASSERT(spfield.functionspace() == 0 || functionspace::Spectral(spfield.functionspace())); if (compute_nfld(gpfield) != compute_nfld(spfield)) { throw_Exception("invtrans: different number of gridpoint fields than spectral fields", Here()); } if ((int)gpfield.shape(0) < ngptot()) { throw_Exception("invtrans: slowest moving index must be >= ngptot", Here()); } const int nfld = compute_nfld(gpfield); // Arrays Trans expects std::vector rgp(nfld * ngptot()); std::vector rsp(nspec2() * nfld); auto rgpview = LocalView(rgp.data(), make_shape(nfld, ngptot())); auto rspview = LocalView(rsp.data(), make_shape(nspec2(), nfld)); // Pack spectral fields { PackSpectral pack(rspview); pack(spfield); } // Do transform { struct ::InvTrans_t transform = ::new_invtrans(trans_.get()); transform.nscalar = nfld; transform.rgp = rgp.data(); transform.rspscalar = rsp.data(); TRANS_CHECK(::trans_invtrans(&transform)); } // Unpack gridpoint fields { UnpackStructuredColumns unpack(rgpview); unpack(gp, gpfield); } } // -------------------------------------------------------------------------------------------- void TransIFS::__invtrans(const Spectral& sp, const Field& spfield, const functionspace::NodeColumns& gp, Field& gpfield, const eckit::Configuration& config) const { FieldSet spfields; spfields.add(spfield); FieldSet gpfields; gpfields.add(gpfield); __invtrans(sp, spfields, gp, gpfields, config); } // -------------------------------------------------------------------------------------------- void TransIFS::__invtrans(const functionspace::Spectral& sp, const FieldSet& spfields, const functionspace::StructuredColumns& gp, FieldSet& gpfields, const eckit::Configuration& config) const { assertCompatibleDistributions(gp, sp); // Count total number of fields and do sanity checks const int nfld = compute_nfld(gpfields); for (idx_t jfld = 0; jfld < gpfields.size(); ++jfld) { const Field& f = gpfields[jfld]; ATLAS_ASSERT(f.functionspace() == 0 || functionspace::StructuredColumns(f.functionspace())); } const int nb_spectral_fields = compute_nfld(spfields); if (nfld != nb_spectral_fields) { std::stringstream msg; msg << "invtrans: different number of gridpoint fields than spectral fields" << "[ " << nfld << " != " << nb_spectral_fields << " ]"; throw_Exception(msg.str(), Here()); } // Arrays Trans expects std::vector rgp(nfld * ngptot()); std::vector rsp(nspec2() * nfld); auto rgpview = LocalView(rgp.data(), make_shape(nfld, ngptot())); auto rspview = LocalView(rsp.data(), make_shape(nspec2(), nfld)); // Pack spectral fields { PackSpectral pack(rspview); for (idx_t jfld = 0; jfld < spfields.size(); ++jfld) { pack(spfields[jfld]); } } // Do transform { struct ::InvTrans_t transform = ::new_invtrans(trans_.get()); transform.nscalar = nfld; transform.rgp = rgp.data(); transform.rspscalar = rsp.data(); TRANS_CHECK(::trans_invtrans(&transform)); } // Unpack the gridpoint fields { UnpackStructuredColumns unpack(rgpview); for (idx_t jfld = 0; jfld < gpfields.size(); ++jfld) { unpack(gp, gpfields[jfld]); } } } // -------------------------------------------------------------------------------------------- void TransIFS::__invtrans(const Spectral& sp, const FieldSet& spfields, const functionspace::NodeColumns& gp, FieldSet& gpfields, const eckit::Configuration& config) const { assertCompatibleDistributions(gp, sp); // Count total number of fields and do sanity checks const int nfld = compute_nfld(gpfields); const int nb_spectral_fields = compute_nfld(spfields); 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()); std::vector rsp(nspec2() * nfld); auto rgpview = LocalView(rgp.data(), make_shape(nfld, ngptot())); auto rspview = LocalView(rsp.data(), make_shape(nspec2(), nfld)); // Pack spectral fields { PackSpectral pack(rspview); for (idx_t jfld = 0; jfld < spfields.size(); ++jfld) { pack(spfields[jfld]); } } // Do transform { struct ::InvTrans_t transform = ::new_invtrans(trans_.get()); transform.nscalar = nfld; transform.rgp = rgp.data(); transform.rspscalar = rsp.data(); TRANS_CHECK(::trans_invtrans(&transform)); } // Unpack the gridpoint fields { UnpackNodeColumns unpack(rgpview, gp); for (idx_t jfld = 0; jfld < gpfields.size(); ++jfld) { unpack(gpfields[jfld]); } } } // -------------------------------------------------------------------------------------------- void TransIFS::__invtrans_grad(const Spectral& sp, const Field& spfield, const functionspace::StructuredColumns& gp, Field& gradfield, const eckit::Configuration& config) const { FieldSet spfields; spfields.add(spfield); FieldSet gradfields; gradfields.add(gradfield); __invtrans_grad(sp, spfields, gp, gradfields, config); } // -------------------------------------------------------------------------------------------- void TransIFS::__invtrans_grad(const Spectral& sp, const Field& spfield, const functionspace::NodeColumns& gp, Field& gradfield, const eckit::Configuration& config) const { FieldSet spfields; spfields.add(spfield); FieldSet gradfields; gradfields.add(gradfield); __invtrans_grad(sp, spfields, gp, gradfields, config); } // -------------------------------------------------------------------------------------------- void TransIFS::__invtrans_grad(const Spectral& sp, const FieldSet& spfields, const functionspace::StructuredColumns& gp, FieldSet& gradfields, const eckit::Configuration& config) const { assertCompatibleDistributions(gp, sp); // Count total number of fields and do sanity checks 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 throw_Exception( "invtrans_grad: different number of gridpoint " "fields than spectral fields", Here()); } // Arrays Trans expects // Allocate space for std::vector rgp(3 * nfld * ngptot()); // (scalars) + (NS ders) + (EW ders) std::vector rsp(nspec2() * nfld); auto rgpview = LocalView(rgp.data(), make_shape(3 * nfld, ngptot())); auto rspview = LocalView(rsp.data(), make_shape(nspec2(), nfld)); // Pack spectral fields { PackSpectral pack(rspview); for (idx_t jfld = 0; jfld < spfields.size(); ++jfld) { pack(spfields[jfld]); } } // Do transform { struct ::InvTrans_t transform = ::new_invtrans(trans_.get()); transform.nscalar = nfld; transform.rgp = rgp.data(); transform.rspscalar = rsp.data(); transform.lscalarders = true; TRANS_CHECK(::trans_invtrans(&transform)); } // Unpack the gridpoint fields { int f = nfld; // skip to where derivatives start for (idx_t dim = 0; dim < 2; ++dim) { for (idx_t jfld = 0; jfld < gradfields.size(); ++jfld) { const idx_t nb_nodes = StructuredColumns(gradfields[jfld].functionspace()).sizeOwned(); const idx_t nlev = gradfields[jfld].levels(); if (nlev) { auto field = make_view(gradfields[jfld]); for (idx_t jlev = 0; jlev < nlev; ++jlev) { for (idx_t jnode = 0; jnode < nb_nodes; ++jnode) { field(jnode, jlev, 1 - dim) = rgpview(f, jnode); } } } else { auto field = make_view(gradfields[jfld]); for (idx_t jnode = 0; jnode < nb_nodes; ++jnode) { field(jnode, 1 - dim) = rgpview(f, jnode); } } ++f; } } } } // -------------------------------------------------------------------------------------------- void TransIFS::__invtrans_grad(const Spectral& sp, const FieldSet& spfields, const functionspace::NodeColumns& gp, FieldSet& gradfields, const eckit::Configuration& config) const { assertCompatibleDistributions(gp, sp); // Count total number of fields and do sanity checks 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 throw_Exception( "invtrans_grad: different number of gridpoint " "fields than spectral fields", Here()); } // Arrays Trans expects // Allocate space for std::vector rgp(3 * nfld * ngptot()); // (scalars) + (NS ders) + (EW ders) std::vector rsp(nspec2() * nfld); auto rgpview = LocalView(rgp.data(), make_shape(3 * nfld, ngptot())); auto rspview = LocalView(rsp.data(), make_shape(nspec2(), nfld)); // Pack spectral fields { PackSpectral pack(rspview); for (idx_t jfld = 0; jfld < spfields.size(); ++jfld) { pack(spfields[jfld]); } } // Do transform { struct ::InvTrans_t transform = ::new_invtrans(trans_.get()); transform.nscalar = nfld; transform.rgp = rgp.data(); transform.rspscalar = rsp.data(); transform.lscalarders = true; TRANS_CHECK(::trans_invtrans(&transform)); } // Unpack the gridpoint fields { mesh::IsGhostNode is_ghost(gp.nodes()); int f = nfld; // skip to where derivatives start for (idx_t dim = 0; dim < 2; ++dim) { for (idx_t jfld = 0; jfld < gradfields.size(); ++jfld) { const idx_t nb_nodes = gradfields[jfld].shape(0); const idx_t nlev = gradfields[jfld].levels(); if (nlev) { auto field = make_view(gradfields[jfld]); for (idx_t jlev = 0; jlev < nlev; ++jlev) { int n = 0; for (idx_t jnode = 0; jnode < nb_nodes; ++jnode) { if (!is_ghost(jnode)) { field(jnode, jlev, 1 - dim) = rgpview(f, n); ++n; } } ATLAS_ASSERT(n == ngptot()); } } else { auto field = make_view(gradfields[jfld]); int n = 0; for (idx_t jnode = 0; jnode < nb_nodes; ++jnode) { if (!is_ghost(jnode)) { field(jnode, 1 - dim) = rgpview(f, n); ++n; } } ATLAS_ASSERT(n == ngptot()); } ++f; } } } } // -------------------------------------------------------------------------------------------- void TransIFS::__invtrans_vordiv2wind(const Spectral& sp, const Field& spvor, const Field& spdiv, const functionspace::StructuredColumns& gp, Field& gpwind, const eckit::Configuration&) const { assertCompatibleDistributions(gp, sp); // Count total number of fields and do sanity checks const int nfld = compute_nfld(spvor); if (spdiv.shape(0) != spvor.shape(0)) { throw_Exception("invtrans: vorticity not compatible with divergence.", Here()); } 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) { throw_Exception("invtrans: wind field is not compatible with vorticity, divergence.", Here()); } if (spdiv.shape(0) != nspec2()) { std::stringstream msg; msg << "invtrans: Spectral vorticity and divergence have wrong dimension: " "nspec2 " << spdiv.shape(0) << " should be " << nspec2(); throw_Exception(msg.str(), Here()); } ATLAS_ASSERT(spvor.rank() == 2); ATLAS_ASSERT(spdiv.rank() == 2); if (spvor.size() == 0) { throw_Exception("invtrans: spectral vorticity field is empty."); } if (spdiv.size() == 0) { throw_Exception("invtrans: spectral divergence field is empty."); } // Arrays Trans expects std::vector rgp(2 * nfld * ngptot()); std::vector rspvor(nspec2() * nfld); std::vector rspdiv(nspec2() * nfld); auto rgpview = LocalView(rgp.data(), make_shape(2 * nfld, ngptot())); auto rspvorview = LocalView(rspvor.data(), make_shape(nspec2(), nfld)); auto rspdivview = LocalView(rspdiv.data(), make_shape(nspec2(), nfld)); // Pack spectral fields PackSpectral pack_vor(rspvorview); pack_vor(spvor); PackSpectral pack_div(rspdivview); pack_div(spdiv); // Do transform { struct ::InvTrans_t transform = ::new_invtrans(trans_.get()); transform.nvordiv = nfld; transform.rgp = rgp.data(); transform.rspvor = rspvor.data(); transform.rspdiv = rspdiv.data(); ATLAS_ASSERT(transform.rspvor); ATLAS_ASSERT(transform.rspdiv); TRANS_CHECK(::trans_invtrans(&transform)); } // Unpack the gridpoint fields { UnpackStructuredColumns unpack(rgpview); int wind_components = 2; unpack(gp, gpwind, wind_components); } } //--------------------------------------------------------------------------------------------- void TransIFS::__invtrans_vordiv2wind(const Spectral& sp, const Field& spvor, const Field& spdiv, const functionspace::NodeColumns& gp, Field& gpwind, const eckit::Configuration&) const { assertCompatibleDistributions(gp, sp); // Count total number of fields and do sanity checks const int nfld = compute_nfld(spvor); if (spdiv.shape(0) != spvor.shape(0)) { throw_Exception("invtrans: vorticity not compatible with divergence.", Here()); } 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) { throw_Exception("invtrans: wind field is not compatible with vorticity, divergence.", Here()); } if (spdiv.shape(0) != nspec2()) { std::stringstream msg; msg << "invtrans: Spectral vorticity and divergence have wrong dimension: " "nspec2 " << spdiv.shape(0) << " should be " << nspec2(); throw_Exception(msg.str(), Here()); } ATLAS_ASSERT(spvor.rank() == 2); ATLAS_ASSERT(spdiv.rank() == 2); if (spvor.size() == 0) { throw_Exception("invtrans: spectral vorticity field is empty."); } if (spdiv.size() == 0) { throw_Exception("invtrans: spectral divergence field is empty."); } // Arrays Trans expects std::vector rgp(2 * nfld * ngptot()); std::vector rspvor(nspec2() * nfld); std::vector rspdiv(nspec2() * nfld); auto rgpview = LocalView(rgp.data(), make_shape(2 * nfld, ngptot())); auto rspvorview = LocalView(rspvor.data(), make_shape(nspec2(), nfld)); auto rspdivview = LocalView(rspdiv.data(), make_shape(nspec2(), nfld)); // Pack spectral fields PackSpectral pack_vor(rspvorview); pack_vor(spvor); PackSpectral pack_div(rspdivview); pack_div(spdiv); // Do transform { struct ::InvTrans_t transform = ::new_invtrans(trans_.get()); transform.nvordiv = nfld; transform.rgp = rgp.data(); transform.rspvor = rspvor.data(); transform.rspdiv = rspdiv.data(); ATLAS_ASSERT(transform.rspvor); ATLAS_ASSERT(transform.rspdiv); TRANS_CHECK(::trans_invtrans(&transform)); } // Unpack the gridpoint fields { UnpackNodeColumns unpack(rgpview, gp); int wind_components = 2; unpack(gpwind, wind_components); } } // -------------------------------------------------------------------------------------------- void TransIFS::__invtrans_adj(const functionspace::Spectral& sp, Field& spfield, const functionspace::StructuredColumns& gp, const Field& gpfield, const eckit::Configuration& config) const { #if ATLAS_HAVE_ECTRANS || defined(TRANS_HAVE_INVTRANS_ADJ) ATLAS_ASSERT(gpfield.functionspace() == 0 || functionspace::StructuredColumns(gpfield.functionspace())); ATLAS_ASSERT(spfield.functionspace() == 0 || functionspace::Spectral(spfield.functionspace())); assertCompatibleDistributions(gp, sp); if (compute_nfld(gpfield) != compute_nfld(spfield)) { throw_Exception("dirtrans: different number of gridpoint fields than spectral fields", Here()); } if ((int)gpfield.shape(0) < ngptot()) { throw_Exception("dirtrans: slowest moving index must be >= ngptot", Here()); } const int nfld = compute_nfld(gpfield); // Arrays Trans expects std::vector rgp(nfld * ngptot()); std::vector rsp(nspec2() * nfld); auto rgpview = LocalView(rgp.data(), make_shape(nfld, ngptot())); auto rspview = LocalView(rsp.data(), make_shape(nspec2(), nfld)); // Pack gridpoints { PackStructuredColumns pack(rgpview); pack(gp, gpfield); } // Do transform { struct ::InvTransAdj_t transform = ::new_invtrans_adj(trans_.get()); transform.nscalar = nfld; transform.rgp = rgp.data(); transform.rspscalar = rsp.data(); TRANS_CHECK(::trans_invtrans_adj(&transform)); } // Unpack spectral { UnpackSpectral unpack(rspview); unpack(spfield); } #else ATLAS_NOTIMPLEMENTED; #endif } // -------------------------------------------------------------------------------------------- void TransIFS::__invtrans_adj(const Spectral& sp, Field& spfield, const functionspace::NodeColumns& gp, const Field& gpfield, const eckit::Configuration& config) const { FieldSet spfields; spfields.add(spfield); FieldSet gpfields; gpfields.add(gpfield); __invtrans_adj(sp, spfields, gp, gpfields, config); } // -------------------------------------------------------------------------------------------- void TransIFS::__invtrans_adj(const functionspace::Spectral& sp, FieldSet& spfields, const functionspace::StructuredColumns& gp, const FieldSet& gpfields, const eckit::Configuration& config) const { #if ATLAS_HAVE_ECTRANS || defined(TRANS_HAVE_INVTRANS_ADJ) assertCompatibleDistributions(gp, sp); // Count total number of fields and do sanity checks const idx_t nfld = compute_nfld(gpfields); for (idx_t jfld = 0; jfld < gpfields.size(); ++jfld) { const Field& f = gpfields[jfld]; ATLAS_ASSERT(f.functionspace() == 0 || functionspace::StructuredColumns(f.functionspace())); } const int trans_sp_nfld = compute_nfld(spfields); if (nfld != trans_sp_nfld) { throw_Exception("invtrans_adj: different number of gridpoint fields than spectral fields", Here()); } // Arrays Trans expects std::vector rgp(nfld * ngptot()); std::vector rsp(nspec2() * nfld); auto rgpview = LocalView(rgp.data(), make_shape(nfld, ngptot())); auto rspview = LocalView(rsp.data(), make_shape(nspec2(), nfld)); // Pack gridpoints { PackStructuredColumns pack(rgpview); for (idx_t jfld = 0; jfld < gpfields.size(); ++jfld) { pack(gp, gpfields[jfld]); } } // Do transform { struct ::InvTransAdj_t transform = ::new_invtrans_adj(trans_.get()); transform.nscalar = int(nfld); transform.rgp = rgp.data(); transform.rspscalar = rsp.data(); TRANS_CHECK(::trans_invtrans_adj(&transform)); } // Unpack the spectral fields { UnpackSpectral unpack(rspview); for (idx_t jfld = 0; jfld < spfields.size(); ++jfld) { unpack(spfields[jfld]); } } #else ATLAS_NOTIMPLEMENTED; #endif } // -------------------------------------------------------------------------------------------- void TransIFS::__invtrans_adj(const Spectral& sp, FieldSet& spfields, const functionspace::NodeColumns& gp, const FieldSet& gpfields, const eckit::Configuration& config) const { #if ATLAS_HAVE_ECTRANS || defined(TRANS_HAVE_INVTRANS_ADJ) assertCompatibleDistributions(gp, sp); // Count total number of fields and do sanity checks const idx_t nfld = compute_nfld(gpfields); for (idx_t jfld = 0; jfld < gpfields.size(); ++jfld) { const Field& f = gpfields[jfld]; ATLAS_ASSERT(f.functionspace() == 0 || functionspace::StructuredColumns(f.functionspace())); } const int trans_sp_nfld = compute_nfld(spfields); if (nfld != trans_sp_nfld) { throw_Exception("invtrans_adj: different number of gridpoint fields than spectral fields", Here()); } // Arrays Trans expects std::vector rgp(nfld * ngptot()); std::vector rsp(nspec2() * nfld); auto rgpview = LocalView(rgp.data(), make_shape(nfld, ngptot())); auto rspview = LocalView(rsp.data(), make_shape(nspec2(), nfld)); // Pack gridpoints { PackNodeColumns pack(rgpview, gp); for (idx_t jfld = 0; jfld < gpfields.size(); ++jfld) { pack(gpfields[jfld]); } } // Do transform { struct ::InvTransAdj_t transform = ::new_invtrans_adj(trans_.get()); transform.nscalar = int(nfld); transform.rgp = rgp.data(); transform.rspscalar = rsp.data(); TRANS_CHECK(::trans_invtrans_adj(&transform)); } // Unpack the spectral fields { UnpackSpectral unpack(rspview); for (idx_t jfld = 0; jfld < spfields.size(); ++jfld) { unpack(spfields[jfld]); } } #else ATLAS_NOTIMPLEMENTED; #endif } //--------------------------------------------------------------------------------------------- void TransIFS::__invtrans_grad_adj(const Spectral& sp, Field& spfield, const functionspace::StructuredColumns& gp, const Field& gradfield, const eckit::Configuration& config) const { FieldSet spfields; spfields.add(spfield); FieldSet gradfields; gradfields.add(gradfield); __invtrans_grad_adj(sp, spfields, gp, gradfields, config); } //--------------------------------------------------------------------------------------------- void TransIFS::__invtrans_grad_adj(const Spectral& sp, Field& spfield, const functionspace::NodeColumns& gp, const Field& gradfield, const eckit::Configuration& config) const { FieldSet spfields; spfields.add(spfield); FieldSet gradfields; gradfields.add(gradfield); __invtrans_grad_adj(sp, spfields, gp, gradfields, config); } //--------------------------------------------------------------------------------------------- void TransIFS::__invtrans_grad_adj(const Spectral& sp, FieldSet& spfields, const functionspace::StructuredColumns& gp, const FieldSet& gradfields, const eckit::Configuration& config) const { #if ATLAS_HAVE_ECTRANS || defined(TRANS_HAVE_INVTRANS_ADJ) assertCompatibleDistributions(gp, sp); // Count total number of fields and do sanity checks const idx_t nfld = compute_nfld(gradfields); for (idx_t jfld = 0; jfld < gradfields.size(); ++jfld) { const Field& f = gradfields[jfld]; ATLAS_ASSERT(f.functionspace() == 0 || functionspace::StructuredColumns(f.functionspace())); } const int trans_sp_nfld = compute_nfld(spfields); if (nfld != trans_sp_nfld) { throw_Exception("__invtrans_grad_adj: different number of gridpoint fields than spectral fields", Here()); } // Arrays Trans expects std::vector rgp(nfld * ngptot()); std::vector rsp(nspec2() * nfld); auto rgpview = LocalView(rgp.data(), make_shape(nfld, ngptot())); auto rspview = LocalView(rsp.data(), make_shape(nspec2(), nfld)); // Pack gridpoints { PackStructuredColumns pack(rgpview); for (idx_t jfld = 0; jfld < gradfields.size(); ++jfld) { pack(gp, gradfields[jfld]); } } // Do transform { struct ::InvTransAdj_t transform = ::new_invtrans_adj(trans_.get()); transform.nscalar = nfld; transform.rgp = rgp.data(); transform.rspscalar = rsp.data(); transform.lscalarders = true; TRANS_CHECK(::trans_invtrans_adj(&transform)); } // Unpack the spectral fields { UnpackSpectral unpack(rspview); for (idx_t jfld = 0; jfld < spfields.size(); ++jfld) { unpack(spfields[jfld]); } } #else ATLAS_NOTIMPLEMENTED; #endif } //--------------------------------------------------------------------------------------------- void TransIFS::__invtrans_grad_adj(const Spectral& sp, FieldSet& spfields, const functionspace::NodeColumns& gp, const FieldSet& gradfields, const eckit::Configuration& config) const { #if ATLAS_HAVE_ECTRANS || defined(TRANS_HAVE_INVTRANS_ADJ) assertCompatibleDistributions(gp, sp); // Count total number of fields and do sanity checks const idx_t nfld = compute_nfld(gradfields); for (idx_t jfld = 0; jfld < gradfields.size(); ++jfld) { const Field& f = gradfields[jfld]; ATLAS_ASSERT(f.functionspace() == 0 || functionspace::StructuredColumns(f.functionspace())); } const int trans_sp_nfld = compute_nfld(spfields); if (nfld != trans_sp_nfld) { throw_Exception("dirtrans: different number of gridpoint fields than spectral fields", Here()); } // Arrays Trans expects std::vector rgp(nfld * ngptot()); std::vector rsp(nspec2() * nfld); auto rgpview = LocalView(rgp.data(), make_shape(nfld, ngptot())); auto rspview = LocalView(rsp.data(), make_shape(nspec2(), nfld)); // Pack gridpoints { PackNodeColumns pack(rgpview, gp); for (idx_t jfld = 0; jfld < gradfields.size(); ++jfld) { pack(gradfields[jfld]); } } // Do transform { struct ::InvTransAdj_t transform = ::new_invtrans_adj(trans_.get()); transform.nscalar = nfld; transform.rgp = rgp.data(); transform.rspscalar = rsp.data(); transform.lscalarders = true; TRANS_CHECK(::trans_invtrans_adj(&transform)); } // Unpack the spectral fields { UnpackSpectral unpack(rspview); for (idx_t jfld = 0; jfld < spfields.size(); ++jfld) { unpack(spfields[jfld]); } } #else ATLAS_NOTIMPLEMENTED; #endif } // -------------------------------------------------------------------------------------------- void TransIFS::__invtrans_vordiv2wind_adj(const Spectral& sp, Field& spvor, Field& spdiv, const functionspace::StructuredColumns& gp, const Field& gpwind, const eckit::Configuration&) const { #if ATLAS_HAVE_ECTRANS || defined(TRANS_HAVE_INVTRANS_ADJ) assertCompatibleDistributions(gp, sp); // Count total number of fields and do sanity checks const size_t nfld = compute_nfld(spvor); if (spdiv.shape(0) != spvor.shape(0)) { throw_Exception("invtrans_vordiv2wind_adj: vorticity not compatible with divergence.", Here()); } if (spdiv.shape(1) != spvor.shape(1)) { throw_Exception("invtrans_vordiv2wind_adj: vorticity not compatible with divergence.", Here()); } const size_t nwindfld = compute_nfld(gpwind); 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; msg << "invtrans_vordiv2wind_adj: Spectral vorticity and divergence have wrong dimension: " "nspec2 " << spdiv.shape(0) << " should be " << nspec2(); throw_Exception(msg.str(), Here()); } if (spvor.size() == 0) { throw_Exception("invtrans_vordiv2wind_adj: spectral vorticity field is empty."); } if (spdiv.size() == 0) { throw_Exception("invtrans_vordiv2wind_adj: spectral divergence field is empty."); } // Arrays Trans expects std::vector rgp(2 * nfld * ngptot()); std::vector rspvor(nspec2() * nfld); std::vector rspdiv(nspec2() * nfld); auto rgpview = LocalView(rgp.data(), make_shape(2 * nfld, ngptot())); auto rspvorview = LocalView(rspvor.data(), make_shape(nspec2(), nfld)); auto rspdivview = LocalView(rspdiv.data(), make_shape(nspec2(), nfld)); // Pack gridpoints { PackStructuredColumns pack(rgpview); int wind_components = 2; pack(gp, gpwind, wind_components); } // Do transform { struct ::InvTransAdj_t transform = ::new_invtrans_adj(trans_.get()); transform.nvordiv = int(nfld); transform.rgp = rgp.data(); transform.rspvor = rspvor.data(); transform.rspdiv = rspdiv.data(); ATLAS_ASSERT(transform.rspvor); ATLAS_ASSERT(transform.rspdiv); TRANS_CHECK(::trans_invtrans_adj(&transform)); } // Pack spectral fields UnpackSpectral unpack_vor(rspvorview); unpack_vor(spvor); UnpackSpectral unpack_div(rspdivview); unpack_div(spdiv); #else ATLAS_NOTIMPLEMENTED; #endif } void TransIFS::__invtrans_vordiv2wind_adj(const Spectral& sp, Field& spvor, Field& spdiv, const functionspace::NodeColumns& gp, const Field& gpwind, const eckit::Configuration&) const { #if ATLAS_HAVE_ECTRANS || defined(TRANS_HAVE_INVTRANS_ADJ) assertCompatibleDistributions(gp, sp); // Count total number of fields and do sanity checks const size_t nfld = compute_nfld(spvor); if (spdiv.shape(0) != spvor.shape(0)) { throw_Exception("invtrans_vordiv2wind_adj: vorticity not compatible with divergence.", Here()); } if (spdiv.shape(1) != spvor.shape(1)) { throw_Exception("invtrans_vordiv2wind_adj: vorticity not compatible with divergence.", Here()); } const size_t nwindfld = compute_nfld(gpwind); 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; msg << "invtrans_vordiv2wind_adj: Spectral vorticity and divergence have wrong dimension: " "nspec2 " << spdiv.shape(0) << " should be " << nspec2(); throw_Exception(msg.str(), Here()); } if (spvor.size() == 0) { throw_Exception("invtrans_vordiv2wind_adj: spectral vorticity field is empty."); } if (spdiv.size() == 0) { throw_Exception("invtrans_vordiv2wind_adj: spectral divergence field is empty."); } // Arrays Trans expects std::vector rgp(2 * nfld * ngptot()); std::vector rspvor(nspec2() * nfld); std::vector rspdiv(nspec2() * nfld); auto rgpview = LocalView(rgp.data(), make_shape(2 * nfld, ngptot())); auto rspvorview = LocalView(rspvor.data(), make_shape(nspec2(), nfld)); auto rspdivview = LocalView(rspdiv.data(), make_shape(nspec2(), nfld)); // Pack gridpoints { PackNodeColumns pack(rgpview, gp); int wind_components = 2; pack(gpwind, wind_components); } // Do transform { struct ::InvTransAdj_t transform = ::new_invtrans_adj(trans_.get()); transform.nvordiv = int(nfld); transform.rgp = rgp.data(); transform.rspvor = rspvor.data(); transform.rspdiv = rspdiv.data(); ATLAS_ASSERT(transform.rspvor); ATLAS_ASSERT(transform.rspdiv); TRANS_CHECK(::trans_invtrans_adj(&transform)); } // Pack spectral fields UnpackSpectral unpack_vor(rspvorview); unpack_vor(spvor); UnpackSpectral unpack_div(rspdivview); unpack_div(spdiv); #else ATLAS_NOTIMPLEMENTED; #endif } /////////////////////////////////////////////////////////////////////////////// void TransIFS::distspec(const int nb_fields, const int origin[], const double global_spectra[], double spectra[]) const { struct ::DistSpec_t args = new_distspec(trans_.get()); args.nfld = nb_fields; args.rspecg = global_spectra; args.nfrom = origin; args.rspec = spectra; TRANS_CHECK(::trans_distspec(&args)); } ///////////////////////////////////////////////////////////////////////////// void TransIFS::gathspec(const int nb_fields, const int destination[], const double spectra[], double global_spectra[]) const { struct ::GathSpec_t args = new_gathspec(trans_.get()); args.nfld = nb_fields; args.rspecg = global_spectra; args.nto = destination; args.rspec = spectra; TRANS_CHECK(::trans_gathspec(&args)); } ///////////////////////////////////////////////////////////////////////////// void TransIFS::distgrid(const int nb_fields, const int origin[], const double global_fields[], double fields[]) const { struct ::DistGrid_t args = new_distgrid(trans_.get()); args.nfld = nb_fields; args.nfrom = origin; args.rgpg = global_fields; args.rgp = fields; TRANS_CHECK(::trans_distgrid(&args)); } ///////////////////////////////////////////////////////////////////////////// void TransIFS::gathgrid(const int nb_fields, const int destination[], const double fields[], double global_fields[]) const { struct ::GathGrid_t args = new_gathgrid(trans_.get()); args.nfld = nb_fields; args.nto = destination; args.rgp = fields; args.rgpg = global_fields; TRANS_CHECK(::trans_gathgrid(&args)); } /////////////////////////////////////////////////////////////////////////////// void TransIFS::specnorm(const int nb_fields, const double spectra[], double norms[], int rank) const { struct ::SpecNorm_t args = new_specnorm(trans_.get()); args.nfld = nb_fields; args.rspec = spectra; args.rnorm = norms; args.nmaster = rank + 1; TRANS_CHECK(::trans_specnorm(&args)); } /////////////////////////////////////////////////////////////////////////////// } // namespace trans } // namespace atlas atlas-0.46.0/src/atlas/trans/ifs/VorDivToUVIFS.cc0000664000175000017500000000454115160212070021532 0ustar alastairalastair/* * (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/library/config.h" #if ATLAS_HAVE_ECTRANS #include "ectrans/transi.h" #else #include "transi/trans.h" #endif #include "atlas/functionspace/Spectral.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Log.h" #include "atlas/trans/ifs/VorDivToUVIFS.h" using atlas::FunctionSpace; using atlas::functionspace::Spectral; namespace atlas { namespace trans { namespace { static VorDivToUVBuilder builder_ifs("ifs"); // Deprecated static VorDivToUVBuilder builder_ectrans("ectrans"); } // namespace namespace { void trans_check(const int code, const char* msg, const eckit::CodeLocation& location) { if (code != TRANS_SUCCESS) { std::stringstream errmsg; errmsg << "atlas::trans ERROR: " << msg << " failed: \n"; errmsg << ::trans_error_msg(code); throw_Exception(errmsg.str(), location); } } #define TRANS_CHECK(CALL) trans_check(CALL, #CALL, Here()) } // namespace void VorDivToUVIFS::execute(const int nb_coeff, const int nb_fields, const double vorticity[], const double divergence[], double U[], double V[], const eckit::Configuration&) const { struct ::VorDivToUV_t vordiv_to_UV = new_vordiv_to_UV(); vordiv_to_UV.rspvor = vorticity; vordiv_to_UV.rspdiv = divergence; vordiv_to_UV.rspu = U; vordiv_to_UV.rspv = V; vordiv_to_UV.nfld = nb_fields; vordiv_to_UV.ncoeff = nb_coeff; vordiv_to_UV.nsmax = truncation_; TRANS_CHECK(::trans_vordiv_to_UV(&vordiv_to_UV)); } VorDivToUVIFS::VorDivToUVIFS(const int truncation, const eckit::Configuration&): truncation_(truncation) {} VorDivToUVIFS::VorDivToUVIFS(const FunctionSpace& fs, const eckit::Configuration&): truncation_(Spectral(fs).truncation()) {} VorDivToUVIFS::~VorDivToUVIFS() = default; } // namespace trans } // namespace atlas atlas-0.46.0/src/atlas/trans/ifs/TransIFS.h0000664000175000017500000005030615160212070020474 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include "eckit/filesystem/PathName.h" #include "atlas/array/LocalView.h" #include "atlas/functionspace/Spectral.h" #include "atlas/grid/Grid.h" #include "atlas/grid/StructuredGrid.h" #include "atlas/runtime/Exception.h" #include "atlas/trans/detail/TransImpl.h" //----------------------------------------------------------------------------- // Forward declarations struct Trans_t; // declared in "ectrans/transi.h" namespace atlas { class Field; class FieldSet; namespace field { class FieldImpl; class FieldSetImpl; } // namespace field } // namespace atlas namespace atlas { namespace array { class Array; } } // namespace atlas namespace atlas { namespace functionspace { class StructuredColumns; class NodeColumns; class Spectral; } // namespace functionspace } // namespace atlas namespace atlas { namespace functionspace { namespace detail { class NodeColumns; class Spectral; } // namespace detail } // namespace functionspace } // namespace atlas namespace atlas { namespace grid { namespace detail { namespace partitioner { class TransPartitioner; } } // namespace detail } // namespace grid } // namespace atlas //----------------------------------------------------------------------------- namespace atlas { namespace trans { //----------------------------------------------------------------------------- class TransIFS : public trans::TransImpl { private: typedef struct ::Trans_t Trans_t; public: TransIFS(const Grid&, const long truncation, const eckit::Configuration& = util::NoConfig()); TransIFS(const Grid&, const Domain&, const long truncation, const eckit::Configuration& = util::NoConfig()); TransIFS(const Cache&, const Grid&, const long truncation, const eckit::Configuration& = util::NoConfig()); TransIFS(const Cache&, const Grid&, const Domain&, const long truncation, const eckit::Configuration& = util::NoConfig()); virtual ~TransIFS() override; operator ::Trans_t*() const { return trans(); } ::Trans_t* trans() const { return trans_.get(); } virtual int truncation() const override; virtual size_t nb_spectral_coefficients() const override; virtual size_t nb_spectral_coefficients_global() const override; virtual const Grid& grid() const override { return grid_; } virtual const functionspace::Spectral& spectral() const override; // pure virtual interface virtual void dirtrans(const Field& gpfield, Field& spfield, const eckit::Configuration& = util::NoConfig()) const override; virtual void dirtrans(const FieldSet& gpfields, FieldSet& spfields, const eckit::Configuration& = util::NoConfig()) const override; virtual void dirtrans_wind2vordiv(const Field& gpwind, Field& spvor, Field& spdiv, const eckit::Configuration& = util::NoConfig()) const override; virtual void dirtrans_adj(const Field& spfield, Field& gpfield, const eckit::Configuration& = util::NoConfig()) const override; virtual void dirtrans_adj(const FieldSet& spfields, FieldSet& gpfields, const eckit::Configuration& = util::NoConfig()) const override; virtual void dirtrans_wind2vordiv_adj(const Field& spvor, const Field& spdiv, Field& gpwind, const eckit::Configuration& = util::NoConfig()) const override; virtual void invtrans(const Field& spfield, Field& gpfield, const eckit::Configuration& = util::NoConfig()) const override; virtual void invtrans(const FieldSet& spfields, FieldSet& gpfields, const eckit::Configuration& = util::NoConfig()) const override; virtual void invtrans_grad(const Field& spfield, Field& gradfield, const eckit::Configuration& = util::NoConfig()) const override; virtual void invtrans_grad(const FieldSet& spfields, FieldSet& gradfields, const eckit::Configuration& = util::NoConfig()) const override; virtual void invtrans_vordiv2wind(const Field& spvor, const Field& spdiv, Field& gpwind, const eckit::Configuration& = util::NoConfig()) const override; virtual void invtrans_adj(const Field& gpfield, Field& spfield, const eckit::Configuration& = util::NoConfig()) const override; virtual void invtrans_adj(const FieldSet& gpfields, FieldSet& spfields, const eckit::Configuration& = util::NoConfig()) const override; virtual void invtrans_grad_adj(const Field& gradfield, Field& spfield, const eckit::Configuration& = util::NoConfig()) const override; virtual void invtrans_grad_adj(const FieldSet& spfields, FieldSet& gradfields, const eckit::Configuration& = util::NoConfig()) const override; virtual void invtrans_vordiv2wind_adj(const Field& gpwind, Field& spvor, Field& spdiv, const eckit::Configuration& = util::NoConfig()) const override; // -- IFS style API -- // These fields have special interpretation required. You need to know what // you're doing. // See IFS trans library. /*! * @brief invtrans * @param nb_scalar_fields * @param scalar_spectra [NSPEC2][nb_scalar_fields] * @param nb_vordiv_fields * @param vorticity_spectra [NSPEC2][nb_vordiv_fields] * @param divergence_spectra [NSPEC2][nb_vordiv_fields] * @param gp_fields Ordering: [NGPBLKS][NFLD][NPROMA] if distributed, * [NFLD][NGPTOTG] if global ( add option::global() * ) */ virtual void invtrans(const int nb_scalar_fields, const double scalar_spectra[], const int nb_vordiv_fields, const double vorticity_spectra[], const double divergence_spectra[], double gp_fields[], const eckit::Configuration& = util::NoConfig()) const override; /*! * @brief invtrans * @param nb_fields * @param scalar_spectra * @param scalar_fields */ virtual void invtrans(const int nb_scalar_fields, const double scalar_spectra[], double gp_fields[], const eckit::Configuration& = util::NoConfig()) const override; /*! * @brief Inverse transform of vorticity/divergence to wind(U/V) * @param nb_fields [in] Number of fields ( both components of wind count as 1 ) */ virtual void invtrans(const int nb_vordiv_fields, const double vorticity_spectra[], const double divergence_spectra[], double gp_fields[], const eckit::Configuration& = util::NoConfig()) const override; /*! * @brief invtrans_adj * @param nb_scalar_fields * @param scalar_spectra [NSPEC2][nb_scalar_fields] * @param nb_vordiv_fields * @param vorticity_spectra [NSPEC2][nb_vordiv_fields] * @param divergence_spectra [NSPEC2][nb_vordiv_fields] * @param gp_fields Ordering: [NGPBLKS][NFLD][NPROMA] if distributed, * [NFLD][NGPTOTG] if global ( add option::global() * ) */ virtual void invtrans_adj(const int nb_scalar_fields, const double gp_fields[], const int nb_vordiv_fields, double vorticity_spectra[], double divergence_spectra[], double scalar_spectra[], const eckit::Configuration& = util::NoConfig()) const override; /*! * @brief invtrans_adj * @param nb_fields * @param scalar_spectra * @param scalar_fields */ virtual void invtrans_adj(const int nb_scalar_fields, const double gp_fields[], double scalar_spectra[], const eckit::Configuration& = util::NoConfig()) const override; /*! * @brief invtrans_adj * @param nb_scalar_fields * @param scalar_spectra [NSPEC2][nb_scalar_fields] * @param nb_vordiv_fields * @param vorticity_spectra [NSPEC2][nb_vordiv_fields] * @param divergence_spectra [NSPEC2][nb_vordiv_fields] * @param gp_fields Ordering: [NGPBLKS][NFLD][NPROMA] if distributed, * [NFLD][NGPTOTG] if global ( add option::global() * ) */ virtual void invtrans_adj(const int nb_vordiv_fields, const double gp_fields[], double vorticity_spectra[], double divergence_spectra[], const eckit::Configuration& = util::NoConfig()) const override; /*! * @brief Direct transform of scalar fields */ virtual void dirtrans(const int nb_fields, const double scalar_fields[], double scalar_spectra[], const eckit::Configuration& = util::NoConfig()) const override; /*! * @brief Direct transform of wind(U/V) to vorticity/divergence * @param nb_fields [in] Number of fields ( both components of wind count as 1 ) */ virtual void dirtrans(const int nb_fields, const double wind_fields[], double vorticity_spectra[], double divergence_spectra[], const eckit::Configuration& = util::NoConfig()) const override; // implementations public: void __dirtrans(const functionspace::StructuredColumns&, const Field& gpfield, const functionspace::Spectral&, Field& spfield, const eckit::Configuration& = util::NoConfig()) const; void __dirtrans(const functionspace::NodeColumns&, const Field& gpfield, const functionspace::Spectral&, Field& spfield, const eckit::Configuration& = util::NoConfig()) const; void __dirtrans(const functionspace::StructuredColumns&, const FieldSet& gpfields, const functionspace::Spectral&, FieldSet& spfields, const eckit::Configuration& = util::NoConfig()) const; void __dirtrans(const functionspace::NodeColumns&, const FieldSet& gpfields, const functionspace::Spectral&, FieldSet& spfields, const eckit::Configuration& = util::NoConfig()) const; void __dirtrans_wind2vordiv(const functionspace::StructuredColumns&, const Field& gpwind, const functionspace::Spectral&, Field& spvor, Field& spdiv, const eckit::Configuration& = util::NoConfig()) const; void __dirtrans_wind2vordiv(const functionspace::NodeColumns&, const Field& gpwind, const functionspace::Spectral&, Field& spvor, Field& spdiv, const eckit::Configuration& = util::NoConfig()) const; void __dirtrans_adj(const functionspace::Spectral&, const Field& spfield, const functionspace::StructuredColumns&, Field& gpfield, const eckit::Configuration& = util::NoConfig()) const; void __dirtrans_adj(const functionspace::Spectral&, const Field& spfield, const functionspace::NodeColumns&, Field& gpfield, const eckit::Configuration& = util::NoConfig()) const; void __dirtrans_adj(const functionspace::Spectral&, const FieldSet& spfields, const functionspace::StructuredColumns&, FieldSet& gpfields, const eckit::Configuration& = util::NoConfig()) const; void __dirtrans_adj(const functionspace::Spectral&, const FieldSet& spfields, const functionspace::NodeColumns&, FieldSet& gpfields, const eckit::Configuration& = util::NoConfig()) const; void __dirtrans_wind2vordiv_adj(const functionspace::Spectral&, const Field& spvor, const Field& spdiv, const functionspace::StructuredColumns&, Field& gpwind, const eckit::Configuration& = util::NoConfig()) const; void __dirtrans_wind2vordiv_adj(const functionspace::Spectral&, const Field& spvor, const Field& spdiv, const functionspace::NodeColumns&, Field& gpwind, const eckit::Configuration& = util::NoConfig()) const; void __invtrans(const functionspace::Spectral&, const Field& spfield, const functionspace::StructuredColumns&, Field& gpfield, const eckit::Configuration& = util::NoConfig()) const; void __invtrans(const functionspace::Spectral&, const Field& spfield, const functionspace::NodeColumns&, Field& gpfield, const eckit::Configuration& = util::NoConfig()) const; void __invtrans(const functionspace::Spectral&, const FieldSet& spfields, const functionspace::StructuredColumns&, FieldSet& gpfields, const eckit::Configuration& = util::NoConfig()) const; void __invtrans(const functionspace::Spectral&, const FieldSet& spfields, const functionspace::NodeColumns&, FieldSet& gpfields, const eckit::Configuration& = util::NoConfig()) const; void __invtrans_grad(const functionspace::Spectral& sp, const Field& spfield, const functionspace::StructuredColumns& gp, Field& gradfield, const eckit::Configuration& = util::NoConfig()) const; void __invtrans_grad(const functionspace::Spectral& sp, const Field& spfield, const functionspace::NodeColumns& gp, Field& gradfield, const eckit::Configuration& = util::NoConfig()) const; void __invtrans_grad(const functionspace::Spectral& sp, const FieldSet& spfields, const functionspace::StructuredColumns& gp, FieldSet& gradfields, const eckit::Configuration& = util::NoConfig()) const; void __invtrans_grad(const functionspace::Spectral& sp, const FieldSet& spfields, const functionspace::NodeColumns& gp, FieldSet& gradfields, const eckit::Configuration& = util::NoConfig()) const; void __invtrans_vordiv2wind(const functionspace::Spectral&, const Field& spvor, const Field& spdiv, const functionspace::StructuredColumns&, Field& gpwind, const eckit::Configuration& = util::NoConfig()) const; void __invtrans_vordiv2wind(const functionspace::Spectral&, const Field& spvor, const Field& spdiv, const functionspace::NodeColumns&, Field& gpwind, const eckit::Configuration& = util::NoConfig()) const; void __invtrans_adj(const functionspace::Spectral&, Field& spfield, const functionspace::StructuredColumns&, const Field& gpfield, const eckit::Configuration& = util::NoConfig()) const; void __invtrans_adj(const functionspace::Spectral&, Field& spfield, const functionspace::NodeColumns&, const Field& gpfield, const eckit::Configuration& = util::NoConfig()) const; void __invtrans_adj(const functionspace::Spectral&, FieldSet& spfields, const functionspace::StructuredColumns&, const FieldSet& gpfields, const eckit::Configuration& = util::NoConfig()) const; void __invtrans_adj(const functionspace::Spectral&, FieldSet& spfields, const functionspace::NodeColumns&, const FieldSet& gpfields, const eckit::Configuration& = util::NoConfig()) const; void __invtrans_grad_adj(const functionspace::Spectral& sp, Field& spfield, const functionspace::StructuredColumns& gp, const Field& gradfield, const eckit::Configuration& = util::NoConfig()) const; void __invtrans_grad_adj(const functionspace::Spectral& sp, Field& spfield, const functionspace::NodeColumns& gp, const Field& gradfield, const eckit::Configuration& = util::NoConfig()) const; void __invtrans_grad_adj(const functionspace::Spectral& sp, FieldSet& spfields, const functionspace::StructuredColumns& gp, const FieldSet& gradfields, const eckit::Configuration& = util::NoConfig()) const; void __invtrans_grad_adj(const functionspace::Spectral& sp, FieldSet& spfields, const functionspace::NodeColumns& gp, const FieldSet& gradfields, const eckit::Configuration& = util::NoConfig()) const; void __invtrans_vordiv2wind_adj(const functionspace::Spectral&, Field& spvor, Field& spdiv, const functionspace::StructuredColumns&, const Field& gpwind, const eckit::Configuration& = util::NoConfig()) const; void __invtrans_vordiv2wind_adj(const functionspace::Spectral&, Field& spvor, Field& spdiv, const functionspace::NodeColumns&, const Field& gpwind, const eckit::Configuration& = util::NoConfig()) const; public: void specnorm(const int nb_fields, const double spectra[], double norms[], int rank = 0) const; protected: void assertCompatibleDistributions(const FunctionSpace& gp, const FunctionSpace& /*sp*/) const; private: void ctor(const Grid&, long nsmax, const eckit::Configuration&); void ctor_rgg(const long nlat, const idx_t pl[], long nsmax, const eckit::Configuration&); void ctor_lonlat(const long nlon, const long nlat, long nsmax, const eckit::Configuration&); private: friend class grid::detail::partitioner::TransPartitioner; /// @brief Constructor for grid-only setup (e.g. for /// partitioning/parallelisation routines) TransIFS(const Grid& g, const eckit::Configuration& = util::NoConfig()); virtual int handle() const override; int ndgl() const; int nsmax() const; int ngptot() const; int ngptotg() const; int ngptotmx() const; int nspec() const; int nspec2() const; int nspec2g() const; int nspec2mx() const; int n_regions_NS() const; int n_regions_EW() const; int nump() const; int nproc() const; int myproc(int proc0 = 0) const; const int* nloen(int& size) const; array::LocalView nloen() const; const int* n_regions(int& size) const; array::LocalView n_regions() const; const int* nfrstlat(int& size) const; array::LocalView nfrstlat() const; const int* nlstlat(int& size) const; array::LocalView nlstlat() const; const int* nptrfrstlat(int& size) const; array::LocalView nptrfrstlat() const; const int* nsta(int& sizef2, int& sizef1) const; array::LocalView nsta() const; const int* nonl(int& sizef2, int& sizef1) const; array::LocalView nonl() const; const int* nmyms(int& size) const; array::LocalView nmyms() const; const int* nasm0(int& size) const; array::LocalView nasm0() const; const int* nvalue(int& size) const; array::LocalView nvalue() const; public: /*! * @brief distspec * @param nb_fields * @param origin * @param global_spectra * @param spectra */ void distspec(const int nb_fields, const int origin[], const double global_spectra[], double spectra[]) const; /*! * @brief gathspec * @param nb_fields * @param destination * @param spectra * @param global_spectra */ void gathspec(const int nb_fields, const int destination[], const double spectra[], double global_spectra[]) const; /*! * @brief distgrid * @param nb_fields * @param origin * @param global_fields * @param fields */ void distgrid(const int nb_fields, const int origin[], const double global_fields[], double fields[]) const; /*! * @brief gathgrid * @param nb_fields * @param destination * @param fields * @param global_fields */ void gathgrid(const int nb_fields, const int destination[], const double fields[], double global_fields[]) const; private: friend class functionspace::detail::Spectral; mutable std::shared_ptr<::Trans_t> trans_; StructuredGrid grid_; const void* cache_{nullptr}; size_t cachesize_{0}; protected: mutable functionspace::Spectral spectral_; }; // ------------------------------------------------------------------ } // namespace trans } // namespace atlas atlas-0.46.0/src/atlas/trans/ifs/TransIFSNodeColumns.h0000664000175000017500000000264715160212070022650 0ustar alastairalastair/* * (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/trans/ifs/TransIFS.h" //----------------------------------------------------------------------------- // Forward declarations namespace atlas { namespace functionspace { class NodeColumns; class Spectral; } // namespace functionspace } // namespace atlas //----------------------------------------------------------------------------- namespace atlas { namespace trans { //----------------------------------------------------------------------------- class TransIFSNodeColumns : public trans::TransIFS { public: TransIFSNodeColumns(const functionspace::NodeColumns&, const functionspace::Spectral&, const eckit::Configuration& = util::Config()); TransIFSNodeColumns(const Cache&, const functionspace::NodeColumns&, const functionspace::Spectral&, const eckit::Configuration& = util::Config()); virtual ~TransIFSNodeColumns(); }; //----------------------------------------------------------------------------- } // namespace trans } // namespace atlas atlas-0.46.0/src/atlas/trans/ifs/LegendreCacheCreatorIFS.cc0000664000175000017500000000766215160212070023543 0ustar alastairalastair/* * (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/trans/ifs/LegendreCacheCreatorIFS.h" #include #include #include #include "eckit/utils/MD5.h" #include "atlas/grid.h" #include "atlas/option.h" #include "atlas/runtime/Exception.h" #include "atlas/trans/Trans.h" namespace atlas { namespace trans { namespace { static LegendreCacheCreatorBuilder builder_ifs("ifs"); // Deprecated static LegendreCacheCreatorBuilder builder_ectrans("ectrans"); } // namespace namespace { std::string truncate(const std::string& str) { const int trunc = std::min(10ul, str.size()); return str.substr(0, trunc); } std::string hash(const Grid& grid) { eckit::MD5 h; StructuredGrid structured(grid); if (structured && not grid.projection()) { for (auto& y : structured.y()) { h.add(std::lround(y * 1.e8)); } } else { grid.hash(h); } return truncate(h.digest()); } std::string hash_pl(const Grid& grid) { eckit::MD5 h; StructuredGrid structured(grid); ATLAS_ASSERT(structured); for (auto& n : structured.nx()) { h.add(long(n)); } return truncate(h.digest()); } std::string hash(const eckit::Configuration& config) { eckit::MD5 h; // Add options and other unique keys h << "flt" << config.getBool("flt", false); return truncate(h.digest()); } } // namespace std::string LegendreCacheCreatorIFS::uid() const { if (unique_identifier_.empty()) { std::ostringstream stream; stream << "ifs-T" << truncation_ << "-"; GaussianGrid gaussian(grid_); if (gaussian) { if (RegularGaussianGrid(grid_)) { stream << "RegularGaussianN" << gaussian.N(); } else { stream << "ReducedGaussianN" << gaussian.N() << "-PL" << hash_pl(grid_); } } else if (RegularLonLatGrid(grid_)) { auto g = RegularLonLatGrid(grid_); if (g.standard() || g.shifted()) { stream << (g.standard() ? "L" : "S") << g.nx() << "x" << g.ny(); } else { // We cannot make more assumptions on reusability for different grids stream << "grid-" << hash(grid_); } } else { // We cannot make more assumptions on reusability for different grids stream << "grid-" << hash(grid_); } stream << "-OPT" << hash(config_); unique_identifier_ = stream.str(); } return unique_identifier_; } LegendreCacheCreatorIFS::~LegendreCacheCreatorIFS() = default; bool LegendreCacheCreatorIFS::supported() const { if (GaussianGrid(grid_)) { return true; } else if (RegularLonLatGrid(grid_)) { auto g = RegularLonLatGrid(grid_); if (g.standard() || g.shifted()) { return true; } } return false; } LegendreCacheCreatorIFS::LegendreCacheCreatorIFS(const Grid& grid, int truncation, const eckit::Configuration& config): grid_(grid), truncation_(truncation), config_(config) {} void LegendreCacheCreatorIFS::create(const std::string& path) const { Trans(grid_, truncation_, config_ | option::type("ectrans") | option::write_legendre(path)); } Cache LegendreCacheCreatorIFS::create() const { return TransCache(Trans(grid_, truncation_, config_ | option::type("ectrans"))); } size_t LegendreCacheCreatorIFS::estimate() const { return size_t(truncation_ * truncation_ * truncation_) / 2 * sizeof(double); } } // namespace trans } // namespace atlas atlas-0.46.0/src/atlas/trans/ifs/LegendreCacheCreatorIFS.h0000664000175000017500000000264415160212070023400 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include "atlas/grid/Grid.h" #include "atlas/trans/LegendreCacheCreator.h" #include "atlas/util/Config.h" //----------------------------------------------------------------------------- namespace atlas { namespace trans { //----------------------------------------------------------------------------- class LegendreCacheCreatorIFS : public trans::LegendreCacheCreatorImpl { public: LegendreCacheCreatorIFS(const Grid&, int truncation, const eckit::Configuration& = util::NoConfig()); virtual ~LegendreCacheCreatorIFS(); virtual bool supported() const override; virtual std::string uid() const override; virtual void create(const std::string& path) const override; virtual Cache create() const override; virtual size_t estimate() const override; private: const Grid grid_; const int truncation_; const util::Config config_; mutable std::string unique_identifier_; }; // ------------------------------------------------------------------ } // namespace trans } // namespace atlas atlas-0.46.0/src/atlas/trans/VorDivToUV.cc0000664000175000017500000001232715160212070020410 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ // file deepcode ignore CppMemoryLeak: static pointers for global registry are OK and will be cleaned up at end #include "eckit/thread/AutoLock.h" #include "eckit/thread/Mutex.h" #include "eckit/utils/Hash.h" #include "atlas/functionspace.h" #include "atlas/grid/Grid.h" #include "atlas/library/defines.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Log.h" #include "atlas/trans/VorDivToUV.h" // For factory registration only #if ATLAS_HAVE_TRANS #include "atlas/trans/ifs/VorDivToUVIFS.h" #define TRANS_DEFAULT "ectrans" #else #define TRANS_DEFAULT "local" #endif #include "atlas/trans/local/VorDivToUVLocal.h" // --> recommended "local" namespace atlas { namespace trans { VorDivToUVImpl::~VorDivToUVImpl() = default; namespace { static eckit::Mutex* local_mutex = nullptr; static std::map* m = nullptr; static pthread_once_t once = PTHREAD_ONCE_INIT; static void init() { local_mutex = new eckit::Mutex(); m = new std::map(); } template void load_builder() { VorDivToUVBuilder("tmp"); } struct force_link { force_link() { #if ATLAS_HAVE_TRANS load_builder(); #endif load_builder(); } }; VorDivToUVFactory& factory(const std::string& name) { std::map::const_iterator j = m->find(name); if (j == m->end()) { Log::error() << "No VorDivToUVFactory for [" << name << "]" << std::endl; Log::error() << "VorDivToUVFactory are:" << std::endl; for (j = m->begin(); j != m->end(); ++j) { Log::error() << " " << (*j).first << std::endl; } throw_Exception(std::string("No VorDivToUVFactory called ") + name); } return *j->second; } } // namespace VorDivToUVFactory::VorDivToUVFactory(const std::string& name): name_(name) { pthread_once(&once, init); eckit::AutoLock lock(local_mutex); ATLAS_ASSERT(m->find(name) == m->end()); (*m)[name] = this; } VorDivToUVFactory::~VorDivToUVFactory() { eckit::AutoLock lock(local_mutex); m->erase(name_); } bool VorDivToUVFactory::has(const std::string& name) { pthread_once(&once, init); eckit::AutoLock lock(local_mutex); static force_link static_linking; return (m->find(name) != m->end()); } void VorDivToUVFactory::list(std::ostream& out) { pthread_once(&once, init); eckit::AutoLock lock(local_mutex); static force_link static_linking; const char* sep = ""; for (std::map::const_iterator j = m->begin(); j != m->end(); ++j) { out << sep << (*j).first; sep = ", "; } } VorDivToUV::Implementation* VorDivToUVFactory::build(const FunctionSpace& sp, const eckit::Configuration& config) { pthread_once(&once, init); eckit::AutoLock lock(local_mutex); static force_link static_linking; std::string suffix("(" + sp.type() + ")"); std::string name = config.getString("type", TRANS_DEFAULT) + suffix; Log::debug() << "Looking for TransFactory [" << name << "]" << std::endl; if (not config.has("type") and not has(name)) { name = std::string("local") + suffix; Log::debug() << "Looking for TransFactory [" << name << "]" << std::endl; } return factory(name).make(sp, config); } VorDivToUV::Implementation* VorDivToUVFactory::build(int truncation, const eckit::Configuration& config) { pthread_once(&once, init); eckit::AutoLock lock(local_mutex); static force_link static_linking; std::string name = config.getString("type", TRANS_DEFAULT); Log::debug() << "Looking for VorDivToUVFactory [" << name << "]" << std::endl; if (not config.has("type") and not has(name)) { name = std::string("local"); Log::debug() << "Looking for VorDivToUVFactory [" << name << "]" << std::endl; } return factory(name).make(truncation, config); } VorDivToUV::VorDivToUV(const FunctionSpace& sp, const eckit::Configuration& config): Handle(VorDivToUVFactory::build(sp, config)) {} VorDivToUV::VorDivToUV(int truncation, const eckit::Configuration& config): Handle(VorDivToUVFactory::build(truncation, config)) {} int VorDivToUV::truncation() const { return get()->truncation(); } // -- IFS type fields -- // These fields have special interpretation required. You need to know what // you're doing. // See IFS trans library. void VorDivToUV::execute(const int nb_coeff, const int nb_fields, const double vorticity[], const double divergence[], double U[], double V[], const eckit::Configuration& config) const { get()->execute(nb_coeff, nb_fields, vorticity, divergence, U, V, config); } } // namespace trans } // namespace atlas atlas-0.46.0/src/atlas/trans/Trans.cc0000664000175000017500000002313415160212070017506 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "eckit/utils/Hash.h" #include "atlas/functionspace.h" #include "atlas/grid/Grid.h" #include "atlas/library/defines.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Log.h" #include "atlas/trans/Trans.h" #include "atlas/trans/detail/TransFactory.h" #include "atlas/trans/detail/TransImpl.h" namespace atlas { namespace trans { namespace { util::Config options(const eckit::Configuration& config) { util::Config opts = Trans::config(); opts.set(eckit::LocalConfiguration(config)); return opts; } } // namespace void Trans::listBackends(std::ostream& out) { TransFactory::list(out); } bool Trans::hasBackend(const std::string& backend) { return TransFactory::has(backend); } void Trans::backend(const std::string& backend) { ATLAS_ASSERT(hasBackend(backend)); TransFactory::backend(backend); } std::string Trans::backend() { return TransFactory::backend(); } const eckit::Configuration& Trans::config() { return TransFactory::config(); } void Trans::config(const eckit::Configuration& _config) { TransFactory::config(_config); } Trans::Trans(const FunctionSpace& gp, const FunctionSpace& sp, const eckit::Configuration& config): Handle(TransFactory::build(gp, sp, config)) {} Trans::Trans(const Grid& grid, int truncation, const eckit::Configuration& config): Handle(TransFactory::build(grid, truncation, config)) {} Trans::Trans(const Grid& grid, const Domain& domain, int truncation, const eckit::Configuration& config): Handle(TransFactory::build(grid, domain, truncation, config)) {} Trans::Trans(const Cache& cache, const FunctionSpace& gp, const FunctionSpace& sp, const eckit::Configuration& config): Handle(TransFactory::build(cache, gp, sp, config)) {} Trans::Trans(const Cache& cache, const Grid& grid, int truncation, const eckit::Configuration& config): Handle(TransFactory::build(cache, grid, truncation, config)) {} Trans::Trans(const Cache& cache, const Grid& grid, const Domain& domain, int truncation, const eckit::Configuration& config): Handle(TransFactory::build(cache, grid, domain, truncation, config)) {} int Trans::truncation() const { return get()->truncation(); } const Grid& Trans::grid() const { return get()->grid(); } const functionspace::Spectral& Trans::spectral() const { return get()->spectral(); } size_t Trans::spectralCoefficients() const { return get()->spectralCoefficients(); } void Trans::dirtrans(const Field& gpfield, Field& spfield, const eckit::Configuration& config) const { get()->dirtrans(gpfield, spfield, options(config)); } void Trans::dirtrans(const FieldSet& gpfields, FieldSet& spfields, const eckit::Configuration& config) const { get()->dirtrans(gpfields, spfields, options(config)); } void Trans::dirtrans_wind2vordiv(const Field& gpwind, Field& spvor, Field& spdiv, const eckit::Configuration& config) const { get()->dirtrans_wind2vordiv(gpwind, spvor, spdiv, options(config)); } void Trans::dirtrans_adj(const Field& spfield, Field& gpfield, const eckit::Configuration& config) const { get()->dirtrans_adj(spfield, gpfield, options(config)); } void Trans::dirtrans_adj(const FieldSet& spfields, FieldSet& gpfields, const eckit::Configuration& config) const { get()->dirtrans_adj(spfields, gpfields, options(config)); } void Trans::dirtrans_wind2vordiv_adj(const Field& spvor, const Field& spdiv, Field& gpwind, const eckit::Configuration& config) const { get()->dirtrans_wind2vordiv_adj(spvor, spdiv, gpwind, options(config)); } void Trans::invtrans(const Field& spfield, Field& gpfield, const eckit::Configuration& config) const { get()->invtrans(spfield, gpfield, options(config)); } void Trans::invtrans(const FieldSet& spfields, FieldSet& gpfields, const eckit::Configuration& config) const { get()->invtrans(spfields, gpfields, options(config)); } void Trans::invtrans_grad(const Field& spfield, Field& gradfield, const eckit::Configuration& config) const { get()->invtrans_grad(spfield, gradfield, options(config)); } void Trans::invtrans_grad(const FieldSet& spfields, FieldSet& gradfields, const eckit::Configuration& config) const { get()->invtrans_grad(spfields, gradfields, options(config)); } void Trans::invtrans_vordiv2wind(const Field& spvor, const Field& spdiv, Field& gpwind, const eckit::Configuration& config) const { get()->invtrans_vordiv2wind(spvor, spdiv, gpwind, options(config)); } void Trans::invtrans_adj(const Field& gpfield, Field& spfield, const eckit::Configuration& config) const { get()->invtrans_adj(gpfield, spfield, options(config)); } void Trans::invtrans_adj(const FieldSet& gpfields, FieldSet& spfields, const eckit::Configuration& config) const { get()->invtrans_adj(gpfields, spfields, options(config)); } void Trans::invtrans_grad_adj(const Field& gradfield, Field& spfield, const eckit::Configuration& config) const { get()->invtrans_grad_adj(gradfield, spfield, options(config)); } void Trans::invtrans_grad_adj(const FieldSet& gradfields, FieldSet& spfields, const eckit::Configuration& config) const { get()->invtrans_grad_adj(gradfields, spfields, options(config)); } void Trans::invtrans_vordiv2wind_adj(const Field& gpwind, Field& spvor, Field& spdiv, const eckit::Configuration& config) const { get()->invtrans_vordiv2wind_adj(gpwind, spvor, spdiv, options(config)); } // -- IFS type fields -- // These fields have special interpretation required. You need to know what // you're doing. // See IFS trans library. /*! * @brief invtrans * @param nb_scalar_fields * @param scalar_spectra * @param nb_vordiv_fields * @param vorticity_spectra * @param divergence_spectra * @param gp_fields */ void Trans::invtrans(const int nb_scalar_fields, const double scalar_spectra[], const int nb_vordiv_fields, const double vorticity_spectra[], const double divergence_spectra[], double gp_fields[], const eckit::Configuration& config) const { get()->invtrans(nb_scalar_fields, scalar_spectra, nb_vordiv_fields, vorticity_spectra, divergence_spectra, gp_fields, options(config)); } /*! * @brief invtrans * @param nb_fields * @param scalar_spectra * @param scalar_fields */ void Trans::invtrans(const int nb_scalar_fields, const double scalar_spectra[], double gp_fields[], const eckit::Configuration& config) const { get()->invtrans(nb_scalar_fields, scalar_spectra, gp_fields, options(config)); } /*! * @brief Inverse transform of vorticity/divergence to wind(U/V) * @param nb_fields [in] Number of fields ( both components of wind count as 1 ) */ void Trans::invtrans(const int nb_vordiv_fields, const double vorticity_spectra[], const double divergence_spectra[], double gp_fields[], const eckit::Configuration& config) const { get()->invtrans(nb_vordiv_fields, vorticity_spectra, divergence_spectra, gp_fields, options(config)); } /*! * @brief invtrans_adj * @param nb_scalar_fields * @param scalar_spectra * @param nb_vordiv_fields * @param vorticity_spectra * @param divergence_spectra * @param gp_fields */ void Trans::invtrans_adj(const int nb_scalar_fields, const double gp_fields[], const int nb_vordiv_fields, double vorticity_spectra[], double divergence_spectra[], double scalar_spectra[], const eckit::Configuration& config) const { get()->invtrans_adj(nb_scalar_fields, gp_fields, nb_vordiv_fields, vorticity_spectra, divergence_spectra, scalar_spectra, options(config)); } /*! * @brief invtrans_adj * @param nb_fields * @param scalar_spectra * @param scalar_fields */ void Trans::invtrans_adj(const int nb_scalar_fields, const double gp_fields[], double scalar_spectra[], const eckit::Configuration& config) const { get()->invtrans_adj(nb_scalar_fields, gp_fields, scalar_spectra, options(config)); } /*! * @brief Adjoint of Inverse transform of vorticity/divergence to wind(U/V) * @param nb_fields [in] Number of fields ( both components of wind count as 1 ) */ void Trans::invtrans_adj(const int nb_vordiv_fields, const double gp_fields[], double vorticity_spectra[], double divergence_spectra[], const eckit::Configuration& config) const { get()->invtrans_adj(nb_vordiv_fields, gp_fields, vorticity_spectra, divergence_spectra, options(config)); } /*! * @brief Direct transform of scalar fields */ void Trans::dirtrans(const int nb_fields, const double scalar_fields[], double scalar_spectra[], const eckit::Configuration& config) const { get()->dirtrans(nb_fields, scalar_fields, scalar_spectra, options(config)); } /*! * @brief Direct transform of wind(U/V) to vorticity/divergence * @param nb_fields [in] Number of fields ( both components of wind count as 1 ) */ void Trans::dirtrans(const int nb_fields, const double wind_fields[], double vorticity_spectra[], double divergence_spectra[], const eckit::Configuration& config) const { get()->dirtrans(nb_fields, wind_fields, vorticity_spectra, divergence_spectra, options(config)); } } // namespace trans } // namespace atlas atlas-0.46.0/src/atlas/trans/detail/0000775000175000017500000000000015160212070017347 5ustar alastairalastairatlas-0.46.0/src/atlas/trans/detail/TransFactory.h0000664000175000017500000001130615160212070022140 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include "atlas/runtime/Exception.h" #include "atlas/util/Config.h" #include "atlas/util/Factory.h" //---------------------------------------------------------------------------------------------------------------------- // forward declarations namespace atlas { class Grid; class FunctionSpace; class Domain; namespace trans { class TransImpl; class Cache; } // namespace trans } // namespace atlas //---------------------------------------------------------------------------------------------------------------------- namespace atlas { namespace trans { //---------------------------------------------------------------------------------------------------------------------- class TransFactory : public util::Factory { public: static std::string className() { return "TransFactory"; } public: /*! * \brief build Trans * \return TransImpl */ static const TransImpl* build(const FunctionSpace& gp, const FunctionSpace& sp, const eckit::Configuration& = util::Config()); static const TransImpl* build(const Grid&, int truncation, const eckit::Configuration& = util::Config()); static const TransImpl* build(const Grid&, const Domain&, int truncation, const eckit::Configuration& = util::Config()); static const TransImpl* build(const Cache&, const FunctionSpace& gp, const FunctionSpace& sp, const eckit::Configuration& = util::Config()); static const TransImpl* build(const Cache&, const Grid&, int truncation, const eckit::Configuration& = util::Config()); static const TransImpl* build(const Cache&, const Grid&, const Domain&, int truncation, const eckit::Configuration& = util::Config()); static void list(std::ostream& out); static bool has(const std::string& backend); static void backend(const std::string&); static std::string backend(); static const eckit::Configuration& config(); static void config(const eckit::Configuration&); public: TransFactory(const std::string& name, const std::string& backend); virtual ~TransFactory(); TransFactory() {} private: std::string name_; std::string backend_; virtual const TransImpl* make(const Cache&, const FunctionSpace& /*gp*/, const FunctionSpace& /*sp*/, const eckit::Configuration&) { return nullptr; } virtual const TransImpl* make(const Cache&, const Grid& /*gp*/, const Domain&, int /*truncation*/, const eckit::Configuration&) { return nullptr; } }; //---------------------------------------------------------------------------------------------------------------------- template class TransBuilderFunctionSpace : public TransFactory { virtual const TransImpl* make(const Cache& cache, const FunctionSpace& gp, const FunctionSpace& sp, const eckit::Configuration& config) override { return new T(cache, gp, sp, config); } virtual const TransImpl* make(const Cache&, const Grid&, const Domain&, int, const eckit::Configuration&) override { throw_Exception("This function should not be called", Here()); } public: TransBuilderFunctionSpace(const std::string& name, const std::string& backend): TransFactory(name, backend) {} TransBuilderFunctionSpace() {} }; template class TransBuilderGrid : public TransFactory { virtual const TransImpl* make(const Cache& cache, const Grid& grid, const Domain& domain, int truncation, const eckit::Configuration& config) override { return new T(cache, grid, domain, truncation, config); } virtual const TransImpl* make(const Cache&, const FunctionSpace&, const FunctionSpace&, const eckit::Configuration&) override { throw_Exception("This function should not be called", Here()); } public: TransBuilderGrid(const std::string& name, const std::string& backend): TransFactory(name, backend) {} TransBuilderGrid() {} }; //---------------------------------------------------------------------------------------------------------------------- } // namespace trans } // namespace atlas atlas-0.46.0/src/atlas/trans/detail/TransFactory.cc0000664000175000017500000001776115160212070022311 0ustar alastairalastair/* * (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 "TransFactory.h" #include "atlas/functionspace.h" #include "atlas/grid/Grid.h" #include "atlas/library/config.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Log.h" #include "atlas/trans/Cache.h" #include "atlas/trans/Trans.h" #include "atlas/trans/local/TransLocal.h" #if ATLAS_HAVE_TRANS #include "atlas/trans/ifs/TransIFS.h" #include "atlas/trans/ifs/TransIFSNodeColumns.h" #include "atlas/trans/ifs/TransIFSStructuredColumns.h" #endif namespace atlas { namespace trans { //---------------------------------------------------------------------------------------------------------------------- void force_link() { static struct Link { Link() { TransBuilderGrid(); #if ATLAS_HAVE_TRANS TransBuilderGrid(); TransBuilderFunctionSpace(); TransBuilderFunctionSpace(); #endif } } link; } //---------------------------------------------------------------------------------------------------------------------- namespace { struct default_backend { #if ATLAS_HAVE_TRANS std::string value = "ectrans"; #else std::string value = "local"; #endif static default_backend instance() { static default_backend x; return x; } private: default_backend() = default; }; } // namespace class TransBackend { using lock_guard = std::lock_guard; protected: TransBackend() { default_options_ = util::Config("type", default_backend::instance().value); } private: mutable std::mutex mutex_; std::map backends_; util::Config default_options_; public: std::vector keys() const { lock_guard lock(mutex_); std::vector _keys; _keys.reserve(backends_.size()); for (const auto& key_value : backends_) { _keys.emplace_back(key_value.first); } return _keys; } void list(std::ostream& out) const { lock_guard lock(mutex_); const char* sep = ""; for (const auto& map_pair : backends_) { out << sep << map_pair.first; sep = ", "; } } bool has(const std::string& backend) const { lock_guard lock(mutex_); return (backends_.find(backend) != backends_.end()); } void add(const std::string& backend) { lock_guard lock(mutex_); if (backends_.find(backend) == backends_.end()) { backends_[backend] = 1; } else { ++backends_[backend]; } } void remove(const std::string& backend) { lock_guard lock(mutex_); --backends_[backend]; if (backends_[backend] == 0) { backends_.erase(backend); } } void backend(const std::string& backend) { lock_guard lock(mutex_); default_options_.set("type", backend); } std::string backend() { return default_options_.getString("type"); } void config(const eckit::Configuration& config) { std::string type = default_options_.getString("type"); default_options_ = config; if (not config.has("type")) { default_options_.set("type", type); } } const eckit::Configuration& config() { return default_options_; } public: static TransBackend& instance() { static TransBackend env; return env; } }; TransFactory::TransFactory(const std::string& name, const std::string& backend): Factory(name), name_(name), backend_(backend) { TransBackend::instance().add(backend); } TransFactory::~TransFactory() { TransBackend::instance().remove(backend_); } void TransFactory::list(std::ostream& out) { TransBackend::instance().list(out); } bool TransFactory::has(const std::string& backend) { return TransBackend::instance().has(backend); } void TransFactory::backend(const std::string& backend) { TransBackend::instance().backend(backend); } std::string TransFactory::backend() { return TransBackend::instance().backend(); } const eckit::Configuration& TransFactory::config() { return TransBackend::instance().config(); } void TransFactory::config(const eckit::Configuration& config) { TransBackend::instance().config(config); } const TransImpl* TransFactory::build(const FunctionSpace& gp, const FunctionSpace& sp, const eckit::Configuration& config) { return build(Cache(), gp, sp, config); } const TransImpl* TransFactory::build(const Cache& cache, const FunctionSpace& gp, const FunctionSpace& sp, const eckit::Configuration& config) { force_link(); if (cache.trans()) { Log::debug() << "Creating Trans from cache, ignoring any other arguments" << std::endl; return cache.trans(); } util::Config options = TransBackend::instance().config(); options.set(eckit::LocalConfiguration(config)); std::string backend = options.getString("type"); Log::debug() << "Looking for TransFactory [" << backend << "]" << std::endl; if (!TransBackend::instance().has(backend)) { Log::error() << "No TransFactory for [" << backend << "]" << std::endl; Log::error() << "TransFactories are :" << std::endl; TransBackend::instance().list(Log::error()); Log::error() << std::endl; throw_Exception(std::string("No TransFactory called ") + backend); } std::string suffix("(" + gp.type() + "," + sp.type() + ")"); std::string builder = backend + suffix; Log::debug() << "Looking for TransFactory [" << builder << "]" << std::endl; auto factory = get(builder); return factory->make(cache, gp, sp, options); } const TransImpl* TransFactory::build(const Grid& grid, int truncation, const eckit::Configuration& config) { return build(Cache(), grid, truncation, config); } const TransImpl* TransFactory::build(const Grid& grid, const Domain& domain, int truncation, const eckit::Configuration& config) { return build(Cache(), grid, domain, truncation, config); } const TransImpl* TransFactory::build(const Cache& cache, const Grid& grid, int truncation, const eckit::Configuration& config) { return build(cache, grid, grid.domain(), truncation, config); } const TransImpl* TransFactory::build(const Cache& cache, const Grid& grid, const Domain& domain, int truncation, const eckit::Configuration& config) { force_link(); if (cache.trans()) { Log::debug() << "Creating Trans from cache, ignoring any other arguments" << std::endl; return cache.trans(); } util::Config options = TransBackend::instance().config(); options.set(eckit::LocalConfiguration(config)); std::string backend = options.getString("type"); Log::debug() << "Looking for TransFactory [" << backend << "]" << std::endl; if (!TransBackend::instance().has(backend)) { Log::error() << "No TransFactory for [" << backend << "]" << std::endl; Log::error() << "TransFactories are :" << std::endl; TransBackend::instance().list(Log::error()); Log::error() << std::endl; throw_Exception(std::string("No TransFactory called ") + backend); } std::string builder = backend; auto factory = get(builder); return factory->make(cache, grid, domain, truncation, options); } //---------------------------------------------------------------------------------------------------------------------- } // namespace trans } // namespace atlas atlas-0.46.0/src/atlas/trans/detail/TransInterface.cc0000664000175000017500000002472115160212070022574 0ustar alastairalastair/* * (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 "TransInterface.h" #include #include "atlas/array.h" #include "atlas/field/Field.h" #include "atlas/field/FieldSet.h" #include "atlas/functionspace/Spectral.h" #include "atlas/library/config.h" #include "atlas/mesh/Nodes.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Log.h" #include "atlas/trans/Trans.h" #include "atlas/trans/detail/TransFactory.h" #include "atlas/trans/detail/TransImpl.h" namespace atlas { namespace trans { class TransInterface { private: const TransImpl* trans_; public: TransInterface(const TransImpl* trans): trans_(trans) {} int handle() const { return trans_->handle(); } }; /////////////////////////////////////////////////////////////////////////////// extern "C" { int atlas__Trans__has_backend(const char* backend) { return Trans::hasBackend(std::string(backend)); } void atlas__Trans__set_backend(const char* backend) { Trans::backend(std::string(backend)); } void atlas__Trans__backend(char*& backend, size_t& size) { std::string s = Trans::backend(); size = s.size(); backend = new char[size + 1]; std::strncpy(backend, s.c_str(), size + 1); } TransImpl* atlas__Trans__new(const Grid::Implementation* grid, int truncation) { ATLAS_ASSERT(grid != nullptr, "Grid must not be null"); TransImpl* trans; { Grid g(grid); Trans t(g, truncation); trans = t.get(); trans->attach(); } trans->detach(); return trans; } TransImpl* atlas__Trans__new_config(const Grid::Implementation* grid, int truncation, const eckit::Configuration* config) { ATLAS_ASSERT(grid != nullptr, "Grid must not be null"); ATLAS_ASSERT(config != nullptr, "config must not be null"); TransImpl* trans; { Grid g(grid); Trans t(g, truncation, *config); trans = t.get(); trans->attach(); } trans->detach(); return trans; } void atlas__Trans__delete(TransImpl* This) { ATLAS_ASSERT(This != nullptr); delete This; } int atlas__Trans__handle(const TransImpl* This) { ATLAS_ASSERT(This != nullptr); return TransInterface(This).handle(); } void atlas__Trans__invtrans_scalar(const TransImpl* t, int nb_fields, double scalar_spectra[], double scalar_fields[]) { ATLAS_ASSERT(t != nullptr); return t->invtrans(nb_fields, scalar_spectra, scalar_fields); } void atlas__Trans__invtrans_vordiv2wind(const TransImpl* t, int nb_fields, double vorticity_spectra[], double divergence_spectra[], double wind_fields[]) { ATLAS_ASSERT(t != nullptr); return t->invtrans(nb_fields, vorticity_spectra, divergence_spectra, wind_fields); } void atlas__Trans__invtrans_adj_scalar(const TransImpl* t, int nb_fields, double scalar_fields[], double scalar_spectra[]) { ATLAS_ASSERT(t != nullptr); return t->invtrans_adj(nb_fields, scalar_fields, scalar_spectra); } void atlas__Trans__invtrans_vordiv2wind_adj(const TransImpl* t, int nb_fields, double wind_fields[], double vorticity_spectra[], double divergence_spectra[]) { ATLAS_ASSERT(t != nullptr); return t->invtrans_adj(nb_fields, wind_fields, vorticity_spectra, divergence_spectra); } void atlas__Trans__dirtrans_scalar(const TransImpl* t, int nb_fields, double scalar_fields[], double scalar_spectra[]) { ATLAS_ASSERT(t != nullptr); return t->dirtrans(nb_fields, scalar_fields, scalar_spectra); } void atlas__Trans__dirtrans_wind2vordiv(const TransImpl* t, int nb_fields, double wind_fields[], double vorticity_spectra[], double divergence_spectra[]) { ATLAS_ASSERT(t != nullptr); return t->dirtrans(nb_fields, wind_fields, vorticity_spectra, divergence_spectra); } int atlas__Trans__truncation(const TransImpl* This) { ATLAS_ASSERT(This != nullptr); return This->truncation(); } const Grid::Implementation* atlas__Trans__grid(const TransImpl* This) { ATLAS_ASSERT(This != nullptr); ATLAS_ASSERT(This->grid()); return This->grid().get(); } const functionspace::FunctionSpaceImpl* atlas__Trans__spectral(const TransImpl* This) { ATLAS_ASSERT(This != nullptr); const auto spectral = This->spectral(); ATLAS_ASSERT(spectral); return spectral.get(); } void atlas__Trans__dirtrans_fieldset(const TransImpl* This, const field::FieldSetImpl* gpfields, field::FieldSetImpl* spfields, const eckit::Configuration* parameters) { ATLAS_ASSERT(This != nullptr); ATLAS_ASSERT(gpfields); ATLAS_ASSERT(spfields); ATLAS_ASSERT(parameters); FieldSet fspfields(spfields); This->dirtrans(gpfields, fspfields, *parameters); } void atlas__Trans__dirtrans_field(const TransImpl* This, const field::FieldImpl* gpfield, field::FieldImpl* spfield, const eckit::Configuration* parameters) { ATLAS_ASSERT(This != nullptr); ATLAS_ASSERT(spfield); ATLAS_ASSERT(gpfield); ATLAS_ASSERT(parameters); Field fspfield(spfield); This->dirtrans(gpfield, fspfield, *parameters); } void atlas__Trans__invtrans_fieldset(const TransImpl* This, const field::FieldSetImpl* spfields, field::FieldSetImpl* gpfields, const eckit::Configuration* parameters) { ATLAS_ASSERT(This != nullptr); ATLAS_ASSERT(spfields); ATLAS_ASSERT(gpfields); ATLAS_ASSERT(parameters); FieldSet fgpfields(gpfields); This->invtrans(spfields, fgpfields, *parameters); } void atlas__Trans__invtrans_field(const TransImpl* This, const field::FieldImpl* spfield, field::FieldImpl* gpfield, const eckit::Configuration* parameters) { ATLAS_ASSERT(This != nullptr); ATLAS_ASSERT(spfield); ATLAS_ASSERT(gpfield); ATLAS_ASSERT(parameters); Field fgpfield(gpfield); This->invtrans(spfield, fgpfield, *parameters); } void atlas__Trans__invtrans_adj_fieldset(const TransImpl* This, const field::FieldSetImpl* gpfields, field::FieldSetImpl* spfields, const eckit::Configuration* parameters) { ATLAS_ASSERT(This != nullptr); ATLAS_ASSERT(spfields); ATLAS_ASSERT(gpfields); ATLAS_ASSERT(parameters); FieldSet fspfields(spfields); This->invtrans_adj(gpfields, fspfields, *parameters); } void atlas__Trans__invtrans_adj_field(const TransImpl* This, const field::FieldImpl* spfield, field::FieldImpl* gpfield, const eckit::Configuration* parameters) { ATLAS_ASSERT(This != nullptr); ATLAS_ASSERT(spfield); ATLAS_ASSERT(gpfield); ATLAS_ASSERT(parameters); Field fspfield(spfield); This->invtrans_adj(gpfield, fspfield, *parameters); } void atlas__Trans__dirtrans_wind2vordiv_field(const TransImpl* This, const field::FieldImpl* gpwind, field::FieldImpl* spvor, field::FieldImpl* spdiv, const eckit::Configuration* parameters) { ATLAS_ASSERT(This != nullptr); ATLAS_ASSERT(gpwind); ATLAS_ASSERT(spvor); ATLAS_ASSERT(spdiv); ATLAS_ASSERT(parameters); Field fspvor(spvor); Field fspdiv(spdiv); This->dirtrans_wind2vordiv(gpwind, fspvor, fspdiv, *parameters); } void atlas__Trans__invtrans_vordiv2wind_field(const TransImpl* This, const field::FieldImpl* spvor, const field::FieldImpl* spdiv, field::FieldImpl* gpwind, const eckit::Configuration* parameters) { ATLAS_ASSERT(This != nullptr); ATLAS_ASSERT(spvor); ATLAS_ASSERT(spdiv); ATLAS_ASSERT(gpwind); ATLAS_ASSERT(parameters); Field fgpwind(gpwind); This->invtrans_vordiv2wind(spvor, spdiv, fgpwind, *parameters); } void atlas__Trans__invtrans(const TransImpl* This, int nb_scalar_fields, double scalar_spectra[], int nb_vordiv_fields, double vorticity_spectra[], double divergence_spectra[], double gp_fields[], const eckit::Configuration* parameters) { ATLAS_ASSERT(This != nullptr); This->invtrans(nb_scalar_fields, scalar_spectra, nb_vordiv_fields, vorticity_spectra, divergence_spectra, gp_fields, *parameters); } void atlas__Trans__invtrans_grad_field(const TransImpl* This, const field::FieldImpl* spfield, field::FieldImpl* gpfield, const eckit::Configuration* config) { ATLAS_ASSERT(This != nullptr); ATLAS_ASSERT(spfield); ATLAS_ASSERT(gpfield); Field fgpfield(gpfield); This->invtrans_grad(spfield, fgpfield, *config); } void atlas__Trans__invtrans_vordiv2wind_adj_field(const TransImpl* This, const field::FieldImpl* gpwind, field::FieldImpl* spvor, field::FieldImpl* spdiv, const eckit::Configuration* parameters) { ATLAS_ASSERT(This != nullptr); ATLAS_ASSERT(spvor); ATLAS_ASSERT(spdiv); ATLAS_ASSERT(gpwind); ATLAS_ASSERT(parameters); Field fspvor(spvor); Field fspdiv(spdiv); This->invtrans_vordiv2wind_adj(gpwind, fspvor, fspdiv, *parameters); } void atlas__Trans__invtrans_adj(const TransImpl* This, int nb_scalar_fields, double gp_fields[], int nb_vordiv_fields, double vorticity_spectra[], double divergence_spectra[], double scalar_spectra[], const eckit::Configuration* parameters) { ATLAS_ASSERT(This != nullptr); This->invtrans_adj(nb_scalar_fields, gp_fields, nb_vordiv_fields, vorticity_spectra, divergence_spectra, scalar_spectra, *parameters); } void atlas__Trans__invtrans_grad_adj_field(const TransImpl* This, const field::FieldImpl* gpfield, field::FieldImpl* spfield, const eckit::Configuration* config) { ATLAS_ASSERT(This != nullptr); ATLAS_ASSERT(spfield); ATLAS_ASSERT(gpfield); Field fspfield(spfield); This->invtrans_grad_adj(gpfield, fspfield, *config); } } } // namespace trans } // namespace atlas atlas-0.46.0/src/atlas/trans/detail/TransImpl.h0000664000175000017500000001712215160212070021434 0ustar alastairalastair/* * (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/trans/Cache.h" #include "atlas/util/Config.h" #include "atlas/util/Object.h" //----------------------------------------------------------------------------- // Forward declarations namespace atlas { class Field; class FieldSet; class FunctionSpace; class Grid; class Domain; namespace functionspace { class Spectral; } } // namespace atlas //----------------------------------------------------------------------------- namespace atlas { namespace trans { //----------------------------------------------------------------------------- class TransImpl : public util::Object { public: virtual std::string type() const { return "wrong value"; } virtual ~TransImpl() = 0; virtual int truncation() const = 0; virtual size_t nb_spectral_coefficients() const = 0; virtual size_t nb_spectral_coefficients_global() const = 0; virtual const Grid& grid() const = 0; virtual const functionspace::Spectral& spectral() const = 0; virtual void dirtrans(const Field& gpfield, Field& spfield, const eckit::Configuration& = util::NoConfig()) const = 0; virtual void dirtrans(const FieldSet& gpfields, FieldSet& spfields, const eckit::Configuration& = util::NoConfig()) const = 0; virtual void dirtrans_wind2vordiv(const Field& gpwind, Field& spvor, Field& spdiv, const eckit::Configuration& = util::NoConfig()) const = 0; virtual void dirtrans_adj(const Field& spfield, Field& gpfield, const eckit::Configuration& = util::NoConfig()) const = 0; virtual void dirtrans_adj(const FieldSet& spfields, FieldSet& gpfields, const eckit::Configuration& = util::NoConfig()) const = 0; virtual void dirtrans_wind2vordiv_adj(const Field& spvor, const Field& spdiv, Field& gpwind, const eckit::Configuration& = util::NoConfig()) const = 0; virtual void invtrans(const Field& spfield, Field& gpfield, const eckit::Configuration& = util::NoConfig()) const = 0; virtual void invtrans(const FieldSet& spfields, FieldSet& gpfields, const eckit::Configuration& = util::NoConfig()) const = 0; virtual void invtrans_grad(const Field& spfield, Field& gradfield, const eckit::Configuration& = util::NoConfig()) const = 0; virtual void invtrans_grad(const FieldSet& spfields, FieldSet& gradfields, const eckit::Configuration& = util::NoConfig()) const = 0; virtual void invtrans_vordiv2wind(const Field& spvor, const Field& spdiv, Field& gpwind, const eckit::Configuration& = util::NoConfig()) const = 0; virtual void invtrans_adj(const Field& gpfield, Field& spfield, const eckit::Configuration& = util::NoConfig()) const = 0; virtual void invtrans_adj(const FieldSet& gpfields, FieldSet& spfields, const eckit::Configuration& = util::NoConfig()) const = 0; virtual void invtrans_grad_adj(const Field& gradfield, Field& gpfield, const eckit::Configuration& = util::NoConfig()) const = 0; virtual void invtrans_grad_adj(const FieldSet& gradfields, FieldSet& spfields, const eckit::Configuration& = util::NoConfig()) const = 0; virtual void invtrans_vordiv2wind_adj(const Field& gpwind, Field& spvor, Field& spdiv, const eckit::Configuration& = util::NoConfig()) const = 0; // -- IFS type fields -- // These fields have special interpretation required. You need to know what // you're doing. // See IFS trans library. /*! * @brief invtrans * @param nb_scalar_fields * @param scalar_spectra * @param nb_vordiv_fields * @param vorticity_spectra * @param divergence_spectra * @param gp_fields */ virtual void invtrans(const int nb_scalar_fields, const double scalar_spectra[], const int nb_vordiv_fields, const double vorticity_spectra[], const double divergence_spectra[], double gp_fields[], const eckit::Configuration& = util::NoConfig()) const = 0; /*! * @brief invtrans * @param nb_fields * @param scalar_spectra * @param scalar_fields */ virtual void invtrans(const int nb_scalar_fields, const double scalar_spectra[], double gp_fields[], const eckit::Configuration& = util::NoConfig()) const = 0; /*! * @brief Inverse transform of vorticity/divergence to wind(U/V) * @param nb_fields [in] Number of fields ( both components of wind count as 1 * ) */ virtual void invtrans(const int nb_vordiv_fields, const double vorticity_spectra[], const double divergence_spectra[], double gp_fields[], const eckit::Configuration& = util::NoConfig()) const = 0; /*! * @brief invtrans_adj * @param nb_scalar_fields * @param scalar_spectra * @param nb_vordiv_fields * @param vorticity_spectra * @param divergence_spectra * @param gp_fields */ virtual void invtrans_adj(const int nb_scalar_fields, const double gp_fields[], const int nb_vordiv_fields, double vorticity_spectra[], double divergence_spectra[], double scalar_spectra[], const eckit::Configuration& = util::NoConfig()) const = 0; /*! * @brief invtrans_adj * @param nb_fields * @param scalar_spectra * @param scalar_fields */ virtual void invtrans_adj(const int nb_scalar_fields, const double gp_fields[], double scalar_spectra[], const eckit::Configuration& = util::NoConfig()) const = 0; /*! * @brief Adjoint of Inverse transform of vorticity/divergence to wind(U/V) * @param nb_fields [in] Number of fields ( both components of wind count as 1 * ) */ virtual void invtrans_adj(const int nb_vordiv_fields, const double wind_fields[], double vorticity_spectra[], double divergence_spectra[], const eckit::Configuration& = util::NoConfig()) const = 0; /*! * @brief Direct transform of scalar fields */ virtual void dirtrans(const int nb_fields, const double scalar_fields[], double scalar_spectra[], const eckit::Configuration& = util::NoConfig()) const = 0; /*! * @brief Direct transform of wind(U/V) to vorticity/divergence * @param nb_fields [in] Number of fields ( both components of wind count as 1 * ) */ virtual void dirtrans(const int nb_fields, const double wind_fields[], double vorticity_spectra[], double divergence_spectra[], const eckit::Configuration& = util::NoConfig()) const = 0; // deprecated virtual size_t spectralCoefficients() const { return nb_spectral_coefficients(); } private: friend class TransInterface; // Only for TransIFS backend virtual int handle() const; }; //---------------------------------------------------------------------------------------------------------------------- } // namespace trans } // namespace atlas atlas-0.46.0/src/atlas/trans/detail/TransImpl.cc0000664000175000017500000000117015160212070021566 0ustar alastairalastair/* * (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 "TransImpl.h" #include "atlas/runtime/Exception.h" namespace atlas { namespace trans { TransImpl::~TransImpl() = default; int TransImpl::handle() const { ATLAS_NOTIMPLEMENTED; } } // namespace trans } // namespace atlas atlas-0.46.0/src/atlas/trans/detail/TransInterface.h0000664000175000017500000001430615160212070022434 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include "atlas/grid/Grid.h" #include "atlas/trans/detail/TransImpl.h" //----------------------------------------------------------------------------- // Forward declarations namespace eckit { class Configuration; } // namespace eckit namespace atlas { class Field; class FieldSet; namespace field { class FieldImpl; class FieldSetImpl; } // namespace field namespace functionspace { class FunctionSpaceImpl; } } // namespace atlas //----------------------------------------------------------------------------- namespace atlas { namespace trans { //----------------------------------------------------------------------------- // C wrapper interfaces to C++ routines extern "C" { int atlas__Trans__has_backend(const char* backend); void atlas__Trans__set_backend(const char* backend); void atlas__Trans__backend(char*& backend, size_t& size); TransImpl* atlas__Trans__new(const Grid::Implementation* grid, int truncation); TransImpl* atlas__Trans__new_config(const Grid::Implementation* grid, int truncation, const eckit::Configuration* config); void atlas__Trans__delete(TransImpl* trans); void atlas__Trans__invtrans(const TransImpl* t, int nb_scalar_fields, double scalar_spectra[], int nb_vordiv_fields, double vorticity_spectra[], double divergence_spectra[], double gp_fields[], const eckit::Configuration* parameters); void atlas__Trans__invtrans_scalar(const TransImpl* t, int nb_fields, double scalar_spectra[], double scalar_fields[]); void atlas__Trans__invtrans_vordiv2wind(const TransImpl* t, int nb_fields, double vorticity_spectra[], double divergence_spectra[], double wind_fields[]); void atlas__Trans__invtrans_adj(const TransImpl* t, int nb_scalar_fields, double gp_fields[], int nb_vordiv_fields, double vorticity_spectra[], double divergence_spectra[], double scalar_spectra[], const eckit::Configuration* parameters); void atlas__Trans__invtrans_adj_scalar(const TransImpl* t, int nb_fields, double scalar_fields[], double scalar_spectra[]); void atlas__Trans__invtrans_vordiv2wind_adj(const TransImpl* t, int nb_fields, double wind_fields[], double vorticity_spectra[], double divergence_spectra[]); void atlas__Trans__dirtrans_scalar(const TransImpl* t, int nb_fields, double scalar_fields[], double scalar_spectra[]); void atlas__Trans__dirtrans_wind2vordiv(const TransImpl* t, int nb_fields, double wind_fields[], double vorticity_spectra[], double divergence_spectra[]); void atlas__Trans__dirtrans_wind2vordiv_field(const TransImpl* This, const field::FieldImpl* gpwind, field::FieldImpl* spvor, field::FieldImpl* spdiv, const eckit::Configuration* parameters); // void atlas__Trans__specnorm( const TransImpl* t, int nb_fields, double spectra[], double norms[], int rank ); void atlas__Trans__dirtrans_fieldset(const TransImpl* This, const field::FieldSetImpl* gpfields, field::FieldSetImpl* spfields, const eckit::Configuration* parameters); void atlas__Trans__dirtrans_field(const TransImpl* This, const field::FieldImpl* gpfield, field::FieldImpl* spfield, const eckit::Configuration* parameters); void atlas__Trans__invtrans_fieldset(const TransImpl* This, const field::FieldSetImpl* spfields, field::FieldSetImpl* gpfields, const eckit::Configuration* parameters); void atlas__Trans__invtrans_field(const TransImpl* This, const field::FieldImpl* spfield, field::FieldImpl* gpfield, const eckit::Configuration* parameters); void atlas__Trans__invtrans_grad_field(const TransImpl* This, const field::FieldImpl* spfield, field::FieldImpl* gpfield, const eckit::Configuration* parameters); void atlas__Trans__invtrans_vordiv2wind_field(const TransImpl* This, const field::FieldImpl* spvor, const field::FieldImpl* spdiv, field::FieldImpl* gpwind, const eckit::Configuration* parameters); void atlas__Trans__invtrans_adj_fieldset(const TransImpl* This, const field::FieldSetImpl* gpfields, field::FieldSetImpl* spfields, const eckit::Configuration* parameters); void atlas__Trans__invtrans_adj_field(const TransImpl* This, const field::FieldImpl* gpfield, field::FieldImpl* spfield, const eckit::Configuration* parameters); void atlas__Trans__invtrans_grad_adj_field(const TransImpl* This, const field::FieldImpl* gpfield, field::FieldImpl* spfield, const eckit::Configuration* parameters); void atlas__Trans__invtrans_vordiv2wind_adj_field(const TransImpl* This, const field::FieldImpl* gpwind, field::FieldImpl* spvor, field::FieldImpl* spdiv, const eckit::Configuration* parameters); int atlas__Trans__handle(const TransImpl* trans); int atlas__Trans__truncation(const TransImpl* This); // int atlas__Trans__nspec2( const TransImpl* This ); // int atlas__Trans__nspec2g( const TransImpl* This ); // int atlas__Trans__ngptot( const TransImpl* This ); // int atlas__Trans__ngptotg( const TransImpl* This ); const Grid::Implementation* atlas__Trans__grid(const TransImpl* This); const functionspace::FunctionSpaceImpl* atlas__Trans__spectral(const TransImpl* This); } // ------------------------------------------------------------------ } // namespace trans } // namespace atlas atlas-0.46.0/src/atlas/trans/Cache.h0000664000175000017500000001014115160212070017256 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include "eckit/filesystem/PathName.h" #include "atlas/util/ObjectHandle.h" //----------------------------------------------------------------------------- // Forward declarations namespace atlas { class Field; class FieldSet; class FunctionSpace; class Grid; class Domain; namespace trans { class TransImpl; class Trans; } // namespace trans } // namespace atlas //----------------------------------------------------------------------------- namespace atlas { namespace trans { //----------------------------------------------------------------------------- class TransCacheEntry { public: operator bool() const { return size() != 0; } virtual ~TransCacheEntry() = default; virtual size_t size() const = 0; virtual const void* data() const = 0; }; //----------------------------------------------------------------------------- class EmptyCacheEntry final : public TransCacheEntry { public: virtual size_t size() const override { return 0; } virtual const void* data() const override { return nullptr; } }; //----------------------------------------------------------------------------- class TransCacheFileEntry final : public TransCacheEntry { public: TransCacheFileEntry(const eckit::PathName& path); virtual ~TransCacheFileEntry() override; virtual size_t size() const override { return size_; } virtual const void* data() const override { return data_; } private: void* data_ = nullptr; size_t size_ = 0; }; //----------------------------------------------------------------------------- class TransCacheMemoryEntry final : public TransCacheEntry { public: TransCacheMemoryEntry(const void* data, size_t size); virtual const void* data() const override { return data_; } virtual size_t size() const override { return size_; } private: const void* data_; const size_t size_; }; //----------------------------------------------------------------------------- class TransCacheOwnedMemoryEntry final : public TransCacheEntry { public: TransCacheOwnedMemoryEntry(size_t size); virtual ~TransCacheOwnedMemoryEntry() override; virtual const void* data() const override { return data_; } virtual size_t size() const override { return size_; } private: void* data_ = nullptr; const size_t size_ = 0; }; //----------------------------------------------------------------------------- class Cache { public: Cache(); Cache(const Cache& other) = default; operator bool() const; const TransImpl* trans() const { return trans_.get(); } const TransCacheEntry& legendre() const { return *legendre_; } const TransCacheEntry& fft() const { return *fft_; } virtual ~Cache(); protected: Cache(const std::shared_ptr& legendre); Cache(const std::shared_ptr& legendre, const std::shared_ptr& fft); Cache(const TransImpl*); private: util::ObjectHandle trans_; std::shared_ptr legendre_; std::shared_ptr fft_; }; class TransCache : public Cache { public: TransCache(const Trans&); }; class LegendreCache : public Cache { public: LegendreCache(size_t size); LegendreCache(const void* address, size_t size); LegendreCache(const eckit::PathName& path); }; class LegendreFFTCache : public Cache { public: LegendreFFTCache(const void* legendre_address, size_t legendre_size, const void* fft_address, size_t fft_size); LegendreFFTCache(const eckit::PathName& legendre_path, const eckit::PathName& fft_path); }; //---------------------------------------------------------------------------------------------------------------------- } // namespace trans } // namespace atlas atlas-0.46.0/src/atlas/trans/LegendreCacheCreator.h0000664000175000017500000000620415160212070022251 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include "atlas/trans/Trans.h" #include "atlas/util/Config.h" #include "atlas/util/Object.h" #include "atlas/util/ObjectHandle.h" //----------------------------------------------------------------------------- // Forward declarations namespace atlas { class Grid; } // namespace atlas //----------------------------------------------------------------------------- namespace atlas { namespace trans { //----------------------------------------------------------------------------- class LegendreCacheCreatorImpl : public util::Object { public: virtual ~LegendreCacheCreatorImpl() = 0; virtual bool supported() const = 0; virtual std::string uid() const = 0; virtual void create(const std::string& path) const = 0; virtual Cache create() const = 0; virtual size_t estimate() const = 0; }; // ------------------------------------------------------------------ class LegendreCacheCreator : DOXYGEN_HIDE(public util::ObjectHandle) { public: using Handle::Handle; LegendreCacheCreator() = default; LegendreCacheCreator(const Grid&, int truncation, const eckit::Configuration& = util::NoConfig()); bool supported() const; std::string uid() const; void create(const std::string& path) const; Cache create() const; size_t estimate() const; }; //---------------------------------------------------------------------------------------------------------------------- class LegendreCacheCreatorFactory { public: /*! * \brief build Trans * \return TransImpl */ static LegendreCacheCreatorImpl* build(const Grid&, int truncation, const eckit::Configuration& = util::Config()); /*! * \brief list all registered trans implementations */ static void list(std::ostream&); static bool has(const std::string& name); private: std::string name_; virtual LegendreCacheCreatorImpl* make(const Grid& /*gp*/, int truncation, const eckit::Configuration&) { return nullptr; } protected: LegendreCacheCreatorFactory(const std::string&); virtual ~LegendreCacheCreatorFactory(); }; //---------------------------------------------------------------------------------------------------------------------- template class LegendreCacheCreatorBuilder : public LegendreCacheCreatorFactory { virtual LegendreCacheCreatorImpl* make(const Grid& grid, int truncation, const eckit::Configuration& config) { return new T(grid, truncation, config); } public: LegendreCacheCreatorBuilder(const std::string& name): LegendreCacheCreatorFactory(name) {} }; //---------------------------------------------------------------------------------------------------------------------- } // namespace trans } // namespace atlas atlas-0.46.0/src/atlas/trans/Trans.h0000664000175000017500000001717615160212070017361 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include "atlas/library/config.h" #include "atlas/trans/Cache.h" #include "atlas/util/Config.h" #include "atlas/util/ObjectHandle.h" //----------------------------------------------------------------------------- // Forward declarations namespace atlas { class Field; class FieldSet; class FunctionSpace; class Grid; class Domain; namespace functionspace { class Spectral; } } // namespace atlas //----------------------------------------------------------------------------- namespace atlas { namespace trans { //---------------------------------------------------------------------------------------------------------------------- class TransImpl; class Trans : DOXYGEN_HIDE(public util::ObjectHandle) { public: static void listBackends(std::ostream&); static bool hasBackend(const std::string&); static void backend(const std::string&); static std::string backend(); static void config(const eckit::Configuration&); static const eckit::Configuration& config(); using Handle::Handle; Trans() = default; Trans(const FunctionSpace& gp, const FunctionSpace& sp, const eckit::Configuration& = util::NoConfig()); Trans(const Grid&, int truncation, const eckit::Configuration& = util::NoConfig()); Trans(const Grid&, const Domain&, int truncation, const eckit::Configuration& = util::NoConfig()); Trans(const Cache&, const FunctionSpace& gp, const FunctionSpace& sp, const eckit::Configuration& = util::NoConfig()); Trans(const Cache&, const Grid&, int truncation, const eckit::Configuration& = util::NoConfig()); Trans(const Cache&, const Grid&, const Domain&, int truncation, const eckit::Configuration& = util::NoConfig()); void hash(eckit::Hash&) const; int truncation() const; size_t spectralCoefficients() const; const Grid& grid() const; const functionspace::Spectral& spectral() const; void dirtrans(const Field& gpfield, Field& spfield, const eckit::Configuration& = util::NoConfig()) const; void dirtrans(const FieldSet& gpfields, FieldSet& spfields, const eckit::Configuration& = util::NoConfig()) const; void dirtrans_wind2vordiv(const Field& gpwind, Field& spvor, Field& spdiv, const eckit::Configuration& = util::NoConfig()) const; void dirtrans_adj(const Field& spfield, Field& gpfield, const eckit::Configuration& = util::NoConfig()) const; void dirtrans_adj(const FieldSet& spfields, FieldSet& gpfields, const eckit::Configuration& = util::NoConfig()) const; void dirtrans_wind2vordiv_adj(const Field& spvor, const Field& spdiv, Field& gpwind, const eckit::Configuration& = util::NoConfig()) const; void invtrans(const Field& spfield, Field& gpfield, const eckit::Configuration& = util::NoConfig()) const; void invtrans(const FieldSet& spfields, FieldSet& gpfields, const eckit::Configuration& = util::NoConfig()) const; void invtrans_grad(const Field& spfield, Field& gradfield, const eckit::Configuration& = util::NoConfig()) const; void invtrans_grad(const FieldSet& spfields, FieldSet& gradfields, const eckit::Configuration& = util::NoConfig()) const; void invtrans_vordiv2wind(const Field& spvor, const Field& spdiv, Field& gpwind, const eckit::Configuration& = util::NoConfig()) const; void invtrans_adj(const Field& gpfield, Field& spfield, const eckit::Configuration& = util::NoConfig()) const; void invtrans_adj(const FieldSet& gpfields, FieldSet& spfields, const eckit::Configuration& = util::NoConfig()) const; void invtrans_grad_adj(const Field& gradfield, Field& spfield, const eckit::Configuration& = util::NoConfig()) const; void invtrans_grad_adj(const FieldSet& gradfields, FieldSet& spfields, const eckit::Configuration& = util::NoConfig()) const; void invtrans_vordiv2wind_adj(const Field& gpwind, Field& spvor, Field& spdiv, const eckit::Configuration& = util::NoConfig()) const; // -- IFS type fields -- // These fields have special interpretation required. You need to know what // you're doing. // See IFS trans library. /*! * @brief invtrans * @param nb_scalar_fields * @param scalar_spectra * @param nb_vordiv_fields * @param vorticity_spectra * @param divergence_spectra * @param gp_fields */ void invtrans(const int nb_scalar_fields, const double scalar_spectra[], const int nb_vordiv_fields, const double vorticity_spectra[], const double divergence_spectra[], double gp_fields[], const eckit::Configuration& = util::NoConfig()) const; /*! * @brief invtrans * @param nb_fields * @param scalar_spectra * @param scalar_fields */ void invtrans(const int nb_scalar_fields, const double scalar_spectra[], double gp_fields[], const eckit::Configuration& = util::NoConfig()) const; /*! * @brief Inverse transform of vorticity/divergence to wind(U/V) * @param nb_fields [in] Number of fields ( both components of wind count as 1 * ) */ void invtrans(const int nb_vordiv_fields, const double vorticity_spectra[], const double divergence_spectra[], double gp_fields[], const eckit::Configuration& = util::NoConfig()) const; /*! * @brief invtrans_adj * @param nb_scalar_fields * @param scalar_spectra * @param nb_vordiv_fields * @param vorticity_spectra * @param divergence_spectra * @param gp_fields */ void invtrans_adj(const int nb_scalar_fields, const double gp_fields[], const int nb_vordiv_fields, double vorticity_spectra[], double divergence_spectra[], double scalar_spectra[], const eckit::Configuration& = util::NoConfig()) const; /*! * @brief invtrans_adj * @param nb_fields * @param scalar_spectra * @param scalar_fields */ void invtrans_adj(const int nb_scalar_fields, const double gp_fields[], double scalar_spectra[], const eckit::Configuration& = util::NoConfig()) const; /*! * @brief Adjoint of Inverse transform of vorticity/divergence to wind(U/V) * @param nb_fields [in] Number of fields ( both components of wind count as 1 * ) */ void invtrans_adj(const int nb_vordiv_fields, const double gp_fields[], double vorticity_spectra[], double divergence_spectra[], const eckit::Configuration& = util::NoConfig()) const; /*! * @brief Direct transform of scalar fields */ void dirtrans(const int nb_fields, const double scalar_fields[], double scalar_spectra[], const eckit::Configuration& = util::NoConfig()) const; /*! * @brief Direct transform of wind(U/V) to vorticity/divergence * @param nb_fields [in] Number of fields ( both components of wind count as 1 * ) */ void dirtrans(const int nb_fields, const double wind_fields[], double vorticity_spectra[], double divergence_spectra[], const eckit::Configuration& = util::NoConfig()) const; }; //---------------------------------------------------------------------------------------------------------------------- } // namespace trans } // namespace atlas atlas-0.46.0/src/atlas/trans/VorDivToUV.h0000664000175000017500000001032715160212070020250 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include "atlas/library/config.h" #include "atlas/util/Config.h" #include "atlas/util/Object.h" #include "atlas/util/ObjectHandle.h" //----------------------------------------------------------------------------- // Forward declarations namespace atlas { class Field; class FieldSet; class FunctionSpace; class Grid; } // namespace atlas //----------------------------------------------------------------------------- namespace atlas { namespace trans { //----------------------------------------------------------------------------- class VorDivToUVImpl : public util::Object { public: virtual ~VorDivToUVImpl() = 0; virtual int truncation() const = 0; // -- IFS type fields -- // These fields have special interpretation required. You need to know what // you're doing. // See IFS trans library. /*! * @brief Compute spectral wind (U/V) from spectral vorticity/divergence * * U = u*cos(lat) * V = v*cos(lat) * * @param nb_fields [in] Number of fields * @param vorticity [in] Spectral vorticity * @param divergence [in] Spectral divergence * @param U [out] Spectral wind U = u*cos(lat) * @param V [out] Spectral wind V = v*cos(lat) */ virtual void execute(const int nb_coeff, const int nb_fields, const double vorticity[], const double divergence[], double U[], double V[], const eckit::Configuration& = util::NoConfig()) const = 0; }; // ------------------------------------------------------------------ class VorDivToUVFactory { public: /*! * \brief build VorDivToUV * \return VorDivToUVImpl */ static VorDivToUVImpl* build(const FunctionSpace& sp, const eckit::Configuration& = util::NoConfig()); static VorDivToUVImpl* build(int truncation, const eckit::Configuration& = util::NoConfig()); /*! * \brief list all registered trans implementations */ static void list(std::ostream&); static bool has(const std::string& name); private: std::string name_; virtual VorDivToUVImpl* make(const FunctionSpace& sp, const eckit::Configuration&) = 0; virtual VorDivToUVImpl* make(int truncation, const eckit::Configuration&) = 0; protected: VorDivToUVFactory(const std::string&); virtual ~VorDivToUVFactory(); }; //---------------------------------------------------------------------------------------------------------------------- template class VorDivToUVBuilder : public VorDivToUVFactory { virtual VorDivToUVImpl* make(const FunctionSpace& sp, const eckit::Configuration& config) { return new T(sp, config); } virtual VorDivToUVImpl* make(int truncation, const eckit::Configuration& config) { return new T(truncation, config); } public: VorDivToUVBuilder(const std::string& name): VorDivToUVFactory(name) {} }; //---------------------------------------------------------------------------------------------------------------------- class VorDivToUV : DOXYGEN_HIDE(public util::ObjectHandle) { public: using Handle::Handle; VorDivToUV() = default; VorDivToUV(const FunctionSpace& sp, const eckit::Configuration& = util::NoConfig()); VorDivToUV(int truncation, const eckit::Configuration& = util::NoConfig()); void hash(eckit::Hash&) const; int truncation() const; // -- IFS type fields -- // These fields have special interpretation required. You need to know what // you're doing. // See IFS trans library. virtual void execute(const int nb_coeff, const int nb_fields, const double vorticity[], const double divergence[], double U[], double V[], const eckit::Configuration& = util::NoConfig()) const; }; //---------------------------------------------------------------------------------------------------------------------- } // namespace trans } // namespace atlas atlas-0.46.0/src/atlas/output/0000775000175000017500000000000015160212070016316 5ustar alastairalastairatlas-0.46.0/src/atlas/output/Gmsh.h0000664000175000017500000000325215160212070017367 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include "atlas/output/Output.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/util/Config.h" namespace eckit { class Parametrisation; class PathName; } // namespace eckit namespace atlas { namespace output { namespace detail { class GmshIO; } } // namespace output } // namespace atlas namespace atlas { namespace output { // ----------------------------------------------------------------------------- class GmshFileStream : public std::ofstream { public: static std::string parallelPathName(const eckit::PathName& path, int part = mpi::rank()); GmshFileStream(const eckit::PathName& file_path, const char* mode, int part = mpi::rank()); }; // ----------------------------------------------------------------------------- class Gmsh : public Output { public: Gmsh(const Output& output); Gmsh(std::ostream&); Gmsh(std::ostream&, const eckit::Parametrisation&); Gmsh(const eckit::PathName&, const std::string& mode); Gmsh(const eckit::PathName&, const std::string& mode, const eckit::Parametrisation&); Gmsh(const eckit::PathName&); Gmsh(const eckit::PathName&, const eckit::Parametrisation&); }; // ----------------------------------------------------------------------------- } // namespace output } // namespace atlas atlas-0.46.0/src/atlas/output/Output.cc0000664000175000017500000001740615160212070020135 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ // file deepcode ignore CppMemoryLeak: static pointers for global registry are OK and will be cleaned up at end #include #include #include "eckit/thread/AutoLock.h" #include "eckit/thread/Mutex.h" #include "atlas/field/Field.h" #include "atlas/field/FieldSet.h" #include "atlas/functionspace/FunctionSpace.h" #include "atlas/mesh/Mesh.h" #include "atlas/output/Gmsh.h" #include "atlas/output/Output.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Log.h" using atlas::FieldSet; using atlas::FunctionSpace; using atlas::Mesh; using atlas::field::FieldImpl; using atlas::field::FieldSetImpl; namespace atlas { namespace output { static eckit::Mutex* local_mutex = nullptr; static std::map* m = nullptr; static pthread_once_t once = PTHREAD_ONCE_INIT; static void init() { local_mutex = new eckit::Mutex(); m = new std::map(); } namespace detail { OutputImpl::OutputImpl() = default; OutputImpl::~OutputImpl() = default; } // namespace detail Output::Output(const std::string& key, std::ostream& stream, const eckit::Parametrisation& params): Handle(detail::OutputFactory::build(key, stream, params)) {} /// Write mesh file const Output& Output::write(const Mesh& m, const eckit::Parametrisation& c) const { get()->write(m, c); return *this; } /// Write field to file const Output& Output::write(const Field& f, const eckit::Parametrisation& c) const { get()->write(f, c); return *this; } /// Write fieldset to file using FunctionSpace const Output& Output::write(const FieldSet& f, const eckit::Parametrisation& c) const { get()->write(f, c); return *this; } /// Write field to file using Functionspace const Output& Output::write(const Field& f, const FunctionSpace& fs, const eckit::Parametrisation& c) const { get()->write(f, fs, c); return *this; } /// Write fieldset to file using FunctionSpace const Output& Output::write(const FieldSet& f, const FunctionSpace& fs, const eckit::Parametrisation& c) const { get()->write(f, fs, c); return *this; } namespace detail { OutputFactory::OutputFactory(const std::string& name): name_(name) { pthread_once(&once, init); eckit::AutoLock lock(local_mutex); ATLAS_ASSERT(m); if (m->find(name) != m->end()) { throw_Exception("Duplicate OutputFactory entry " + name); } (*m)[name] = this; } OutputFactory::~OutputFactory() { pthread_once(&once, init); eckit::AutoLock lock(local_mutex); ATLAS_ASSERT(m); m->erase(name_); } void OutputFactory::list(std::ostream& out) { pthread_once(&once, init); eckit::AutoLock lock(local_mutex); ATLAS_ASSERT(m); const char* sep = ""; for (std::map::const_iterator j = m->begin(); j != m->end(); ++j) { out << sep << (*j).first; sep = ", "; } } const OutputImpl* OutputFactory::build(const std::string& name, std::ostream& stream) { pthread_once(&once, init); eckit::AutoLock lock(local_mutex); ATLAS_ASSERT(m); std::map::const_iterator j = m->find(name); Log::debug() << "Looking for OutputFactory [" << name << "]" << std::endl; if (j == m->end()) { Log::error() << "No OutputFactory for [" << name << "]" << std::endl; Log::error() << "OutputFactories are:" << std::endl; for (j = m->begin(); j != m->end(); ++j) { Log::error() << " " << (*j).first << std::endl; } throw_Exception(std::string("No OutputFactory called ") + name); } return (*j).second->make(stream); } const OutputImpl* OutputFactory::build(const std::string& name, std::ostream& stream, const eckit::Parametrisation& param) { pthread_once(&once, init); eckit::AutoLock lock(local_mutex); ATLAS_ASSERT(m); std::map::const_iterator j = m->find(name); Log::debug() << "Looking for OutputFactory [" << name << "]" << std::endl; if (j == m->end()) { Log::error() << "No OutputFactory for [" << name << "]" << std::endl; Log::error() << "OutputFactories are:" << std::endl; for (j = m->begin(); j != m->end(); ++j) { Log::error() << " " << (*j).first << std::endl; } throw_Exception(std::string("No OutputFactory called ") + name); } return (*j).second->make(stream, param); } extern "C" { void atlas__Output__delete(OutputImpl* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialisd atlas_Output"); delete This; } const OutputImpl* atlas__Output__create(const char* factory_key, std::ostream* stream, const eckit::Parametrisation* config) { ATLAS_ASSERT(config != nullptr, "Cannot access uninitialisd atlas_Config"); const OutputImpl* output(nullptr); { Output o(std::string{factory_key}, *stream, *config); output = o.get(); output->attach(); } output->detach(); return output; } void atlas__Output__write_mesh(const OutputImpl* This, Mesh::Implementation* mesh, const eckit::Parametrisation* params) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialisd atlas_Output"); Mesh m(mesh); This->write(m, *params); } void atlas__Output__write_fieldset(const OutputImpl* This, const FieldSetImpl* fieldset, const eckit::Parametrisation* config) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialisd atlas_Output"); ATLAS_ASSERT(fieldset != nullptr, "Cannot access uninitialisd atlas_FieldSet"); This->write(fieldset, *config); } void atlas__Output__write_field(const OutputImpl* This, const FieldImpl* field, const eckit::Parametrisation* config) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialisd atlas_Output"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialisd atlas_Field"); ATLAS_ASSERT(config != nullptr, "Cannot access uninitialisd atlas_Config"); This->write(field, *config); } void atlas__Output__write_fieldset_fs(const OutputImpl* This, const FieldSetImpl* fieldset, const functionspace::FunctionSpaceImpl* functionspace, const eckit::Parametrisation* params) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialisd atlas_Output"); ATLAS_ASSERT(fieldset != nullptr, "Cannot access uninitialisd atlas_FieldSet"); ATLAS_ASSERT(functionspace != nullptr, "Cannot access uninitialisd atlas_FunctionSpace"); This->write(fieldset, functionspace, *params); } void atlas__Output__write_field_fs(const OutputImpl* This, const FieldImpl* field, const functionspace::FunctionSpaceImpl* functionspace, const eckit::Parametrisation* config) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialisd atlas_Output"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialisd atlas_Field"); ATLAS_ASSERT(functionspace != nullptr, "Cannot access uninitialisd atlas_FunctionSpace"); ATLAS_ASSERT(config != nullptr, "Cannot access uninitialisd atlas_Config"); This->write(field, functionspace, *config); } } } // namespace detail } // namespace output } // namespace atlas atlas-0.46.0/src/atlas/output/GmshImpl.h0000664000175000017500000000477015160212070020217 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include "atlas/output/Output.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/util/Config.h" namespace atlas { namespace output { namespace detail { class GmshIO; } } // namespace output } // namespace atlas namespace atlas { namespace output { namespace detail { // ----------------------------------------------------------------------------- class GmshImpl : public OutputImpl { public: GmshImpl(std::ostream&); GmshImpl(std::ostream&, const eckit::Parametrisation&); GmshImpl(const eckit::PathName&, const std::string& mode); GmshImpl(const eckit::PathName&, const std::string& mode, const eckit::Parametrisation&); GmshImpl(const eckit::PathName&); GmshImpl(const eckit::PathName&, const eckit::Parametrisation&); virtual ~GmshImpl(); /// Write mesh file virtual void write(const Mesh&, const eckit::Parametrisation& = util::NoConfig()) const; /// Write field to file virtual void write(const Field&, const eckit::Parametrisation& = util::NoConfig()) const; /// Write fieldset to file using FunctionSpace virtual void write(const FieldSet&, const eckit::Parametrisation& = util::NoConfig()) const; /// Write field to file using Functionspace virtual void write(const Field&, const FunctionSpace&, const eckit::Parametrisation& = util::NoConfig()) const; /// Write fieldset to file using FunctionSpace virtual void write(const FieldSet&, const FunctionSpace&, const eckit::Parametrisation& = util::NoConfig()) const; public: struct Configuration { bool binary; bool edges; bool elements; bool gather; bool ghost; bool info; std::vector levels; std::string nodes; std::string file; std::string openmode; std::string coordinates; }; static void setGmshConfiguration(detail::GmshIO&, const Configuration&); private: mutable Configuration config_; void defaults(); }; // ----------------------------------------------------------------------------- } // namespace detail } // namespace output } // namespace atlas atlas-0.46.0/src/atlas/output/detail/0000775000175000017500000000000015160212070017560 5ustar alastairalastairatlas-0.46.0/src/atlas/output/detail/GmshInterface.cc0000664000175000017500000000242315160212070022607 0ustar alastairalastair/* * (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/output/detail/GmshImpl.h" #include "atlas/output/detail/GmshInterface.h" namespace atlas { namespace output { namespace detail { // ----------------------------------------------------------------------------- extern "C" { GmshImpl* atlas__output__Gmsh__create_pathname_mode(const char* pathname, const char* mode) { return new GmshImpl(std::string(pathname), std::string(mode)); } GmshImpl* atlas__output__Gmsh__create_pathname_mode_config(const char* pathname, const char* mode, const eckit::Parametrisation* config) { return new GmshImpl(std::string(pathname), std::string(mode), *config); } } // extern C //---------------------------------------------------------------------------------------------------------------------- } // namespace detail } // namespace output } // namespace atlas atlas-0.46.0/src/atlas/output/detail/GmshIO.h0000664000175000017500000001474715160212070021074 0ustar alastairalastair/* * (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 #include "atlas/functionspace/CellColumns.h" #include "atlas/functionspace/NodeColumns.h" #include "atlas/functionspace/StructuredColumns.h" #include "atlas/util/Metadata.h" namespace atlas { class Field; class FieldSet; class Mesh; namespace field { class FieldImpl; class FieldSetImpl; } // namespace field namespace functionspace { class FunctionSpaceImpl; } } // namespace atlas namespace atlas { namespace array { class Array; } } // namespace atlas namespace atlas { namespace util { namespace io { class GmshFortranInterface; } } // namespace util } // namespace atlas namespace atlas { namespace output { class Gmsh; } } // namespace atlas namespace atlas { namespace output { namespace detail { //---------------------------------------------------------------------------------------------------------------------- class GmshIO { public: // members using Metadata = util::Metadata; Metadata options; private: // types typedef std::ios_base::openmode openmode; public: // methods GmshIO(); ~GmshIO(); public: Mesh read(const eckit::PathName& file_path) const; void read(const eckit::PathName& file_path, Mesh& mesh) const; /// Write mesh file /// Extra file with available mesh information is written to: /// - filename_info.msh void write(const Mesh& mesh, const eckit::PathName& file_path) const; /// Write field to file /// Depending on argument "mode", the fields will be appended, /// or existing file will be overwritten void write(const Field& field, const eckit::PathName& file_path, openmode mode = std::ios::out) const; /// Write fieldset to file using FunctionSpace /// Depending on argument "mode", the fields will be appended, /// or existing file will be overwritten void write(const FieldSet& fieldset, const FunctionSpace&, const eckit::PathName& file_path, openmode mode = std::ios::out) const; /// Write field to file using Functionspace /// Depending on argument "mode", the fields will be appended, /// or existing file will be overwritten void write(const Field& field, const FunctionSpace&, const eckit::PathName& file_path, openmode mode = std::ios::out) const; private: /// Write fieldset to file using Nodes functionspace /// Depending on argument "mode", the fields will be appended, /// or existing file will be overwritten 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 Nodes functionspace /// Depending on argument "mode", the fields will be appended, /// or existing file will be overwritten void write_delegate(const FieldSet& fieldset, const functionspace::NoFunctionSpace&, 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 void write_delegate(const FieldSet& fieldset, const functionspace::StructuredColumns&, const eckit::PathName& file_path, openmode mode = std::ios::out) const; /// Write field to file using Nodes functionspace /// Depending on argument "mode", the fields will be appended, /// or existing file will be overwritten void write_delegate(const Field& field, const functionspace::NodeColumns&, const eckit::PathName& file_path, openmode mode = std::ios::out) const; /// Write field to file using Cells functionspace /// Depending on argument "mode", the fields will be appended, /// or existing file will be overwritten void write_delegate(const Field& field, const functionspace::CellColumns&, const eckit::PathName& file_path, openmode mode = std::ios::out) const; /// Write field to file using Nodes functionspace /// Depending on argument "mode", the fields will be appended, /// or existing file will be overwritten void write_delegate(const Field& field, const functionspace::NoFunctionSpace&, const eckit::PathName& file_path, openmode mode = std::ios::out) const; /// Write field to file using StructuredColumns functionspace /// Depending on argument "mode", the fields will be appended, /// or existing file will be overwritten void write_delegate(const Field& field, const functionspace::StructuredColumns&, const eckit::PathName& file_path, openmode mode = std::ios::out) const; }; //---------------------------------------------------------------------------------------------------------------------- // C wrapper interfaces to C++ routines extern "C" { GmshIO* atlas__Gmsh__new(); void atlas__Gmsh__delete(GmshIO* This); Mesh::Implementation* atlas__Gmsh__read(GmshIO* This, char* file_path); void atlas__Gmsh__write(GmshIO* This, Mesh::Implementation* mesh, char* file_path); Mesh::Implementation* atlas__read_gmsh(char* file_path); void atlas__write_gmsh_mesh(const Mesh::Implementation* mesh, char* file_path); void atlas__write_gmsh_fieldset(const field::FieldSetImpl* fieldset, functionspace::FunctionSpaceImpl* function_space, char* file_path, int mode); void atlas__write_gmsh_field(const field::FieldImpl* field, functionspace::FunctionSpaceImpl* function_space, char* file_path, int mode); } //---------------------------------------------------------------------------------------------------------------------- } // namespace detail } // namespace output } // namespace atlas atlas-0.46.0/src/atlas/output/detail/PointCloudIO.cc0000664000175000017500000003347215160212070022410 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ // file deepcode ignore MissingOpenCheckOnFile: False positive #include #include #include #include #include "eckit/filesystem/PathName.h" #include "atlas/array/ArrayView.h" #include "atlas/array/DataType.h" #include "atlas/array/MakeView.h" #include "atlas/field/Field.h" #include "atlas/field/FieldSet.h" #include "atlas/functionspace/FunctionSpace.h" #include "atlas/functionspace/NodeColumns.h" #include "atlas/mesh/Mesh.h" #include "atlas/mesh/Nodes.h" #include "atlas/output/detail/PointCloudIO.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Log.h" #include "atlas/util/CoordinateEnums.h" namespace atlas { namespace output { namespace detail { // ------------------------------------------------------------------ namespace { std::string sanitize_field_name(const std::string& s) { // replace non-printable characters, then trim right & left std::string r(s); std::replace_if(r.begin(), r.end(), ::isspace, '_'); r.erase(r.find_last_not_of('_') + 1); r.erase(0, r.find_first_not_of('_')); if (!r.length()) { r = "_"; } return r; } } // end anonymous namespace // ------------------------------------------------------------------ Mesh PointCloudIO::read(const eckit::PathName& path, std::vector& vfnames) { const std::string msg("PointCloudIO::read: "); Log::debug() << "PointCloudIO reading " << path << std::endl; Mesh mesh; vfnames.clear(); { std::string line; std::istringstream iss; std::ostringstream oss; size_t nb_pts; // # points/nodes size_t nb_columns; // # data columns (lon,lat,field1,field2,...) size_t nb_fld; // # fields (nb_fld := nb_columns-2) // open file and read all of header & data std::ifstream f(path.asString().c_str()); if (!f.is_open()) { throw_CantOpenFile(path.asString()); } // header, part 1: // determine number of rows/columns // (read all of line, look for "PointCloudIO" signature, nb_pts, nb_columns, // ...) std::getline(f, line); iss.str(line); iss >> line >> nb_pts >> nb_columns; if (line != "PointCloudIO") { std::stringstream errmsg; errmsg << msg << "beginning of file `" << path << "` not found (expected: PointCloudIO, got: " << line << ")"; throw_Exception(errmsg.str(), Here()); } if (nb_pts == 0) { throw_AssertionFailed(msg + " invalid number of points (failed: nb_pts>0)"); } if (nb_columns < 2) { throw_AssertionFailed(msg + " invalid number of columns (failed: nb_columns>=2)"); } mesh.nodes().resize(static_cast(nb_pts)); mesh::Nodes& nodes = mesh.nodes(); 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()); part.assign(0); // header, part 2: // determine columns' labels // (check end of first line for possible column labels, starting from // defaults) vfnames.resize(nb_columns); for (size_t j = 0; j < nb_columns; ++j) { std::stringstream name; name.str("column_"); name << (j + 1); vfnames[j] = (iss && iss >> line) ? sanitize_field_name(line) : name.str(); } // (preallocate data, and define fields without the first two columns // (lon,lat) because they are treated differently) vfnames.erase(vfnames.begin(), vfnames.begin() + 2); nb_fld = nb_columns - 2; // always >= 0, considering previous check std::vector> fields; for (size_t j = 0; j < nb_fld; ++j) { fields.emplace_back(array::make_view(nodes.add( Field(vfnames[j], array::make_datatype(), array::make_shape(static_cast(nb_pts)))))); } size_t i, j; // (index for node/row and field/column, out of scope to check // at end of loops) for (i = 0; f && i < nb_pts; ++i) { std::getline(f, line); iss.clear(); iss.str(line); // NOTE always expects (lon,lat) order, maybe make it configurable? PointXY pxy; iss >> pxy.x() >> pxy.y(); xy(i, (size_t)XX) = pxy.x(); xy(i, (size_t)YY) = pxy.y(); lonlat(i, (size_t)LON) = pxy.x(); lonlat(i, (size_t)LAT) = pxy.y(); glb_idx(i) = i + 1; for (j = 0; iss && j < nb_fld; ++j) { iss >> fields[j](i); } if (j < nb_fld) { oss << " Invalid number of fields in data section, on line " << (i + 1) << ", read " << j << " fields, expected " << nb_fld << "."; throw_AssertionFailed(msg + oss.str()); } } if (i < nb_pts) { oss << " Invalid number of lines in data section, read " << (i) << " lines, expected " << nb_pts << "."; throw_AssertionFailed(msg + oss.str()); } f.close(); } return mesh; } Mesh PointCloudIO::read(const eckit::PathName& path) { std::vector vfnames; return read(path, vfnames); } void PointCloudIO::write(const eckit::PathName& path, const Mesh& mesh) { const std::string msg("PointCloudIO::write: "); Log::debug() << "PointCloudIO writing " << path << std::endl; // operate in mesh function space, creating transversing data structures // @warning: several copy operations here const mesh::Nodes& nodes = mesh.nodes(); auto lonlat = array::make_view(nodes.lonlat()); if (!lonlat.size()) { throw_Exception(msg + "invalid number of points (failed: nb_pts>0)"); } // get the fields (sanitized) names and values // (bypasses fields ("lonlat"|"lonlat") as shape(1)!=1) std::vector vfnames; std::vector> vfvalues; for (idx_t i = 0; i < nodes.nb_fields(); ++i) { const Field& field = nodes.field(i); if (((field.rank() == 1 && field.shape(0) == lonlat.shape(0)) || (field.rank() == 2 && field.shape(0) == lonlat.shape(0) && field.shape(1) == 1)) && field.datatype() == array::DataType::real64()) { // FIXME: no support for non-double types! vfnames.push_back(sanitize_field_name(field.name())); vfvalues.push_back(array::make_view(field)); } } std::ofstream f(path.asString().c_str()); if (!f.is_open()) { throw_CantOpenFile(path.asString()); } const size_t Npts = lonlat.shape(0); const size_t Nfld = vfvalues.size(); // header f << "PointCloudIO\t" << Npts << '\t' << (2 + Nfld) << "\tlon\tlat"; for (size_t j = 0; j < Nfld; ++j) { f << '\t' << vfnames[j]; } f << '\n'; // data for (size_t i = 0; i < Npts; ++i) { f << lonlat(i, (size_t)0) << '\t' << lonlat(i, (size_t)1); for (size_t j = 0; j < Nfld; ++j) { f << '\t' << vfvalues[j](i); } f << '\n'; } f.close(); } void PointCloudIO::write(const eckit::PathName& path, const FieldSet& fieldset, const functionspace::NodeColumns& function_space) { const std::string msg("PointCloudIO::write: "); Log::debug() << "PointCloudIO writing " << path << std::endl; // operate in field sets with same grid and consistent size(s), creating // transversing data structures // @warning: several copy operations here ATLAS_ASSERT(fieldset.size()); array::ArrayView lonlat = array::make_view(function_space.nodes().xy()); if (!lonlat.size()) { throw_Exception(msg + "invalid number of points (failed: nb_pts>0)"); } // get the fields (sanitized) names and values // (bypasses fields ("lonlat"|"lonlat") as shape(1)!=1) std::vector vfnames; std::vector> vfvalues; for (idx_t i = 0; i < fieldset.size(); ++i) { const Field& field = fieldset[i]; if (field.shape(0) == lonlat.shape(0) && field.rank() == 1 && field.name() != "glb_idx") // FIXME: no support for non-int types! { vfnames.push_back(sanitize_field_name(field.name())); vfvalues.push_back(array::make_view(field)); } } std::ofstream f(path.asString().c_str()); if (!f.is_open()) { throw_CantOpenFile(path.asString()); } const size_t Npts = lonlat.shape(0), Nfld = vfvalues.size(); // header f << "PointCloudIO\t" << Npts << '\t' << (2 + Nfld) << "\tlon\tlat"; for (size_t j = 0; j < Nfld; ++j) { f << '\t' << vfnames[j]; } f << '\n'; f.precision(std::numeric_limits::digits10); // data for (size_t i = 0; i < Npts; ++i) { f << lonlat(i, (size_t)0) << '\t' << lonlat(i, (size_t)1); for (size_t j = 0; j < Nfld; ++j) { f << '\t' << vfvalues[j](i); } f << '\n'; } f.close(); } void PointCloudIO::write(const eckit::PathName& path, const std::vector& pts) { Log::debug() << "PointCloudIO writing " << path << std::endl; std::ofstream f(path.asString().c_str()); if (!f.is_open()) { throw_CantOpenFile(path.asString()); } // header f << "PointCloudIO\t" << pts.size() << '\t' << 2 << "\tlon\tlat\n"; // data for (size_t i = 0; i < pts.size(); ++i) { f << pts[i].lon() << '\t' << pts[i].lat() << '\n'; } f.close(); } void PointCloudIO::write(const eckit::PathName& path, const std::vector& lon, const std::vector& lat, const std::vector*>& vfvalues, const std::vector& vfnames) { Log::debug() << "PointCloudIO writing " << path << std::endl; const std::string msg("PointCloudIO::write: "); const size_t Npts(lon.size()), Nfld(vfvalues.size()); if (Npts != lat.size()) { throw_Exception(msg + "number of points inconsistent (failed: #lon == #lat)"); } if (Nfld != vfnames.size()) { throw_Exception(msg + "number of fields inconsistent (failed: #vfvalues == #vfnames)"); } for (size_t j = 0; j < Nfld; ++j) { if (Npts != vfvalues[j]->size()) { throw_Exception(msg + "number of points inconsistent (failed: " "#lon == #lat == #*vfvalues[])"); } } std::ofstream f(path.asString().c_str()); if (!f.is_open()) { throw_CantOpenFile(path.asString()); } // header f << "PointCloudIO\t" << Npts << '\t' << (2 + Nfld) << "\tlon\tlat"; for (size_t j = 0; j < Nfld; ++j) { f << '\t' << sanitize_field_name(vfnames[j]); } f << '\n'; // data for (size_t i = 0; i < Npts; ++i) { f << lon[i] << '\t' << lat[i]; for (size_t j = 0; j < Nfld; ++j) { f << '\t' << vfvalues[j]->operator[](i); } f << '\n'; } f.close(); } void PointCloudIO::write(const eckit::PathName& path, const int& nb_pts, const double* lon, const double* lat, const int& nb_fld, const double** afvalues, const char** afnames) { Log::debug() << "PointCloudIO writing " << path << std::endl; const std::string msg("PointCloudIO::write: "); const size_t Npts(nb_pts > 0 ? nb_pts : 0), Nfld(nb_fld > 0 && afvalues && afnames ? nb_fld : 0); if (!Npts) { throw_Exception(msg + "invalid number of points (nb_nodes)"); } if (!lon) { throw_Exception(msg + "invalid array describing longitude (lon)"); } if (!lat) { throw_Exception(msg + "invalid array describing latitude (lat)"); } std::ofstream f(path.asString().c_str()); if (!f.is_open()) { throw_CantOpenFile(path.asString()); } // header f << "PointCloudIO\t" << Npts << '\t' << (2 + Nfld) << "\tlon\tlat"; for (size_t j = 0; j < Nfld; ++j) { f << '\t' << sanitize_field_name(afnames[j]); } f << '\n'; // data for (size_t i = 0; i < Npts; ++i) { f << lon[i] << '\t' << lat[i]; for (size_t j = 0; j < Nfld; ++j) { f << '\t' << afvalues[j][i]; } f << '\n'; } f.close(); } // ------------------------------------------------------------------ // C wrapper interfaces to C++ routines // PointCloudIO* atlas__pointcloud__new() // { return new PointCloudIO(); } // // // void atlas__pointcloud__delete (PointCloudIO* This) // { delete This; } // // // mesh::Mesh* atlas__pointcloud__read (PointCloudIO* This, char* file_path) // { return This->read(file_path); } // // // mesh::Mesh* atlas__read_pointcloud (char* file_path) // { return PointCloudIO::read(file_path); } // // // void atlas__write_pointcloud_fieldset (char* file_path, const // field::FieldSetImpl* fieldset, const functionspace::detail::NodeColumns* // functionspace) // { PointCloudIO::write(file_path, *fieldset, *functionspace); } // ------------------------------------------------------------------ } // namespace detail } // namespace output } // namespace atlas atlas-0.46.0/src/atlas/output/detail/GmshImpl.h0000664000175000017500000000504115160212070021451 0ustar alastairalastair/* * (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/output/Output.h" #include "atlas/util/Config.h" namespace atlas { namespace output { namespace detail { class GmshIO; } // namespace detail } // namespace output } // namespace atlas namespace atlas { namespace output { namespace detail { // ----------------------------------------------------------------------------- class GmshImpl : public OutputImpl { public: GmshImpl(std::ostream&); GmshImpl(std::ostream&, const eckit::Parametrisation&); GmshImpl(const eckit::PathName&, const std::string& mode); GmshImpl(const eckit::PathName&, const std::string& mode, const eckit::Parametrisation&); GmshImpl(const eckit::PathName&); GmshImpl(const eckit::PathName&, const eckit::Parametrisation&); virtual ~GmshImpl(); /// Write mesh file virtual void write(const Mesh&, const eckit::Parametrisation& = util::NoConfig()) const; /// Write field to file virtual void write(const Field&, const eckit::Parametrisation& = util::NoConfig()) const; /// Write fieldset to file using FunctionSpace virtual void write(const FieldSet&, const eckit::Parametrisation& = util::NoConfig()) const; /// Write field to file using Functionspace virtual void write(const Field&, const FunctionSpace&, const eckit::Parametrisation& = util::NoConfig()) const; /// Write fieldset to file using FunctionSpace virtual void write(const FieldSet&, const FunctionSpace&, const eckit::Parametrisation& = util::NoConfig()) const; public: struct Configuration { bool binary; bool edges; bool elements; bool gather; bool ghost; bool info; bool configured_land_water; bool land; bool water; std::vector levels; std::string nodes; std::string file; std::string openmode; std::string coordinates; }; static void setGmshConfiguration(detail::GmshIO&, const Configuration&); private: mutable Configuration config_; void defaults(); }; // ----------------------------------------------------------------------------- } // namespace detail } // namespace output } // namespace atlas atlas-0.46.0/src/atlas/output/detail/GmshImpl.cc0000664000175000017500000001700215160212070021607 0ustar alastairalastair/* * (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 "atlas/field/Field.h" #include "atlas/field/FieldSet.h" #include "atlas/functionspace/FunctionSpace.h" #include "atlas/mesh/Mesh.h" #include "atlas/mesh/Nodes.h" #include "atlas/mesh/actions/BuildXYZField.h" #include "atlas/output/detail/GmshIO.h" #include "atlas/output/detail/GmshImpl.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Log.h" namespace atlas { namespace output { namespace detail { // ----------------------------------------------------------------------------- void GmshImpl::defaults() { config_.binary = false; config_.nodes = "xy"; config_.gather = false; config_.ghost = false; config_.elements = true; config_.edges = false; config_.levels.clear(); config_.file = "output.msh"; config_.info = false; config_.openmode = "w"; config_.coordinates = "xy"; config_.configured_land_water = false; config_.land = true; config_.water = true; } // ----------------------------------------------------------------------------- namespace /*anonymous*/ { // ----------------------------------------------------------------------------- void merge(GmshImpl::Configuration& present, const eckit::Parametrisation& update) { update.get("binary", present.binary); update.get("nodes", present.nodes); update.get("gather", present.gather); update.get("ghost", present.ghost); update.get("elements", present.elements); update.get("edges", present.edges); update.get("levels", present.levels); update.get("file", present.file); update.get("info", present.info); update.get("openmode", present.openmode); update.get("coordinates", present.coordinates); if (update.has("water") || update.has("land")) { update.get("land", present.land); update.get("water", present.water); present.configured_land_water = true; } } // ----------------------------------------------------------------------------- detail::GmshIO writer(const GmshImpl::Configuration& c) { detail::GmshIO gmsh; GmshImpl::setGmshConfiguration(gmsh, c); return gmsh; } // ----------------------------------------------------------------------------- std::ios_base::openmode openmode(const GmshImpl::Configuration& c) { std::ios_base::openmode omode(std::ios_base::out); if (std::string(c.openmode) == "w") { omode = std::ios_base::out; } else if (std::string(c.openmode) == "a") { omode = std::ios_base::app; } if (c.binary) { omode |= std::ios::binary; } return omode; } // ----------------------------------------------------------------------------- } // anonymous namespace // ----------------------------------------------------------------------------- void GmshImpl::setGmshConfiguration(detail::GmshIO& gmsh, const GmshImpl::Configuration& c) { gmsh.options.set("ascii", not c.binary); gmsh.options.set("nodes", c.nodes); gmsh.options.set("gather", c.gather); gmsh.options.set("ghost", c.ghost); gmsh.options.set("elements", c.elements); gmsh.options.set("edges", c.edges); gmsh.options.set("levels", c.levels); gmsh.options.set("info", c.info); gmsh.options.set("nodes", c.coordinates); if (c.configured_land_water) { gmsh.options.set("land", c.land); gmsh.options.set("water", c.water); } } // ----------------------------------------------------------------------------- GmshImpl::GmshImpl(std::ostream&) { defaults(); ATLAS_NOTIMPLEMENTED; // JIRA issue ATLAS-254 } // ----------------------------------------------------------------------------- GmshImpl::GmshImpl(std::ostream&, const eckit::Parametrisation& config) { defaults(); merge(config_, config); ATLAS_NOTIMPLEMENTED; // JIRA issue ATLAS-254 } // ----------------------------------------------------------------------------- GmshImpl::GmshImpl(const eckit::PathName& file, const std::string& mode) { defaults(); config_.file = file.asString(); config_.openmode = std::string(mode); } // ----------------------------------------------------------------------------- GmshImpl::GmshImpl(const eckit::PathName& file, const std::string& mode, const eckit::Parametrisation& config) { defaults(); merge(config_, config); config_.file = file.asString(); config_.openmode = std::string(mode); } // ----------------------------------------------------------------------------- GmshImpl::GmshImpl(const eckit::PathName& file) { defaults(); config_.file = file.asString(); } // ----------------------------------------------------------------------------- GmshImpl::GmshImpl(const eckit::PathName& file, const eckit::Parametrisation& config) { defaults(); merge(config_, config); config_.file = file.asString(); } // ----------------------------------------------------------------------------- GmshImpl::~GmshImpl() = default; // ----------------------------------------------------------------------------- void GmshImpl::write(const Mesh& mesh, const eckit::Parametrisation& config) const { GmshImpl::Configuration c = config_; merge(c, config); if (c.coordinates == "xyz" and not mesh.nodes().has_field("xyz")) { Log::debug() << "Building xyz representation for nodes" << std::endl; mesh::actions::BuildXYZField("xyz")(const_cast(mesh)); } writer(c).write(mesh, c.file); config_.openmode = "a"; } // ----------------------------------------------------------------------------- void GmshImpl::write(const Field& field, const eckit::Parametrisation& config) const { GmshImpl::Configuration c = config_; merge(c, config); writer(c).write(field, c.file, openmode(c)); config_.openmode = "a"; } // ----------------------------------------------------------------------------- void GmshImpl::write(const FieldSet& fields, const eckit::Parametrisation& config) const { GmshImpl::Configuration c = config_; merge(c, config); writer(c).write(fields, fields.field(0).functionspace(), c.file, openmode(c)); config_.openmode = "a"; } // ----------------------------------------------------------------------------- void GmshImpl::write(const Field& field, const FunctionSpace& functionspace, const eckit::Parametrisation& config) const { GmshImpl::Configuration c = config_; merge(c, config); writer(c).write(field, functionspace, c.file, openmode(c)); config_.openmode = "a"; } // ----------------------------------------------------------------------------- void GmshImpl::write(const FieldSet& fields, const FunctionSpace& functionspace, const eckit::Parametrisation& config) const { GmshImpl::Configuration c = config_; merge(c, config); writer(c).write(fields, functionspace, c.file, openmode(c)); config_.openmode = "a"; } // ----------------------------------------------------------------------------- static OutputBuilder __gmsh("gmsh"); // ----------------------------------------------------------------------------- } // namespace detail } // namespace output } // namespace atlas atlas-0.46.0/src/atlas/output/detail/PointCloudIO.h0000664000175000017500000001116215160212070022242 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include #include "atlas/util/Point.h" // forward declarations namespace eckit { class PathName; } namespace atlas { class Field; class FieldSet; } // namespace atlas namespace atlas { namespace grid { class Grid; class Unstructured; } // namespace grid } // namespace atlas namespace atlas { class Mesh; } namespace atlas { namespace functionspace { class NodeColumns; } } // namespace atlas namespace atlas { namespace output { namespace detail { /** * @brief PointCloudIO supports: * - reading Mesh * - writing of Mesh * @warning only supports reading/writing doubles scalar fields */ class PointCloudIO { public: /** * @brief Read PointCloudIO file into a Mesh * @param path input file path * @param Grid data structure pointer to use * @return Grid data structure pointer */ static Mesh read(const eckit::PathName& path); /** * @brief Read PointCloudIO file into a Mesh * @param path input file path * @param vfnames names of fields to read * @return Mesh pointer */ static Mesh read(const eckit::PathName& path, std::vector& vfnames); /** * @brief Write Grid to PointCloudIO file (overwrites possibly existing file) * @param path output file path * @param grid Grid data structure */ static void write(const eckit::PathName& path, const Mesh& mesh); /** * @brief Write FieldSet to PointCloudIO file (overwrites possibly existing * file) * @param path output file path * @param fieldset FieldSet data structure */ static void write(const eckit::PathName& path, const FieldSet& fieldset, const functionspace::NodeColumns& function_space); /** * @brief Write lan/lon to PointCloudIO file (overwrites possibly existing file) * @note length of vectors lat and lon should be the same * @param path output file path * @param lon vector containing longitude values * @param lat vector containing latitude values */ static void write(const eckit::PathName& path, const std::vector& pts); /** * @brief Write lan/lon and fields to PointCloudIO file (overwrites possibly * existing file) * @param path output file path * @param nb_pts number of points in unstructured grid * @param lon array (of length nb_pts) pointer containing longitude * @param lat array (of length nb_pts) pointer containing latitude * @param nb_fld number of fields (default none) * @param afvalues array (of length nb_fld) of arrays (of length nb_pts), * containing field values * @param afnames array (of length nb_fld) of field names */ static void write(const eckit::PathName& path, const int& nb_pts, const double* lon, const double* lat, const int& nb_fld = 0, const double** afvalues = NULL, const char** afnames = NULL); /** * @brief Write lan/lon and fields to PointCloudIO file (overwrites possibly * existing file) * @note length of vectors lat, lon and *vfvalues[] (each pointee) should be the * same * @note length of vectors vfvalues and vfnames should be the same * @param path output file path * @param lon vector containing longitude values * @param lat vector containing latitude values * @param vfvalues vector of vector pointers, each pointing to field values * vectors * @param vfnames vector of field names */ static void write(const eckit::PathName& path, const std::vector& lon, const std::vector& lat, const std::vector*>& vfvalues = std::vector*>(), const std::vector& vfnames = std::vector()); }; // C wrapper interfaces to C++ routines extern "C" { // PointCloudIO* atlas__pointcloud__new (); // void atlas__pointcloud__delete(PointCloudIO* This); // mesh::Mesh* atlas__pointcloud__read(PointCloudIO* This, char* file_path); // mesh::Mesh* atlas__read_pointcloud(char* file_path); // void atlas__write_pointcloud_fieldset(char* file_path, const // field::FieldImpl* fieldset, const functionspace::FunctionSpaceImpl* // functionspace); // void atlas__write_pointcloud_field(char* file_path, const field::FieldImpl* // field, const functionspace::FunctionSpaceImpl* functionspace); } } // namespace detail } // namespace output } // namespace atlas atlas-0.46.0/src/atlas/output/detail/GmshIO.cc0000664000175000017500000017412215160212070021224 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ // file deepcode ignore MissingOpenCheckOnFile: False positive #include #include #include #include #include #include "eckit/filesystem/PathName.h" #include "eckit/config/Resource.h" #include "atlas/array.h" #include "atlas/array/ArrayView.h" #include "atlas/array/IndexView.h" #include "atlas/array/MakeView.h" #include "atlas/field/Field.h" #include "atlas/field/FieldSet.h" #include "atlas/field/MissingValue.h" #include "atlas/functionspace/FunctionSpace.h" #include "atlas/mesh/ElementType.h" #include "atlas/mesh/Elements.h" #include "atlas/mesh/HybridElements.h" #include "atlas/mesh/Mesh.h" #include "atlas/mesh/Nodes.h" #include "atlas/output/detail/GmshIO.h" #include "atlas/parallel/GatherScatter.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Log.h" #include "atlas/util/Constants.h" #include "atlas/util/CoordinateEnums.h" using atlas::functionspace::NodeColumns; using atlas::util::Metadata; using atlas::util::Topology; using eckit::PathName; namespace atlas { namespace output { namespace detail { namespace { class GmshFile : public std::ofstream { public: GmshFile(const PathName& file_path, std::ios_base::openmode mode, int part = static_cast(mpi::rank())) { PathName par_path(file_path); int mpi_size = static_cast(mpi::size()); if (mpi::size() == 1 || part == -1) { std::ofstream::open(par_path.localPath(), mode); } else { if (mpi::rank() == 0) { PathName par_path(file_path); std::ofstream par_file(par_path.localPath(), std::ios_base::out); for (int p = 0; p < mpi_size; ++p) { PathName loc_path(file_path); // loc_path = loc_path.baseName(false) + "_p" + to_str(p) + ".msh"; loc_path = loc_path.baseName(false) + ".msh.p" + std::to_string(p); par_file << "Merge \"" << loc_path << "\";" << std::endl; } par_file.close(); } PathName path(file_path); // path = path.dirName() + "/" + path.baseName(false) + "_p" + // to_str(part) + ".msh"; path = path.dirName() + "/" + path.baseName(false) + ".msh.p" + std::to_string(part); std::ofstream::open(path.localPath(), mode); } } }; enum GmshElementTypes { LINE = 1, TRIAG = 2, QUAD = 3, POINT = 15 }; // ---------------------------------------------------------------------------- void write_header_ascii(std::ostream& out) { out << "$MeshFormat\n"; out << "2.2 0 " << sizeof(double) << "\n"; out << "$EndMeshFormat\n"; } // ---------------------------------------------------------------------------- // ---------------------------------------------------------------------------- void write_header_binary(std::ostream& out) { out << "$MeshFormat\n"; out << "2.2 1 " << sizeof(double) << "\n"; int one = 1; out.write(reinterpret_cast(&one), sizeof(int)); out << "\n$EndMeshFormat\n"; } // ---------------------------------------------------------------------------- namespace { // anonymous template array::LocalView make_level_view(const Field& field, int ndata, int jlev) { using namespace array; if (field.levels()) { if (field.variables()) { return make_view(field).slice(Range::to(ndata), jlev, Range::all()); } else { return make_view(field).slice(Range::to(ndata), jlev, Range::dummy()); } } else { if (field.variables()) { return make_view(field).slice(Range::to(ndata), Range::all()); } else { return make_view(field).slice(Range::to(ndata), Range::dummy()); } } } template void write_level(std::ostream& out, GlobalIndex gidx, const array::LocalView& data, IncludeIndex include) { using value_type = typename std::remove_const::type; int ndata = data.shape(0); int nvars = data.shape(1); if (nvars == 1) { for (idx_t n = 0; n < ndata; ++n) { if (include(n)) { out << gidx(n) << " " << data(n, 0) << "\n"; } } } else if (nvars <= 3) { std::array data_vec; data_vec.fill(static_cast(0)); for (idx_t n = 0; n < ndata; ++n) { if (include(n)) { for (idx_t v = 0; v < nvars; ++v) { data_vec[v] = data(n, v); } out << gidx(n); for (int v = 0; v < 3; ++v) { out << " " << data_vec[v]; } out << "\n"; } } } else if (nvars <= 9) { std::array data_vec; data_vec.fill(static_cast(0)); if (nvars == 4) { for (int n = 0; n < ndata; ++n) { if (include(n)) { for (int i = 0; i < 2; ++i) { for (int j = 0; j < 2; ++j) { data_vec[i * 3 + j] = data(n, i * 2 + j); } } out << gidx(n); for (int v = 0; v < 9; ++v) { out << " " << data_vec[v]; } out << "\n"; } } } else if (nvars == 9) { for (int n = 0; n < ndata; ++n) { if (include(n)) { for (int i = 0; i < 3; ++i) { for (int j = 0; j < 3; ++j) { data_vec[i * 3 + j] = data(n, i * 2 + j); } } out << gidx(n); for (int v = 0; v < 9; ++v) { out << " " << data_vec[v]; } out << "\n"; } } } else { ATLAS_NOTIMPLEMENTED; } } else { ATLAS_NOTIMPLEMENTED; } } template void write_level(std::ostream& out, GlobalIndex gidx, const array::LocalView& data) { write_level(out, gidx, data, [](idx_t) { return true; }); } std::vector get_levels(int nlev, const Metadata& gmsh_options) { std::vector lev; std::vector gmsh_levels; gmsh_options.get("levels", gmsh_levels); if (gmsh_levels.empty() || nlev == 1) { lev.resize(nlev); for (int ilev = 0; ilev < nlev; ++ilev) { lev[ilev] = ilev; } } else { lev = gmsh_levels; } return lev; } std::string field_lev(const Field& field, int jlev) { if (field.levels()) { char str[6] = {0, 0, 0, 0, 0, 0}; auto str_len = std::snprintf(str, sizeof(str), "[%03d]", jlev); ATLAS_ASSERT(str_len == 5); return std::string(str); } else { return std::string(); } } double field_time(const Field& field) { return field.metadata().has("time") ? field.metadata().get("time") : 0.; } int field_step(const Field& field) { return field.metadata().has("step") ? field.metadata().get("step") : 0; } int field_vars(int nvars) { if (nvars == 1) { return nvars; } else if (nvars <= 3) { return 3; } else if (nvars <= 9) { return 9; } else { return nvars; } } } // namespace // ---------------------------------------------------------------------------- template void write_field_nodes(const Metadata& gmsh_options, const functionspace::NodeColumns& function_space, const Field& field, std::ostream& out) { Log::debug() << "writing NodeColumns field " << field.name() << " defined in NodeColumns..." << std::endl; bool gather(gmsh_options.get("gather") && mpi::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_nodes(), field.shape(0)); idx_t nvars = std::max(1, field.variables()); auto gidx = array::make_view(function_space.nodes().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.nodes().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_nodes_global(), field_glb.shape(0)); } auto missing = field::MissingValue(field); std::vector lev = get_levels(nlev, gmsh_options); for (size_t ilev = 0; ilev < lev.size(); ++ilev) { int jlev = lev[ilev]; if ((gather && mpi::rank() == 0) || !gather) { auto data = gather ? make_level_view(field_glb, ndata, jlev) : make_level_view(field, ndata, jlev); auto include_idx = [&](idx_t n) { for (idx_t v = 0; v < nvars; ++v) { if (missing(data(n, v))) { return false; } } return true; }; idx_t ndata_nonmissing = [&] { if (missing) { idx_t c = 0; for (idx_t n = 0; n < ndata; ++n) { c += include_idx(n); } return c; } return ndata; }(); out << "$NodeData\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_nonmissing << "\n"; out << mpi::rank() << "\n"; if (missing) { write_level(out, gidx, data, include_idx); } else { write_level(out, gidx, data); } out << "$EndNodeData\n"; } } } // ---------------------------------------------------------------------------- template void write_field_nodes(const Metadata& gmsh_options, const functionspace::NoFunctionSpace& function_space, const Field& field, std::ostream& out) { Log::debug() << "writing field " << field.name() << " defined without functionspace..." << std::endl; // unused: bool binary( !gmsh_options.get( "ascii" ) ); idx_t nlev = std::max(1, field.levels()); idx_t ndata = field.shape(0); idx_t nvars = std::max(1, field.variables()); auto gidx = [](idx_t inode) { return inode + 1; }; auto missing = field::MissingValue(field); std::vector lev = get_levels(nlev, gmsh_options); for (size_t ilev = 0; ilev < lev.size(); ++ilev) { int jlev = lev[ilev]; auto data = make_level_view(field, ndata, jlev); auto include_idx = [&](idx_t n) { for (idx_t v = 0; v < nvars; ++v) { if (missing(data(n, v))) { return false; } } return true; }; idx_t ndata_nonmissing = [&] { if (missing) { idx_t c = 0; for (idx_t n = 0; n < ndata; ++n) { c += include_idx(n); } return c; } return ndata; }(); out << "$NodeData\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_nonmissing << "\n"; out << mpi::rank() << "\n"; if (missing) { write_level(out, gidx, data, include_idx); } else { write_level(out, gidx, data); } out << "$EndNodeData\n"; } } // ---------------------------------------------------------------------------- // ---------------------------------------------------------------------------- void print_field_lev(char field_lev[], size_t size, int jlev) { ATLAS_ASSERT(size > 5); std::snprintf(field_lev, size, "[%03d]", jlev); } /* unused void print_field_lev( char field_lev[], long jlev ) { std::sprintf( field_lev, "[%03ld]", jlev ); } void print_field_lev( char field_lev[], unsigned long jlev ) { std::sprintf( field_lev, "[%03lu]", jlev ); } **/ // ---------------------------------------------------------------------------- template void write_field_nodes(const Metadata& gmsh_options, const functionspace::StructuredColumns& function_space, const Field& field, std::ostream& out) { Log::debug() << "writing StructuredColumns field " << field.name() << "..." << std::endl; bool gather(gmsh_options.get("gather") && mpi::size() > 1); // unused: bool binary( !gmsh_options.get( "ascii" ) ); idx_t nlev = std::max(1, field.levels()); idx_t ndata = std::min(function_space.sizeOwned(), field.shape(0)); idx_t nvars = std::max(1, field.variables()); auto gidx = array::make_view(function_space.global_index()); Field gidx_glb; Field field_glb; if (gather) { gidx_glb = function_space.createField(function_space.global_index(), option::name("gidx_glb") | option::global()); function_space.gather(function_space.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 = field_glb.shape(0); } auto missing = field::MissingValue(field); std::vector lev = get_levels(nlev, gmsh_options); for (size_t ilev = 0; ilev < lev.size(); ++ilev) { int jlev = lev[ilev]; char field_lev[6] = {0, 0, 0, 0, 0, 0}; if (field.levels()) { print_field_lev(field_lev, sizeof(field_lev), jlev); } auto data = gather ? make_level_view(field_glb, ndata, jlev) : make_level_view(field, ndata, jlev); auto include_idx = [&](idx_t n) { for (idx_t v = 0; v < nvars; ++v) { if (missing(data(n, v))) { return false; } } return true; }; idx_t ndata_nonmissing = [&] { if (missing) { idx_t c = 0; for (idx_t n = 0; n < ndata; ++n) { c += include_idx(n); } return c; } return ndata; }(); out << "$NodeData\n"; out << "1\n"; out << "\"" << field.name() << field_lev << "\"\n"; out << "1\n"; out << field_time(field) << "\n"; out << "4\n"; out << field_step(field) << "\n"; out << field_vars(nvars) << "\n"; out << ndata_nonmissing << "\n"; out << mpi::rank() << "\n"; if (missing) { write_level(out, gidx, data, include_idx); } else { write_level(out, gidx, data); } out << "$EndNodeData\n"; } } // ---------------------------------------------------------------------------- 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") && mpi::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()); auto 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)); } auto missing = field::MissingValue(field); std::vector lev = get_levels(nlev, gmsh_options); for (size_t ilev = 0; ilev < lev.size(); ++ilev) { int jlev = lev[ilev]; auto data = gather ? make_level_view(field_glb, ndata, jlev) : make_level_view(field, ndata, jlev); auto include_idx = [&](idx_t n) { for (idx_t v = 0; v < nvars; ++v) { if (missing(data(n, v))) { return false; } } return true; }; idx_t ndata_nonmissing = [&] { if (missing) { idx_t c = 0; for (idx_t n = 0; n < ndata; ++n) { c += include_idx(n); } return c; } return ndata; }(); if ((gather && mpi::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_nonmissing << "\n"; out << mpi::rank() << "\n"; if (missing) { write_level(out, gidx, data, include_idx); } else { write_level(out, gidx, data); } out << "$EndElementData\n"; } } #endif } // ---------------------------------------------------------------------------- #if 0 template< typename DATA_TYPE > void write_field_elems(const Metadata& gmsh_options, const FunctionSpace& function_space, const Field& field, std::ostream& out) { Log::info() << "writing field " << field.name() << "..." << std::endl; bool gather( gmsh_options.get("gather") ); bool binary( !gmsh_options.get("ascii") ); int nlev = field.metadata().has("nb_levels") ? field.metadata().get("nb_levels") : 1; int ndata = field.shape(0); int nvars = field.shape(1)/nlev; array::ArrayView gidx ( function_space.field( "glb_idx" ) ); array::ArrayView data ( field ); array::ArrayT field_glb_arr; array::ArrayT gidx_glb_arr; if( gather ) { mpl::GatherScatter& fullgather = function_space.fullgather(); ndata = fullgather.glb_dof(); field_glb_arr.resize(ndata,field.shape(1)); gidx_glb_arr.resize(ndata); array::ArrayView data_glb( field_glb_arr ); array::ArrayView gidx_glb( gidx_glb_arr ); fullgather.gather( gidx, gidx_glb ); fullgather.gather( data, data_glb ); gidx = array::ArrayView( gidx_glb_arr ); data = data_glb; } double time = field.metadata().has("time") ? field.metadata().get("time") : 0.; size_t step = field.metadata().has("step") ? field.metadata().get("step") : 0 ; int nnodes = IndexView( function_space.field("nodes") ).shape(1); for (int jlev=0; jlev(&gidx(jelem)),sizeof(int)); out.write(reinterpret_cast(&nnodes),sizeof(int)); for (size_t n=0; n(&value),sizeof(double)); } } } else if( nvars <= 3 ) { double value[3] = {0,0,0}; for (size_t jelem=0; jelem(&value),sizeof(double)*3); } } } out <<"\n"; } else { if( nvars == 1) { for (size_t jelem=0; jelem data_vec(3,0.); for (size_t jelem=0; jelem("nodes", "xy"); // Gather fields to one proc before writing options.set("gather", false); // Output of ghost nodes / elements options.set("ghost", false); // ASCII format (true) or binary (false) options.set("ascii", true); // Output of elements options.set("elements", true); // Output of edges options.set("edges", true); // Levels of fields to use options.set>("levels", std::vector()); } GmshIO::~GmshIO() = default; Mesh GmshIO::read(const PathName& file_path) const { Mesh mesh; GmshIO::read(file_path, mesh); return mesh; } namespace { mesh::ElementType* make_element_type(int type) { if (type == QUAD) { return mesh::ElementType::create("Quadrilateral"); } if (type == TRIAG) { return mesh::ElementType::create("Triangle"); } if (type == LINE) { return mesh::ElementType::create("Line"); } throw_Exception("Element type not supported", Here()); } } // namespace void GmshIO::read(const PathName& file_path, Mesh& mesh) const { std::ifstream file; file.open(file_path.localPath(), std::ios::in | std::ios::binary); if (!file.is_open()) { throw_CantOpenFile(file_path); } std::string line; while (line != "$MeshFormat") { std::getline(file, line); } double version; int binary; int size_of_real; file >> version >> binary >> size_of_real; while (line != "$Nodes") { std::getline(file, line); } // Create nodes idx_t nb_nodes; file >> nb_nodes; mesh.nodes().resize(nb_nodes); mesh::Nodes& nodes = mesh.nodes(); //nodes.add( Field( "xyz", array::make_datatype(), array::make_shape( nb_nodes, 3 ) ) ); // 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; double x, y, z; double xyz[3]; double xmax = -std::numeric_limits::max(); double zmax = -std::numeric_limits::max(); gidx_t max_glb_idx = 0; while (binary && file.peek() == '\n') { file.get(); } for (idx_t n = 0; n < nb_nodes; ++n) { if (binary) { file.read(reinterpret_cast(&g), sizeof(int)); file.read(reinterpret_cast(&xyz), sizeof(double) * 3); x = xyz[XX]; y = xyz[YY]; z = xyz[ZZ]; } else { file >> g >> x >> y >> z; } 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); } int nb_elements = 0; while (line != "$Elements") { std::getline(file, line); } file >> nb_elements; if (binary) { while (file.peek() == '\n') { file.get(); } int accounted_elems = 0; while (accounted_elems < nb_elements) { int header[3]; int data[100]; file.read(reinterpret_cast(&header), sizeof(int) * 3); int etype = header[0]; size_t netype = header[1]; size_t ntags = header[2]; accounted_elems += netype; mesh::Elements* elements; if (etype == LINE) { size_t jtype = mesh.edges().add(make_element_type(etype), netype); elements = &mesh.edges().elements(jtype); } else { size_t jtype = mesh.cells().add(make_element_type(etype), netype); elements = &mesh.cells().elements(jtype); } size_t nnodes_per_elem = elements->element_type().nb_nodes(); mesh::BlockConnectivity& conn = elements->node_connectivity(); array::ArrayView egidx = array::make_view(elements->global_index()); array::ArrayView epart = array::make_view(elements->partition()); size_t dsize = 1 + ntags + nnodes_per_elem; int part; for (size_t e = 0; e < netype; ++e) { file.read(reinterpret_cast(&data), sizeof(int) * dsize); part = 0; egidx(e) = data[0]; epart(e) = part; for (size_t n = 0; n < nnodes_per_elem; ++n) { conn.set(e, n, glb_to_loc[data[1 + ntags + n]]); } } } } else { // Find out which element types are inside int position = file.tellg(); std::vector nb_etype(20, 0); int elements_max_glb_idx(0); int etype; for (int e = 0; e < nb_elements; ++e) { file >> g >> etype; std::getline(file, line); // finish line ++nb_etype[etype]; elements_max_glb_idx = std::max(elements_max_glb_idx, g); } // Allocate data structures for quads, triags, edges int nb_quads = nb_etype[QUAD]; int nb_triags = nb_etype[TRIAG]; //int nb_edges = nb_etype[LINE]; 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::BlockConnectivity& quad_nodes = quads.node_connectivity(); mesh::BlockConnectivity& triag_nodes = triags.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()); 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() ); // Now read all elements file.seekg(position, std::ios::beg); int gn0, gn1, gn2, gn3; int quad = 0, triag = 0; int ntags, tags[100]; for (int e = 0; e < nb_elements; ++e) { file >> g >> etype >> ntags; for (int t = 0; t < ntags; ++t) { file >> tags[t]; } int part = 0; if (ntags > 3) { part = std::max(part, *std::max_element(tags + 3, tags + ntags - 1)); // one positive, others negative } idx_t enodes[4] = {-1, -1, -1, -1}; switch (etype) { case (QUAD): file >> gn0 >> gn1 >> gn2 >> gn3; quad_glb_idx(quad) = g; quad_part(quad) = part; enodes[0] = glb_to_loc[gn0]; enodes[1] = glb_to_loc[gn1]; enodes[2] = glb_to_loc[gn2]; enodes[3] = glb_to_loc[gn3]; quad_nodes.set(quad, enodes); ++quad; break; case (TRIAG): file >> gn0 >> gn1 >> gn2; triag_glb_idx(triag) = g; triag_part(triag) = part; enodes[0] = glb_to_loc[gn0]; enodes[1] = glb_to_loc[gn1]; enodes[2] = glb_to_loc[gn2]; triag_nodes.set(triag, enodes); ++triag; 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; break; case (POINT): file >> gn0; break; default: std::cout << "etype " << etype << std::endl; throw_Exception("ERROR: element type not supported", Here()); } } } file.close(); } void GmshIO::write(const Mesh& mesh, const PathName& file_path) const { mpi::Scope scope(mesh.mpi_comm()); int part = mesh.metadata().has("part") ? mesh.metadata().get("part") : mpi::rank(); bool include_ghost = options.get("ghost") && options.get("elements"); bool land_water_flag = options.has("water") || options.has("land"); bool include_water = options.getBool("water", false); bool include_land = options.getBool("land", false); std::string nodes_field = options.get("nodes"); const mesh::Nodes& nodes = mesh.nodes(); const Field coords_field = nodes.field(nodes_field); array::ArrayT dummy_double(1, 1); array::ArrayT dummy_idx(1, 1); bool coords_is_idx = coords_field.datatype().kind() == array::make_datatype().kind(); auto coords = array::make_view(coords_is_idx ? dummy_double : coords_field.array()); auto coords_idx = array::make_view(coords_is_idx ? coords_field.array() : dummy_idx); bool coords_is_lonlat = coords_field.name() == "lonlat"; double filter_edge_ratio = eckit::Resource("$ATLAS_GMSH_FILTER_EDGE_RATIO",0.); auto glb_idx = array::make_view(nodes.global_index()); const idx_t surfdim = nodes.field(nodes_field).shape(1); // nb of variables in coords bool include_patch = (surfdim == 3); ATLAS_ASSERT(surfdim == 2 || surfdim == 3); Log::debug() << "writing mesh to gmsh file " << file_path << std::endl; bool binary = !options.get("ascii"); openmode mode = std::ios::out; if (binary) { mode = std::ios::out | std::ios::binary; } GmshFile file(file_path, mode, part); // Header if (binary) { write_header_binary(file); } else { write_header_ascii(file); } // Nodes const idx_t nb_nodes = nodes.size(); file << "$Nodes\n"; file << nb_nodes << "\n"; double xyz[3] = {0., 0., 0.}; for (idx_t n = 0; n < nb_nodes; ++n) { gidx_t g = glb_idx(n); if (coords_is_idx) { for (idx_t d = 0; d < surfdim; ++d) { xyz[d] = coords_idx(n, d); } } else { for (idx_t d = 0; d < surfdim; ++d) { xyz[d] = coords(n, d); } } if (binary) { file.write(reinterpret_cast(&g), sizeof(gidx_t)); file.write(reinterpret_cast(&xyz), sizeof(double) * 3); } else { file << g << " " << xyz[XX] << " " << xyz[YY] << " " << xyz[ZZ] << "\n"; } } if (binary) { file << "\n"; } file << "$EndNodes\n"; // Elements file << "$Elements\n"; { std::vector grouped_elements; if (options.get("elements")) { grouped_elements.push_back(&mesh.cells()); } if (options.get("edges")) { grouped_elements.push_back(&mesh.edges()); } idx_t nb_elements(0); for (const mesh::HybridElements* hybrid : grouped_elements) { nb_elements += hybrid->size(); const auto hybrid_halo = array::make_view(hybrid->halo()); const auto hybrid_flags = array::make_view(hybrid->flags()); const auto& node_connectivity = hybrid->node_connectivity(); auto include = [&](idx_t e) { auto topology = Topology::view(hybrid_flags(e)); if (land_water_flag && not(include_water && include_land)) { if (include_water && !topology.check(Topology::WATER)) { return false; } if (include_land && !topology.check(Topology::LAND)) { return false; } } if (not include_ghost) { if (topology.check(Topology::GHOST) || hybrid_halo(e)) { return false; } } if (not include_patch) { if (topology.check(Topology::PATCH)) { return false; } } if (topology.check(Topology::INVALID)) { return false; } if (coords_is_lonlat) { if (node_connectivity.cols(e) == 3) { auto x0 = [&]() { return coords(node_connectivity(e,0),LON); }; auto x1 = [&]() { return coords(node_connectivity(e,1),LON); }; auto x2 = [&]() { return coords(node_connectivity(e,2),LON); }; auto y0 = [&]() { return coords(node_connectivity(e,0),LAT); }; auto y1 = [&]() { return coords(node_connectivity(e,1),LAT); }; auto y2 = [&]() { return coords(node_connectivity(e,2),LAT); }; auto triangle_area = (x0() * (y1() - y2()) + x1() * (y2() - y0()) + x2() * (y0() - y1())) * 0.5; if (triangle_area <= 0 ) { return false; } // edge length comparison if (filter_edge_ratio > 0.) { auto dx10 = (x1()-x0()); auto dx21 = (x2()-x1()); auto dx02 = (x0()-x2()); auto dy10 = (y1()-y0()); auto dy21 = (y2()-y1()); auto dy02 = (y0()-y2()); auto d10 = dx10*dx10 + dy10*dy10; auto d21 = dx21*dx21 + dy21*dy21; auto d02 = dx02*dx02 + dy02*dy02; auto dmin = std::min(d10, std::min(d21, d02)); auto dmax = std::max(d10, std::max(d21, d02)); if (dmax > filter_edge_ratio * dmin) { return false; } } } } return true; }; for (idx_t e = 0; e < hybrid->size(); ++e) { if (not include(e)) { --nb_elements; } } } file << nb_elements << "\n"; for (const mesh::HybridElements* hybrid : grouped_elements) { for (idx_t etype = 0; etype < hybrid->nb_types(); ++etype) { const mesh::Elements& elements = hybrid->elements(etype); const mesh::ElementType& element_type = elements.element_type(); const mesh::BlockConnectivity& node_connectivity = elements.node_connectivity(); size_t nb_nodes = node_connectivity.cols(); int gmsh_elem_type; if (element_type.name() == "Line") { gmsh_elem_type = 1; } else if (element_type.name() == "Triangle") { gmsh_elem_type = 2; } else if (element_type.name() == "Quadrilateral") { gmsh_elem_type = 3; } else if (element_type.name() == "Pentagon") { // Hack: treat as quadrilateral and ignore 5th point gmsh_elem_type = 3; nb_nodes = 4; } else { ATLAS_NOTIMPLEMENTED; } auto elems_glb_idx = elements.view(elements.global_index()); auto elems_partition = elements.view(elements.partition()); auto elems_halo = elements.view(elements.halo()); auto elems_flags = elements.view(elements.flags()); auto include = [&](idx_t e) { auto topology = Topology::view(elems_flags(e)); if (land_water_flag && not(include_water && include_land)) { if (include_water && !topology.check(Topology::WATER)) { return false; } if (include_land && !topology.check(Topology::LAND)) { return false; } } if (not include_ghost) { if (topology.check(Topology::GHOST) || elems_halo(e)) { return false; } } if (not include_patch) { if (topology.check(Topology::PATCH)) { return false; } } if (topology.check(Topology::INVALID)) { return false; } if (coords_is_lonlat) { if (nb_nodes == 3) { auto x0 = [&]() { return coords(node_connectivity(e,0),LON); }; auto x1 = [&]() { return coords(node_connectivity(e,1),LON); }; auto x2 = [&]() { return coords(node_connectivity(e,2),LON); }; auto y0 = [&]() { return coords(node_connectivity(e,0),LAT); }; auto y1 = [&]() { return coords(node_connectivity(e,1),LAT); }; auto y2 = [&]() { return coords(node_connectivity(e,2),LAT); }; auto triangle_area = (x0() * (y1() - y2()) + x1() * (y2() - y0()) + x2() * (y0() - y1())) * 0.5; if (triangle_area <= 0 ) { return false; } // edge length comparison if (filter_edge_ratio > 0.) { auto dx10 = (x1()-x0()); auto dx21 = (x2()-x1()); auto dx02 = (x0()-x2()); auto dy10 = (y1()-y0()); auto dy21 = (y2()-y1()); auto dy02 = (y0()-y2()); auto d10 = dx10*dx10 + dy10*dy10; auto d21 = dx21*dx21 + dy21*dy21; auto d02 = dx02*dx02 + dy02*dy02; auto dmin = std::min(d10, std::min(d21, d02)); auto dmax = std::max(d10, std::max(d21, d02)); if (dmax > filter_edge_ratio * dmin) { return false; } } } } return true; }; if (binary) { idx_t nb_elems = elements.size(); if (!include_ghost) { for (idx_t elem = 0; elem < elements.size(); ++elem) { if (elems_halo(elem)) { --nb_elems; } } } int header[3]; int data[9]; header[0] = gmsh_elem_type; header[1] = nb_elems; header[2] = 4; // nb_tags file.write(reinterpret_cast(&header), sizeof(int) * 3); data[1] = 1; data[2] = 1; data[3] = 1; size_t datasize = sizeof(int) * (5 + nb_nodes); for (idx_t elem = 0; elem < nb_elems; ++elem) { if (include_ghost || !elems_halo(elem)) { data[0] = elems_glb_idx(elem); data[4] = elems_partition(elem); for (idx_t n = 0; n < nb_nodes; ++n) { data[5 + n] = glb_idx(node_connectivity(elem, n)); } file.write(reinterpret_cast(&data), datasize); } } } else { std::stringstream ss_elem_info; ss_elem_info << " " << gmsh_elem_type << " 4 1 1 1 "; std::string elem_info = ss_elem_info.str(); for (idx_t elem = 0; elem < elements.size(); ++elem) { if (include(elem)) { file << elems_glb_idx(elem) << elem_info << elems_partition(elem); for (idx_t n = 0; n < nb_nodes; ++n) { file << " " << glb_idx(node_connectivity(elem, n)); } file << "\n"; } } } } } } if (binary) { file << "\n"; } file << "$EndElements\n"; file << std::flush; // Optional mesh information file if (options.has("info") && options.get("info")) { PathName mesh_info(file_path); mesh_info = mesh_info.dirName() + "/" + mesh_info.baseName(false) + "_info.msh"; //[next] make NodesFunctionSpace accept const mesh functionspace::NodeColumns function_space(const_cast(mesh)); FieldSet fieldset; auto lat = array::make_view(fieldset.add(Field("lat", array::make_datatype(), {nodes.size()}))); auto lon = array::make_view(fieldset.add(Field("lon", array::make_datatype(), {nodes.size()}))); auto lonlat = array::make_view(nodes.lonlat()); for (idx_t n = 0; n < nodes.size(); ++n) { lon(n) = lonlat(n, 0); lat(n) = lonlat(n, 1); } write(fieldset, function_space, mesh_info, std::ios_base::out); std::vector extra_fields = {"partition", "water", "dual_volumes", "dual_delta_sph", "ghost", "halo", "remote_index"}; for (auto& f : extra_fields) { if (nodes.has_field(f)) { write(nodes.field(f), function_space, mesh_info, std::ios_base::app); } } //[next] if( mesh.has_function_space("edges") ) //[next] { //[next] FunctionSpace& edges = mesh.function_space( "edges" ); //[next] if (edges.has_field("dual_normals")) //[next] { //[next] write(edges.field("dual_normals"),mesh_info,std::ios_base::app); //[next] } //[next] if (edges.has_field("skewness")) //[next] { //[next] write(edges.field("skewness"),mesh_info,std::ios_base::app); //[next] } //[next] if (edges.has_field("arc_length")) //[next] { //[next] write(edges.field("arc_length"),mesh_info,std::ios_base::app); //[next] } //[next] } } file.close(); } // ---------------------------------------------------------------------------- void GmshIO::write(const Field& field, const PathName& file_path, openmode mode) const { if (!field.functionspace()) { FieldSet fieldset; fieldset.add(field); write(fieldset, field.functionspace(), file_path, mode); } else if (functionspace::NodeColumns(field.functionspace())) { FieldSet fieldset; fieldset.add(field); write(fieldset, field.functionspace(), file_path, mode); } else if (functionspace::StructuredColumns(field.functionspace())) { FieldSet fieldset; 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() << "] but requires a [functionspace::NodeColumns " << "or functionspace::StructuredColumns]"; throw_AssertionFailed(msg.str(), Here()); } } // ---------------------------------------------------------------------------- // ---------------------------------------------------------------------------- void GmshIO::write_delegate(const Field& field, const functionspace::NodeColumns& functionspace, const PathName& file_path, openmode mode) const { FieldSet fieldset; fieldset.add(field); write_delegate(fieldset, functionspace, file_path, mode); } // ---------------------------------------------------------------------------- void GmshIO::write_delegate(const Field& field, const functionspace::NoFunctionSpace& functionspace, const eckit::PathName& file_path, GmshIO::openmode mode) const { FieldSet fieldset; fieldset.add(field); write_delegate(fieldset, functionspace, file_path, mode); } // ---------------------------------------------------------------------------- void GmshIO::write_delegate(const Field& field, const functionspace::CellColumns& functionspace, const eckit::PathName& file_path, GmshIO::openmode mode) const { FieldSet fieldset; fieldset.add(field); write_delegate(fieldset, functionspace, file_path, mode); } // ---------------------------------------------------------------------------- void GmshIO::write_delegate(const Field& field, const functionspace::StructuredColumns& functionspace, const PathName& file_path, openmode mode) const { FieldSet fieldset; fieldset.add(field); write_delegate(fieldset, functionspace, file_path, mode); } // ---------------------------------------------------------------------------- // ---------------------------------------------------------------------------- void GmshIO::write_delegate(const FieldSet& fieldset, const functionspace::NodeColumns& functionspace, const PathName& file_path, 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(mpi::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_nodes(options, functionspace, field, file); } else if (field.datatype() == array::DataType::int64()) { write_field_nodes(options, functionspace, field, file); } else if (field.datatype() == array::DataType::real32()) { write_field_nodes(options, functionspace, field, file); } else if (field.datatype() == array::DataType::real64()) { write_field_nodes(options, functionspace, field, file); } file << std::flush; } file.close(); } void GmshIO::write_delegate(const FieldSet& fieldset, const functionspace::NoFunctionSpace& 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(mpi::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_nodes(options, functionspace, field, file); } else if (field.datatype() == array::DataType::int64()) { write_field_nodes(options, functionspace, field, file); } else if (field.datatype() == array::DataType::real32()) { write_field_nodes(options, functionspace, field, file); } else if (field.datatype() == array::DataType::real64()) { write_field_nodes(options, functionspace, field, file); } file << std::flush; } 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(mpi::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(); } // ---------------------------------------------------------------------------- // ---------------------------------------------------------------------------- void GmshIO::write_delegate(const FieldSet& fieldset, const functionspace::StructuredColumns& functionspace, const PathName& file_path, 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(mpi::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_nodes(options, functionspace, field, file); } else if (field.datatype() == array::DataType::int64()) { write_field_nodes(options, functionspace, field, file); } else if (field.datatype() == array::DataType::real32()) { write_field_nodes(options, functionspace, field, file); } else if (field.datatype() == array::DataType::real64()) { write_field_nodes(options, functionspace, field, file); } file << std::flush; } file.close(); } // ---------------------------------------------------------------------------- // ---------------------------------------------------------------------------- void GmshIO::write(const FieldSet& fieldset, const FunctionSpace& funcspace, const eckit::PathName& file_path, openmode mode) const { mpi::Scope scope(funcspace.mpi_comm()); if (functionspace::NodeColumns(funcspace)) { write_delegate(fieldset, functionspace::NodeColumns(funcspace), file_path, mode); } 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 if (not funcspace) { write_delegate(fieldset, functionspace::NoFunctionSpace(), file_path, mode); } else { ATLAS_NOTIMPLEMENTED; } } // ---------------------------------------------------------------------------- // ---------------------------------------------------------------------------- void GmshIO::write(const Field& field, const FunctionSpace& funcspace, const eckit::PathName& file_path, openmode mode) const { mpi::Scope scope(funcspace.mpi_comm()); if (functionspace::NodeColumns(funcspace)) { write_delegate(field, functionspace::NodeColumns(funcspace), file_path, mode); } else if (functionspace::StructuredColumns(funcspace)) { write_delegate(field, functionspace::StructuredColumns(funcspace), file_path, mode); } else if (functionspace::CellColumns(funcspace)) { write_delegate(field, functionspace::CellColumns(funcspace), file_path, mode); } else { ATLAS_NOTIMPLEMENTED; } } // ---------------------------------------------------------------------------- class GmshFortranInterface { public: static Mesh::Implementation* atlas__Gmsh__read(GmshIO* This, char* file_path); static void atlas__Gmsh__write(GmshIO* This, Mesh::Implementation* mesh, char* file_path); static Mesh::Implementation* atlas__read_gmsh(char* file_path); static void atlas__write_gmsh_mesh(const Mesh::Implementation* mesh, char* file_path); static void atlas__write_gmsh_fieldset(const field::FieldSetImpl* fieldset, functionspace::FunctionSpaceImpl* functionspace, char* file_path, int mode); static void atlas__write_gmsh_field(const field::FieldImpl* field, functionspace::FunctionSpaceImpl* functionspace, char* file_path, int mode); }; Mesh::Implementation* GmshFortranInterface::atlas__Gmsh__read(GmshIO* This, char* file_path) { Mesh::Implementation* m; { Mesh mesh = This->read(PathName(file_path)); mesh.get()->attach(); m = mesh.get(); } m->detach(); return m; } void GmshFortranInterface::atlas__Gmsh__write(GmshIO* This, Mesh::Implementation* mesh, char* file_path) { Mesh m(mesh); This->write(m, PathName(file_path)); } Mesh::Implementation* GmshFortranInterface::atlas__read_gmsh(char* file_path) { Mesh::Implementation* m; { Mesh mesh = GmshIO().read(PathName(file_path)); mesh.get()->attach(); m = mesh.get(); } m->detach(); return m; } void GmshFortranInterface::atlas__write_gmsh_mesh(const Mesh::Implementation* mesh, char* file_path) { GmshIO writer; writer.write(mesh, PathName(file_path)); } void GmshFortranInterface::atlas__write_gmsh_fieldset(const field::FieldSetImpl* fieldset, functionspace::FunctionSpaceImpl* functionspace, char* file_path, int /*mode*/) { GmshIO writer; writer.write(fieldset, functionspace, PathName(file_path)); } void GmshFortranInterface::atlas__write_gmsh_field(const field::FieldImpl* field, functionspace::FunctionSpaceImpl* functionspace, char* file_path, int /*mode*/) { GmshIO writer; writer.write(field, functionspace, PathName(file_path)); } extern "C" { // ---------------------------------------------------------------------------- // C wrapper interfaces to C++ routines // ---------------------------------------------------------------------------- GmshIO* atlas__Gmsh__new() { return new GmshIO(); } void atlas__Gmsh__delete(GmshIO* This) { delete This; } Mesh::Implementation* atlas__Gmsh__read(GmshIO* This, char* file_path) { return GmshFortranInterface::atlas__Gmsh__read(This, file_path); } void atlas__Gmsh__write(GmshIO* This, Mesh::Implementation* mesh, char* file_path) { GmshFortranInterface::atlas__Gmsh__write(This, mesh, file_path); } Mesh::Implementation* atlas__read_gmsh(char* file_path) { return GmshFortranInterface::atlas__read_gmsh(file_path); } void atlas__write_gmsh_mesh(const Mesh::Implementation* mesh, char* file_path) { GmshFortranInterface::atlas__write_gmsh_mesh(mesh, file_path); } void atlas__write_gmsh_fieldset(const field::FieldSetImpl* fieldset, functionspace::FunctionSpaceImpl* functionspace, char* file_path, int mode) { GmshFortranInterface::atlas__write_gmsh_fieldset(fieldset, functionspace, file_path, mode); } void atlas__write_gmsh_field(const field::FieldImpl* field, functionspace::FunctionSpaceImpl* functionspace, char* file_path, int mode) { GmshFortranInterface::atlas__write_gmsh_field(field, functionspace, file_path, mode); } } // ---------------------------------------------------------------------------- } // namespace detail } // namespace output } // namespace atlas atlas-0.46.0/src/atlas/output/detail/GmshInterface.h0000664000175000017500000000204715160212070022453 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once namespace eckit { class Parametrisation; } namespace atlas { namespace output { namespace detail { class GmshImpl; // ----------------------------------------------------------------------------- extern "C" { GmshImpl* atlas__output__Gmsh__create_pathname_mode(const char* pathname, const char* mode); GmshImpl* atlas__output__Gmsh__create_pathname_mode_config(const char* pathname, const char* mode, const eckit::Parametrisation* params); } // ----------------------------------------------------------------------------- } // namespace detail } // namespace output } // namespace atlas atlas-0.46.0/src/atlas/output/Output.h0000664000175000017500000001333315160212070017772 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include #include "eckit/config/Parametrisation.h" #include "eckit/serialisation/FileStream.h" #include "atlas/library/config.h" #include "atlas/util/Config.h" #include "atlas/util/Object.h" #include "atlas/util/ObjectHandle.h" namespace eckit { class Parametrisation; class PathName; } // namespace eckit namespace atlas { class Mesh; namespace mesh { namespace detail { class MeshImpl; } } // namespace mesh } // namespace atlas namespace atlas { class Field; class FieldSet; namespace field { class FieldImpl; class FieldSetImpl; } // namespace field } // namespace atlas namespace atlas { class FunctionSpace; namespace functionspace { class FunctionSpaceImpl; } } // namespace atlas namespace atlas { namespace output { using PathName = eckit::PathName; // ----------------------------------------------------------------------------- namespace detail { class OutputImpl : public util::Object { public: typedef atlas::util::Config Parameters; public: OutputImpl(); virtual ~OutputImpl(); /// Write mesh file virtual void write(const Mesh&, const eckit::Parametrisation& = util::NoConfig()) const = 0; /// Write field to file virtual void write(const Field&, const eckit::Parametrisation& = util::NoConfig()) const = 0; /// Write fieldset to file using FunctionSpace virtual void write(const FieldSet&, const eckit::Parametrisation& = util::NoConfig()) const = 0; /// Write field to file using Functionspace virtual void write(const Field&, const FunctionSpace&, const eckit::Parametrisation& = util::NoConfig()) const = 0; /// Write fieldset to file using FunctionSpace virtual void write(const FieldSet&, const FunctionSpace&, const eckit::Parametrisation& = util::NoConfig()) const = 0; }; } // namespace detail class Output : DOXYGEN_HIDE(public util::ObjectHandle) { public: using Handle::Handle; Output() = default; Output(const std::string&, std::ostream&, const eckit::Parametrisation& = util::NoConfig()); /// Write mesh file const Output& write(const Mesh&, const eckit::Parametrisation& = util::NoConfig()) const; /// Write field to file const Output& write(const Field&, const eckit::Parametrisation& = util::NoConfig()) const; /// Write fieldset to file using FunctionSpace const Output& write(const FieldSet&, const eckit::Parametrisation& = util::NoConfig()) const; /// Write field to file using Functionspace const Output& write(const Field&, const FunctionSpace&, const eckit::Parametrisation& = util::NoConfig()) const; /// Write fieldset to file using FunctionSpace const Output& write(const FieldSet&, const FunctionSpace&, const eckit::Parametrisation& = util::NoConfig()) const; }; namespace detail { class OutputFactory { public: /*! * \brief build Output with factory key, and default options * \return mesh generator */ static const OutputImpl* build(const std::string&, std::ostream&); /*! * \brief build Output with factory key inside parametrisation, * and options specified in parametrisation as well * \return mesh generator */ static const OutputImpl* build(const std::string&, std::ostream&, const eckit::Parametrisation&); /*! * \brief list all registered mesh generators */ static void list(std::ostream&); private: std::string name_; virtual const OutputImpl* make(std::ostream&) = 0; virtual const OutputImpl* make(std::ostream&, const eckit::Parametrisation&) = 0; protected: OutputFactory(const std::string&); virtual ~OutputFactory(); }; template class OutputBuilder : public OutputFactory { virtual const OutputImpl* make(std::ostream& stream) { return new T(stream); } virtual const OutputImpl* make(std::ostream& stream, const eckit::Parametrisation& param) { return new T(stream, param); } public: OutputBuilder(const std::string& name): OutputFactory(name) {} }; // ----------------------------------------------------------------------------- extern "C" { void atlas__Output__delete(OutputImpl* This); const OutputImpl* atlas__Output__create(const char* factory_key, std::ostream* stream, const eckit::Parametrisation* params); void atlas__Output__write_mesh(const OutputImpl* This, mesh::detail::MeshImpl* mesh, const eckit::Parametrisation* params); void atlas__Output__write_fieldset(const OutputImpl* This, const field::FieldSetImpl* fieldset, const eckit::Parametrisation* params); void atlas__Output__write_field(const OutputImpl* This, const field::FieldImpl* field, const eckit::Parametrisation* params); void atlas__Output__write_fieldset_fs(const OutputImpl* This, const field::FieldSetImpl* fieldset, const functionspace::FunctionSpaceImpl* functionspace, const eckit::Parametrisation* params); void atlas__Output__write_field_fs(const OutputImpl* This, const field::FieldImpl* field, const functionspace::FunctionSpaceImpl* functionspace, const eckit::Parametrisation* params); } } // namespace detail } // namespace output } // namespace atlas atlas-0.46.0/src/atlas/output/Gmsh.cc0000664000175000017500000000570015160212070017525 0ustar alastairalastair/* * (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 "eckit/exception/Exceptions.h" #include "atlas/output/Gmsh.h" #include "atlas/output/detail/GmshImpl.h" #include "atlas/parallel/mpi/mpi.h" namespace atlas { namespace output { // ----------------------------------------------------------------------------- std::string GmshFileStream::parallelPathName(const eckit::PathName& path, int part) { std::stringstream s; // s << path.dirName() << "/" << path.baseName(false) << "_p" << part << // ".msh"; s << path.asString() << ".p" << part; return s.str(); } // ----------------------------------------------------------------------------- GmshFileStream::GmshFileStream(const eckit::PathName& file_path, const char* mode, int part): std::ofstream() { eckit::PathName par_path(file_path); std::ios_base::openmode omode = std::ios_base::out; if (std::string(mode) == "w") { omode = std::ios_base::out; } else if (std::string(mode) == "a") { omode = std::ios_base::app; } if (part < 0 || mpi::size() == 1) { std::ofstream::open(file_path.localPath(), omode); } else { if (mpi::rank() == 0) { eckit::PathName par_path(file_path); std::ofstream par_file(par_path.localPath(), std::ios_base::out); if (!par_file.is_open()) { throw eckit::CantOpenFile(par_path); } for (idx_t p = 0; p < mpi::size(); ++p) { par_file << "Merge \"" << parallelPathName(file_path, p) << "\";" << std::endl; } par_file.close(); } eckit::PathName path(parallelPathName(file_path, part)); std::ofstream::open(path.localPath(), omode); } } //---------------------------------------------------------------------------------------------------------------------- Gmsh::Gmsh(const Output& output): Output(output) {} Gmsh::Gmsh(std::ostream& s): Output(new detail::GmshImpl(s)) {} Gmsh::Gmsh(std::ostream& s, const eckit::Parametrisation& c): Output(new detail::GmshImpl(s, c)) {} Gmsh::Gmsh(const eckit::PathName& p, const std::string& mode): Output(new detail::GmshImpl(p, mode)) {} Gmsh::Gmsh(const eckit::PathName& p, const std::string& mode, const eckit::Parametrisation& c): Output(new detail::GmshImpl(p, mode, c)) {} Gmsh::Gmsh(const eckit::PathName& p): Output(new detail::GmshImpl(p)) {} Gmsh::Gmsh(const eckit::PathName& p, const eckit::Parametrisation& c): Output(new detail::GmshImpl(p, c)) {} } // namespace output } // namespace atlas atlas-0.46.0/src/atlas/library/0000775000175000017500000000000015160212070016422 5ustar alastairalastairatlas-0.46.0/src/atlas/library/FloatingPointExceptions.cc0000664000175000017500000004003115160212070023546 0ustar alastairalastair/* * (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/library/FloatingPointExceptions.h" #include #include #include #include #include "eckit/config/LibEcKit.h" #include "eckit/config/Resource.h" #include "eckit/runtime/Main.h" #include "eckit/utils/StringTools.h" #include "eckit/utils/Translator.h" #include "atlas/library/config.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Log.h" #include "atlas/runtime/Trace.h" namespace { int getEnv(const std::string& env, int default_value) { const char* cenv = ::getenv(env.c_str()); if (cenv != nullptr) { return eckit::Translator()(cenv); } return default_value; } } // namespace #if ATLAS_HAVE_FEENABLEEXCEPT static int atlas_feenableexcept(unsigned int excepts) { return ::feenableexcept(excepts); } static int atlas_fedisableexcept(unsigned int excepts) { return ::fedisableexcept(excepts); } #elif defined(__APPLE__) static int atlas_feenableexcept(unsigned int excepts) { static fenv_t fenv; unsigned int new_excepts = excepts & FE_ALL_EXCEPT; unsigned int old_excepts; // previous masks if (::fegetenv(&fenv)) { return -1; } #if defined(__arm64__) old_excepts = fenv.__fpsr & FE_ALL_EXCEPT; fenv.__fpsr |= new_excepts; fenv.__fpcr |= (new_excepts << 8); #else old_excepts = fenv.__control & FE_ALL_EXCEPT; fenv.__control &= ~new_excepts; fenv.__mxcsr &= ~(new_excepts << 7); #endif return ::fesetenv(&fenv) ? -1 : old_excepts; } static int atlas_fedisableexcept(unsigned int excepts) { static fenv_t fenv; unsigned int new_excepts = excepts & FE_ALL_EXCEPT; unsigned int old_excepts; // all previous masks if (::fegetenv(&fenv)) { return -1; } #if defined(__arm64__) old_excepts = fenv.__fpsr & FE_ALL_EXCEPT; fenv.__fpsr &= ~new_excepts; fenv.__fpcr &= ~(new_excepts << 8); #else old_excepts = fenv.__control & FE_ALL_EXCEPT; fenv.__control |= new_excepts; fenv.__mxcsr |= (new_excepts << 7); #endif return ::fesetenv(&fenv) ? -1 : old_excepts; } #else static int atlas_feenableexcept(unsigned int excepts) { return 0; } static int atlas_fedisableexcept(unsigned int excepts) { return 0; } #endif namespace atlas { namespace library { static std::map str_to_except = {{"FE_INVALID", FE_INVALID}, {"FE_INEXACT", FE_INEXACT}, {"FE_DIVBYZERO", FE_DIVBYZERO}, {"FE_OVERFLOW", FE_OVERFLOW}, {"FE_UNDERFLOW", FE_UNDERFLOW}, {"FE_ALL_EXCEPT", FE_ALL_EXCEPT}}; static std::map except_to_str = {{FE_INVALID, "FE_INVALID"}, {FE_INEXACT, "FE_INEXACT"}, {FE_DIVBYZERO, "FE_DIVBYZERO"}, {FE_OVERFLOW, "FE_OVERFLOW"}, {FE_UNDERFLOW, "FE_UNDERFLOW"}, {FE_ALL_EXCEPT, "FE_ALL_EXCEPT"}}; static std::map str_to_signal = {{"SIGINT", SIGINT}, {"SIGILL", SIGILL}, {"SIGABRT", SIGABRT}, {"SIGFPE", SIGFPE}, {"SIGKILL", SIGKILL}, {"SIGSEGV", SIGSEGV}, {"SIGTERM", SIGTERM}}; static std::map signal_to_str = {{SIGINT, "SIGINT"}, {SIGILL, "SIGILL"}, {SIGABRT, "SIGABRT"}, {SIGFPE, "SIGFPE"}, {SIGKILL, "SIGKILL"}, {SIGSEGV, "SIGSEGV"}, {SIGTERM, "SIGTERM"}}; // ------------------------------------------------------------------------------------ class Signal { // Not sure if this should be made public (in header file) just yet public: Signal(); Signal(int signum); Signal(int signum, signal_action_t); Signal(int signum, signal_handler_t signal_handler); operator int() const { return signum_; } int signum() const { return signum_; } const std::string& code() const { return signal_to_str[signum_]; } std::string str() const { return str_; } const signal_handler_t& handler() const { return signal_action_.sa_handler; } const struct sigaction* action() const { return &signal_action_; } private: friend std::ostream& operator<<(std::ostream&, const Signal&); int signum_; std::string str_; struct sigaction signal_action_; }; // ------------------------------------------------------------------------------------ class Signals { // Not sure if this should be made public (in header file) just yet private: Signals(); public: static Signals& instance(); void setSignalHandlers(); void setSignalHandler(const Signal&); void restoreSignalHandler(int signum); void restoreAllSignalHandlers(); const Signal& signal(int signum) const; private: using registered_signals_t = std::map; registered_signals_t registered_signals_; eckit::Channel& out_; }; // ------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------ [[noreturn]] void atlas_signal_handler(int signum, siginfo_t* si, [[maybe_unused]] void* ucontext) { Signal signal = Signals::instance().signal(signum); std::string signal_code; if (signum == SIGFPE) { switch (si->si_code) { case FPE_FLTDIV: signal_code = " [FE_DIVBYZERO]"; break; case FPE_FLTINV: signal_code = " [FE_INVALID]"; break; case FPE_FLTOVF: signal_code = " [FE_OVERFLOW]"; break; case FPE_FLTUND: signal_code = " [FE_UNDERFLOW]"; break; case FPE_FLTRES: signal_code = " [FE_INEXACT]"; break; } } #if defined(__APPLE__) && defined(__arm64__) if (signum == SIGILL) { // On Apple Silicon a SIGFPE may be posing as a SIGILL // See: // https://developer.apple.com/forums/thread/689159?answerId=733736022 // https://developer.arm.com/documentation/ddi0595/2020-12/AArch64-Registers/ESR-EL1--Exception-Syndrome-Register--EL1-?lang=en#fieldset_0-24_0_16-1_1 auto esr = reinterpret_cast(ucontext)->uc_mcontext->__es.__esr; auto is_floating_point_exception = [&esr]() { constexpr unsigned long fpe_mask = 2952790016; // bits: 10110000000000000000000000000000 constexpr std::bitset<32> fpe_mask_bits(fpe_mask); return((fpe_mask_bits & std::bitset<32>(esr)) == fpe_mask_bits); }; auto test_esr = [&esr](auto pos) -> bool { return std::bitset<32>(esr).test(pos); }; if (is_floating_point_exception()) { // SIGILL is posing as a SIGFPE signal = Signals::instance().signal(SIGFPE); constexpr size_t IOF = 0; // invalid operation constexpr size_t DZF = 1; // divide-by-zero constexpr size_t OFF = 2; // overflow constexpr size_t UFF = 3; // undeflow constexpr size_t IXF = 4; // inexact constexpr size_t IDF = 7; // denormal if (test_esr(IOF)) { signal_code = " [FE_INVALID]"; } else if(test_esr(DZF)) { signal_code = " [FE_DIVBYZERO]"; } else if(test_esr(OFF)) { signal_code = " [FE_OVERFLOW]"; } else if(test_esr(UFF)) { signal_code = " [FE_UNDERFLOW]"; } else if(test_esr(IXF)) { signal_code = " [FE_INEXACT]"; } else if(test_esr(IDF)) { signal_code = " [FE_DENORMAL]"; } } } #endif std::ostream& out = Log::error(); out << "\n" << "=========================================\n" << signal << signal_code << " (signal intercepted by atlas)\n"; out << "-----------------------------------------\n" << "BACKTRACE\n" << "-----------------------------------------\n" << backtrace() << "\n" << "=========================================\n" << std::endl; Signals::instance().restoreSignalHandler(signum); eckit::LibEcKit::instance().abort(); // Just in case we end up here, which normally we shouldn't. std::_Exit(EXIT_FAILURE); } //---------------------------------------------------------------------------------------------------------------------- Signals::Signals(): out_([&]() -> eckit::Channel& { if (getEnv("ATLAS_LOG_RANK", 0) == int(mpi::rank())) { return Log::debug(); } static eckit::Channel sink; return sink; }()) {} Signals& Signals::instance() { static Signals signals; return signals; } void Signals::restoreSignalHandler(int signum) { if (registered_signals_.find(signum) != registered_signals_.end()) { out_ << "\n"; std::signal(signum, SIG_DFL); out_ << "Atlas restored default signal handler for signal " << std::setw(7) << std::left << registered_signals_[signum].code() << " [" << registered_signals_[signum] << "]\n"; out_ << std::endl; registered_signals_.erase(signum); } } void Signals::restoreAllSignalHandlers() { out_ << "\n"; for (registered_signals_t::const_iterator it = registered_signals_.begin(); it != registered_signals_.end(); ++it) { std::signal(it->first, SIG_DFL); out_ << "Atlas restored default signal handler for signal " << std::setw(7) << std::left << it->second.code() << " [" << it->second.str() << "]\n"; } out_ << std::endl; registered_signals_.clear(); } const Signal& Signals::signal(int signum) const { return registered_signals_.at(signum); } std::ostream& operator<<(std::ostream& out, const Signal& signal) { out << signal.str(); return out; } void Signals::setSignalHandlers() { setSignalHandler(SIGINT); setSignalHandler(SIGILL); setSignalHandler(SIGABRT); setSignalHandler(SIGFPE); setSignalHandler(SIGSEGV); setSignalHandler(SIGTERM); } void Signals::setSignalHandler(const Signal& signal) { registered_signals_[signal] = signal; sigaction(signal, signal.action(), nullptr); out_ << "Atlas registered signal handler for signal " << std::setw(7) << std::left << signal.code() << " [" << signal << "]" << std::endl; } Signal::Signal(): signum_(0), str_() { signal_action_.sa_handler = SIG_DFL; } Signal::Signal(int signum): Signal(signum, atlas_signal_handler) {} Signal::Signal(int signum, signal_handler_t signal_handler): signum_(signum), str_(strsignal(signum)) { memset(&signal_action_, 0, sizeof(signal_action_)); sigemptyset(&signal_action_.sa_mask); signal_action_.sa_handler = signal_handler; signal_action_.sa_flags = 0; } Signal::Signal(int signum, signal_action_t signal_action): signum_(signum), str_(strsignal(signum)) { memset(&signal_action_, 0, sizeof(signal_action_)); sigemptyset(&signal_action_.sa_mask); signal_action_.sa_sigaction = signal_action; signal_action_.sa_flags = SA_SIGINFO; } void enable_floating_point_exceptions() { auto& out = [&]() -> eckit::Channel& { if (getEnv("ATLAS_LOG_RANK", 0) == int(mpi::rank())) { return Log::debug(); } static eckit::Channel sink; return sink; }(); // Following line gives runtime errors with Cray 8.6 due to compiler bug (but works with Cray 8.5 and Cray 8.7) // std::vector floating_point_exceptions = eckit::Resource>( "atlasFPE;$ATLAS_FPE", {"false"} ); // Instead, manually access environment std::vector floating_point_exceptions{"false"}; const char* ATLAS_FPE = ::getenv("ATLAS_FPE"); if (ATLAS_FPE != nullptr) { std::vector tmp = eckit::Translator>()(ATLAS_FPE); floating_point_exceptions = tmp; // Above trick with "tmp" is what avoids the Cray 8.6 compiler bug } else { if (eckit::Main::ready()) { floating_point_exceptions = eckit::Resource>("atlasFPE", {"false"}); } } { bool _enable = false; int _excepts = 0; auto enable = [&](int except) { _excepts |= except; _enable = true; out << "Atlas enabled floating point exception " << except_to_str[except] << std::endl; }; bool skip_map = false; if (floating_point_exceptions.size() == 1) { std::string s = eckit::StringTools::lower(floating_point_exceptions[0]); if (s == "no" || s == "off" || s == "false" || s == "0") { _enable = false; skip_map = true; } else if (s == "yes" || s == "on" || s == "true" || s == "1") { enable(FE_INVALID); enable(FE_DIVBYZERO); enable(FE_OVERFLOW); skip_map = true; } } if (not skip_map) { for (auto& s : floating_point_exceptions) { if (str_to_except.find(s) == str_to_except.end()) { throw eckit::UserError( s + " is not a valid floating point exception code. " "Valid codes: [FE_INVALID,FE_INEXACT,FE_DIVBYZERO,FE_OVERFLOW,FE_ALL_EXCEPT]", Here()); } enable(str_to_except[s]); } } if (_enable) { atlas_feenableexcept(_excepts); } } } bool enable_floating_point_exception(int except) { auto check_flag = [](int flags, int bits) -> bool { return (flags & bits) == bits; }; int previous = atlas_feenableexcept(except); return !check_flag(previous,except); } bool disable_floating_point_exception(int except) { auto check_flag = [](int flags, int bits) -> bool { return (flags & bits) == bits; }; int previous = atlas_fedisableexcept(except); return !check_flag(previous,except); } bool enable_floating_point_exception(const std::string& floating_point_exception) { auto it = str_to_except.find(floating_point_exception); if (it == str_to_except.end()) { throw eckit::UserError( floating_point_exception + " is not a valid floating point exception code. " "Valid codes: [FE_INVALID,FE_INEXACT,FE_DIVBYZERO,FE_OVERFLOW,FE_ALL_EXCEPT]", Here()); } return enable_floating_point_exception(it->second); } bool disable_floating_point_exception(const std::string& floating_point_exception) { auto it = str_to_except.find(floating_point_exception); if (it == str_to_except.end()) { throw eckit::UserError( floating_point_exception + " is not a valid floating point exception code. " "Valid codes: [FE_INVALID,FE_INEXACT,FE_DIVBYZERO,FE_OVERFLOW,FE_ALL_EXCEPT]", Here()); } return disable_floating_point_exception(it->second); } void enable_atlas_signal_handler() { bool enable = false; const char* ATLAS_SIGNAL_HANDLER = ::getenv("ATLAS_SIGNAL_HANDLER"); if (ATLAS_SIGNAL_HANDLER != nullptr) { bool tmp = eckit::Translator()(ATLAS_SIGNAL_HANDLER); enable = tmp; // Above trick with "tmp" is what avoids the Cray 8.6 compiler bug } else { if (eckit::Main::ready()) { enable = eckit::Resource("atlasSignalHandler", false); } } if (enable) { Signals::instance().setSignalHandlers(); } } } // namespace library } // namespace atlas atlas-0.46.0/src/atlas/library/config.h0000664000175000017500000000346115160212070020044 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include "eckit/eckit.h" #include "atlas/atlas_ecbuild_config.h" #include "atlas/library/defines.h" #include "hic/hic_config.h" #ifndef DOXYGEN_SHOULD_SKIP_THIS #define DOXYGEN_HIDE(X) X #endif #define ATLAS_HAVE_TRACE 1 #define ATLAS_HAVE_TRACE_BARRIERS 1 #define ATLAS_HIC_COMPILER HIC_COMPILER #define ATLAS_HOST_COMPILE HIC_HOST_COMPILE #define ATLAS_DEVICE_COMPILE HIC_DEVICE_COMPILE #define ATLAS_HOST_DEVICE HIC_HOST_DEVICE #define ATLAS_DEVICE HIC_DEVICE #define ATLAS_HOST HIC_HOST #define ATLAS_GLOBAL HIC_GLOBAL #if HIC_BACKEND_CUDA || HIC_BACKEND_HIP #define ATLAS_HAVE_GPU 1 #else #define ATLAS_HAVE_GPU 0 #endif namespace atlas { /// @typedef gidx_t /// Integer type for global indices #if ATLAS_BITS_GLOBAL == 32 using gidx_t = int; #else using gidx_t = long; #endif /// @typedef idx_t /// Integer type for indices in connectivity tables #if (ATLAS_BITS_LOCAL == 32) using idx_t = int; #else using idx_t = long; #endif /// @typedef uidx_t /// Integer type for unique indices typedef gidx_t uidx_t; #define ATLAS_ECKIT_VERSION_AT_LEAST(x, y, z) (ATLAS_ECKIT_VERSION_INT >= x * 10000 + y * 100 + z) #if ATLAS_ECKIT_VERSION_AT_LEAST(1, 19, 0) #define ATLAS_ECKIT_HAVE_ECKIT_585 1 #else #define ATLAS_ECKIT_HAVE_ECKIT_585 0 #endif #define ATLAS_ECTRANS_VERSION_AT_LEAST(x, y, z) (ATLAS_ECTRANS_VERSION_INT >= x * 10000 + y * 100 + z) } // namespace atlas atlas-0.46.0/src/atlas/library/Plugin.cc0000664000175000017500000000172215160212070020171 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "Plugin.h" #include "atlas/library/Library.h" namespace atlas { //---------------------------------------------------------------------------------------------------------------------- Plugin::Plugin(const std::string& name, const std::string& libname): eckit::system::Plugin(name, libname) { atlas::Library::instance().registerPlugin(*this); } Plugin::~Plugin() { atlas::Library::instance().deregisterPlugin(*this); } //---------------------------------------------------------------------------------------------------------------------- } // namespace atlas atlas-0.46.0/src/atlas/library/defines.h.in0000664000175000017500000001354015160212070020620 0ustar alastairalastair#if 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. */ // clang-format off #endif #ifndef atlas_library_defines_h #define atlas_library_defines_h #define ATLAS_HAVE_OMP @atlas_HAVE_OMP_CXX@ #define ATLAS_OMP_TASK_SUPPORTED @ATLAS_OMP_TASK_SUPPORTED@ #define ATLAS_OMP_TASK_UNTIED_SUPPORTED @ATLAS_OMP_TASK_UNTIED_SUPPORTED@ #define ATLAS_HAVE_GPU @atlas_HAVE_GPU@ #define ATLAS_HAVE_GPU_AWARE_MPI @atlas_HAVE_GPU_AWARE_MPI@ #define ATLAS_HAVE_ACC @atlas_HAVE_ACC@ #define ATLAS_HAVE_QHULL @atlas_HAVE_QHULL@ #define ATLAS_HAVE_CGAL @atlas_HAVE_CGAL@ #define ATLAS_HAVE_TESSELATION @atlas_HAVE_TESSELATION@ #define ATLAS_HAVE_FORTRAN @atlas_HAVE_FORTRAN@ #define ATLAS_HAVE_EIGEN @atlas_HAVE_EIGEN@ #define ATLAS_HAVE_FFTW @atlas_HAVE_FFTW@ #define ATLAS_HAVE_POCKETFFT @atlas_HAVE_POCKETFFT@ #define ATLAS_HAVE_MPI @atlas_HAVE_MPI@ #define ATLAS_HAVE_PROJ @atlas_HAVE_PROJ@ #define ATLAS_BITS_GLOBAL @ATLAS_BITS_GLOBAL@ #define ATLAS_ARRAYVIEW_BOUNDS_CHECKING @atlas_HAVE_BOUNDSCHECKING@ #define ATLAS_INDEXVIEW_BOUNDS_CHECKING @atlas_HAVE_BOUNDSCHECKING@ #define ATLAS_VECTOR_BOUNDS_CHECKING @atlas_HAVE_BOUNDSCHECKING@ #define ATLAS_INIT_SNAN @atlas_HAVE_INIT_SNAN@ #define ATLAS_HAVE_GRIDTOOLS_STORAGE @atlas_HAVE_GRIDTOOLS_STORAGE@ #define ATLAS_GRIDTOOLS_STORAGE_BACKEND_HOST @ATLAS_GRIDTOOLS_STORAGE_BACKEND_HOST@ #define ATLAS_GRIDTOOLS_STORAGE_BACKEND_CUDA @ATLAS_GRIDTOOLS_STORAGE_BACKEND_CUDA@ #define ATLAS_HAVE_TRANS @atlas_HAVE_ECTRANS@ #define ATLAS_HAVE_ECTRANS @atlas_HAVE_PACKAGE_ECTRANS@ #define ATLAS_HAVE_FEENABLEEXCEPT @atlas_HAVE_FEENABLEEXCEPT@ #define ATLAS_HAVE_FEDISABLEEXCEPT @atlas_HAVE_FEDISABLEEXCEPT@ #define ATLAS_BUILD_TYPE_DEBUG @atlas_BUILD_TYPE_DEBUG@ #define ATLAS_BUILD_TYPE_RELEASE @atlas_BUILD_TYPE_RELEASE@ #define ATLAS_ECKIT_VERSION_INT @ATLAS_ECKIT_VERSION_INT@ #define ATLAS_ECTRANS_VERSION_INT @ATLAS_ECTRANS_VERSION_INT@ #define ATLAS_ECKIT_DEVELOP @ATLAS_ECKIT_DEVELOP@ #define ATLAS_HAVE_FUNCTIONSPACE @atlas_HAVE_ATLAS_FUNCTIONSPACE@ #define ATLAS_BITS_LOCAL @ATLAS_BITS_LOCAL@ #if defined( __GNUC__ ) || defined( __clang__ ) #define ATLAS_MAYBE_UNUSED __attribute__( ( unused ) ) #define ATLAS_ALWAYS_INLINE __attribute__( ( always_inline ) ) inline #else #define ATLAS_MAYBE_UNUSED #define ATLAS_ALWAYS_INLINE inline #endif #define ATLAS_UNREACHABLE() __builtin_unreachable() #if defined(__NVCOMPILER) # define ATLAS_SUPPRESS_WARNINGS_PUSH _Pragma( "diag push" ) # define ATLAS_SUPPRESS_WARNINGS_POP _Pragma( "diag pop" ) # define ATLAS_SUPPRESS_WARNINGS_INTEGER_SIGN_CHANGE _Pragma( "diag_suppress integer_sign_change" ) # define ATLAS_SUPPRESS_WARNINGS_CODE_IS_UNREACHABLE _Pragma( "diag_suppress code_is_unreachable" ) #elif defined(__INTEL_LLVM_COMPILER) # define ATLAS_SUPPRESS_WARNINGS_PUSH _Pragma( "clang diagnostic push") # define ATLAS_SUPPRESS_WARNINGS_POP _Pragma( "clang diagnostic pop" ) # define ATLAS_SUPPRESS_WARNINGS_UNUSED_BUT_SET_VARIABLE _Pragma( "clang diagnostic ignored \"-Wunused-but-set-variable\"") #elif defined(__INTEL_COMPILER) # define ATLAS_SUPPRESS_WARNINGS_PUSH _Pragma( "warning push" ) # define ATLAS_SUPPRESS_WARNINGS_POP _Pragma( "warning pop" ) # define ATLAS_SUPPRESS_WARNINGS_INTEGER_SIGN_CHANGE _Pragma( "warning disable 68" ) # define ATLAS_SUPPRESS_WARNINGS_BOTH_INLINE_NOINLINE _Pragma( "warning disable 2196" ) #elif defined(__clang__) # define ATLAS_SUPPRESS_WARNINGS_PUSH _Pragma( "clang diagnostic push") # define ATLAS_SUPPRESS_WARNINGS_POP _Pragma( "clang diagnostic pop" ) # define ATLAS_SUPPRESS_WARNINGS_UNUSED_BUT_SET_VARIABLE _Pragma( "clang diagnostic ignored \"-Wunused-but-set-variable\"") #elif defined(__GNUC__) # define ATLAS_SUPPRESS_WARNINGS_PUSH _Pragma( "GCC diagnostic push" ) \ _Pragma( "GCC diagnostic ignored \"-Wpragmas\"" ) \ _Pragma( "GCC diagnostic ignored \"-Wunknown-warning-option\"" ) # define ATLAS_SUPPRESS_WARNINGS_POP _Pragma( "GCC diagnostic pop" ) # define ATLAS_SUPPRESS_WARNINGS_TEMPLATE_ID_CDTOR _Pragma( "GCC diagnostic ignored \"-Wtemplate-id-cdtor\"" ) #endif #if !defined(ATLAS_SUPPRESS_WARNINGS_PUSH) # define ATLAS_SUPPRESS_WARNINGS_PUSH #endif #if !defined(ATLAS_SUPPRESS_WARNINGS_POP) # define ATLAS_SUPPRESS_WARNINGS_POP #endif #if !defined(ATLAS_SUPPRESS_WARNINGS_INTEGER_SIGN_CHANGE) # define ATLAS_SUPPRESS_WARNINGS_INTEGER_SIGN_CHANGE #endif #if !defined(ATLAS_SUPPRESS_WARNINGS_CODE_IS_UNREACHABLE) # define ATLAS_SUPPRESS_WARNINGS_CODE_IS_UNREACHABLE #endif #if !defined(ATLAS_SUPPRESS_WARNINGS_TEMPLATE_ID_CDTOR) # define ATLAS_SUPPRESS_WARNINGS_TEMPLATE_ID_CDTOR #endif #if !defined(ATLAS_SUPPRESS_WARNINGS_UNUSED_BUT_SET_VARIABLE) # define ATLAS_SUPPRESS_WARNINGS_UNUSED_BUT_SET_VARIABLE #endif #if !defined(ATLAS_SUPPRESS_WARNINGS_BOTH_INLINE_NOINLINE) # define ATLAS_SUPPRESS_WARNINGS_BOTH_INLINE_NOINLINE #endif #endif atlas-0.46.0/src/atlas/library/git_sha1.h.in0000664000175000017500000000145015160212070020677 0ustar alastairalastair/* * (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 #define ATLAS_GIT_SHA1 "@atlas_GIT_SHA1@" #include #include namespace atlas { namespace library { inline const char* git_sha1( unsigned int chars = 7 ) { static std::string sha1( ATLAS_GIT_SHA1 ); if ( sha1.empty() ) { return "not available"; } sha1 = sha1.substr( 0, std::min( chars, 40u ) ); return sha1.c_str(); } } // namespace library } // namespace atlas atlas-0.46.0/src/atlas/library/Library.h0000664000175000017500000000650615160212070020206 0ustar alastairalastair/* * (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 #include "eckit/filesystem/PathName.h" #include "eckit/system/Library.h" #include "eckit/system/Plugin.h" namespace eckit { class Parametrisation; class PathName; } // namespace eckit namespace atlas { namespace mpi { void finalise(); void finalize(); } // namespace mpi void initialise(int argc, char** argv); void initialize(int argc, char** argv); void initialise(); void initialize(); void finalise(); void finalize(); //---------------------------------------------------------------------------------------------------------------------- class Library : public eckit::system::Library { public: static Library& instance(); virtual std::string version() const override; virtual std::string gitsha1(unsigned int count) const override; std::string gitsha1() const { return gitsha1(7); } void initialise(int argc, char** argv); void initialise(const eckit::Parametrisation&); void initialise(); void finalise(); struct Information { friend std::ostream& operator<<(std::ostream& s, const Information& i) { i.print(s); return s; } void print(std::ostream&) const; }; Information information() const { return Information(); } virtual eckit::Channel& infoChannel() const; virtual eckit::Channel& warningChannel() const; virtual eckit::Channel& traceChannel() const; virtual eckit::Channel& debugChannel() const override; bool trace() const { return trace_; } virtual bool debug() const override { return debug_; } bool traceBarriers() const { return trace_barriers_; } bool traceMemory() const { return trace_memory_; } Library(); void registerPlugin(eckit::system::Plugin&); void deregisterPlugin(eckit::system::Plugin&); const std::vector& plugins() { return plugins_; } std::string cachePath() const; std::string dataPath() const; std::string linalgDenseBackend() const; std::string linalgSparseBackend() const; std::string linalgFFTBackend() const; void registerDataPath(const std::string&); protected: virtual const void* addr() const override; bool initialized_{false}; bool debug_{false}; bool info_{true}; bool warning_{true}; bool trace_{false}; bool trace_memory_{false}; bool trace_barriers_{false}; bool trace_report_{false}; mutable std::unique_ptr info_channel_; mutable std::unique_ptr warning_channel_; mutable std::unique_ptr trace_channel_; mutable std::unique_ptr debug_channel_; private: std::vector plugins_; mutable std::vector data_paths_; size_t atlas_io_trace_hook_; }; using Atlas = Library; //---------------------------------------------------------------------------------------------------------------------- } // namespace atlas atlas-0.46.0/src/atlas/library/detail/0000775000175000017500000000000015160212070017664 5ustar alastairalastairatlas-0.46.0/src/atlas/library/detail/Debug.h0000664000175000017500000000773715160212070021101 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include "eckit/config/Resource.h" #include "atlas/field.h" #include "atlas/functionspace.h" #include "atlas/mesh.h" #include "atlas/parallel/mpi/mpi.h" namespace atlas { namespace debug { inline gidx_t global_index(int i = 0) { static std::vector g = eckit::Resource>("$ATLAS_DEBUG_GLOBAL_INDEX", std::vector{-1}); return g[i]; } inline gidx_t node_global_index(int i = 0) { static std::vector g = eckit::Resource>("$ATLAS_DEBUG_NODE_GLOBAL_INDEX", std::vector{-1}); return g[i]; } inline gidx_t edge_global_index(int i = 0) { static std::vector g = eckit::Resource>("$ATLAS_DEBUG_EDGE_GLOBAL_INDEX", std::vector{-1}); return g[i]; } inline gidx_t cell_global_index(int i = 0) { static std::vector g = eckit::Resource>("$ATLAS_DEBUG_CELL_GLOBAL_INDEX", std::vector{-1}); return g[i]; } inline gidx_t node_uid(int i = 0) { static std::vector g = eckit::Resource>("$ATLAS_DEBUG_NODE_UID", std::vector{-1}); return g[i]; } inline bool is_node_global_index(gidx_t x) { static std::vector v = eckit::Resource>("$ATLAS_DEBUG_NODE_GLOBAL_INDEX", std::vector()); for (gidx_t g : v) { if (x == g) return true; } return false; } inline bool is_global_index(gidx_t x) { static std::vector v = eckit::Resource>("$ATLAS_GLOBAL_INDEX", std::vector()); for (gidx_t g : v) { if (x == g) return true; } return false; } inline bool is_edge_global_index(gidx_t x) { static std::vector v = eckit::Resource>("$ATLAS_DEBUG_EDGE_GLOBAL_INDEX", std::vector()); for (gidx_t g : v) { if (x == g) return true; } return false; } inline bool is_cell_global_index(gidx_t x) { static std::vector v = eckit::Resource>("$ATLAS_DEBUG_CELL_GLOBAL_INDEX", std::vector()); for (gidx_t g : v) { if (x == g) return true; } return false; } inline bool is_node_uid(gidx_t x) { static std::vector v = eckit::Resource>("$ATLAS_DEBUG_NODE_UID", std::vector()); for (gidx_t g : v) { if (x == g) return true; } return false; } inline bool is_cell_uid(gidx_t x) { static std::vector v = eckit::Resource>("$ATLAS_DEBUG_CELL_UID", std::vector()); for (gidx_t g : v) { if (x == g) return true; } return false; } inline int mpi_rank(int i = 0) { static std::vector g = eckit::Resource>("$ATLAS_DEBUG_MPI_RANK", std::vector{-1}); return g[i]; } inline int is_mpi_rank() { static std::vector v = eckit::Resource>("$ATLAS_DEBUG_MPI_RANK", std::vector()); static int r = mpi::rank(); for (long g : v) { if (r == g) return true; } return false; } inline int is_mpi_rank(int x) { static std::vector v = eckit::Resource>("$ATLAS_DEBUG_MPI_RANK", std::vector()); for (long g : v) { if (x == g) return true; } return false; } inline std::string rank_str() { static std::string s = "[" + std::to_string(mpi::rank()) + "] "; return s; } } // namespace debug } // namespace atlas atlas-0.46.0/src/atlas/library/detail/BlackMagic.h0000664000175000017500000001211715160212070022014 0ustar alastairalastair/* * (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 // This file contains preprocessor black magic. It contains macros that // can return the number of arguments passed //----------------------------------------------------------------------------------------------------------- // Public /// Returns the number of passed arguments #define __ATLAS_NARG(...) /// Splice a and b together #define __ATLAS_SPLICE(a, b) #define __ATLAS_STRINGIFY(a) a #define __ATLAS_TYPE(Type, ...) #define __ATLAS_TYPE_SCOPE(Type, ...) //----------------------------------------------------------------------------------------------------------- // Details // Undefine these, to be redefined further down. #undef __ATLAS_NARG #undef __ATLAS_SPLICE #undef __ATLAS_TYPE #undef __ATLAS_TYPE_SCOPE #define __ATLAS_REVERSE 5, 4, 3, 2, 1, 0 #define __ATLAS_ARGN(_1, _2, _3, _4, _5, N, ...) N #define __ATLAS_NARG__(dummy, ...) __ATLAS_ARGN(__VA_ARGS__) #define __ATLAS_NARG_(...) __ATLAS_NARG__(dummy, ##__VA_ARGS__, __ATLAS_REVERSE) #define __ATLAS_SPLICE(a, b) __ATLAS_SPLICE_1(a, b) #define __ATLAS_SPLICE_1(a, b) __ATLAS_SPLICE_2(a, b) #define __ATLAS_SPLICE_2(a, b) a##b #define __ATLAS_ARG16(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, ...) _15 #define __ATLAS_HAS_COMMA(...) __ATLAS_ARG16(__VA_ARGS__, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0) #define __ATLAS_TRIGGER_PARENTHESIS(...) , #define __ATLAS_ISEMPTY(...) \ __ATLAS_ISEMPTY_( /* test if there is just one argument, eventually an empty \ one */ \ __ATLAS_HAS_COMMA(__VA_ARGS__), /* test if \ _TRIGGER_PARENTHESIS_ \ together with the \ argument adds a comma */ \ __ATLAS_HAS_COMMA( \ __ATLAS_TRIGGER_PARENTHESIS __VA_ARGS__), /* test if the argument together with \ a parenthesis adds a comma */ \ __ATLAS_HAS_COMMA(__VA_ARGS__(/*empty*/)), /* test if placing it between \ __ATLAS_TRIGGER_PARENTHESIS and the \ parenthesis adds a comma */ \ __ATLAS_HAS_COMMA(__ATLAS_TRIGGER_PARENTHESIS __VA_ARGS__(/*empty*/))) #define __ATLAS_PASTE5(_0, _1, _2, _3, _4) _0##_1##_2##_3##_4 #define __ATLAS_ISEMPTY_(_0, _1, _2, _3) __ATLAS_HAS_COMMA(__ATLAS_PASTE5(__ATLAS_IS_EMPTY_CASE_, _0, _1, _2, _3)) #define __ATLAS_IS_EMPTY_CASE_0001 , #define __ATLAS_NARG(...) __ATLAS_SPLICE(__ATLAS_CALL_NARG_, __ATLAS_ISEMPTY(__VA_ARGS__))(__VA_ARGS__) #define __ATLAS_CALL_NARG_1(...) 0 #define __ATLAS_CALL_NARG_0 __ATLAS_NARG_ #define __ATLAS_COMMA_ARGS(...) __ATLAS_SPLICE(__ATLAS_COMMA_ARGS_, __ATLAS_ISEMPTY(__VA_ARGS__))(__VA_ARGS__) #define __ATLAS_COMMA_ARGS_1(...) #define __ATLAS_COMMA_ARGS_0(...) , __VA_ARGS__ #define __ATLAS_ARGS_OR_DUMMY(...) \ __ATLAS_SPLICE(__ATLAS_ARGS_OR_DUMMY_, __ATLAS_ISEMPTY(__VA_ARGS__)) \ (__VA_ARGS__) #define __ATLAS_ARGS_OR_DUMMY_0(...) __VA_ARGS__ #define __ATLAS_ARGS_OR_DUMMY_1(...) 0 #define __ATLAS_TYPE(Type, ...) \ __ATLAS_SPLICE(__ATLAS_TYPE_, __ATLAS_ISEMPTY(__VA_ARGS__)) \ (Type, __ATLAS_ARGS_OR_DUMMY(__VA_ARGS__)) #define __ATLAS_TYPE_1(Type, dummy) Type __ATLAS_SPLICE(__variable_, __LINE__) #define __ATLAS_TYPE_0(Type, ...) Type __ATLAS_SPLICE(__variable_, __LINE__)(__VA_ARGS__) #define __ATLAS_TYPE_SCOPE(Type, ...) \ __ATLAS_SPLICE(__ATLAS_TYPE_SCOPE_, __ATLAS_ISEMPTY(__VA_ARGS__)) \ (Type, __ATLAS_ARGS_OR_DUMMY(__VA_ARGS__)) #define __ATLAS_TYPE_SCOPE_1(Type, ...) \ for (bool __ATLAS_SPLICE(__done_, __LINE__) = false; __ATLAS_SPLICE(__done_, __LINE__) != true;) \ for (Type __ATLAS_SPLICE(__variable_, __LINE__); __ATLAS_SPLICE(__done_, __LINE__) != true; \ __ATLAS_SPLICE(__done_, __LINE__) = true) #define __ATLAS_TYPE_SCOPE_0(Type, ...) \ for (bool __ATLAS_SPLICE(__done_, __LINE__) = false; __ATLAS_SPLICE(__done_, __LINE__) != true;) \ for (Type __ATLAS_SPLICE(__variable_, __LINE__)(__VA_ARGS__); __ATLAS_SPLICE(__done_, __LINE__) != true; \ __ATLAS_SPLICE(__done_, __LINE__) = true) atlas-0.46.0/src/atlas/library/Plugin.h0000664000175000017500000000174615160212070020041 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include "eckit/system/Plugin.h" namespace atlas { //---------------------------------------------------------------------------------------------------------------------- class Plugin : public eckit::system::Plugin { public: /// @param [in] name Plugin name /// @param [in] libname Library name as will be used in file system explicit Plugin(const std::string& name, const std::string& libname = ""); virtual ~Plugin(); }; //---------------------------------------------------------------------------------------------------------------------- } // namespace atlas atlas-0.46.0/src/atlas/library/Library.cc0000664000175000017500000004611615160212070020345 0ustar alastairalastair/* * (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/library/Library.h" #include #include #include "eckit/config/LibEcKit.h" #include "eckit/config/Resource.h" #include "eckit/eckit.h" #include "eckit/filesystem/LocalPathName.h" #include "eckit/filesystem/PathExpander.h" #include "eckit/filesystem/PathName.h" #include "eckit/log/Log.h" #include "eckit/log/OStreamTarget.h" #include "eckit/log/PrefixTarget.h" #include "eckit/runtime/Main.h" #include "pluto/pluto.h" #include "eckit/system/SystemInfo.h" #include "eckit/types/Types.h" #include "eckit/utils/Translator.h" #include "eckit/utils/Tokenizer.h" #include "eckit/system/LibraryManager.h" #if ATLAS_ECKIT_HAVE_ECKIT_585 #include "eckit/linalg/LinearAlgebraDense.h" namespace { static bool feature_MKL() { return eckit::linalg::LinearAlgebraDense::hasBackend("mkl"); } } // namespace #else #include "eckit/linalg/LinearAlgebra.h" namespace { static bool feature_MKL() { return eckit::linalg::LinearAlgebra::hasBackend("mkl"); } } // namespace #endif #include "pluto/pluto.h" #include "atlas_io/Trace.h" #include "atlas/library/FloatingPointExceptions.h" #include "atlas/library/Plugin.h" #include "atlas/library/config.h" #include "atlas/library/git_sha1.h" #include "atlas/library/version.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/parallel/omp/omp.h" #include "atlas/parallel/acc/acc.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Log.h" #include "atlas/runtime/Trace.h" #include "atlas/util/Config.h" #if ATLAS_HAVE_TRANS #if ATLAS_HAVE_ECTRANS #include "ectrans/transi.h" #else #include "transi/trans.h" #endif #endif using eckit::LocalPathName; using eckit::Main; using eckit::PathName; namespace atlas { //---------------------------------------------------------------------------------------------------------------------- namespace { std::string str(bool v) { return v ? "ON" : "OFF"; } std::string str(const eckit::system::Library& lib) { std::string gitsha1 = lib.gitsha1(); std::stringstream ss; ss << lib.name() << " version (" << lib.version() << "),"; if (lib.gitsha1() != "not available") { ss << " git-sha1 " << lib.gitsha1(7); } return ss.str(); } std::string getEnv(const std::string& env, const std::string& default_value = "") { char const* val = ::getenv(env.c_str()); return val == nullptr ? default_value : std::string(val); } bool getEnv(const std::string& env, bool default_value) { const char* cenv = ::getenv(env.c_str()); if (cenv != nullptr) { return eckit::Translator()(cenv); } return default_value; } int getEnv(const std::string& env, int default_value) { const char* cenv = ::getenv(env.c_str()); if (cenv != nullptr) { return eckit::Translator()(cenv); } return default_value; } static void add_tokens(std::vector& tokens, const std::string& str, const std::string& sep) { eckit::Tokenizer tokenize{sep}; std::vector tokenized; tokenize(str, tokenized); for (auto& t : tokenized) { if (not t.empty()) { tokens.push_back(eckit::PathExpander::expand(t)); } } }; static void init_data_paths(std::vector& data_paths) { ATLAS_ASSERT(eckit::Main::ready()); add_tokens(data_paths, eckit::LibResource("atlas-data-path;$ATLAS_DATA_PATH", ""), ":"); add_tokens(data_paths, "~atlas/share", ":"); } } // namespace //---------------------------------------------------------------------------------------------------------------------- void initialise(int argc, char** argv) { Library::instance().initialise(argc, argv); } void initialize(int argc, char** argv) { Library::instance().initialise(argc, argv); } void initialise() { Library::instance().initialise(); } void initialize() { Library::instance().initialise(); } void finalise() { Library::instance().finalise(); } void finalize() { Library::instance().finalise(); } static Library libatlas; Library::Library(): eckit::system::Library(std::string("atlas")), debug_(eckit::system::Library::debug()), info_(getEnv("ATLAS_INFO", true)), warning_(getEnv("ATLAS_WARNING", true)), trace_(getEnv("ATLAS_TRACE", false)), trace_memory_(getEnv("ATLAS_TRACE_MEMORY", false)), trace_barriers_(getEnv("ATLAS_TRACE_BARRIERS", false)), trace_report_(getEnv("ATLAS_TRACE_REPORT", false)), atlas_io_trace_hook_(::atlas::io::TraceHookRegistry::invalidId()) { std::string ATLAS_PLUGIN_PATH = getEnv("ATLAS_PLUGIN_PATH"); #if ATLAS_ECKIT_VERSION_AT_LEAST(1, 24, 4) eckit::system::LibraryManager::addPluginSearchPath(ATLAS_PLUGIN_PATH); #else if (ATLAS_PLUGIN_PATH.size()) { std::cout << "WARNING: atlas::Library discovered environment variable ATLAS_PLUGIN_PATH. " << "Currently used version of eckit (" << eckit_version_str() << " [" << eckit_git_sha1() << "]) " << "Support started in eckit version 1.24.4 .\n" << "Alternatively, use combination of environment variables 'PLUGINS_MANIFEST_PATH' " << "and 'LD_LIBRARY_PATH (for UNIX) / DYLD_LIBRARY_PATH (for macOS)' (colon-separated lists)\n" << std::endl; } #endif } void Library::registerPlugin(eckit::system::Plugin& plugin) { plugins_.push_back(&plugin); } void Library::deregisterPlugin(eckit::system::Plugin& plugin) { auto it = std::find(plugins_.begin(), plugins_.end(), &plugin); ATLAS_ASSERT(it != plugins_.end()); plugins_.erase(it); } std::string Library::cachePath() const { auto resource = []() -> std::string { return eckit::LibResource("atlas-cache-path;$ATLAS_CACHE_PATH", "/tmp/cache"); }; static std::string ATLAS_CACHE_PATH = eckit::PathExpander::expand(resource()); return ATLAS_CACHE_PATH; } void Library::registerDataPath(const std::string& path) { if (data_paths_.empty()) { init_data_paths(data_paths_); } add_tokens(data_paths_, path, ":"); } std::string Library::dataPath() const { if (data_paths_.empty()) { init_data_paths(data_paths_); } std::vector paths = data_paths_; auto join = [](const std::vector& v, const std::string& sep) -> std::string { std::stringstream joined; for (size_t i = 0; i < v.size(); ++i) { if (i > 0) { joined << sep; } joined << v[i]; } return joined.str(); }; return join(paths, ":"); } std::string atlas::Library::linalgFFTBackend() const { auto resource = []() -> std::string { return eckit::LibResource("atlas-linalg-fft-backend;$ATLAS_LINALG_FFT_BACKEND", ""); }; static std::string ATLAS_LINALG_FFT_BACKEND = resource(); return ATLAS_LINALG_FFT_BACKEND; } std::string atlas::Library::linalgSparseBackend() const { auto resource = []() -> std::string { return eckit::LibResource("atlas-linalg-sparse-backend;$ATLAS_LINALG_SPARSE_BACKEND", ""); }; static std::string ATLAS_LINALG_SPARSE_BACKEND = resource(); return ATLAS_LINALG_SPARSE_BACKEND; } std::string atlas::Library::linalgDenseBackend() const { auto resource = []() -> std::string { return eckit::LibResource("atlas-linalg-dense-backend;$ATLAS_LINALG_DENSE_BACKEND", ""); }; static std::string ATLAS_LINALG_DENSE_BACKEND = resource(); return ATLAS_LINALG_DENSE_BACKEND; } Library& Library::instance() { return libatlas; } const void* Library::addr() const { return this; } std::string Library::version() const { return atlas::library::version(); } std::string Library::gitsha1(unsigned int count) const { return atlas::library::git_sha1(count); } void Library::initialise(int argc, char** argv) { if (not Main::ready()) { Main::initialise(argc, argv); Main::instance().taskID(eckit::mpi::comm("world").rank()); if (Main::instance().taskID() != 0) { eckit::Log::warning().reset(); eckit::Log::info().reset(); eckit::Log::debug().reset(); atlas::Log::debug().reset(); } Log::debug() << "Atlas initialised eckit::Main.\n"; if (eckit::mpi::comm("world").size() > 1) { Log::debug() << "--> Only MPI rank 0 is logging. Please initialise eckit::Main \n" " before to avoid this behaviour.\n"; } } initialise(); } void Library::initialise(const eckit::Parametrisation& config) { ATLAS_ASSERT(eckit::Main::ready()); if (initialized_) { return; } initialized_ = true; if (config.has("log")) { config.get("log.info", info_); config.get("log.trace", trace_); config.get("log.warning", warning_); config.get("log.debug", debug_); } if (config.has("trace")) { config.get("trace.barriers", trace_barriers_); config.get("trace.report", trace_report_); config.get("trace.memory", trace_memory_); } if (not debug_) { debug_channel_.reset(); } if (not trace_) { trace_channel_.reset(); } if (not info_) { info_channel_.reset(); } if (not warning_) { warning_channel_.reset(); } auto& out = [&]() -> eckit::Channel& { if (getEnv("ATLAS_LOG_RANK", 0) == int(mpi::rank())) { return Log::debug(); } static eckit::Channel sink; return sink; }(); library::enable_floating_point_exceptions(); library::enable_atlas_signal_handler(); if (data_paths_.empty()) { init_data_paths(data_paths_); } atlas_io_trace_hook_ = atlas::io::TraceHookRegistry::add([](const eckit::CodeLocation& loc, const std::string& title) { struct Adaptor : public atlas::io::TraceHook { Adaptor(const eckit::CodeLocation& loc, const std::string& title): trace{loc, title} {} atlas::Trace trace; }; return std::make_unique(loc, title); }); // Summary if (getEnv("ATLAS_LOG_RANK", 0) == int(mpi::rank())) { out << "Executable [" << Main::instance().name() << "]\n"; out << " \n"; out << " current dir [" << PathName(LocalPathName::cwd()).fullName() << "]\n"; out << " \n"; out << " MPI\n"; out << " communicator [" << mpi::comm() << "] \n"; out << " size [" << mpi::size() << "] \n"; out << " rank [" << mpi::rank() << "] \n"; out << " OMP\n"; out << " max_threads [" << atlas_omp_get_max_threads() << "] \n"; out << " GPU\n"; out << " devices [" << pluto::devices() << "] \n"; out << " OpenACC [" << acc::devices() << "] \n"; out << " \n"; out << " log.info [" << str(info_) << "] \n"; out << " log.trace [" << str(trace()) << "] \n"; out << " log.debug [" << str(debug()) << "] \n"; out << " trace.barriers [" << str(traceBarriers()) << "] \n"; out << " trace.report [" << str(trace_report_) << "] \n"; out << " trace.memory [" << str(trace_memory_) << "] \n"; out << " \n"; out << atlas::Library::instance().information(); out << std::flush; } pluto::trace::set(eckit::Log::info()); } void Library::initialise() { initialise(util::NoConfig()); } void Library::finalise() { if( atlas_io_trace_hook_ != atlas::io::TraceHookRegistry::invalidId() ) { atlas::io::TraceHookRegistry::disable(atlas_io_trace_hook_); atlas_io_trace_hook_ = atlas::io::TraceHookRegistry::invalidId(); } if (ATLAS_HAVE_TRACE && trace_report_) { Log::info() << atlas::Trace::report() << std::endl; Log::info() << pluto::memory::report() << std::endl; } if (getEnv("ATLAS_FINALISES_MPI", false)) { Log::debug() << "ATLAS_FINALISES_MPI is set: calling atlas::mpi::finalize()" << std::endl; mpi::finalise(); } // Make sure that these specialised channels that wrap Log::info() are // destroyed before eckit::Log::info gets destroyed. // Just in case someone still tries to log, we reset to empty channels. trace_channel_.reset(new eckit::Channel()); if (pluto::trace::enabled()) { Log::debug() << "Disabling pluto::trace during atlas::Library::finalise()" << std::endl; pluto::trace::out << "PLUTO_TRACE pluto::trace::enable(false)" << std::endl; } pluto::trace::enable(false); Log::debug() << "Atlas finalised" << std::endl; Log::flush(); if (debugChannel()) { debug_channel_.reset(new eckit::Channel(new eckit::PrefixTarget("ATLAS_DEBUG"))); } if (infoChannel()) { info_ = false; info_channel_.reset(new eckit::Channel(new eckit::PrefixTarget("ATLAS_INFO"))); } if (warningChannel()) { warning_ = false; warning_channel_.reset(new eckit::Channel(new eckit::PrefixTarget("ATLAS_WARNING"))); } initialized_ = false; } eckit::Channel& Library::infoChannel() const { if (info_) { return eckit::Log::info(); } else if (!info_channel_) { info_channel_.reset(new eckit::Channel()); } return *info_channel_; } eckit::Channel& Library::warningChannel() const { if (warning_) { return eckit::Log::warning(); } else if (!warning_channel_) { warning_channel_.reset(new eckit::Channel()); } return *warning_channel_; } eckit::Channel& Library::traceChannel() const { if (trace_channel_) { return *trace_channel_; } if (trace_) { trace_channel_.reset( new eckit::Channel(new eckit::PrefixTarget("ATLAS_TRACE", new eckit::OStreamTarget(eckit::Log::info())))); } else { trace_channel_.reset(new eckit::Channel()); } return *trace_channel_; } eckit::Channel& Library::debugChannel() const { if (debug_channel_) { return *debug_channel_; } if (debug_) { debug_channel_.reset(new eckit::Channel(new eckit::PrefixTarget("ATLAS_DEBUG"))); } else { debug_channel_.reset(new eckit::Channel()); } return *debug_channel_; } //---------------------------------------------------------------------------------------------------------------------- void Library::Information::print(std::ostream& out) const { out << "atlas version (" << atlas::Library::instance().version() << "), " << "git-sha1 " << atlas::Library::instance().gitsha1(7) << '\n'; out << " \n"; out << " Build:" << std::endl; out << " build type : " << ATLAS_BUILD_TYPE << '\n' << " timestamp : " << ATLAS_BUILD_TIMESTAMP << '\n' << " source dir : " << ATLAS_DEVELOPER_SRC_DIR << '\n' << " build dir : " << ATLAS_DEVELOPER_BIN_DIR << '\n' << " op. system : " << ATLAS_OS_NAME << " (" << ATLAS_OS_STR << ")" << '\n' << " processor : " << ATLAS_SYS_PROCESSOR << std::endl << " c compiler : " << ATLAS_C_COMPILER_ID << " " << ATLAS_C_COMPILER_VERSION << '\n' << " flags : " << ATLAS_C_FLAGS << '\n' << " c++ compiler : " << ATLAS_CXX_COMPILER_ID << " " << ATLAS_CXX_COMPILER_VERSION << '\n' << " flags : " << ATLAS_CXX_FLAGS << '\n' #ifdef ATLAS_Fortran_COMPILER << " fortran compiler: " << ATLAS_Fortran_COMPILER_ID << " " << ATLAS_Fortran_COMPILER_VERSION << '\n' << " flags : " << ATLAS_Fortran_FLAGS << '\n' #else << " fortran : NO " << '\n' #endif << " \n"; bool feature_fortran(ATLAS_HAVE_FORTRAN); bool feature_OpenMP(ATLAS_HAVE_OMP); bool feature_OpenACC(ATLAS_HAVE_ACC); bool feature_GPU(ATLAS_HAVE_GPU); bool feature_ecTrans(ATLAS_HAVE_ECTRANS); bool feature_FFTW(ATLAS_HAVE_FFTW); bool feature_Eigen(ATLAS_HAVE_EIGEN); bool feature_Tesselation(ATLAS_HAVE_TESSELATION); bool feature_PROJ(ATLAS_HAVE_PROJ); bool feature_BoundsChecking(ATLAS_ARRAYVIEW_BOUNDS_CHECKING); bool feature_Init_sNaN(ATLAS_INIT_SNAN); bool feature_MPI(false); #if ATLAS_HAVE_MPI feature_MPI = true; #endif std::string array_data_store = "Native-host"; #if ATLAS_HAVE_GPU array_data_store = "Native-GPU"; #endif #if ATLAS_HAVE_GRIDTOOLS_STORAGE array_data_store = "Gridtools-host"; #if ATLAS_GRIDTOOLS_STORAGE_BACKEND_CUDA array_data_store = "GridTools-CUDA"; #endif #endif out << " Features:" << '\n' << " Fortran : " << str(feature_fortran) << '\n' << " MPI : " << str(feature_MPI) << '\n' << " OpenMP : " << str(feature_OpenMP) << '\n' << " OpenACC : " << str(feature_OpenACC) << '\n' << " GPU : " << str(feature_GPU) << '\n' << " BoundsChecking : " << str(feature_BoundsChecking) << '\n' << " Init_sNaN : " << str(feature_Init_sNaN) << '\n' << " ecTrans : " << str(feature_ecTrans) << '\n' << " FFTW : " << str(feature_FFTW) << '\n' << " Eigen : " << str(feature_Eigen) << '\n' << " MKL : " << str(feature_MKL()) << '\n' << " Tesselation : " << str(feature_Tesselation) << '\n' << " PROJ : " << str(feature_PROJ) << '\n' << " ArrayDataStore : " << array_data_store << '\n' << " idx_t : " << ATLAS_BITS_LOCAL << " bit integer" << '\n' << " gidx_t : " << ATLAS_BITS_GLOBAL << " bit integer" << '\n'; auto& plugins = Library::instance().plugins(); if (!plugins.empty()) { out << " \n Plugins: \n"; for (auto& plugin : plugins) { out << " " << str(*plugin) << '\n'; } } out << " \n Dependencies: \n"; out << " ecbuild version (" << ECBUILD_VERSION << ")" << '\n'; if (eckit::system::LibraryManager::exists("eckit")) { out << " " << str(eckit::system::LibraryManager::lookup("eckit")) << '\n'; } if (eckit::system::LibraryManager::exists("fckit")) { out << " " << str(eckit::system::LibraryManager::lookup("fckit")) << '\n'; } #if ATLAS_HAVE_TRANS #if ATLAS_HAVE_ECTRANS out << " ectrans version (" << ectrans_version() << "), " << "git-sha1 " << ectrans_git_sha1_abbrev(7) << '\n'; out << " fiat version (" << ectrans_fiat_version() << "), " << "git-sha1 " << ectrans_fiat_git_sha1_abbrev(7) << '\n'; #else out << " transi version (" << transi_version() << "), " << "git-sha1 " << transi_git_sha1_abbrev(7) << '\n'; out << " trans version (" << trans_version() << "), " << "git-sha1 " << trans_git_sha1_abbrev(7) << '\n'; #endif #endif } } // namespace atlas atlas-0.46.0/src/atlas/library/version.h.in0000664000175000017500000000150115160212070020662 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once namespace atlas { namespace library { constexpr const char* version() { return "@atlas_VERSION_STR@"; } constexpr const char* semantic_version() { return "@atlas_VERSION@"; } constexpr long semantic_version_major() { return @atlas_VERSION_MAJOR@; } constexpr long semantic_version_minor() { return @atlas_VERSION_MINOR@; } constexpr long semantic_version_patch() { return @atlas_VERSION_PATCH@; } } } atlas-0.46.0/src/atlas/library/FloatingPointExceptions.h0000664000175000017500000000571315160212070023420 0ustar alastairalastair/* * (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 namespace atlas { namespace library { // ------------------------------------------------------------------------------------ /* @brief Enable floating point exceptions * * An environment variable is responsible to enable: * - ATLAS_FPE=0 --> Default. do not enable anything in Atlas (other libraries or programs may still do so) * - ATLAS_FPE=1 --> FE_INVALID, FE_DIVBYZERO, FE_OVERFLOW * - ATLAS_FPE=VALUE1,VALUE2,... --> Specified codes only. Valid codes: * FE_INVALID, FE_DIVBYZERO, FE_OVERFLOW, FE_UNDERFLOW, FE_INEXACT, FE_ALL_EXCEPT * * @note This function is called automatically within Library::initialize() and should * not be called directly */ void enable_floating_point_exceptions(); /* @brief Enable floating point exception * * Valid codes: * FE_INVALID, FE_DIVBYZERO, FE_OVERFLOW, FE_UNDERFLOW, FE_INEXACT, FE_ALL_EXCEPT * * @return false when the exception was already enabled, true when change was made */ bool enable_floating_point_exception(const std::string&); bool enable_floating_point_exception(int); /* @brief Disable floating point exception * * Valid codes: * FE_INVALID, FE_DIVBYZERO, FE_OVERFLOW, FE_UNDERFLOW, FE_INEXACT, FE_ALL_EXCEPT * * @return false when the exception was already disabled, true when change was made */ bool disable_floating_point_exception(const std::string&); bool disable_floating_point_exception(int); // ------------------------------------------------------------------------------------ /* @brief Enable atlas signal handler for all signals * * An environment variable is responsible to enable: * - ATLAS_SIGNAL_HANDLER=0 --> Default. Atlas will not set any signal handlers * - ATLAS_SIGNAL_HANDLER=1 --> Enable atlas_signal_handler instead of the default for signals: * * Enabled signals: * - SIGABRT * - SIGFPE * - SIGILL * - SIGINT * - SIGSEGV * - SIGTERM * - SIGKILL * * @note This function is called automatically within Library::initialize() and should * not be called directly */ void enable_atlas_signal_handler(); // ------------------------------------------------------------------------------------ using signal_handler_t = void (*)(int); using signal_action_t = void (*)(int, ::siginfo_t*, void*); [[noreturn]] void atlas_signal_handler(int signum, ::siginfo_t* si, void* unused); // ------------------------------------------------------------------------------------ } // namespace library } // namespace atlas atlas-0.46.0/src/atlas/parallel/0000775000175000017500000000000015160212070016552 5ustar alastairalastairatlas-0.46.0/src/atlas/parallel/GatherScatter.h0000664000175000017500000005424415160212070021474 0ustar alastairalastair/* * (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 #include #include "atlas/array/ArrayView.h" #include "atlas/library/config.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/runtime/Exception.h" #include "atlas/util/Object.h" namespace atlas { namespace parallel { //---------------------------------------------------------------------------------------------------------------------- template class Field { public: Field() {} Field(const DATA_TYPE data_[], const idx_t var_strides_[], const idx_t var_shape_[], const idx_t var_rank_): data(const_cast(data_)), var_rank(var_rank_) { var_strides.assign(var_strides_, var_strides_ + var_rank_); var_shape.assign(var_shape_, var_shape_ + var_rank_); } Field(const DATA_TYPE data_[], const idx_t nb_vars): data(const_cast(data_)), var_rank(1) { var_strides.assign(1, 1); var_shape.assign(1, nb_vars); } template Field(const array::ArrayView& arr) { data = const_cast(arr.data()); var_rank = Rank; var_strides.resize(var_rank); var_shape.resize(var_rank); if (arr.rank() > 1) { var_strides[0] = arr.stride(0); var_shape[0] = 1; for (int j = 1; j < Rank; ++j) { var_strides[j] = arr.stride(j); var_shape[j] = arr.shape(j); } } else { var_strides[0] = arr.stride(0); var_shape[0] = 1; } } template Field(const array::LocalView& arr) { data = const_cast(arr.data()); var_rank = Rank; var_strides.resize(var_rank); var_shape.resize(var_rank); if (arr.rank() > 1) { var_strides[0] = arr.stride(0); var_shape[0] = 1; for (int j = 1; j < Rank; ++j) { var_strides[j] = arr.stride(j); var_shape[j] = arr.shape(j); } } else { var_strides[0] = arr.stride(0); var_shape[0] = 1; } } public: DATA_TYPE* data; std::vector var_strides; std::vector var_shape; idx_t var_rank; }; class GatherScatter : public util::Object { public: GatherScatter(); GatherScatter(const std::string& name); virtual ~GatherScatter() {} public: // methods std::string name() const { return name_; } /// @brief Setup /// @param [in] mpi_comm Name of mpi communicator /// @param [in] part List of partitions /// @param [in] remote_idx List of local indices on remote partitions /// @param [in] base values of remote_idx start at "base" /// @param [in] glb_idx List of global indices /// @param [in] parsize size of given lists void setup(const std::string& mpi_comm, const int part[], const idx_t remote_idx[], const int base, const gidx_t glb_idx[], const idx_t parsize); /// @brief Setup /// @param [in] part List of partitions /// @param [in] remote_idx List of local indices on remote partitions /// @param [in] base values of remote_idx start at "base" /// @param [in] glb_idx List of global indices /// @param [in] parsize size of given lists void setup(const int part[], const idx_t remote_idx[], const int base, const gidx_t glb_idx[], const idx_t parsize); /// @brief Setup /// @param [in] mpi_comm Name of mpi communicator /// @param [in] part List of partitions /// @param [in] remote_idx List of local indices on remote partitions /// @param [in] base values of remote_idx start at "base" /// @param [in] glb_idx List of global indices /// @param [in] parsize size of given lists /// @param [in] mask Mask indices not to include in the communication /// pattern (0=include,1=exclude) void setup(const std::string& mpi_comm, const int part[], const idx_t remote_idx[], const int base, const gidx_t glb_idx[], const int mask[], const idx_t parsize); /// @brief Setup /// @param [in] part List of partitions /// @param [in] remote_idx List of local indices on remote partitions /// @param [in] base values of remote_idx start at "base" /// @param [in] glb_idx List of global indices /// @param [in] parsize size of given lists /// @param [in] mask Mask indices not to include in the communication /// pattern (0=include,1=exclude) void setup(const int part[], const idx_t remote_idx[], const int base, const gidx_t glb_idx[], const int mask[], const idx_t parsize); template void gather(const DATA_TYPE ldata[], const idx_t lstrides[], const idx_t lshape[], const idx_t lrank, const idx_t lmpl_idxpos[], const idx_t lmpl_rank, DATA_TYPE gdata[], const idx_t gstrides[], const idx_t gshape[], const idx_t grank, const idx_t gmpl_idxpos[], const idx_t gmpl_rank, const idx_t root) const; template void gather(const DATA_TYPE ldata[], const idx_t lvar_strides[], const idx_t lvar_shape[], const idx_t lvar_rank, DATA_TYPE gdata[], const idx_t gvar_strides[], const idx_t gvar_shape[], const idx_t gvar_rank, const idx_t root = 0) const; template void gather(parallel::Field lfields[], parallel::Field gfields[], const idx_t nb_fields, const idx_t root = 0) const; template void gather(const array::ArrayView& ldata, array::ArrayView& gdata, const idx_t root = 0) const; template void scatter(parallel::Field gfields[], parallel::Field lfields[], const idx_t nb_fields, const idx_t root = 0) const; template void scatter(const DATA_TYPE gdata[], const idx_t gstrides[], const idx_t gshape[], const idx_t grank, const idx_t gmpl_idxpos[], const idx_t gmpl_rank, DATA_TYPE ldata[], const idx_t lstrides[], const idx_t lshape[], const idx_t lrank, const idx_t lmpl_idxpos[], const idx_t lmpl_rank, const idx_t root) const; template void scatter(const DATA_TYPE gdata[], const idx_t gvar_strides[], const idx_t gvar_shape[], const idx_t gvar_rank, DATA_TYPE ldata[], const idx_t lvar_strides[], const idx_t lvar_shape[], const idx_t lvar_rank, const idx_t root = 0) const; template void scatter(const array::ArrayView& gdata, array::ArrayView& ldata, const idx_t root = 0) const; gidx_t glb_dof() const { return glbcnt_; } idx_t loc_dof() const { return loccnt_; } const mpi::Comm& comm() const { return *comm_; } private: // methods template void pack_send_buffer(const parallel::Field& field, const std::vector& sendmap, DATA_TYPE send_buffer[]) const; template void unpack_recv_buffer(const std::vector& recvmap, const DATA_TYPE recv_buffer[], const parallel::Field& field) const; template void var_info(const array::ArrayView& arr, std::vector& varstrides, std::vector& varshape) const; private: // data std::string name_; int loccnt_; int glbcnt_; std::vector glbcounts_; std::vector glbdispls_; // glbmap describes how the global MPI buffer maps to the global field // locmap describes how the local MPI buffer maps to the local field // Scatter: // - pack global_MPI_buffer from global field using glbmap // - MPI_Scatterv( global_MPI_buffer -> local_MPI_buffer) // - unpack local_MPI_buffer into local field using locmap std::vector locmap_; std::vector glbmap_; const mpi::Comm* comm_; idx_t nproc; idx_t myproc; bool is_setup_; idx_t parsize_; friend class Checksum; size_t glb_cnt(idx_t root) const { return myproc == root ? glbcnt_ : 0; } }; //-------------------------------------------------------------------------------------------------- template void GatherScatter::gather(parallel::Field lfields[], parallel::Field gfields[], idx_t nb_fields, const idx_t root) const { if (!is_setup_) { throw_Exception("GatherScatter was not setup", Here()); } for (idx_t jfield = 0; jfield < nb_fields; ++jfield) { const idx_t lvar_size = std::accumulate(lfields[jfield].var_shape.data(), lfields[jfield].var_shape.data() + lfields[jfield].var_rank, 1, std::multiplies()); const idx_t gvar_size = std::accumulate(gfields[jfield].var_shape.data(), gfields[jfield].var_shape.data() + gfields[jfield].var_rank, 1, std::multiplies()); const size_t loc_size = loccnt_ * lvar_size; const size_t glb_size = glb_cnt(root) * gvar_size; std::vector loc_buffer(loc_size); std::vector glb_buffer(glb_size); std::vector glb_displs(nproc); std::vector glb_counts(nproc); for (idx_t jproc = 0; jproc < nproc; ++jproc) { glb_counts[jproc] = glbcounts_[jproc] * gvar_size; glb_displs[jproc] = glbdispls_[jproc] * gvar_size; } /// Pack pack_send_buffer(lfields[jfield], locmap_, loc_buffer.data()); /// Gather ATLAS_TRACE_MPI(GATHER) { comm().gatherv(loc_buffer, glb_buffer, glb_counts, glb_displs, root); } /// Unpack if (myproc == root) unpack_recv_buffer(glbmap_, glb_buffer.data(), gfields[jfield]); } } template void GatherScatter::gather(const DATA_TYPE ldata[], const idx_t lvar_strides[], const idx_t lvar_shape[], const idx_t lvar_rank, DATA_TYPE gdata[], const idx_t gvar_strides[], const idx_t gvar_shape[], const idx_t gvar_rank, const idx_t root) const { parallel::Field lfield(ldata, lvar_strides, lvar_shape, lvar_rank); parallel::Field gfield(gdata, gvar_strides, gvar_shape, gvar_rank); gather(&lfield, &gfield, 1, root); } template void GatherScatter::scatter(parallel::Field gfields[], parallel::Field lfields[], const idx_t nb_fields, const idx_t root) const { if (!is_setup_) { throw_Exception("GatherScatter was not setup", Here()); } for (idx_t jfield = 0; jfield < nb_fields; ++jfield) { const int lvar_size = std::accumulate(lfields[jfield].var_shape.data(), lfields[jfield].var_shape.data() + lfields[jfield].var_rank, 1, std::multiplies()); const int gvar_size = std::accumulate(gfields[jfield].var_shape.data(), gfields[jfield].var_shape.data() + gfields[jfield].var_rank, 1, std::multiplies()); const size_t loc_size = loccnt_ * lvar_size; const size_t glb_size = glb_cnt(root) * gvar_size; std::vector loc_buffer(loc_size); std::vector glb_buffer(glb_size); std::vector glb_displs(nproc); std::vector glb_counts(nproc); for (idx_t jproc = 0; jproc < nproc; ++jproc) { glb_counts[jproc] = glbcounts_[jproc] * gvar_size; glb_displs[jproc] = glbdispls_[jproc] * gvar_size; } /// Pack if (myproc == root) { pack_send_buffer(gfields[jfield], glbmap_, glb_buffer.data()); } /// Scatter ATLAS_TRACE_MPI(SCATTER) { comm().scatterv(glb_buffer.begin(), glb_buffer.end(), glb_counts, glb_displs, loc_buffer.begin(), loc_buffer.end(), root); } /// Unpack unpack_recv_buffer(locmap_, loc_buffer.data(), lfields[jfield]); } } template void GatherScatter::scatter(const DATA_TYPE gdata[], const idx_t gvar_strides[], const idx_t gvar_shape[], const idx_t gvar_rank, DATA_TYPE ldata[], const idx_t lvar_strides[], const idx_t lvar_shape[], const idx_t lvar_rank, const idx_t root) const { parallel::Field gfield(gdata, gvar_strides, gvar_shape, gvar_rank); parallel::Field lfield(ldata, lvar_strides, lvar_shape, lvar_rank); scatter(&gfield, &lfield, 1, root); } template void GatherScatter::pack_send_buffer(const parallel::Field& field, const std::vector& sendmap, DATA_TYPE send_buffer[]) const { const idx_t sendcnt = static_cast(sendmap.size()); idx_t ibuf = 0; const idx_t send_stride = field.var_strides[0] * field.var_shape[0]; switch (field.var_rank) { case 1: for (idx_t p = 0; p < sendcnt; ++p) { const idx_t pp = send_stride * sendmap[p]; for (idx_t i = 0; i < field.var_shape[0]; ++i) { DATA_TYPE tmp = field.data[pp + i * field.var_strides[0]]; send_buffer[ibuf++] = tmp; } } break; case 2: for (idx_t p = 0; p < sendcnt; ++p) { const idx_t pp = send_stride * sendmap[p]; for (idx_t i = 0; i < field.var_shape[0]; ++i) { const idx_t ii = pp + i * field.var_strides[0]; for (idx_t j = 0; j < field.var_shape[1]; ++j) { send_buffer[ibuf++] = field.data[ii + j * field.var_strides[1]]; } } } break; case 3: for (idx_t p = 0; p < sendcnt; ++p) { const idx_t pp = send_stride * sendmap[p]; for (idx_t i = 0; i < field.var_shape[0]; ++i) { const idx_t ii = pp + i * field.var_strides[0]; for (idx_t j = 0; j < field.var_shape[1]; ++j) { const idx_t jj = ii + j * field.var_strides[1]; for (idx_t k = 0; k < field.var_shape[2]; ++k) { send_buffer[ibuf++] = field.data[jj + k * field.var_strides[2]]; } } } } break; default: ATLAS_NOTIMPLEMENTED; } } template void GatherScatter::unpack_recv_buffer(const std::vector& recvmap, const DATA_TYPE recv_buffer[], const parallel::Field& field) const { const idx_t recvcnt = static_cast(recvmap.size()); size_t ibuf = 0; const idx_t recv_stride = field.var_strides[0] * field.var_shape[0]; switch (field.var_rank) { case 1: for (idx_t p = 0; p < recvcnt; ++p) { const idx_t pp = recv_stride * recvmap[p]; for (idx_t i = 0; i < field.var_shape[0]; ++i) { field.data[pp + i * field.var_strides[0]] = recv_buffer[ibuf++]; } } break; case 2: for (idx_t p = 0; p < recvcnt; ++p) { const idx_t pp = recv_stride * recvmap[p]; for (idx_t i = 0; i < field.var_shape[0]; ++i) { const idx_t ii = pp + i * field.var_strides[0]; for (idx_t j = 0; j < field.var_shape[1]; ++j) { field.data[ii + j * field.var_strides[1]] = recv_buffer[ibuf++]; } } } break; case 3: for (idx_t p = 0; p < recvcnt; ++p) { const idx_t pp = recv_stride * recvmap[p]; for (idx_t i = 0; i < field.var_shape[0]; ++i) { const idx_t ii = pp + i * field.var_strides[0]; for (idx_t j = 0; j < field.var_shape[1]; ++j) { const idx_t jj = ii + j * field.var_strides[1]; for (idx_t k = 0; k < field.var_shape[2]; ++k) { field.data[jj + k * field.var_strides[2]] = recv_buffer[ibuf++]; } } } } break; default: ATLAS_NOTIMPLEMENTED; } } template void GatherScatter::var_info(const array::ArrayView& arr, std::vector& varstrides, std::vector& varshape) const { int rank = std::max(1, RANK - 1); varstrides.resize(rank); varshape.resize(rank); if (RANK > 1) { idx_t stride = 1; for (int j = RANK - 1; j > 0; --j) { varstrides[j - 1] = stride; varshape[j - 1] = arr.shape(j); stride *= varshape[j - 1]; } // varstrides.assign(arr.strides()+1,arr.strides()+RANK); // varshape.assign(arr.shape()+1,arr.shape()+RANK); } else { varstrides[0] = 1; varshape[0] = 1; } } template void GatherScatter::gather(const array::ArrayView& ldata, array::ArrayView& gdata, const idx_t root) const { if (ldata.shape(0) == parsize_ && gdata.shape(0) == idx_t(glb_cnt(root))) { std::vector> lfields(1, parallel::Field(ldata)); std::vector> gfields(1, parallel::Field(gdata)); gather(lfields.data(), gfields.data(), 1, root); } else { ATLAS_NOTIMPLEMENTED; // Need to implement with parallel ranks > 1 } } template void GatherScatter::scatter(const array::ArrayView& gdata, array::ArrayView& ldata, const idx_t root) const { if (ldata.shape(0) == parsize_ && gdata.shape(0) == idx_t(glb_cnt(root))) { std::vector> gfields(1, parallel::Field(gdata)); std::vector> lfields(1, parallel::Field(ldata)); scatter(gfields.data(), lfields.data(), 1, root); } else { ATLAS_NOTIMPLEMENTED; // Need to implement with parallel ranks > 1 } } // ------------------------------------------------------------------ // C wrapper interfaces to C++ routines extern "C" { GatherScatter* atlas__GatherScatter__new(); void atlas__GatherScatter__delete(GatherScatter* This); void atlas__GatherScatter__setup32(GatherScatter* This, int part[], idx_t remote_idx[], int base, int glb_idx[], int parsize); void atlas__GatherScatter__setup64(GatherScatter* This, int part[], idx_t remote_idx[], int base, long glb_idx[], int parsize); int atlas__GatherScatter__glb_dof(GatherScatter* This); void atlas__GatherScatter__gather_int(GatherScatter* This, int ldata[], int lvar_strides[], int lvar_shape[], int lvar_rank, int gdata[], int gvar_strides[], int gvar_shape[], int gvar_rank); void atlas__GatherScatter__gather_long(GatherScatter* This, long ldata[], int lvar_strides[], int lvar_shape[], int lvar_rank, long gdata[], int gvar_strides[], int gvar_shape[], int gvar_rank); void atlas__GatherScatter__gather_float(GatherScatter* This, float ldata[], int lvar_strides[], int lvar_shape[], int lvar_rank, float gdata[], int gvar_strides[], int gvar_shape[], int gvar_rank); void atlas__GatherScatter__gather_double(GatherScatter* This, double ldata[], int lvar_strides[], int lvar_shape[], int lvar_rank, double gdata[], int gvar_strides[], int gvar_shape[], int gvar_rank); void atlas__GatherScatter__scatter_int(GatherScatter* This, int gdata[], int gvar_strides[], int gvar_shape[], int gvar_rank, int ldata[], int lvar_strides[], int lvar_shape[], int lvar_rank); void atlas__GatherScatter__scatter_long(GatherScatter* This, long gdata[], int gvar_strides[], int gvar_shape[], int gvar_rank, long ldata[], int lvar_strides[], int lvar_shape[], int lvar_rank); void atlas__GatherScatter__scatter_float(GatherScatter* This, float gdata[], int gvar_strides[], int gvar_shape[], int gvar_rank, float ldata[], int lvar_strides[], int lvar_shape[], int lvar_rank); void atlas__GatherScatter__scatter_double(GatherScatter* This, double gdata[], int gvar_strides[], int gvar_shape[], int gvar_rank, double ldata[], int lvar_strides[], int lvar_shape[], int lvar_rank); } //-------------------------------------------------------------------------------------------------- // typedef GatherScatter Gather; } // namespace parallel } // namespace atlas atlas-0.46.0/src/atlas/parallel/Collect.cc0000664000175000017500000001235515160212070020454 0ustar alastairalastair/* * (C) Copyright 2025- 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 "Collect.h" #include #include #include #include #include "pluto/pluto.h" namespace atlas::parallel { int Collect::devices() const { return pluto::devices(); } void Collect::setup(const idx_t recv_size, const int recv_part[], const idx_t recv_remote_idx[], const int recv_idx_base) { setup(mpi::comm().name(), recv_size, recv_part, recv_remote_idx, recv_idx_base ); } void Collect::setup(const std::string& mpi_comm, const idx_t recv_size, const int recv_part[], const idx_t recv_remote_idx[], const int recv_idx_base) { ATLAS_TRACE("Collect::setup()"); comm_ = &mpi::comm(mpi_comm); mpi_size_ = comm().size(); mpi_rank_ = comm().rank(); int nproc = mpi_size_; recvcnt_ = recv_size; sendcounts_.resize(nproc); sendcounts_.assign(nproc, 0); recvcounts_.resize(nproc); recvcounts_.assign(nproc, 0); senddispls_.resize(nproc); senddispls_.assign(nproc, 0); recvdispls_.resize(nproc); recvdispls_.assign(nproc, 0); atlas_omp_parallel { std::vector recvcounts(nproc, 0); atlas_omp_for(std::size_t jrecv = 0; jrecv < recvcnt_; ++jrecv) { int p = recv_part[jrecv]; ++recvcounts[p]; } atlas_omp_critical { for (int p=0; p < nproc; ++p) { recvcounts_[p] += recvcounts[p]; } } } /* Find the amount of nodes this rank has to send to each other ranks */ ATLAS_TRACE_MPI(ALLTOALL) { comm().allToAll(recvcounts_, sendcounts_); } sendcnt_ = std::accumulate(sendcounts_.begin(), sendcounts_.end(), 0); recvdispls_[0] = 0; senddispls_[0] = 0; for (int jproc = 1; jproc < nproc; ++jproc) // start at 1 { recvdispls_[jproc] = recvcounts_[jproc - 1] + recvdispls_[jproc - 1]; senddispls_[jproc] = sendcounts_[jproc - 1] + senddispls_[jproc - 1]; } /* Fill vector "send_requests" with remote index of nodes needed, but are on other procs We can also fill in the vector "recvmap_" which holds local indices of requested nodes */ std::vector send_requests(recvcnt_); std::vector recv_requests(sendcnt_); std::vector cnt(nproc, 0); recvmap_.resize(recvcnt_); #ifdef __PGI // No idea why PGI compiler (20.7) in Release build ( -fast -O3 ) decides to vectorize following loop #pragma loop novector #endif Log::trace() << __LINE__ << std::endl; for (int jrecv = 0; jrecv < recvcnt_; ++jrecv) { const int p = recv_part[jrecv]; const int req_idx = recvdispls_[p] + cnt[p]; send_requests[req_idx] = recv_remote_idx[jrecv] - recv_idx_base; recvmap_[req_idx] = jrecv; cnt[p]++; } Log::trace() << __LINE__ << std::endl; /* Fill vector "recv_requests" with what is needed by other procs */ ATLAS_TRACE_MPI(ALLTOALL) { ATLAS_ASSERT(send_requests.size() == recvcnt_); ATLAS_ASSERT(std::accumulate(recvcounts_.begin(),recvcounts_.end(),0) == recvcnt_); ATLAS_ASSERT(recv_requests.size() == sendcnt_); ATLAS_ASSERT(std::accumulate(sendcounts_.begin(),sendcounts_.end(),0) == sendcnt_); comm().allToAllv(send_requests.data(), recvcounts_.data(), recvdispls_.data(), recv_requests.data(), sendcounts_.data(), senddispls_.data()); } Log::trace() << __LINE__ << std::endl; /* What needs to be sent to other procs is asked by remote_idx, which is local here */ sendmap_.resize(sendcnt_); for (int jj = 0; jj < sendcnt_; ++jj) { sendmap_[jj] = recv_requests[jj]; } Log::trace() << __LINE__ << std::endl; is_setup_ = true; } void Collect::counts_displs_setup(const idx_t var_size, std::vector& send_counts_init, std::vector& recv_counts_init, std::vector& send_counts, std::vector& recv_counts, std::vector& send_displs, std::vector& recv_displs) const { for (size_t jproc = 0; jproc < static_cast(mpi_size_); ++jproc) { send_counts_init[jproc] = sendcounts_[jproc]; recv_counts_init[jproc] = recvcounts_[jproc]; send_counts[jproc] = sendcounts_[jproc] * var_size; recv_counts[jproc] = recvcounts_[jproc] * var_size; send_displs[jproc] = senddispls_[jproc] * var_size; recv_displs[jproc] = recvdispls_[jproc] * var_size; } } void Collect::wait_for_send(std::vector& send_counts_init, std::vector& send_req) const { ATLAS_TRACE_MPI(WAIT, "mpi-wait send") { for (size_t jproc = 0; jproc < static_cast(mpi_size_); ++jproc) { if (send_counts_init[jproc] > 0) { comm().wait(send_req[jproc]); } } } } } // namespace atlas::parallel atlas-0.46.0/src/atlas/parallel/GatherScatter.cc0000664000175000017500000004111615160212070021624 0ustar alastairalastair/* * (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 "eckit/log/Bytes.h" #include "atlas/array.h" #include "atlas/array/ArrayView.h" #include "atlas/parallel/GatherScatter.h" #include "atlas/parallel/mpi/Statistics.h" #include "atlas/runtime/Log.h" #include "atlas/runtime/Trace.h" #include "atlas/parallel/omp/sort.h" namespace atlas { namespace parallel { namespace { struct IsGhostPoint { IsGhostPoint(const int mypart, const int part[], const idx_t ridx[], const idx_t remote_idx_base, const int N) { part_ = part; ridx_ = ridx; remote_idx_base_ = remote_idx_base; mypart_ = mypart; } bool operator()(idx_t idx) { if (part_[idx] != mypart_) { return true; } if (ridx_[idx] != remote_idx_base_ + idx) { return true; } return false; } int mypart_; const int* part_; const idx_t* ridx_; idx_t remote_idx_base_; }; struct Node { int p; idx_t i; gidx_t g; Node() = default; Node(gidx_t gid, int part, idx_t idx) { g = gid; p = part; i = idx; } bool operator<(const Node& other) const { return (g < other.g); } bool operator==(const Node& other) const { return (g == other.g); } }; } // namespace GatherScatter::GatherScatter(): name_(), is_setup_(false) { } GatherScatter::GatherScatter(const std::string& name): name_(name), is_setup_(false) { } void GatherScatter::setup(const int part[], const idx_t remote_idx[], const int remote_idx_base, const gidx_t glb_idx[], const int mask[], const idx_t parsize) { setup(mpi::comm().name(), part, remote_idx, remote_idx_base, glb_idx, parsize); } void GatherScatter::setup(const std::string& mpi_comm, const int part[], const idx_t remote_idx[], const int remote_idx_base, const gidx_t glb_idx[], const int mask[], const idx_t parsize) { ATLAS_TRACE("GatherScatter::setup"); comm_ = &mpi::comm(mpi_comm); myproc = comm().rank(); nproc = comm().size(); parsize_ = parsize; glbcounts_.resize(nproc); glbcounts_.assign(nproc, 0); glbdispls_.resize(nproc); glbdispls_.assign(nproc, 0); std::vector sendnodes_gidx(parsize_); std::vector sendnodes_part(parsize_); std::vector sendnodes_ridx(parsize_); gidx_t glb_idx_base = 1; loccnt_ = 0; for (idx_t n = 0; n < parsize_; ++n) { if (!mask[n]) { sendnodes_gidx[loccnt_] = glb_idx[n]; sendnodes_part[loccnt_] = part[n]; sendnodes_ridx[loccnt_] = remote_idx[n] - remote_idx_base; ++loccnt_; } } ATLAS_TRACE_MPI(ALLGATHER) { comm().allGather(loccnt_, glbcounts_.begin(), glbcounts_.end()); } size_t glbcnt_size_t = std::accumulate(glbcounts_.begin(), glbcounts_.end(), size_t(0)); if (glbcnt_size_t > std::numeric_limits::max()) { ATLAS_THROW_EXCEPTION("Due to limitation of MPI we cannot use larger counts"); } glbcnt_ = std::accumulate(glbcounts_.begin(), glbcounts_.end(), size_t(0)); glbdispls_[0] = 0; for (idx_t jproc = 1; jproc < nproc; ++jproc) // start at 1 { glbdispls_[jproc] = glbcounts_[jproc - 1] + glbdispls_[jproc - 1]; } idx_t nb_recv_nodes = glbcnt_; std::vector node_sort; try { node_sort.resize(nb_recv_nodes); } catch (const std::bad_alloc& e) { ATLAS_THROW_EXCEPTION("Could not allocate node_sort with size " << eckit::Bytes(nb_recv_nodes * sizeof(Node))); } ATLAS_TRACE_SCOPE("receive nodes global index") { std::vector recvnodes_gidx; try { recvnodes_gidx.resize(glbcnt_); } catch (const std::bad_alloc& e) { ATLAS_THROW_EXCEPTION("Could not allocate recvnodes_gidx with size " << eckit::Bytes(glbcnt_ * sizeof(gidx_t))); } ATLAS_TRACE_MPI(ALLGATHER) { comm().allGatherv(sendnodes_gidx.begin(), sendnodes_gidx.begin() + loccnt_, recvnodes_gidx.data(), glbcounts_.data(), glbdispls_.data()); } atlas_omp_parallel_for(idx_t n = 0; n < nb_recv_nodes; ++n) { node_sort[n].g = recvnodes_gidx[n]; } sendnodes_gidx.clear(); } ATLAS_TRACE_SCOPE("receive nodes partition") { std::vector recvnodes_part; try { recvnodes_part.resize(glbcnt_); } catch (const std::bad_alloc& e) { ATLAS_THROW_EXCEPTION("Could not allocate recvnodes_part with size " << eckit::Bytes(glbcnt_ * sizeof(int))); } ATLAS_TRACE_MPI(ALLGATHER) { comm().allGatherv(sendnodes_part.begin(), sendnodes_part.begin() + loccnt_, recvnodes_part.data(), glbcounts_.data(), glbdispls_.data()); } atlas_omp_parallel_for(idx_t n = 0; n < nb_recv_nodes; ++n) { node_sort[n].p = recvnodes_part[n]; } sendnodes_part.clear(); } ATLAS_TRACE_SCOPE("receive nodes remote index") { std::vector recvnodes_ridx; try { recvnodes_ridx.resize(glbcnt_); } catch (const std::bad_alloc& e) { ATLAS_THROW_EXCEPTION("Could not allocate recvnodes_ridx with size " << eckit::Bytes(glbcnt_ * sizeof(idx_t))); } ATLAS_TRACE_MPI(ALLGATHER) { comm().allGatherv(sendnodes_ridx.begin(), sendnodes_ridx.begin() + loccnt_, recvnodes_ridx.data(), glbcounts_.data(), glbdispls_.data()); } atlas_omp_parallel_for(idx_t n = 0; n < nb_recv_nodes; ++n) { node_sort[n].i = recvnodes_ridx[n]; } sendnodes_ridx.clear(); } bool use_sorted_nodes = false; // `use_sorted_nodes = true` is the behaviour as it has been with atlas versions <= 0.42.0 if (use_sorted_nodes) { // Sort on "g" member, and remove duplicates ATLAS_TRACE_SCOPE("sorting") { // omp::sort(node_sort.begin(), node_sort.end()); std::sort(node_sort.begin(), node_sort.end()); } ATLAS_TRACE_SCOPE("remove duplicates") { node_sort.erase(std::unique(node_sort.begin(), node_sort.end()), node_sort.end()); } } glbcounts_.assign(nproc, 0); glbdispls_.assign(nproc, 0); for (size_t n = 0; n < node_sort.size(); ++n) { ++glbcounts_[node_sort[n].p]; } glbdispls_[0] = 0; for (idx_t jproc = 1; jproc < nproc; ++jproc) // start at 1 { glbdispls_[jproc] = glbcounts_[jproc - 1] + glbdispls_[jproc - 1]; } glbcnt_ = std::accumulate(glbcounts_.begin(), glbcounts_.end(), size_t(0)); loccnt_ = glbcounts_[myproc]; glbmap_.clear(); glbmap_.resize(glbcnt_); locmap_.clear(); locmap_.resize(loccnt_,-1); std::vector idx(nproc, 0); gidx_t n{node_sort.begin()->g - 1}; for (const auto& node : node_sort) { glbmap_[glbdispls_[node.p] + idx[node.p]] = (use_sorted_nodes ? n++ : node.g - glb_idx_base); if (node.p == myproc) { locmap_[idx[node.p]] = node.i; } ++idx[node.p]; } is_setup_ = true; } void GatherScatter::setup(const int part[], const idx_t remote_idx[], const int remote_idx_base, const gidx_t glb_idx[], const idx_t parsize) { setup(mpi::comm().name(), part, remote_idx, remote_idx_base, glb_idx, parsize); } void GatherScatter::setup(const std::string& mpi_comm, const int part[], const idx_t remote_idx[], const int remote_idx_base, const gidx_t glb_idx[], const idx_t parsize) { std::vector mask(parsize); { int mypart = mpi::comm(mpi_comm).rank(); IsGhostPoint is_ghost(mypart, part, remote_idx, remote_idx_base, parsize); for (idx_t jj = 0; jj < parsize; ++jj) { mask[jj] = is_ghost(jj) ? 1 : 0; } } setup(mpi_comm, part, remote_idx, remote_idx_base, glb_idx, mask.data(), parsize); } ///////////////////// GatherScatter* atlas__GatherScatter__new() { return new GatherScatter(); } void atlas__GatherScatter__delete(GatherScatter* This) { delete This; } void atlas__GatherScatter__setup32(GatherScatter* This, int part[], idx_t remote_idx[], int remote_idx_base, int glb_idx[], int parsize) { #if ATLAS_BITS_GLOBAL == 32 This->setup(part, remote_idx, remote_idx_base, glb_idx, parsize); #else std::vector glb_idx_convert(parsize); for (int j = 0; j < parsize; ++j) { glb_idx_convert[j] = glb_idx[j]; } This->setup(part, remote_idx, remote_idx_base, glb_idx_convert.data(), parsize); #endif } void atlas__GatherScatter__setup64(GatherScatter* This, int part[], idx_t remote_idx[], int remote_idx_base, long glb_idx[], int parsize) { #if ATLAS_BITS_GLOBAL == 64 This->setup(part, remote_idx, remote_idx_base, glb_idx, parsize); #else std::vector glb_idx_convert(parsize); for (idx_t j = 0; j < parsize; ++j) { glb_idx_convert[j] = glb_idx[j]; } This->setup(part, remote_idx, remote_idx_base, glb_idx_convert.data(), parsize); #endif } int atlas__GatherScatter__glb_dof(GatherScatter* This) { return This->glb_dof(); } void atlas__GatherScatter__gather_int(GatherScatter* This, int lfield[], int lvar_strides[], int lvar_extents[], int lvar_rank, int gfield[], int gvar_strides[], int gvar_extents[], int gvar_rank) { std::vector lvstrides(lvar_rank); std::vector lvextents(lvar_rank); std::vector gvstrides(gvar_rank); std::vector gvextents(gvar_rank); for (int n = 0; n < lvar_rank; ++n) { lvstrides[n] = lvar_strides[n]; lvextents[n] = lvar_extents[n]; } for (int n = 0; n < gvar_rank; ++n) { gvstrides[n] = gvar_strides[n]; gvextents[n] = gvar_extents[n]; } This->gather(lfield, lvstrides.data(), lvextents.data(), lvar_rank, gfield, gvstrides.data(), gvextents.data(), gvar_rank); } void atlas__GatherScatter__gather_long(GatherScatter* This, long lfield[], int lvar_strides[], int lvar_extents[], int lvar_rank, long gfield[], int gvar_strides[], int gvar_extents[], int gvar_rank) { std::vector lvstrides(lvar_rank); std::vector lvextents(lvar_rank); std::vector gvstrides(gvar_rank); std::vector gvextents(gvar_rank); for (int n = 0; n < lvar_rank; ++n) { lvstrides[n] = lvar_strides[n]; lvextents[n] = lvar_extents[n]; } for (int n = 0; n < gvar_rank; ++n) { gvstrides[n] = gvar_strides[n]; gvextents[n] = gvar_extents[n]; } This->gather(lfield, lvstrides.data(), lvextents.data(), lvar_rank, gfield, gvstrides.data(), gvextents.data(), gvar_rank); } void atlas__GatherScatter__gather_float(GatherScatter* This, float lfield[], int lvar_strides[], int lvar_extents[], int lvar_rank, float gfield[], int gvar_strides[], int gvar_extents[], int gvar_rank) { std::vector lvstrides(lvar_rank); std::vector lvextents(lvar_rank); std::vector gvstrides(gvar_rank); std::vector gvextents(gvar_rank); for (int n = 0; n < lvar_rank; ++n) { lvstrides[n] = lvar_strides[n]; lvextents[n] = lvar_extents[n]; } for (int n = 0; n < gvar_rank; ++n) { gvstrides[n] = gvar_strides[n]; gvextents[n] = gvar_extents[n]; } This->gather(lfield, lvstrides.data(), lvextents.data(), lvar_rank, gfield, gvstrides.data(), gvextents.data(), gvar_rank); } void atlas__GatherScatter__gather_double(GatherScatter* This, double lfield[], int lvar_strides[], int lvar_extents[], int lvar_rank, double gfield[], int gvar_strides[], int gvar_extents[], int gvar_rank) { std::vector lvstrides(lvar_rank); std::vector lvextents(lvar_rank); std::vector gvstrides(gvar_rank); std::vector gvextents(gvar_rank); for (int n = 0; n < lvar_rank; ++n) { lvstrides[n] = lvar_strides[n]; lvextents[n] = lvar_extents[n]; } for (int n = 0; n < gvar_rank; ++n) { gvstrides[n] = gvar_strides[n]; gvextents[n] = gvar_extents[n]; } This->gather(lfield, lvstrides.data(), lvextents.data(), lvar_rank, gfield, gvstrides.data(), gvextents.data(), gvar_rank); } void atlas__GatherScatter__scatter_int(GatherScatter* This, int gfield[], int gvar_strides[], int gvar_extents[], int gvar_rank, int lfield[], int lvar_strides[], int lvar_extents[], int lvar_rank) { std::vector lvstrides(lvar_rank); std::vector lvextents(lvar_rank); std::vector gvstrides(gvar_rank); std::vector gvextents(gvar_rank); for (int n = 0; n < lvar_rank; ++n) { lvstrides[n] = lvar_strides[n]; lvextents[n] = lvar_extents[n]; } for (int n = 0; n < gvar_rank; ++n) { gvstrides[n] = gvar_strides[n]; gvextents[n] = gvar_extents[n]; } This->scatter(gfield, gvstrides.data(), gvextents.data(), gvar_rank, lfield, lvstrides.data(), lvextents.data(), lvar_rank); } void atlas__GatherScatter__scatter_long(GatherScatter* This, long gfield[], int gvar_strides[], int gvar_extents[], int gvar_rank, long lfield[], int lvar_strides[], int lvar_extents[], int lvar_rank) { std::vector lvstrides(lvar_rank); std::vector lvextents(lvar_rank); std::vector gvstrides(gvar_rank); std::vector gvextents(gvar_rank); for (int n = 0; n < lvar_rank; ++n) { lvstrides[n] = lvar_strides[n]; lvextents[n] = lvar_extents[n]; } for (int n = 0; n < gvar_rank; ++n) { gvstrides[n] = gvar_strides[n]; gvextents[n] = gvar_extents[n]; } This->scatter(gfield, gvstrides.data(), gvextents.data(), gvar_rank, lfield, lvstrides.data(), lvextents.data(), lvar_rank); } void atlas__GatherScatter__scatter_float(GatherScatter* This, float gfield[], int gvar_strides[], int gvar_extents[], int gvar_rank, float lfield[], int lvar_strides[], int lvar_extents[], int lvar_rank) { std::vector lvstrides(lvar_rank); std::vector lvextents(lvar_rank); std::vector gvstrides(gvar_rank); std::vector gvextents(gvar_rank); for (int n = 0; n < lvar_rank; ++n) { lvstrides[n] = lvar_strides[n]; lvextents[n] = lvar_extents[n]; } for (int n = 0; n < gvar_rank; ++n) { gvstrides[n] = gvar_strides[n]; gvextents[n] = gvar_extents[n]; } This->scatter(gfield, gvstrides.data(), gvextents.data(), gvar_rank, lfield, lvstrides.data(), lvextents.data(), lvar_rank); } void atlas__GatherScatter__scatter_double(GatherScatter* This, double gfield[], int gvar_strides[], int gvar_extents[], int gvar_rank, double lfield[], int lvar_strides[], int lvar_extents[], int lvar_rank) { std::vector lvstrides(lvar_rank); std::vector lvextents(lvar_rank); std::vector gvstrides(gvar_rank); std::vector gvextents(gvar_rank); for (int n = 0; n < lvar_rank; ++n) { lvstrides[n] = lvar_strides[n]; lvextents[n] = lvar_extents[n]; } for (int n = 0; n < gvar_rank; ++n) { gvstrides[n] = gvar_strides[n]; gvextents[n] = gvar_extents[n]; } This->scatter(gfield, gvstrides.data(), gvextents.data(), gvar_rank, lfield, lvstrides.data(), lvextents.data(), lvar_rank); } ///////////////////// } // namespace parallel } // namespace atlas atlas-0.46.0/src/atlas/parallel/Checksum.h0000664000175000017500000002010615160212070020464 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include "eckit/utils/Translator.h" #include "atlas/array/ArrayView.h" #include "atlas/parallel/GatherScatter.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/util/Checksum.h" #include "atlas/util/Object.h" #include "atlas/util/ObjectHandle.h" namespace atlas { namespace parallel { class Checksum : public util::Object { public: Checksum(); Checksum(const std::string& name); virtual ~Checksum() {} public: // methods std::string name() const { return name_; } /// @brief Setup /// @param [in] mpi_comm MPI communicator /// @param [in] part List of partitions /// @param [in] remote_idx List of local indices on remote partitions /// @param [in] base values of remote_idx start at "base" /// @param [in] glb_idx List of global indices /// @param [in] max_glb_idx maximum glb index we want to Checksum. /// To Checksum everything, set to val > max value in /// domain /// @param [in] parsize size of given lists void setup(const std::string& mpi_comm, const int part[], const idx_t remote_idx[], const int base, const gidx_t glb_idx[], const int parsize); /// @brief Setup /// @param [in] part List of partitions /// @param [in] remote_idx List of local indices on remote partitions /// @param [in] base values of remote_idx start at "base" /// @param [in] glb_idx List of global indices /// @param [in] max_glb_idx maximum glb index we want to Checksum. /// To Checksum everything, set to val > max value in /// domain /// @param [in] parsize size of given lists void setup(const int part[], const idx_t remote_idx[], const int base, const gidx_t glb_idx[], const int parsize); /// @brief Setup /// @param [in] mpi_comm MPI communicator /// @param [in] part List of partitions /// @param [in] remote_idx List of local indices on remote partitions /// @param [in] base values of remote_idx start at "base" /// @param [in] glb_idx List of global indices /// @param [in] mask Mask indices not to include in the communication /// pattern (0=include,1=exclude) /// @param [in] parsize size of given lists void setup(const std::string& mpi_comm, const int part[], const idx_t remote_idx[], const int base, const gidx_t glb_idx[], const int mask[], const int parsize); /// @brief Setup /// @param [in] part List of partitions /// @param [in] remote_idx List of local indices on remote partitions /// @param [in] base values of remote_idx start at "base" /// @param [in] glb_idx List of global indices /// @param [in] mask Mask indices not to include in the communication /// pattern (0=include,1=exclude) /// @param [in] parsize size of given lists void setup(const int part[], const idx_t remote_idx[], const int base, const gidx_t glb_idx[], const int mask[], const int parsize); /// @brief Setup void setup(const util::ObjectHandle&); template std::string execute(const DATA_TYPE lfield[], const int lvar_strides[], const int lvar_extents[], const int lvar_rank) const; template std::string execute(DATA_TYPE lfield[], const int nb_vars) const; template std::string execute(const array::ArrayView& lfield) const; template void var_info(const array::ArrayView& arr, std::vector& varstrides, std::vector& varextents) const; private: // data std::string name_; util::ObjectHandle gather_; bool is_setup_; size_t parsize_; }; template std::string Checksum::execute(const DATA_TYPE data[], const int var_strides[], const int var_extents[], const int var_rank) const { idx_t root = 0; if (!is_setup_) { throw_Exception("Checksum was not setup", Here()); } std::vector local_checksums(parsize_); int var_size = var_extents[0] * var_strides[0]; for (size_t pp = 0; pp < parsize_; ++pp) { local_checksums[pp] = util::checksum(data + pp * var_size, var_size); } std::vector global_checksums(gather_->comm().rank() == root ? gather_->glb_dof() : 0); parallel::Field loc(local_checksums.data(), 1); parallel::Field glb(global_checksums.data(), 1); gather_->gather(&loc, &glb, 1); util::checksum_t glb_checksum = util::checksum(global_checksums.data(), global_checksums.size()); gather_->comm().broadcast(glb_checksum, root); return eckit::Translator()(glb_checksum); } template std::string Checksum::execute(DATA_TYPE lfield[], const int nb_vars) const { int strides[] = {1}; int extents[] = {nb_vars}; return execute(lfield, strides, extents, 1); } template void Checksum::var_info(const array::ArrayView& arr, std::vector& varstrides, std::vector& varshape) const { int rank = std::max(1, RANK - 1); varstrides.resize(rank); varshape.resize(rank); if (RANK > 1) { size_t stride = 1; for (int j = RANK - 1; j > 0; --j) { varstrides[j - 1] = stride; varshape[j - 1] = arr.shape(j); stride *= varshape[j - 1]; } // varstrides.assign(arr.strides()+1,arr.strides()+RANK); // varshape.assign(arr.shape()+1,arr.shape()+RANK); } else { varstrides[0] = 1; varshape[0] = 1; } } template std::string Checksum::execute(const array::ArrayView& lfield) const { if (size_t(lfield.shape(0)) == parsize_) { std::vector lvarstrides, lvarextents; var_info(lfield, lvarstrides, lvarextents); return execute(lfield.data(), lvarstrides.data(), lvarextents.data(), lvarstrides.size()); } else { ATLAS_NOTIMPLEMENTED; // Need to implement with parallel ranks > 1 } } // ------------------------------------------------------------------ // C wrapper interfaces to C++ routines extern "C" { Checksum* atlas__Checksum__new(); void atlas__Checksum__delete(Checksum* This); void atlas__Checksum__setup32(Checksum* This, int part[], idx_t remote_idx[], int base, int glb_idx[], int parsize); void atlas__Checksum__setup64(Checksum* This, int part[], idx_t remote_idx[], int base, long glb_idx[], int parsize); void atlas__Checksum__execute_strided_int(Checksum* This, int lfield[], int lvar_strides[], int lvar_extents[], int lvar_rank, char* checksum); void atlas__Checksum__execute_strided_float(Checksum* This, float lfield[], int lvar_strides[], int lvar_extents[], int lvar_rank, char* checksum); void atlas__Checksum__execute_strided_double(Checksum* This, double lfield[], int lvar_strides[], int lvar_extents[], int lvar_rank, char* checksum); void atlas__Checksum__execute_strided_long(Checksum* This, long lfield[], int lvar_strides[], int lvar_extents[], int lvar_rank, char* checksum); } // ------------------------------------------------------------------ } // namespace parallel } // namespace atlas atlas-0.46.0/src/atlas/parallel/HaloExchange.cc0000664000175000017500000002467415160212070021424 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ /// @file HaloExchange.cc /// @author Willem Deconinck /// @date Nov 2013 #include #include #include #include #include "pluto/pluto.h" #include "atlas/array/Array.h" #include "atlas/parallel/HaloExchange.h" #include "atlas/parallel/mpi/Statistics.h" #include "atlas/util/vector.h" namespace atlas { namespace parallel { namespace { struct IsGhostPoint { IsGhostPoint(const int mypart, const int part[], const idx_t ridx[], const idx_t base, const int /*N*/) { part_ = part; ridx_ = ridx; base_ = base; mypart_ = mypart; } bool operator()(idx_t idx) { if (part_[idx] != mypart_) { return true; } if (ridx_[idx] != base_ + idx) { return true; } return false; } int mypart_; const int* part_; const idx_t* ridx_; idx_t base_; }; } // namespace HaloExchange::HaloExchange() : HaloExchange("") { } HaloExchange::HaloExchange(const std::string& name): name_(name), is_setup_(false) { } HaloExchange::~HaloExchange() = default; void HaloExchange::setup(const int part[], const idx_t remote_idx[], const int base, const idx_t size) { setup(mpi::comm().name(), part, remote_idx, base, size); } void HaloExchange::setup(const int part[], const idx_t remote_idx[], const int base, idx_t parsize, idx_t halo_begin) { setup(mpi::comm().name(), part, remote_idx, base, parsize, halo_begin); } void HaloExchange::setup(const std::string& mpi_comm, const int part[], const idx_t remote_idx[], const int base, const idx_t size) { setup(mpi_comm, part, remote_idx, base, size, 0); } void HaloExchange::setup(const std::string& mpi_comm, const int part[], const idx_t remote_idx[], const int base, idx_t parsize, idx_t halo_begin) { ATLAS_TRACE("HaloExchange::setup"); comm_ = &mpi::comm(mpi_comm); myproc = comm().rank(); nproc = comm().size(); parsize_ = parsize; sendcounts_.resize(nproc); sendcounts_.assign(nproc, 0); recvcounts_.resize(nproc); recvcounts_.assign(nproc, 0); senddispls_.resize(nproc); senddispls_.assign(nproc, 0); recvdispls_.resize(nproc); recvdispls_.assign(nproc, 0); /* Find the amount of nodes this proc has to receive from each other proc */ IsGhostPoint is_ghost(myproc, part, remote_idx, base, parsize_); atlas::vector ghost_points(parsize_); idx_t nghost = 0; atlas_omp_parallel_for(int jj = halo_begin; jj < parsize_; ++jj) { if (is_ghost(jj)) { int p = part[jj]; atlas_omp_critical { ++recvcounts_[p]; ghost_points[nghost] = jj; nghost++; } } } recvcnt_ = std::accumulate(recvcounts_.begin(), recvcounts_.end(), 0); /* Find the amount of nodes this proc has to send to each other proc */ ATLAS_TRACE_MPI(ALLTOALL) { comm().allToAll(recvcounts_, sendcounts_); } sendcnt_ = std::accumulate(sendcounts_.begin(), sendcounts_.end(), 0); recvdispls_[0] = 0; senddispls_[0] = 0; for (int jproc = 1; jproc < nproc; ++jproc) // start at 1 { recvdispls_[jproc] = recvcounts_[jproc - 1] + recvdispls_[jproc - 1]; senddispls_[jproc] = sendcounts_[jproc - 1] + senddispls_[jproc - 1]; } /* Fill vector "send_requests" with remote index of nodes needed, but are on other procs We can also fill in the vector "recvmap_" which holds local indices of requested nodes */ std::vector send_requests(recvcnt_); std::vector recv_requests(sendcnt_); std::vector cnt(nproc, 0); recvmap_.resize(recvcnt_); #ifdef __PGI // No idea why PGI compiler (20.7) in Release build ( -fast -O3 ) decides to vectorize following loop #pragma loop novector #endif for (idx_t jghost = 0; jghost < nghost; ++jghost) { const idx_t jj = ghost_points[jghost]; const int p = part[jj]; const int req_idx = recvdispls_[p] + cnt[p]; send_requests[req_idx] = remote_idx[jj] - base; recvmap_[req_idx] = jj; cnt[p]++; } /* Fill vector "recv_requests" with what is needed by other procs */ ATLAS_TRACE_MPI(ALLTOALL) { comm().allToAllv(send_requests.data(), recvcounts_.data(), recvdispls_.data(), recv_requests.data(), sendcounts_.data(), senddispls_.data()); } /* What needs to be sent to other procs is asked by remote_idx, which is local here */ sendmap_.resize(sendcnt_); for (int jj = 0; jj < sendcnt_; ++jj) { sendmap_[jj] = recv_requests[jj]; } is_setup_ = true; backdoor.parsize = parsize_; } void HaloExchange::wait_for_send(std::vector& send_counts_init, std::vector& send_req) const { ATLAS_TRACE_MPI(WAIT, "mpi-wait send") { for (int jproc = 0; jproc < nproc; ++jproc) { if (send_counts_init[jproc] > 0) { comm().wait(send_req[jproc]); } } } } int HaloExchange::devices() const { return pluto::devices(); } bool HaloExchange::is_device_accessible(const void* ptr) const { return pluto::is_device_accessible(ptr); } namespace { template void execute_halo_exchange(HaloExchange* This, Value field[], int var_strides[], int var_extents[], int var_rank) { // WARNING: Only works if there is only one parallel dimension AND being // slowest moving array::ArrayShape shape{This->backdoor.parsize}; for (int j = 0; j < var_rank; ++j) { shape.push_back(var_extents[j]); } array::ArrayStrides strides{var_extents[0] * var_strides[0]}; for (int j = 0; j < var_rank; ++j) { strides.push_back(var_strides[j]); } std::unique_ptr arr(array::Array::wrap(field, array::ArraySpec{shape, strides})); switch (arr->rank()) { case 1: { This->execute(*arr); break; } case 2: { This->execute(*arr); break; } case 3: { This->execute(*arr); break; } case 4: { This->execute(*arr); break; } default: throw_NotImplemented("Rank not supported in halo exchange", Here()); } } template void execute_adjoint_halo_exchange(HaloExchange* This, Value field[], int var_strides[], int var_extents[], int var_rank) { // WARNING: Only works if there is only one parallel dimension AND being // slowest moving array::ArrayShape shape{This->backdoor.parsize}; for (int j = 0; j < var_rank; ++j) { shape.push_back(var_extents[j]); } array::ArrayStrides strides{var_extents[0] * var_strides[0]}; for (int j = 0; j < var_rank; ++j) { strides.push_back(var_strides[j]); } std::unique_ptr arr(array::Array::wrap(field, array::ArraySpec{shape, strides})); switch (arr->rank()) { case 1: { This->execute_adjoint(*arr); break; } case 2: { This->execute_adjoint(*arr); break; } case 3: { This->execute_adjoint(*arr); break; } case 4: { This->execute_adjoint(*arr); break; } default: throw_NotImplemented("Rank not supported in halo exchange", Here()); } } } // namespace extern "C" { HaloExchange* atlas__HaloExchange__new() { return new HaloExchange(); } void atlas__HaloExchange__delete(HaloExchange* This) { delete This; } void atlas__HaloExchange__setup(HaloExchange* This, int part[], idx_t remote_idx[], int base, int size) { This->setup(part, remote_idx, base, size); } void atlas__HaloExchange__execute_adjoint_strided_int(HaloExchange* This, int field[], int var_strides[], int var_extents[], int var_rank) { execute_adjoint_halo_exchange(This, field, var_strides, var_extents, var_rank); } void atlas__HaloExchange__execute_adjoint_strided_long(HaloExchange* This, long field[], int var_strides[], int var_extents[], int var_rank) { execute_adjoint_halo_exchange(This, field, var_strides, var_extents, var_rank); } void atlas__HaloExchange__execute_adjoint_strided_float(HaloExchange* This, float field[], int var_strides[], int var_extents[], int var_rank) { execute_adjoint_halo_exchange(This, field, var_strides, var_extents, var_rank); } void atlas__HaloExchange__execute_adjoint_strided_double(HaloExchange* This, double field[], int var_strides[], int var_extents[], int var_rank) { execute_adjoint_halo_exchange(This, field, var_strides, var_extents, var_rank); } void atlas__HaloExchange__execute_strided_int(HaloExchange* This, int field[], int var_strides[], int var_extents[], int var_rank) { execute_halo_exchange(This, field, var_strides, var_extents, var_rank); } void atlas__HaloExchange__execute_strided_long(HaloExchange* This, long field[], int var_strides[], int var_extents[], int var_rank) { execute_halo_exchange(This, field, var_strides, var_extents, var_rank); } void atlas__HaloExchange__execute_strided_float(HaloExchange* This, float field[], int var_strides[], int var_extents[], int var_rank) { execute_halo_exchange(This, field, var_strides, var_extents, var_rank); } void atlas__HaloExchange__execute_strided_double(HaloExchange* This, double field[], int var_strides[], int var_extents[], int var_rank) { execute_halo_exchange(This, field, var_strides, var_extents, var_rank); } } ///////////////////// } // namespace parallel } // namespace atlas atlas-0.46.0/src/atlas/parallel/HaloExchange.h0000664000175000017500000004707115160212070021262 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ /// @file HaloExchange.h /// @author Willem Deconinck /// @date Nov 2013 #pragma once #include #include #include #include "atlas/parallel/detail/Packer.h" #include "atlas/parallel/mpi/Statistics.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/array/ArrayView.h" #include "atlas/array/ArrayViewDefs.h" #include "atlas/array/ArrayViewUtil.h" #include "atlas/array/SVector.h" #include "atlas/array_fwd.h" #include "atlas/library/config.h" #include "atlas/library/defines.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Log.h" #include "atlas/util/Object.h" namespace atlas { namespace parallel { class HaloExchange : public util::Object { public: HaloExchange(); HaloExchange(const std::string& name); virtual ~HaloExchange(); public: // methods const std::string& name() const { return name_; } void setup(const int part[], const idx_t remote_idx[], const int base, idx_t size); void setup(const std::string& mpi_comm, const int part[], const idx_t remote_idx[], const int base, idx_t size); void setup(const int part[], const idx_t remote_idx[], const int base, idx_t size, idx_t halo_begin); void setup(const std::string& mpi_comm, const int part[], const idx_t remote_idx[], const int base, idx_t size, idx_t halo_begin); template void execute(array::Array& field, bool on_device = false) const; template void execute_adjoint(array::Array& field, bool on_device = false) const; private: // methods idx_t index(idx_t i, idx_t j, idx_t k, idx_t ni, idx_t nj, idx_t /*nk*/) const { return (i + ni * (j + nj * k)); } idx_t index(idx_t i, idx_t j, idx_t ni, idx_t /*nj*/) const { return (i + ni * j); } template void counts_displs_setup(const idx_t var_size, std::vector& send_counts_init, std::vector& recv_counts_init, std::vector& send_counts, std::vector& recv_counts, std::vector& send_displs, std::vector& recv_displs) const; template void ireceive(int tag, std::vector& recv_displs, std::vector& recv_counts, std::vector& recv_req, DATA_TYPE* recv_buffer) const; template void isend_and_wait_for_receive(int tag, std::vector& recv_counts_init, std::vector& recv_req, std::vector& send_displs, std::vector& send_counts, std::vector& send_req, DATA_TYPE* send_buffer) const; void wait_for_send(std::vector& send_counts, std::vector& send_req) const; template DATA_TYPE* allocate_buffer(const int buffer_size, const bool on_device) const; template void deallocate_buffer(DATA_TYPE* buffer, const int buffer_size, const bool on_device) const; template void pack_send_buffer(const array::ArrayView& array, DATA_TYPE* send_buffer, int send_buffer_size, const bool on_device) const; template void unpack_recv_buffer(const DATA_TYPE* recv_buffer, int recv_buffer_size, array::ArrayView& array, const bool on_device) const; template void pack_recv_adjoint_buffer(const array::ArrayView& array, DATA_TYPE* recv_buffer, int recv_buffer_size, const bool on_device) const; template void unpack_send_adjoint_buffer(const DATA_TYPE* send_buffer, int send_buffer_size, array::ArrayView& array, const bool on_device) const; template void zero_halos(array::ArrayView& array, DATA_TYPE* recv_buffer, int recv_buffer_size, const bool on_device) const; template void var_info(const array::ArrayView& arr, std::vector& varstrides, std::vector& varshape) const; const mpi::Comm& comm() const { return *comm_; } int devices() const; bool is_device_accessible(const void* ptr) const; private: // data std::string name_; bool is_setup_; int sendcnt_; int recvcnt_; std::vector sendcounts_; std::vector senddispls_; std::vector recvcounts_; std::vector recvdispls_; array::SVector sendmap_; array::SVector recvmap_; int parsize_; int nproc; int myproc; const mpi::Comm* comm_; public: struct Backdoor { int parsize; } backdoor; }; template void HaloExchange::execute(array::Array& field, bool on_device) const { ATLAS_TRACE(std::string("HaloExchange::execute")+(on_device?"[device]":"[host]"), {std::string("halo-exchange"), std::string("halo-exchange")+(on_device?".device":".host")}); if (!is_setup_) { throw_Exception("HaloExchange was not setup", Here()); } on_device = on_device && devices() > 0; bool update_device_at_end = false; if (on_device) { ATLAS_ASSERT(field.deviceNeedsUpdate() == false); if (not ATLAS_HAVE_GPU_AWARE_MPI) { // Without GPU aware MPI, we need to do halo exchange on host and copy back to device update_device_at_end = on_device; on_device = false; field.syncHost(); } else { field.syncDevice(); } } else { field.syncHost(); } auto array_view = on_device ? array::make_device_view(field) : array::make_host_view(field); constexpr int parallelDim = array::get_parallel_dim(array_view); idx_t var_size = array::get_var_size(array_view); int tag(1); std::size_t nproc_loc(static_cast(nproc)); std::vector inner_counts(nproc_loc), halo_counts(nproc_loc); std::vector inner_counts_init(nproc_loc), halo_counts_init(nproc_loc); std::vector inner_displs(nproc_loc), halo_displs(nproc_loc); std::vector inner_req(nproc_loc), halo_req(nproc_loc); int inner_size = sendcnt_ * var_size; int halo_size = recvcnt_ * var_size; DATA_TYPE* inner_buffer = allocate_buffer(inner_size, on_device); DATA_TYPE* halo_buffer = allocate_buffer(halo_size, on_device); if (on_device) { ATLAS_ASSERT( is_device_accessible(inner_buffer) ); ATLAS_ASSERT( is_device_accessible(halo_buffer) ); ATLAS_ASSERT( is_device_accessible(array_view.data()) ); } counts_displs_setup(var_size, inner_counts_init, halo_counts_init, inner_counts, halo_counts, inner_displs, halo_displs); ireceive(tag, halo_displs, halo_counts, halo_req, halo_buffer); /// Pack pack_send_buffer(array_view, inner_buffer, inner_size, on_device); isend_and_wait_for_receive(tag, halo_counts_init, halo_req, inner_displs, inner_counts, inner_req, inner_buffer); /// Unpack unpack_recv_buffer(halo_buffer, halo_size, array_view, on_device); wait_for_send(inner_counts_init, inner_req); deallocate_buffer(inner_buffer, inner_size, on_device); deallocate_buffer(halo_buffer, halo_size, on_device); on_device ? field.setHostNeedsUpdate(true) : field.setDeviceNeedsUpdate(true); if (update_device_at_end) { field.syncDevice(); } } template void HaloExchange::execute_adjoint(array::Array& field, bool on_device) const { if (!is_setup_) { throw_Exception("HaloExchange was not setup", Here()); } ATLAS_TRACE(std::string("HaloExchange::execute_adjoint")+(on_device?"[device]":"[host]"), {std::string("halo-exchange-adjoint"),std::string("halo-exchange-adjoint")+(on_device?".device":".host")}); on_device = on_device && devices() > 0; // adjoint on device is not yet implemented! // So copy to host and do halo-exchange there, and copy back to device bool update_device_at_end = on_device; if (on_device) { ATLAS_ASSERT(field.deviceNeedsUpdate() == false); field.syncHost(); on_device = false; } auto array_view = on_device ? array::make_device_view(field) : array::make_host_view(field); constexpr int parallelDim = array::get_parallel_dim(array_view); idx_t var_size = array::get_var_size(array_view); int tag(1); std::size_t nproc_loc(static_cast(nproc)); std::vector halo_counts(nproc_loc), inner_counts(nproc_loc); std::vector halo_counts_init(nproc_loc), inner_counts_init(nproc_loc); std::vector halo_displs(nproc_loc), inner_displs(nproc_loc); std::vector halo_req(nproc_loc), inner_req(nproc_loc); int halo_size = sendcnt_ * var_size; int inner_size = recvcnt_ * var_size; DATA_TYPE* halo_buffer = allocate_buffer(halo_size, on_device); DATA_TYPE* inner_buffer = allocate_buffer(inner_size, on_device); counts_displs_setup(var_size, halo_counts_init, inner_counts_init, halo_counts, inner_counts, halo_displs, inner_displs); ireceive(tag, halo_displs, halo_counts, halo_req, halo_buffer); /// Pack pack_recv_adjoint_buffer(array_view, inner_buffer, inner_size, on_device); /// Send isend_and_wait_for_receive(tag, halo_counts_init, halo_req, inner_displs, inner_counts, inner_req, inner_buffer); /// Unpack unpack_send_adjoint_buffer(halo_buffer, halo_size, array_view, on_device); /// Wait for sending to finish wait_for_send(inner_counts_init, inner_req); zero_halos(array_view, halo_buffer, halo_size, on_device); deallocate_buffer(halo_buffer, halo_size, on_device); deallocate_buffer(inner_buffer, inner_size, on_device); on_device ? field.setHostNeedsUpdate(true) : field.setDeviceNeedsUpdate(true); if (update_device_at_end) { field.syncDevice(); } } template DATA_TYPE* HaloExchange::allocate_buffer(const int buffer_size, const bool on_device) const { DATA_TYPE* buffer{nullptr}; if (on_device) { util::allocate_devicemem(buffer, buffer_size); } else { util::allocate_hostmem(buffer, buffer_size); } return buffer; } template void HaloExchange::deallocate_buffer(DATA_TYPE* buffer, const int buffer_size, const bool on_device) const { if (on_device) { util::delete_devicemem(buffer, buffer_size); } else { util::delete_hostmem(buffer, buffer_size); } } template void HaloExchange::counts_displs_setup(const idx_t var_size, std::vector& send_counts_init, std::vector& recv_counts_init, std::vector& send_counts, std::vector& recv_counts, std::vector& send_displs, std::vector& recv_displs) const { for (size_t jproc = 0; jproc < static_cast(nproc); ++jproc) { send_counts_init[jproc] = sendcounts_[jproc]; recv_counts_init[jproc] = recvcounts_[jproc]; send_counts[jproc] = sendcounts_[jproc] * var_size; recv_counts[jproc] = recvcounts_[jproc] * var_size; send_displs[jproc] = senddispls_[jproc] * var_size; recv_displs[jproc] = recvdispls_[jproc] * var_size; } } template void HaloExchange::ireceive(int tag, std::vector& recv_displs, std::vector& recv_counts, std::vector& recv_req, DATA_TYPE* recv_buffer) const { ATLAS_TRACE_MPI(IRECEIVE) { /// Let MPI know what we like to receive for (size_t jproc = 0; jproc < static_cast(nproc); ++jproc) { if (recv_counts[jproc] > 0) { recv_req[jproc] = comm().iReceive(recv_buffer+recv_displs[jproc], recv_counts[jproc], jproc, tag); } } } } template void HaloExchange::isend_and_wait_for_receive(int tag, std::vector& recv_counts_init, std::vector& recv_req, std::vector& send_displs, std::vector& send_counts, std::vector& send_req, DATA_TYPE* send_buffer) const { /// Send ATLAS_TRACE_MPI(ISEND) { for (size_t jproc = 0; jproc < static_cast(nproc); ++jproc) { if (send_counts[jproc] > 0) { send_req[jproc] = comm().iSend(send_buffer+send_displs[jproc], send_counts[jproc], jproc, tag); } } } /// Wait for receiving to finish ATLAS_TRACE_MPI(WAIT, "mpi-wait receive") { for (size_t jproc = 0; jproc < static_cast(nproc); ++jproc) { if (recv_counts_init[jproc] > 0) { comm().wait(recv_req[jproc]); } } } } template void HaloExchange::zero_halos(array::ArrayView& array, DATA_TYPE* recv_buffer, int recv_size, ATLAS_MAYBE_UNUSED const bool on_device) const { ATLAS_TRACE(); detail::Zeroer::zero(recvcnt_, recvmap_, array, recv_buffer, recv_size, on_device); } template void HaloExchange::pack_send_buffer(const array::ArrayView& array, DATA_TYPE* send_buffer, int send_size, const bool on_device) const { ATLAS_TRACE(); detail::Packer::pack(sendcnt_, sendmap_, array, send_buffer, send_size, on_device); } template void HaloExchange::unpack_recv_buffer(const DATA_TYPE* recv_buffer, int recv_size, array::ArrayView& array, const bool on_device) const { ATLAS_TRACE(); detail::Packer::unpack(recvcnt_, recvmap_, recv_buffer, recv_size, array, on_device); } template void HaloExchange::pack_recv_adjoint_buffer(const array::ArrayView& array, DATA_TYPE* recv_buffer, int recv_size, const bool on_device) const { ATLAS_TRACE(); detail::AdjointPacker::pack(recvcnt_, recvmap_, array, recv_buffer, recv_size, on_device); } template void HaloExchange::unpack_send_adjoint_buffer(const DATA_TYPE* send_buffer, int send_size, array::ArrayView& array, ATLAS_MAYBE_UNUSED const bool on_device) const { ATLAS_TRACE(); detail::AdjointPacker::unpack(sendcnt_, sendmap_, send_buffer, send_size, array, on_device); } // template // void HaloExchange::execute( DATA_TYPE field[], idx_t nb_vars ) const //{ // throw_AssertionFailed("Call not supported"); // idx_t strides[] = {1}; // idx_t shape[] = {nb_vars}; // execute( field, strides, shape, 1); //} // template // void HaloExchange::execute( array::ArrayView&& field ) const //{ // execute(field); //} //---------------------------------------------------------------------------------------------------------------------- // C wrapper interfaces to C++ routines extern "C" { HaloExchange* atlas__HaloExchange__new(); void atlas__HaloExchange__delete(HaloExchange* This); void atlas__HaloExchange__setup(HaloExchange* This, int part[], idx_t remote_idx[], int base, int size); void atlas__HaloExchange__execute_strided_int(HaloExchange* This, int field[], int var_strides[], int var_shape[], int var_rank); void atlas__HaloExchange__execute_strided_long(HaloExchange* This, long field[], int var_strides[], int var_shape[], int var_rank); void atlas__HaloExchange__execute_strided_float(HaloExchange* This, float field[], int var_strides[], int var_shape[], int var_rank); void atlas__HaloExchange__execute_strided_double(HaloExchange* This, double field[], int var_strides[], int var_shape[], int var_rank); void atlas__HaloExchange__execute_int(HaloExchange* This, int field[], int var_rank); void atlas__HaloExchange__execute_float(HaloExchange* This, float field[], int var_rank); void atlas__HaloExchange__execute_double(HaloExchange* This, double field[], int var_rank); void atlas__HaloExchange__execute_adjoint_strided_int(HaloExchange* This, int field[], int var_strides[], int var_shape[], int var_rank); void atlas__HaloExchange__execute_adjoint_strided_long(HaloExchange* This, long field[], int var_strides[], int var_shape[], int var_rank); void atlas__HaloExchange__execute_adjoint_strided_float(HaloExchange* This, float field[], int var_strides[], int var_shape[], int var_rank); void atlas__HaloExchange__execute_adjoint_strided_double(HaloExchange* This, double field[], int var_strides[], int var_shape[], int var_rank); void atlas__HaloExchange__execute_adjoint_int(HaloExchange* This, int field[], int var_rank); void atlas__HaloExchange__execute_adjoint_float(HaloExchange* This, float field[], int var_rank); void atlas__HaloExchange__execute_adjoint_double(HaloExchange* This, double field[], int var_rank); } //---------------------------------------------------------------------------------------------------------------------- } // namespace parallel } // namespace atlas atlas-0.46.0/src/atlas/parallel/omp/0000775000175000017500000000000015160212070017345 5ustar alastairalastairatlas-0.46.0/src/atlas/parallel/omp/fill.h0000664000175000017500000000225015160212070020443 0ustar alastairalastair/* * (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/parallel/omp/omp.h" namespace atlas { namespace omp { template void fill(BidirIt begin, BidirIt end, const T& value) { if (atlas_omp_get_max_threads() > 1) { auto size = std::distance(begin, end); atlas_omp_parallel { auto nthreads = atlas_omp_get_num_threads(); auto tid = atlas_omp_get_thread_num(); auto chunksize = size / nthreads; auto v_begin = begin + chunksize * tid; auto v_end = (tid == nthreads - 1) ? end : v_begin + chunksize; std::fill(v_begin, v_end, value); } } else { std::fill(begin, end, value); } } } // namespace omp } // namespace atlas atlas-0.46.0/src/atlas/parallel/omp/omp.h0000664000175000017500000000370415160212070020315 0ustar alastairalastair/* * (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/library/config.h" void atlas_omp_set_num_threads(int num_threads); int atlas_omp_get_num_threads(void); int atlas_omp_get_max_threads(void); int atlas_omp_get_thread_num(void); int atlas_omp_get_num_procs(void); int atlas_omp_in_parallel(void); void atlas_omp_set_dynamic(int dynamic_threads); int atlas_omp_get_dynamic(void); void atlas_omp_set_nested(int nested); int atlas_omp_get_nested(void); #if ATLAS_HAVE_OMP #define ATLAS_OMP_STR(x) #x #define ATLAS_OMP_STRINGIFY(x) ATLAS_OMP_STR(x) #define atlas_omp_pragma(x) _Pragma(ATLAS_OMP_STRINGIFY(x)) #else #define atlas_omp_pragma(x) #endif #define atlas_omp_parallel_for atlas_omp_pragma(omp parallel for) for #define atlas_omp_for atlas_omp_pragma(omp for) for #define atlas_omp_parallel atlas_omp_pragma(omp parallel) #define atlas_omp_critical atlas_omp_pragma(omp critical) #ifndef DOXYGEN_SHOULD_SKIP_THIS template class atlas_omp_scoped_helper { public: atlas_omp_scoped_helper(T p): value(p), once_(true) {} bool once() const { return once_; } void done() { once_ = false; } T value; private: bool once_; }; #define _atlas_omp_scoped(T, VAR, VAL) for (atlas_omp_scoped_helper VAR(VAL); VAR.once(); VAR.done()) #endif #define atlas_omp_critical_ordered \ _atlas_omp_scoped(const size_t, _nthreads, atlas_omp_get_num_threads()) \ atlas_omp_pragma( omp for ordered schedule(static,1) )\ for( size_t _thread=0; _thread<_nthreads.value; ++_thread )\ atlas_omp_pragma( omp ordered ) #undef _atlas_omp_scoped atlas-0.46.0/src/atlas/parallel/omp/sort.h0000664000175000017500000002146615160212070020516 0ustar alastairalastair/* * (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 #include "atlas/library/config.h" #include "atlas/parallel/omp/omp.h" #if ATLAS_HAVE_OMP && ATLAS_OMP_TASK_SUPPORTED #include #define ATLAS_HAVE_OMP_SORTING 1 #else #define ATLAS_HAVE_OMP_SORTING 0 #endif // Bug in Cray 8.5 or below results in segmentation fault in atlas_test_omp_sort #if ATLAS_HAVE_OMP_SORTING && defined(_CRAYC) #if _RELEASE <= 8 && _RELEASE_MINOR < 6 #undef ATLAS_HAVE_OMP_SORTING #define ATLAS_HAVE_OMP_SORTING 0 #endif #endif namespace atlas { namespace omp { /** * sort * ==== * * 1) template * void sort ( RandomAccessIterator first, RandomAccessIterator last ); * * 2) template * void sort ( RandomAccessIterator first, RandomAccessIterator last, Compare comp ); * * Sort elements in range * Sorts the elements in the range [first,last) into ascending order. * * The elements are compared using operator< for the first version, and comp for the second. * * Equivalent elements are not guaranteed to keep their original relative order (see stable_sort). * * Parameters * ---------- * first, last * Random-access iterators to the initial and final positions of the sequence to be sorted. The range used is [first,last), * which contains all the elements between first and last, including the element pointed by first but not the element pointed by last. * RandomAccessIterator shall point to a type for which swap is properly defined and which is both move-constructible and move-assignable. * comp * Binary function that accepts two elements in the range as arguments, and returns a value convertible to bool. * The value returned indicates whether the element passed as first argument is considered to go before the second in the specific strict weak ordering it defines. * The function shall not modify any of its arguments. * This can either be a function pointer or a function object. * * * * merge_blocks * ============ * * 1) template * void merge_blocks( RandomAccessIterator first, RandomAccessIterator last, * RandomAccessIterator2 blocks_size_first, RandomAccessIterator2 blocks_size_last ); * * * 1) template * void merge_blocks( RandomAccessIterator first, RandomAccessIterator last, * RandomAccessIterator2 blocks_size_first, RandomAccessIterator2 blocks_size_last, * Compare compare ); * * Sort elements in range [first + *blocks_first, first + *blocks_last) using a merge sort * where each block in range [blocks_first,blocks_last) is already sorted. * * Parameters * ---------- * first, last * Random-access iterators for bounding the sequence to be sorted * blocks_begin, blocks_end * Random-access iterators that define offsets from paramter "first" of blocks that are already sorted */ namespace detail { #if ATLAS_HAVE_OMP_SORTING template void merge_sort_recursive(const RandomAccessIterator& iterator, size_t begin, size_t end, Compare compare) { auto size = end - begin; if (size >= 256) { auto mid = begin + size / 2; //#pragma omp taskgroup // --> it would be preferred to use taskgroup and taskyield instead of taskwait, // but this leads to segfaults on Cray (cce/8.5.8) { #if ATLAS_OMP_TASK_UNTIED_SUPPORTED #pragma omp task shared(iterator) untied if (size >= (1 << 15)) #else #pragma omp task shared(iterator) #endif merge_sort_recursive(iterator, begin, mid, compare); #if ATLAS_OMP_TASK_UNTIED_SUPPORTED #pragma omp task shared(iterator) untied if (size >= (1 << 15)) #else #pragma omp task shared(iterator) #endif merge_sort_recursive(iterator, mid, end, compare); //#pragma omp taskyield #pragma omp taskwait } std::inplace_merge(iterator + begin, iterator + mid, iterator + end, compare); } else { std::sort(iterator + begin, iterator + end, compare); } } #endif #if ATLAS_HAVE_OMP_SORTING template void merge_blocks_recursive(const RandomAccessIterator& iterator, const Indexable& blocks, size_t blocks_begin, size_t blocks_end, Compare compare) { if (blocks_end <= blocks_begin + 1) { // recursion done, go back out return; } size_t blocks_mid = (blocks_begin + blocks_end) / 2; //#pragma omp taskgroup // --> it would be preferred to use taskgroup and taskyield instead of taskwait, // but this leads to segfaults on Cray (cce/8.5.8) { #pragma omp task shared(iterator, blocks) merge_blocks_recursive(iterator, blocks, blocks_begin, blocks_mid, compare); #pragma omp task shared(iterator, blocks) merge_blocks_recursive(iterator, blocks, blocks_mid, blocks_end, compare); //#pragma omp taskyield #pragma omp taskwait } auto begin = iterator + blocks[blocks_begin]; auto mid = iterator + blocks[blocks_mid]; auto end = iterator + blocks[blocks_end]; std::inplace_merge(begin, mid, end, compare); } #endif template void merge_blocks_recursive_seq(RandomAccessIterator& iterator, const Indexable& blocks, size_t blocks_begin, size_t blocks_end, Compare compare) { if (blocks_end <= blocks_begin + 1) { // recursion done, go back out return; } size_t blocks_mid = (blocks_begin + blocks_end) / 2; { merge_blocks_recursive_seq(iterator, blocks, blocks_begin, blocks_mid, compare); merge_blocks_recursive_seq(iterator, blocks, blocks_mid, blocks_end, compare); } auto begin = iterator + blocks[blocks_begin]; auto mid = iterator + blocks[blocks_mid]; auto end = iterator + blocks[blocks_end]; std::inplace_merge(begin, mid, end, compare); } } // namespace detail template void sort(RandomAccessIterator first, RandomAccessIterator last, Compare compare) { #if ATLAS_HAVE_OMP_SORTING if (atlas_omp_get_max_threads() > 1) { #pragma omp parallel #pragma omp single detail::merge_sort_recursive(first, 0, std::distance(first, last), compare); } else { std::sort(first, last, compare); } #else std::sort(first, last, compare); #endif } template void sort(RandomAccessIterator first, RandomAccessIterator last) { using value_type = typename std::iterator_traits::value_type; ::atlas::omp::sort(first, last, std::less()); } template void merge_blocks(RandomAccessIterator first, RandomAccessIterator last, RandomAccessIterator2 blocks_size_first, RandomAccessIterator2 blocks_size_last, Compare compare) { using size_type = typename std::iterator_traits::value_type; size_type nb_blocks = std::distance(blocks_size_first, blocks_size_last); std::vector blocks_displs(nb_blocks + 1); blocks_displs[0] = 0; for (size_t i = 1; i < blocks_displs.size(); ++i) { blocks_displs[i] = blocks_displs[i - 1] + blocks_size_first[i - 1]; } #if ATLAS_HAVE_OMP_SORTING if (atlas_omp_get_max_threads() > 1) { #pragma omp parallel #pragma omp single detail::merge_blocks_recursive(first, blocks_displs, 0, nb_blocks, compare); } else { detail::merge_blocks_recursive_seq(first, blocks_displs, 0, nb_blocks, compare); } #else detail::merge_blocks_recursive_seq(first, blocks_displs, 0, nb_blocks, compare); #endif } template void merge_blocks(RandomAccessIterator first, RandomAccessIterator last, RandomAccessIterator2 blocks_size_first, RandomAccessIterator2 blocks_size_last) { using value_type = typename std::iterator_traits::value_type; ::atlas::omp::merge_blocks(first, last, blocks_size_first, blocks_size_last, std::less()); } } // namespace omp } // namespace atlas #undef ATLAS_OMP_TASK_UNTIED_SUPPORTED #undef ATLAS_HAVE_OMP_SORTING atlas-0.46.0/src/atlas/parallel/omp/omp.cc0000664000175000017500000000572215160212070020455 0ustar alastairalastair/* * (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/parallel/omp/omp.h" #ifdef _OPENMP #include #else extern "C" void omp_set_num_threads(int num_threads); extern "C" int omp_get_num_threads(void); extern "C" int omp_get_max_threads(void); extern "C" int omp_get_thread_num(void); extern "C" int omp_get_num_procs(void); extern "C" int omp_in_parallel(void); extern "C" int omp_set_dynamic(int dynamic_threads); extern "C" int omp_get_dynamic(void); extern "C" void omp_set_nested(int nested); extern "C" int omp_get_nested(void); #endif #if ATLAS_HAVE_OMP extern "C" { #pragma weak omp_set_num_threads #pragma weak omp_get_num_threads #pragma weak omp_get_max_threads #pragma weak omp_get_thread_num #pragma weak omp_get_num_procs #pragma weak omp_in_parallel #pragma weak omp_set_dynamic #pragma weak omp_get_dynamic #pragma weak omp_set_nested #pragma weak omp_get_nested } #endif void atlas_omp_set_num_threads(int num_threads) { #if ATLAS_HAVE_OMP if (omp_set_num_threads) { omp_set_num_threads(num_threads); } #endif } int atlas_omp_get_num_threads(void) { #if ATLAS_HAVE_OMP if (omp_get_num_threads) { return omp_get_num_threads(); } else { return 1; } #else return 1; #endif } int atlas_omp_get_max_threads(void) { #if ATLAS_HAVE_OMP if (omp_get_max_threads) { return omp_get_max_threads(); } else { return 1; } #else return 1; #endif } int atlas_omp_get_thread_num(void) { #if ATLAS_HAVE_OMP if (omp_get_thread_num) { return omp_get_thread_num(); } else { return 0; } #else return 0; #endif } int atlas_omp_get_num_procs(void) { #if ATLAS_HAVE_OMP if (omp_get_num_procs) { return omp_get_num_procs(); } else { return 1; } #else return 1; #endif } int atlas_omp_in_parallel(void) { #if ATLAS_HAVE_OMP if (omp_in_parallel) { return omp_in_parallel(); } else { return 0; } #else return 0; #endif } void atlas_omp_set_dynamic(int dynamic_threads) { #if ATLAS_HAVE_OMP if (omp_set_dynamic) { omp_set_dynamic(dynamic_threads); } #endif } int atlas_omp_get_dynamic(void) { #if ATLAS_HAVE_OMP if (omp_get_dynamic) { return omp_get_dynamic(); } else { return 0; } #else return 0; #endif } void atlas_omp_set_nested(int nested) { #if ATLAS_HAVE_OMP if (omp_set_nested) { omp_set_nested(nested); } #endif } int atlas_omp_get_nested(void) { #if ATLAS_HAVE_OMP if (omp_get_nested) { return omp_get_nested(); } else { return 0; } #else return 0; #endif } atlas-0.46.0/src/atlas/parallel/omp/copy.h0000664000175000017500000000260015160212070020466 0ustar alastairalastair/* * (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/parallel/omp/omp.h" #include #include "atlas/runtime/Log.h" namespace atlas { namespace omp { template OutputIterator copy(InputIterator first, InputIterator last, OutputIterator result) { if (atlas_omp_get_max_threads() > 1) { auto size = std::distance(first, last); atlas_omp_parallel { auto nthreads = atlas_omp_get_num_threads(); auto tid = atlas_omp_get_thread_num(); auto chunksize = size / nthreads; auto v_begin = first + chunksize * tid; auto v_end = (tid == nthreads - 1) ? last : v_begin + chunksize; auto result_begin = result + chunksize * tid; std::copy(v_begin, v_end, result_begin); } return result + size; } else { return std::copy(first, last, result); } } } // namespace omp } // namespace atlas atlas-0.46.0/src/atlas/parallel/Checksum.cc0000664000175000017500000001111415160212070020621 0ustar alastairalastair/* * (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/parallel/Checksum.h" namespace atlas { namespace parallel { Checksum::Checksum(): name_() { is_setup_ = false; } Checksum::Checksum(const std::string& name): name_(name) { is_setup_ = false; } void Checksum::setup(const std::string& mpi_comm, const int part[], const idx_t remote_idx[], const int base, const gidx_t glb_idx[], const int parsize) { parsize_ = parsize; gather_ = util::ObjectHandle(new GatherScatter()); gather_->setup(mpi_comm, part, remote_idx, base, glb_idx, parsize); is_setup_ = true; } void Checksum::setup(const int part[], const idx_t remote_idx[], const int base, const gidx_t glb_idx[], const int parsize) { setup(mpi::comm().name(), part, remote_idx, base, glb_idx, parsize); } void Checksum::setup(const std::string& mpi_comm, const int part[], const idx_t remote_idx[], const int base, const gidx_t glb_idx[], const int mask[], const int parsize) { parsize_ = parsize; gather_ = util::ObjectHandle(new GatherScatter()); gather_->setup(mpi_comm, part, remote_idx, base, glb_idx, mask, parsize); is_setup_ = true; } void Checksum::setup(const int part[], const idx_t remote_idx[], const int base, const gidx_t glb_idx[], const int mask[], const int parsize) { setup(mpi::comm().name(), part, remote_idx, base, glb_idx, mask, parsize); } void Checksum::setup(const util::ObjectHandle& gather) { gather_ = gather; parsize_ = gather->parsize_; is_setup_ = true; } ///////////////////// Checksum* atlas__Checksum__new() { return new Checksum(); } void atlas__Checksum__delete(Checksum* This) { delete This; } void atlas__Checksum__setup32(Checksum* This, int part[], idx_t remote_idx[], int base, int glb_idx[], int parsize) { #if ATLAS_BITS_GLOBAL == 32 This->setup(part, remote_idx, base, glb_idx, parsize); #else std::vector glb_idx_convert(parsize); for (int j = 0; j < parsize; ++j) { glb_idx_convert[j] = glb_idx[j]; } This->setup(part, remote_idx, base, glb_idx_convert.data(), parsize); #endif } void atlas__Checksum__setup64(Checksum* This, int part[], idx_t remote_idx[], int base, long glb_idx[], int parsize) { #if ATLAS_BITS_GLOBAL == 64 This->setup(part, remote_idx, base, glb_idx, parsize); #else std::vector glb_idx_convert(parsize); for (int j = 0; j < parsize; ++j) { glb_idx_convert[j] = glb_idx[j]; } This->setup(part, remote_idx, base, glb_idx_convert.data(), parsize); #endif } void atlas__Checksum__execute_strided_int(Checksum* This, int lfield[], int lvar_strides[], int lvar_extents[], int lvar_rank, char* checksum) { std::strcpy(checksum, This->execute(lfield, lvar_strides, lvar_extents, lvar_rank).c_str()); } void atlas__Checksum__execute_strided_float(Checksum* This, float lfield[], int lvar_strides[], int lvar_extents[], int lvar_rank, char* checksum) { std::strcpy(checksum, This->execute(lfield, lvar_strides, lvar_extents, lvar_rank).c_str()); } void atlas__Checksum__execute_strided_double(Checksum* This, double lfield[], int lvar_strides[], int lvar_extents[], int lvar_rank, char* checksum) { std::strcpy(checksum, This->execute(lfield, lvar_strides, lvar_extents, lvar_rank).c_str()); } void atlas__Checksum__execute_strided_long(Checksum* This, long lfield[], int lvar_strides[], int lvar_extents[], int lvar_rank, char* checksum) { std::strcpy(checksum, This->execute(lfield, lvar_strides, lvar_extents, lvar_rank).c_str()); } // const char* atlas__Checksum__execute_strided_double (Checksum* This, // double lfield[], int // lvar_strides[], int // lvar_extents[], int lvar_rank) //{ // std::string checksum = // This->execute(lfield,lvar_strides,lvar_extents,lvar_rank); // return checksum.c_str(); //} ///////////////////// } // namespace parallel } // namespace atlas atlas-0.46.0/src/atlas/parallel/detail/0000775000175000017500000000000015160212070020014 5ustar alastairalastairatlas-0.46.0/src/atlas/parallel/detail/zero_index.h0000664000175000017500000000437715160212070022346 0ustar alastairalastair/* * (C) British Crown Copyright 2020 Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ #pragma once #include "atlas/array/ArrayView.h" #include "atlas/array/SVector.h" namespace atlas::parallel::detail { template struct zero_index { template ATLAS_HOST_DEVICE static void apply(idx_t& buf_idx, const idx_t node_idx, array::ArrayView& field, DATA_TYPE* recv_buffer, Idx... idxs) { for (idx_t i = 0; i < field.template shape(); ++i) { zero_index::apply(buf_idx, node_idx, field, recv_buffer, idxs..., i); } } }; template struct zero_index { template ATLAS_HOST_DEVICE static void apply(idx_t& buf_idx, const idx_t node_idx, array::ArrayView& field, DATA_TYPE* recv_buffer, Idx... idxs) { field(idxs...) = 0; } }; template struct zero_index { template ATLAS_HOST_DEVICE static void apply(idx_t& buf_idx, const idx_t node_idx, array::ArrayView& field, DATA_TYPE* recv_buffer, Idx... idxs) { zero_index::apply(buf_idx, node_idx, field, recv_buffer, idxs..., node_idx); } }; template struct zero_index { template ATLAS_HOST_DEVICE static void apply(idx_t& buf_idx, const idx_t node_idx, array::ArrayView& field, DATA_TYPE* recv_buffer, Idx... idxs) { field(idxs...) = 0; } }; } // namespace atlas::parallel::detail atlas-0.46.0/src/atlas/parallel/detail/Packer.h0000664000175000017500000001176115160212070021400 0ustar alastairalastair/* * (C) Copyright 2025- 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 #include "atlas/array/ArrayView.h" #include "atlas/array/SVector.h" #include "atlas/runtime/Exception.h" namespace atlas::parallel::detail { template struct HostPacker { static void pack(const int sendcnt, array::SVector const& sendmap, const array::ArrayView& array, DATA_TYPE* send_buffer, int send_buffer_size); static void unpack(const int recvcnt, array::SVector const& recvmap, const DATA_TYPE* recv_buffer, int recv_buffer_size, array::ArrayView& array); }; template struct HostAdjointPacker { static void unpack(const int recvcnt, array::SVector const& recvmap, const DATA_TYPE* recv_buffer, int recv_buffer_size, array::ArrayView& array); }; template struct HostZeroer { static void zero(const int sendcnt, array::SVector const& sendmap, array::ArrayView& array, DATA_TYPE* recv_buffer, int recv_buffer_size); }; #if ATLAS_HAVE_GPU template struct DevicePacker { static void pack(const int sendcnt, array::SVector const& sendmap, const array::ArrayView& array, DATA_TYPE* send_buffer, int send_buffer_size); static void unpack(const int sendcnt, array::SVector const& recvmap, const DATA_TYPE* recv_buffer, int recv_buffer_size, array::ArrayView& array); }; #else template using DevicePacker = HostPacker; #endif template struct Packer { static void pack(const int sendcnt, array::SVector const& sendmap, const array::ArrayView& array, DATA_TYPE* send_buffer, int send_buffer_size, bool on_device) { if (not on_device) { HostPacker::pack(sendcnt, sendmap, array, send_buffer, send_buffer_size); } else { DevicePacker::pack(sendcnt, sendmap, array, send_buffer, send_buffer_size); } } static void unpack(const int recvcnt, array::SVector const& recvmap, const DATA_TYPE* recv_buffer, int recv_buffer_size, array::ArrayView& array, bool on_device) { if (not on_device) { HostPacker::unpack(recvcnt, recvmap, recv_buffer, recv_buffer_size, array); } else { DevicePacker::unpack(recvcnt, recvmap, recv_buffer, recv_buffer_size, array); } } }; template struct AdjointPacker { static void pack(const int sendcnt, array::SVector const& sendmap, const array::ArrayView& array, DATA_TYPE* send_buffer, int send_buffer_size, bool on_device) { Packer::pack(sendcnt, sendmap, array, send_buffer, send_buffer_size, on_device); } static void unpack(const int recvcnt, array::SVector const& recvmap, const DATA_TYPE* recv_buffer, int recv_buffer_size, array::ArrayView& array, bool on_device) { if (on_device) { ATLAS_NOTIMPLEMENTED; } else { HostAdjointPacker::unpack(recvcnt, recvmap, recv_buffer, recv_buffer_size, array); } } }; template struct Zeroer { static void zero(const int sendcnt, array::SVector const& sendmap, array::ArrayView& array, DATA_TYPE* recv_buffer, int recv_buffer_size, bool on_device) { if (on_device) { ATLAS_NOTIMPLEMENTED; } else { HostZeroer::zero(sendcnt, sendmap, array, recv_buffer, recv_buffer_size); } } }; //---------------------------------------------------------------------------------------------------------------------- } // namespace atlas::parallel::detail atlas-0.46.0/src/atlas/parallel/detail/adjoint_unpack_index.h0000664000175000017500000000462415160212070024353 0ustar alastairalastair/* * (C) British Crown Copyright 2020 Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ #pragma once #include "atlas/array/ArrayView.h" #include "atlas/array/SVector.h" namespace atlas::parallel::detail { template struct adjoint_unpack_index { template ATLAS_HOST_DEVICE static void apply(idx_t& buf_idx, const idx_t node_idx, const DATA_TYPE* recv_buffer, array::ArrayView& field, Idx... idxs) { for (idx_t i = 0; i < field.template shape(); ++i) { adjoint_unpack_index::apply(buf_idx, node_idx, recv_buffer, field, idxs..., i); } } }; template struct adjoint_unpack_index { template ATLAS_HOST_DEVICE static void apply(idx_t& buf_idx, const idx_t node_idx, const DATA_TYPE* recv_buffer, array::ArrayView& field, Idx... idxs) { field(idxs...) += recv_buffer[buf_idx++]; } }; template struct adjoint_unpack_index { template ATLAS_HOST_DEVICE static void apply(idx_t& buf_idx, const idx_t node_idx, const DATA_TYPE* recv_buffer, array::ArrayView& field, Idx... idxs) { adjoint_unpack_index::apply(buf_idx, node_idx, recv_buffer, field, idxs..., node_idx); } }; template struct adjoint_unpack_index { template ATLAS_HOST_DEVICE static void apply(idx_t& buf_idx, const idx_t node_idx, const DATA_TYPE* recv_buffer, array::ArrayView& field, Idx... idxs) { field(idxs...) += recv_buffer[buf_idx++]; } }; } // namespace atlas::parallel::detail atlas-0.46.0/src/atlas/parallel/detail/Packer.cc0000664000175000017500000001107615160212070021535 0ustar alastairalastair/* * (C) Copyright 2025- 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 "Packer.h" #include "pack_index.h" #include "adjoint_unpack_index.h" #include "zero_index.h" namespace atlas::parallel::detail { template void HostPacker::pack(const int sendcnt, array::SVector const& sendmap, const array::ArrayView& array, DATA_TYPE* send_buffer, int /*send_buffer_size*/) { idx_t ibuf = 0; for (int node_cnt = 0; node_cnt < sendcnt; ++node_cnt) { const idx_t node_idx = sendmap[node_cnt]; pack_index::apply(ibuf, node_idx, array, send_buffer); } } template void HostPacker::unpack(const int recvcnt, array::SVector const& recvmap, const DATA_TYPE* recv_buffer, int /*recv_buffer_size*/, array::ArrayView& array) { idx_t ibuf = 0; for (int node_cnt = 0; node_cnt < recvcnt; ++node_cnt) { const idx_t node_idx = recvmap[node_cnt]; unpack_index::apply(ibuf, node_idx, recv_buffer, array); } } template void HostAdjointPacker::unpack(const int recvcnt, array::SVector const& recvmap, const DATA_TYPE* recv_buffer, int /*recv_buffer_size*/, array::ArrayView& array) { idx_t ibuf = 0; for (int node_cnt = 0; node_cnt < recvcnt; ++node_cnt) { const idx_t node_idx = recvmap[node_cnt]; adjoint_unpack_index::apply(ibuf, node_idx, recv_buffer, array); } } template void HostZeroer::zero(const int sendcnt, array::SVector const& sendmap, array::ArrayView& array, DATA_TYPE* recv_buffer, int recv_buffer_size) { idx_t ibuf = 0; for (int node_cnt = 0; node_cnt < sendcnt; ++node_cnt) { const idx_t node_idx = sendmap[node_cnt]; zero_index::apply(ibuf, node_idx, array, recv_buffer); } } #define ATLAS_REPEAT_MACRO(n, m, p) ATLAS_REPEAT_MACRO_ ## n(m, p) // expands to m(0,p) m(1,p) ... m(n-1,p) #define ATLAS_REPEAT_MACRO_0(m, p) #define ATLAS_REPEAT_MACRO_1(m, p) ATLAS_REPEAT_MACRO_0(m, p) m(0, p) #define ATLAS_REPEAT_MACRO_2(m, p) ATLAS_REPEAT_MACRO_1(m, p) m(1, p) #define ATLAS_REPEAT_MACRO_3(m, p) ATLAS_REPEAT_MACRO_2(m, p) m(2, p) #define ATLAS_REPEAT_MACRO_4(m, p) ATLAS_REPEAT_MACRO_3(m, p) m(3, p) #define ATLAS_REPEAT_MACRO_5(m, p) ATLAS_REPEAT_MACRO_4(m, p) m(4, p) #define ATLAS_REPEAT_MACRO_6(m, p) ATLAS_REPEAT_MACRO_5(m, p) m(5, p) #define EXPLICIT_TEMPLATE_INSTANTIATION( ParallelDim, RANK ) \ template struct HostPacker; \ template struct HostPacker; \ template struct HostPacker; \ template struct HostPacker; \ template struct HostPacker; \ template struct HostAdjointPacker; \ template struct HostAdjointPacker; \ template struct HostAdjointPacker; \ template struct HostAdjointPacker; \ template struct HostAdjointPacker; \ template struct HostZeroer; \ template struct HostZeroer; \ template struct HostZeroer; \ template struct HostZeroer; \ template struct HostZeroer; #define EXPLICIT_TEMPLATE_INSTANTIATION_RANK(RANK) \ ATLAS_REPEAT_MACRO(RANK, EXPLICIT_TEMPLATE_INSTANTIATION, RANK) EXPLICIT_TEMPLATE_INSTANTIATION_RANK(1) EXPLICIT_TEMPLATE_INSTANTIATION_RANK(2) EXPLICIT_TEMPLATE_INSTANTIATION_RANK(3) EXPLICIT_TEMPLATE_INSTANTIATION_RANK(4) EXPLICIT_TEMPLATE_INSTANTIATION_RANK(5) } //namespace atlas::array::detail atlas-0.46.0/src/atlas/parallel/detail/pack_index.h0000664000175000017500000001117115160212070022273 0ustar alastairalastair/* * (C) Copyright 2025- 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/array/ArrayView.h" #include "atlas/array/SVector.h" namespace atlas::parallel::detail { template struct pack_index { template ATLAS_HOST_DEVICE static void apply(idx_t& buf_idx, const idx_t node_idx, const array::ArrayView& field, DATA_TYPE* send_buffer, Idx... idxs) { for (idx_t i = 0; i < field.template shape(); ++i) { pack_index::apply(buf_idx, node_idx, field, send_buffer, idxs..., i); } } }; template struct pack_index { template ATLAS_HOST_DEVICE static void apply(idx_t& buf_idx, const idx_t node_idx, const array::ArrayView& field, DATA_TYPE* send_buffer, Idx... idxs) { send_buffer[buf_idx++] = field(idxs...); } }; template struct pack_index { template ATLAS_HOST_DEVICE static void apply(idx_t& buf_idx, const idx_t node_idx, const array::ArrayView& field, DATA_TYPE* send_buffer, Idx... idxs) { pack_index::apply(buf_idx, node_idx, field, send_buffer, idxs..., node_idx); } }; template struct pack_index { template ATLAS_HOST_DEVICE static void apply(idx_t& buf_idx, const idx_t node_idx, const array::ArrayView& field, DATA_TYPE* send_buffer, Idx... idxs) { send_buffer[buf_idx++] = field(idxs...); } }; template struct unpack_index { template ATLAS_HOST_DEVICE static void apply(idx_t& buf_idx, const idx_t node_idx, const DATA_TYPE* recv_buffer, array::ArrayView& field, Idx... idxs) { for (idx_t i = 0; i < field.template shape(); ++i) { unpack_index::apply(buf_idx, node_idx, recv_buffer, field, idxs..., i); } } }; template struct unpack_index { template ATLAS_HOST_DEVICE static void apply(idx_t& buf_idx, const idx_t node_idx, const DATA_TYPE* recv_buffer, array::ArrayView& field, Idx... idxs) { field(idxs...) = recv_buffer[buf_idx++]; } }; template struct unpack_index { template ATLAS_HOST_DEVICE static void apply(idx_t& buf_idx, const idx_t node_idx, const DATA_TYPE* recv_buffer, array::ArrayView& field, Idx... idxs) { unpack_index::apply(buf_idx, node_idx, recv_buffer, field, idxs..., node_idx); } }; template struct unpack_index { template ATLAS_HOST_DEVICE static void apply(idx_t& buf_idx, const idx_t node_idx, const DATA_TYPE* recv_buffer, array::ArrayView& field, Idx... idxs) { field(idxs...) = recv_buffer[buf_idx++]; } }; } // namespace atlas::parallel::detail atlas-0.46.0/src/atlas/parallel/detail/DevicePacker.hic0000664000175000017500000002265515160212070023040 0ustar alastairalastair/* * (C) Copyright 2025- 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 "Packer.h" #include "pack_index.h" #include "atlas/array/SVector.h" #include "atlas/runtime/Exception.h" #include "hic/hic.h" namespace atlas::parallel::detail { bool is_device_accessible(const void* ptr) { hicPointerAttributes attr; hicError_t code = hicPointerGetAttributes(&attr, ptr); if( code != hicSuccess ) { static_cast(hicGetLastError()); return false; } return ptr == attr.devicePointer; } template struct get_buffer_index{ template ATLAS_HOST_DEVICE static idx_t apply(const array::ArrayView& field, const idx_t node_cnt, const idx_t var1_idx) { return field.shape(RANK-1) * field.shape(RANK-2) * node_cnt + field.shape(RANK-1) * var1_idx; } }; template struct get_buffer_index{ template ATLAS_HOST_DEVICE static idx_t apply(const array::ArrayView& field, const idx_t node_cnt, const idx_t var1_idx) { return field.shape(1) * node_cnt + var1_idx; } }; template __global__ void pack_kernel(const int sendcnt, const int* sendmap_ptr, const idx_t sendmap_size, const array::ArrayView field, DATA_TYPE* send_buffer, const idx_t send_buffer_size, const typename std::enable_if::type = 0) { const array::SVector sendmap(const_cast(sendmap_ptr), sendmap_size); const idx_t node_cnt = blockIdx.x*blockDim.x + threadIdx.x; if(node_cnt >= sendcnt) return; send_buffer[node_cnt] = field(sendmap[node_cnt]); } template __global__ void pack_kernel(const int sendcnt, const int* sendmap_ptr, const idx_t sendmap_size, const array::ArrayView field, DATA_TYPE* send_buffer, const idx_t send_buffer_size, const typename std::enable_if=2, int>::type = 0) { const array::SVector sendmap(const_cast(sendmap_ptr), sendmap_size); const idx_t node_cnt = blockIdx.x*blockDim.x + threadIdx.x; const idx_t var1_idx = blockIdx.y*blockDim.y + threadIdx.y; if(node_cnt >= sendcnt || var1_idx >= field.shape(1) ) return; idx_t buff_idx = get_buffer_index::apply(field, node_cnt, var1_idx); const idx_t node_idx = sendmap[node_cnt]; pack_index<0, (RANK>=3) ? (RANK-2) : 0, 2>::apply(buff_idx, node_idx, field, send_buffer, node_idx,var1_idx); } template __global__ void unpack_kernel(const int recvcnt, const int* recvmap_ptr, const idx_t recvmap_size, const DATA_TYPE* recv_buffer, const idx_t recv_buffer_size, array::ArrayView field, const typename std::enable_if::type = 0) { const array::SVector recvmap(const_cast(recvmap_ptr), recvmap_size); idx_t node_cnt = blockIdx.x*blockDim.x + threadIdx.x; if(node_cnt >= recvcnt) return; const idx_t node_idx = recvmap[node_cnt]; field(node_idx) = recv_buffer[node_cnt]; } template __global__ void unpack_kernel(const int recvcnt, const int* recvmap_ptr, const idx_t recvmap_size, const DATA_TYPE* recv_buffer, const idx_t recv_buffer_size, array::ArrayView field, const typename std::enable_if=2, int>::type = 0) { const array::SVector recvmap(const_cast(recvmap_ptr), recvmap_size); const idx_t node_cnt = blockIdx.x*blockDim.x + threadIdx.x; const idx_t var1_idx = blockIdx.y*blockDim.y + threadIdx.y; if(node_cnt >= recvcnt || var1_idx >= field.shape(1) ) return; const idx_t node_idx = recvmap[node_cnt]; idx_t buff_idx = get_buffer_index::apply(field, node_cnt, var1_idx); unpack_index<0, (RANK>=3) ? (RANK-2) : 0, 2>::apply(buff_idx, node_idx, recv_buffer, field,node_idx,var1_idx); } template struct get_first_non_parallel_dim { static_assert((ParallelDim <= RANK), "Error: parallelDim larger than RANK"); constexpr static int apply() { return (DimCnt == ParallelDim) ? get_first_non_parallel_dim::apply() : DimCnt; } }; template struct get_first_non_parallel_dim { static_assert((ParallelDim <= RANK), "Error: parallelDim larger than RANK"); constexpr static int apply() { return -1; } }; template struct get_n_hic_blocks { template static unsigned int apply(const array::ArrayView& hfield, const unsigned int block_size_y) { constexpr auto dim = get_first_non_parallel_dim::apply(); return (hfield.shape(dim)+block_size_y-1)/block_size_y; } }; template<> struct get_n_hic_blocks<0, 1> { template static unsigned int apply(const array::ArrayView& hfield, const unsigned int block_size_y) { return 1; } }; template void DevicePacker::pack( const int sendcnt, array::SVector const & sendmap, const array::ArrayView& array, DATA_TYPE* send_buffer, int send_buffer_size ) { const unsigned int block_size_x = 32; const unsigned int block_size_y = (RANK==1) ? 1 : 4; const unsigned int nblocks_x = (sendcnt+block_size_x-1)/block_size_x; const unsigned int nblocks_y = get_n_hic_blocks::apply(array, block_size_y); dim3 threads(block_size_x, block_size_y); dim3 blocks(nblocks_x, nblocks_y); HIC_CALL(hicDeviceSynchronize()); hicError_t err = hicGetLastError(); if (err != hicSuccess) { std::string msg = std::string("Error synchronizing device")+ hicGetErrorString(err); throw_Exception(msg); } pack_kernel<<>>(sendcnt, sendmap.data(), sendmap.size(), array, send_buffer, send_buffer_size); err = hicGetLastError(); if (err != hicSuccess) throw_Exception("Error launching GPU packing kernel"); HIC_CALL(hicDeviceSynchronize()); err = hicGetLastError(); if (err != hicSuccess) { std::string msg = std::string("Error synchronizing device")+ hicGetErrorString(err); throw_Exception(msg); } } template void DevicePacker::unpack(const int recvcnt, array::SVector const & recvmap, const DATA_TYPE* recv_buffer , int recv_buffer_size, array::ArrayView& array) { const unsigned int block_size_x = 32; const unsigned int block_size_y = (RANK==1) ? 1 : 4; unsigned int nblocks_y = get_n_hic_blocks::apply(array, block_size_y); dim3 threads(block_size_x, block_size_y); dim3 blocks((recvcnt+block_size_x-1)/block_size_x, nblocks_y); HIC_CALL(hicDeviceSynchronize()); hicError_t err = hicGetLastError(); if (err != hicSuccess) { std::string msg = std::string("Error synchronizing device")+ hicGetErrorString(err); throw_Exception(msg); } unpack_kernel<<>>(recvcnt, recvmap.data(), recvmap.size(), recv_buffer, recv_buffer_size, array); err = hicGetLastError(); if (err != hicSuccess) { std::string msg = std::string("Error launching GPU packing kernel")+ hicGetErrorString(err); throw_Exception(msg); } HIC_CALL(hicDeviceSynchronize()); err = hicGetLastError(); if (err != hicSuccess) { std::string msg = std::string("Error synchronizing device")+ hicGetErrorString(err); throw_Exception(msg); } } #define ATLAS_REPEAT_MACRO(n, m, p) ATLAS_REPEAT_MACRO_ ## n(m, p) // expands to m(0,p) m(1,p) ... m(n-1,p) #define ATLAS_REPEAT_MACRO_0(m, p) #define ATLAS_REPEAT_MACRO_1(m, p) ATLAS_REPEAT_MACRO_0(m, p) m(0, p) #define ATLAS_REPEAT_MACRO_2(m, p) ATLAS_REPEAT_MACRO_1(m, p) m(1, p) #define ATLAS_REPEAT_MACRO_3(m, p) ATLAS_REPEAT_MACRO_2(m, p) m(2, p) #define ATLAS_REPEAT_MACRO_4(m, p) ATLAS_REPEAT_MACRO_3(m, p) m(3, p) #define ATLAS_REPEAT_MACRO_5(m, p) ATLAS_REPEAT_MACRO_4(m, p) m(4, p) #define ATLAS_REPEAT_MACRO_6(m, p) ATLAS_REPEAT_MACRO_5(m, p) m(5, p) #define EXPLICIT_TEMPLATE_INSTANTIATION( ParallelDim, RANK ) \ template struct DevicePacker; \ template struct DevicePacker; \ template struct DevicePacker; \ template struct DevicePacker; \ template struct DevicePacker; #define EXPLICIT_TEMPLATE_INSTANTIATION_RANK(RANK) \ ATLAS_REPEAT_MACRO(RANK, EXPLICIT_TEMPLATE_INSTANTIATION, RANK) EXPLICIT_TEMPLATE_INSTANTIATION_RANK(1) EXPLICIT_TEMPLATE_INSTANTIATION_RANK(2) EXPLICIT_TEMPLATE_INSTANTIATION_RANK(3) EXPLICIT_TEMPLATE_INSTANTIATION_RANK(4) EXPLICIT_TEMPLATE_INSTANTIATION_RANK(5) } //namespace atlas::parallel::detail atlas-0.46.0/src/atlas/parallel/acc/0000775000175000017500000000000015160212070017300 5ustar alastairalastairatlas-0.46.0/src/atlas/parallel/acc/acc.h0000664000175000017500000000164115160212070020201 0ustar alastairalastair/* * (C) Copyright 2024- 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 namespace atlas::acc { enum class CompilerId { unknown, nvidia, cray, }; int devices(); void map(void* host_data, void* device_data, std::size_t bytes); void unmap(void* host_data); bool is_present(void* host_data, std::size_t bytes); void* deviceptr(void* host_data); CompilerId compiler_id(); } #if _OPENACC #define ATLAS_ACC_STR(x) #x #define ATLAS_ACC_STRINGIFY(x) ATLAS_ACC_STR(x) #define atlas_acc_pragma(x) _Pragma(ATLAS_ACC_STRINGIFY(x)) #else #define atlas_acc_pragma(x) #endif atlas-0.46.0/src/atlas/parallel/acc/acc.cc0000664000175000017500000000364215160212070020342 0ustar alastairalastair/* * (C) Copyright 2024- 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 "acc.h" #include "atlas/library/defines.h" #if ATLAS_HAVE_ACC #include "pluto/pluto.h" #include "atlas_acc_support/atlas_acc.h" #endif namespace atlas::acc { int devices() { #if ATLAS_HAVE_ACC static int num_devices = [](){ if (pluto::devices() == 0) { return 0; } auto devicetype = atlas_acc_get_device_type(); int _num_devices = atlas_acc_get_num_devices(); if (_num_devices == 1 && devicetype == atlas_acc_device_host) { --_num_devices; } return _num_devices; }(); return num_devices; #else return 0; #endif } void map(void* host_data, void* device_data, std::size_t bytes) { #if ATLAS_HAVE_ACC atlas_acc_map_data(host_data, device_data, bytes); #endif } void unmap(void* host_data) { #if ATLAS_HAVE_ACC atlas_acc_unmap_data(host_data); #endif } bool is_present(void* host_data, std::size_t bytes) { #if ATLAS_HAVE_ACC return atlas_acc_is_present(host_data, bytes); #else return false; #endif } void* deviceptr(void* host_data) { #if ATLAS_HAVE_ACC return atlas_acc_deviceptr(host_data); #else return nullptr; #endif } CompilerId compiler_id() { #if ATLAS_HAVE_ACC static CompilerId id = []() { switch (atlas_acc_compiler_id()) { case atlas_acc_compiler_id_cray: return CompilerId::cray; case atlas_acc_compiler_id_nvidia: return CompilerId::nvidia; default: return CompilerId::unknown; } }(); return id; #else return CompilerId::unknown; #endif } } atlas-0.46.0/src/atlas/parallel/Collect.h0000664000175000017500000002112715160212070020313 0ustar alastairalastair/* * (C) Copyright 2025- 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/array/Array.h" #include "atlas/array/ArrayViewUtil.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/parallel/detail/Packer.h" #include "atlas/runtime/Log.h" #include "atlas/runtime/Trace.h" #include "atlas/runtime/Exception.h" namespace atlas::parallel { class Collect { public: void setup(const idx_t recv_size, const int recv_part[], const idx_t recv_remote_idx[], const int recv_idx_base); void setup(const std::string& mpi_comm, const idx_t recv_size, const int recv_part[], const idx_t recv_remote_idx[], const int recv_idx_base); template void execute(array::Array& send, array::Array& recv, bool on_device = false) const; private: template DATA_TYPE* allocate_buffer(const int buffer_size, const bool on_device) const; template void deallocate_buffer(DATA_TYPE* buffer, const int buffer_size, const bool on_device) const; void counts_displs_setup(const idx_t var_size, std::vector& send_counts_init, std::vector& recv_counts_init, std::vector& send_counts, std::vector& recv_counts, std::vector& send_displs, std::vector& recv_displs) const; template void ireceive(int tag, std::vector& recv_displs, std::vector& recv_counts, std::vector& recv_req, DATA_TYPE* recv_buffer) const; template void pack_send_buffer(const array::ArrayView& array, DATA_TYPE* send_buffer, int send_size, const bool on_device) const; template void unpack_recv_buffer(const DATA_TYPE* recv_buffer, int recv_size, array::ArrayView& array, const bool on_device) const; template void isend_and_wait_for_receive(int tag, std::vector& recv_counts_init, std::vector& recv_req, std::vector& send_displs, std::vector& send_counts, std::vector& send_req, DATA_TYPE* send_buffer) const; void wait_for_send(std::vector& send_counts_init, std::vector& send_req) const; const mpi::Comm& comm() const { return *comm_; } int devices() const; private: bool is_setup_; int sendcnt_; int recvcnt_; std::vector sendcounts_; std::vector senddispls_; std::vector recvcounts_; std::vector recvdispls_; array::SVector sendmap_; array::SVector recvmap_; int mpi_size_; int mpi_rank_; const mpi::Comm* comm_; }; template void Collect::execute(array::Array& send, array::Array& recv, bool on_device) const { ATLAS_TRACE("Collect", {"collect"}); if (!is_setup_) { throw_Exception("Collect was not setup", Here()); } if( send.rank() != recv.rank() ){ throw_Exception("send and recv arrays are of different rank"); } on_device = on_device && devices() > 0; auto send_view = on_device ? array::make_device_view(send) : array::make_host_view(send); auto recv_view = on_device ? array::make_device_view(recv) : array::make_host_view(recv); constexpr int parallelDim = array::get_parallel_dim(send_view); idx_t var_size = array::get_var_size(send_view); int tag(1); std::size_t nproc_loc(static_cast(mpi_size_)); std::vector send_counts(nproc_loc), recv_counts(nproc_loc); std::vector send_counts_init(nproc_loc), recv_counts_init(nproc_loc); std::vector send_displs(nproc_loc), recv_displs(nproc_loc); std::vector send_req(nproc_loc), recv_req(nproc_loc); int send_size = sendcnt_ * var_size; int recv_size = recvcnt_ * var_size; DATA_TYPE* send_buffer = allocate_buffer(send_size, on_device); DATA_TYPE* recv_buffer = allocate_buffer(recv_size, on_device); counts_displs_setup(var_size, send_counts_init, recv_counts_init, send_counts, recv_counts, send_displs, recv_displs); ireceive(tag, recv_displs, recv_counts, recv_req, recv_buffer); /// Pack pack_send_buffer(send_view, send_buffer, send_size, on_device); isend_and_wait_for_receive(tag, recv_counts_init, recv_req, send_displs, send_counts, send_req, send_buffer); /// Unpack unpack_recv_buffer(recv_buffer, recv_size, recv_view, on_device); wait_for_send(send_counts_init, send_req); deallocate_buffer(send_buffer, send_size, on_device); deallocate_buffer(recv_buffer, recv_size, on_device); } template DATA_TYPE* Collect::allocate_buffer(const int buffer_size, const bool on_device) const { DATA_TYPE* buffer{nullptr}; if (on_device) { util::allocate_devicemem(buffer, buffer_size); } else { util::allocate_hostmem(buffer, buffer_size); } return buffer; } template void Collect::deallocate_buffer(DATA_TYPE* buffer, const int buffer_size, const bool on_device) const { if (on_device) { util::delete_devicemem(buffer, buffer_size); } else { util::delete_hostmem(buffer, buffer_size); } } template void Collect::ireceive(int tag, std::vector& recv_displs, std::vector& recv_counts, std::vector& recv_req, DATA_TYPE* recv_buffer) const { ATLAS_TRACE_MPI(IRECEIVE) { /// Let MPI know what we like to receive for (size_t jproc = 0; jproc < static_cast(mpi_size_); ++jproc) { if (recv_counts[jproc] > 0) { recv_req[jproc] = comm().iReceive(recv_buffer+recv_displs[jproc], recv_counts[jproc], jproc, tag); } } } } template void Collect::pack_send_buffer(const array::ArrayView& array, DATA_TYPE* send_buffer, int send_size, const bool on_device) const { ATLAS_TRACE(); detail::Packer::pack(sendcnt_, sendmap_, array, send_buffer, send_size, on_device); } template void Collect::unpack_recv_buffer(const DATA_TYPE* recv_buffer, int recv_size, array::ArrayView& array, const bool on_device) const { ATLAS_TRACE(); detail::Packer::unpack(recvcnt_, recvmap_, recv_buffer, recv_size, array, on_device); } template void Collect::isend_and_wait_for_receive(int tag, std::vector& recv_counts_init, std::vector& recv_req, std::vector& send_displs, std::vector& send_counts, std::vector& send_req, DATA_TYPE* send_buffer) const { /// Send ATLAS_TRACE_MPI(ISEND) { for (size_t jproc = 0; jproc < static_cast(mpi_size_); ++jproc) { if (send_counts[jproc] > 0) { send_req[jproc] = comm().iSend(send_buffer+send_displs[jproc], send_counts[jproc], jproc, tag); } } } /// Wait for receiving to finish ATLAS_TRACE_MPI(WAIT, "mpi-wait receive") { for (size_t jproc = 0; jproc < static_cast(mpi_size_); ++jproc) { if (recv_counts_init[jproc] > 0) { comm().wait(recv_req[jproc]); } } } } } // namespace atlas::parallel atlas-0.46.0/src/atlas/parallel/mpi/0000775000175000017500000000000015160212070017337 5ustar alastairalastairatlas-0.46.0/src/atlas/parallel/mpi/Statistics.h0000664000175000017500000000426415160212070021650 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include "atlas/parallel/mpi/mpi.h" #include "atlas/runtime/Trace.h" #define ATLAS_TRACE_MPI(...) #if ATLAS_HAVE_TRACE #include "atlas/library/detail/BlackMagic.h" #undef ATLAS_TRACE_MPI #define ATLAS_TRACE_MPI(...) ATLAS_TRACE_MPI_(::atlas::mpi::Trace, Here(), __VA_ARGS__) #define ATLAS_TRACE_MPI_(Type, location, operation, ...) \ __ATLAS_TYPE_SCOPE(Type, location, __ATLAS_TRACE_MPI_ENUM(operation) __ATLAS_COMMA_ARGS(__VA_ARGS__)) #define __ATLAS_TRACE_MPI_ENUM(operation) ::atlas::mpi::Operation::__ATLAS_STRINGIFY(operation) #endif namespace atlas { namespace mpi { struct StatisticsTimerTraits { using Barriers = runtime::trace::NoBarriers; using Tracing = runtime::trace::NoLogging; }; enum class Operation { BROADCAST, ALLREDUCE, ALLGATHER, ALLTOALL, REDUCE, GATHER, SCATTER, BARRIER, SENDRECEIVE, ISEND, IRECEIVE, WAIT, _COUNT_ }; static const std::string& name(Operation c) { static std::array(Operation::_COUNT_)> names{ "mpi.broadcast", "mpi.allreduce", "mpi.allgather", "mpi.alltoall", "mpi.reduce", "mpi.gather", "mpi.scatter", "mpi.barrier", "mpi.sendreceive", "mpi.isend", "mpi.ireceive", "mpi.wait"}; return names[static_cast(c)]; } class Trace : public runtime::trace::TraceT { using Base = runtime::trace::TraceT; public: Trace(const eckit::CodeLocation& loc, Operation c): Base(loc, name(c), make_labels(c)) {} Trace(const eckit::CodeLocation& loc, Operation c, const std::string& title): Base(loc, title, make_labels(c)) {} private: static std::vector make_labels(Operation c) { return {"mpi", name(c)}; } }; } // namespace mpi } // namespace atlas atlas-0.46.0/src/atlas/parallel/mpi/mpi.cc0000664000175000017500000001417615160212070020444 0ustar alastairalastair/* * (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/parallel/mpi/mpi.h" #include #include #include #include #include #include #include "atlas/runtime/Log.h" #include "atlas/runtime/Exception.h" namespace atlas::mpi { void finalize() { finalise(); } void finalise() { Log::debug() << "atlas::mpi::finalize() --> Finalizing MPI" << std::endl; eckit::mpi::finaliseAllComms(); } namespace { const Comm& comm_world() { static const Comm& world = atlas::mpi::comm("world"); return world; } const Comm& comm_self() { static const Comm& self = atlas::mpi::comm("self"); return self; } int comm_world_int() { static int world = comm_world().communicator(); return world; } int comm_self_int() { static int self = comm_self().communicator(); return self; } std::string comm_name_from_communicator( int communicator ) { return "int." + std::to_string(communicator); } std::optional> lookup_comm( int communicator ) { // First check if the communicator corresponds to the well-known comm_world or comm_self, or the current default comm // Then fall back to a more expensive lookup in the registered communicators, if any if ( communicator == comm_world_int() ) { return comm_world(); } else if ( communicator == comm_self_int() ) { return comm_self(); } else { const auto& default_comm = comm(); if ( default_comm.communicator() == communicator ) { return default_comm; } else { for( std::string name : eckit::mpi::listComms() ) { const auto& comm = atlas::mpi::comm(name); if ( comm.communicator() == communicator ) { return comm; } } } } return std::nullopt; } class CommScope { public: CommScope() { Log::debug() << "atlas::mpi::scope::push()" << std::endl; previous_comm_name_ = mpi::comm().name(); } CommScope(std::string_view name) { Log::debug() << "atlas::mpi::scope::push(" << name << ")" << std::endl; previous_comm_name_ = mpi::comm().name(); eckit::mpi::setCommDefault(name.data()); } CommScope(const Comm& comm) { Log::debug() << "atlas::mpi::scope::push(" << comm.name() << ")" << std::endl; previous_comm_name_ = mpi::comm().name(); eckit::mpi::setCommDefault(comm.name().data()); } CommScope(int communicator) { previous_comm_name_ = mpi::comm().name(); auto comm_opt = lookup_comm(communicator); if (comm_opt) { Log::debug() << "atlas::mpi::scope::push(" << communicator << ") : found existing comm " << comm_opt->get().name() << std::endl; eckit::mpi::setCommDefault(comm_opt->get().name().data()); } else { new_registered_comm_name_ = comm_name_from_communicator(communicator); Log::debug() << "atlas::mpi::scope::push(" << communicator << ") : registered new comm " << new_registered_comm_name_ << std::endl; register_comm(new_registered_comm_name_, communicator); eckit::mpi::setCommDefault(new_registered_comm_name_.c_str()); } } ~CommScope() { Log::debug() << "atlas::mpi::scope::pop() : restored to " << previous_comm_name_; eckit::mpi::setCommDefault(previous_comm_name_.c_str()); if (not new_registered_comm_name_.empty()) { Log::debug() << " and unregistered comm " << new_registered_comm_name_; unregister_comm(new_registered_comm_name_); } Log::debug() << '\n'; } private: std::string previous_comm_name_; std::string new_registered_comm_name_; }; class ScopeStack { using value_type = std::unique_ptr; using container_type = std::vector; using stack_type = std::stack; public: void push() { stack_.push(std::make_unique()); } void push(std::string_view name) { stack_.push(std::make_unique(name)); } void push(const Comm& comm) { stack_.push(std::make_unique(comm)); } void push(int communicator) { stack_.push(std::make_unique(communicator)); } void pop() { if( !stack_.empty() ) { stack_.pop(); } } static ScopeStack& instance() { static ScopeStack instance; return instance; } private: ScopeStack() { container_type underlying; underlying.reserve(64); stack_ = stack_type(std::move(underlying)); } private: stack_type stack_; }; } void scope::push() { ScopeStack::instance().push(); } void scope::push(std::string_view name) { ScopeStack::instance().push(name); } void scope::push(const Comm& comm) { ScopeStack::instance().push(comm); } void scope::push(int communicator) { ScopeStack::instance().push(communicator); } void scope::pop() { ScopeStack::instance().pop(); } bool has_comm( std::string_view name ) { return eckit::mpi::hasComm(name.data()); } const Comm& comm(int communicator) { auto comm_opt = lookup_comm( communicator ); if ( comm_opt ) { return comm_opt->get(); } throw_Exception("No MPI communicator found with integer value " + std::to_string(communicator), Here()); } const Comm& register_comm(std::string_view name, int communicator) { eckit::mpi::addComm(name.data(), communicator); return comm(name); } void unregister_comm(std::string_view name) { #if ATLAS_ECKIT_VERSION_AT_LEAST(2, 0, 0) eckit::mpi::unregisterComm(name.data()); #else Log::warning() << "atlas::mpi::unregister_comm is a no-op with eckit versions prior to 2.0.0" << std::endl; #endif } } // namespace atlas::mpi atlas-0.46.0/src/atlas/parallel/mpi/mpi.h0000664000175000017500000000727315160212070020306 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include "eckit/mpi/Comm.h" #include "atlas/parallel/mpi/Statistics.h" namespace atlas::mpi { using Comm = eckit::mpi::Comm; inline const Comm& comm() { return eckit::mpi::comm(); } inline const Comm& comm(std::string_view name) { return eckit::mpi::comm(name.data()); } const Comm& comm(int communicator); bool has_comm(std::string_view name); const Comm& register_comm(std::string_view name, int communicator); void unregister_comm(std::string_view name); inline idx_t rank() { return static_cast(comm().rank()); } inline int size() { return static_cast(comm().size()); } void finalize(); void finalise(); namespace scope { /// @brief Push a new scope for MPI communicators. The current default communicator is restored when the scope is destructed using pop() void push(); /// @brief Push a new scope for MPI communicators. The given communicator is set as the default communicator for the duration of the scope. The previous default communicator is restored when the scope is destructed using pop() void push(std::string_view name); /// @brief Push a new scope for MPI communicators. The given communicator is set as the default communicator for the duration of the scope. The previous default communicator is restored when the scope is destructed using pop() void push(const Comm& comm); /// @brief Push a new scope for MPI communicators. The given communicator is set as the default communicator for the duration of the scope. The previous default communicator is restored when the scope is destructed using pop() /// If the given communicator is not already registered, it will be registered with a generated name "int." for the duration of the scope, and unregistered when the scope is destructed. void push(int communicator); /// @brief Pop the current MPI communicator scope, restoring the previous default communicator void pop(); } /// @brief RAII helper class to manage MPI communicator scopes. The constructor pushes a new scope, and the destructor pops the scope, ensuring that the previous default communicator is restored even in case of exceptions. struct Scope { Scope() { scope::push(); } Scope(std::string_view name) { scope::push(name); } Scope(const Comm& comm) { scope::push(comm); }; /// @brief Constructor using integer value. If the given communicator is not already registered, it will be registered with a generated name "int." for the duration of the scope, and unregistered when the scope is destructed. /// @param communicator The integer value of the MPI communicator to use for the scope Scope(int communicator) { scope::push(communicator); } Scope(const Scope&) = delete; Scope(Scope&&) = delete; Scope& operator=(const Scope&) = delete; Scope& operator=(Scope&&) = delete; ~Scope() { scope::pop(); } }; [[deprecated("Use atlas::mpi::scope::push instead")]] inline void push() { scope::push(); } [[deprecated("Use atlas::mpi::scope::pop instead")]] inline void pop() { scope::pop(); } [[deprecated("Use atlas::mpi::scope::push instead")]] inline void push(std::string_view name) { scope::push(name); } [[deprecated("Use atlas::mpi::scope::pop instead")]] inline void pop(std::string_view /*name*/) { scope::pop(); } } // namespace atlas::mpi atlas-0.46.0/src/atlas/parallel/mpi/Buffer.h0000664000175000017500000000412415160212070020722 0ustar alastairalastair/* * (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/array/LocalView.h" #include "atlas/parallel/mpi/mpi.h" namespace atlas { namespace mpi { /// @brief Buffer /// /// Class that keeps allocation of a MPI buffer including /// counts and displacements, but with added index operator[] /// that returns an array::ArrayView of the part /// of the buffer for a processor index. template struct Buffer; // ---------------------------------------------------------------------------------- template struct Buffer : eckit::mpi::Buffer {}; template class BufferView { public: BufferView(DATA_TYPE* data, size_t size): data_(data), size_(size) {} size_t size() const { return size_; } const DATA_TYPE& operator()(const size_t i) const { return data_[i]; } const DATA_TYPE& operator[](const size_t i) const { return data_[i]; } private: DATA_TYPE* data_; size_t size_; }; template struct Buffer : public eckit::mpi::Buffer { Buffer(size_t size): eckit::mpi::Buffer(size) {} // array::make_shape( // eckit::mpi::Buffer::counts[p] // ) ); // } BufferView operator[](int p) { return BufferView( eckit::mpi::Buffer::buffer.data() + eckit::mpi::Buffer::displs[p], eckit::mpi::Buffer::counts[p]); } }; // ---------------------------------------------------------------------------------- } // namespace mpi } // namespace atlas atlas-0.46.0/src/atlas/parallel/Locate.h0000664000175000017500000001116515160212070020136 0ustar alastairalastair/* * (C) Copyright 2025 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 #include #include "atlas/mdspan.h" #include "atlas/util/vector.h" namespace atlas::parallel { class Locator { public: // Helper class that gives bracket-operator to a simple functor returning type T for a given index template struct fspan { using value_type = std::remove_const_t; size_t size() const { return size_; } value_type operator[](size_t i) const { return (*func_)(i); } fspan(const std::function* func, std::size_t size) : func_(func), size_(size) { } private: const std::function* func_{nullptr}; std::size_t size_{0}; }; template> using span = mdspan, Layout, Accessor>; static void locate_partition( // context fspan distribution, int distribution_base, // input span global_index, const gidx_t global_index_base, // output span partition, const int partition_base); static void locate_partition( // context span distribution, int distribution_base, // input span global_index, const gidx_t global_index_base, // output span partition, const int partition_base); // Given global_index, find the corresponding partition and remote_index // This could be costly as it involves memory and communication static void locate_remote_index( // context std::string_view mpi_comm, span my_glb_idx, const gidx_t my_global_index_base, span my_ghost, // input span global_index, const gidx_t global_index_base, span partition, const int partition_base, // output span remote_index, const idx_t remote_index_base ); // Given global_index, find the corresponding partition and remote_index // This could be costly as it involves memory and communication // local information: my_size, my_glb_idx, my_ghost // global information: distribution // requested indices to locate: size, global_index // output of locate: partition, remote_index, remote_index_base static void locate( // context std::string_view mpi_comm, span my_glb_idx, const gidx_t my_global_index_base, span my_ghost, fspan distribution, const int distribution_base, // input span global_index, const gidx_t global_index_base, // output span partition, const int partition_base, span remote_index, const idx_t remote_index_base); static void locate( // context std::string_view mpi_comm, span my_glb_idx, const gidx_t my_global_index_base, span my_ghost, span distribution, const int distribution_base, // input span global_index, const gidx_t global_index_base, // output span partition, const int partition_base, span remote_index, const idx_t remote_index_base); virtual void locate( // input span global_index, const gidx_t global_index_base, // output span partition, const int partition_base, span remote_index, const idx_t remote_index_base) const = 0; virtual void locate( // input const std::vector global_index, const gidx_t global_index_base, // output std::vector& partition, const int partition_base, std::vector& remote_index, const idx_t remote_index_base) const { locate( span{global_index.data(), global_index.size()}, global_index_base, span{partition.data(), partition.size()}, partition_base, span{remote_index.data(), remote_index.size()}, remote_index_base ); } }; } // namespace atlas::parallel atlas-0.46.0/src/atlas/parallel/Locate.cc0000664000175000017500000001415615160212070020277 0ustar alastairalastair/* * (C) Copyright 2025 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 "Locate.h" #include #include "atlas/parallel/mpi/mpi.h" #include "atlas/runtime/Trace.h" namespace atlas::parallel { // Given distribution and global_index, find the corresponding partition for each global index // The global-index is typically 1-based void Locator::locate_partition( fspan distribution, const int distribution_base, span global_index, const gidx_t global_index_base, span partition, const int partition_base) { ATLAS_TRACE("atlas::util::locate_partition"); std::size_t size = global_index.size(); for (std::size_t j = 0; j < size; ++j) { auto gidx = global_index[j] - global_index_base; ATLAS_ASSERT(gidx < distribution.size()); auto p = distribution[gidx] - distribution_base; partition[j] = p + partition_base; } } // Given distribution and global_index, find the corresponding partition for each global index // The global-index is typically 1-based void Locator::locate_partition( span distribution, const int distribution_base, span global_index, const gidx_t global_index_base, span partition, const int partition_base) { ATLAS_TRACE("atlas::util::locate_partition"); std::size_t size = global_index.size(); for (std::size_t j = 0; j < size; ++j) { gidx_t gidx = global_index[j] - global_index_base; ATLAS_ASSERT(gidx < distribution.size()); int p = distribution[gidx] - distribution_base; partition[j] = p + partition_base; } } // Given global_index, find the corresponding partition and remote_index // This could be costly as it involves memory and communication void Locator::locate_remote_index( const std::string_view mpi_comm, span my_glb_idx, const gidx_t my_glb_idx_base, span my_ghost, span global_index, const gidx_t global_index_base, span partition, const int partition_base, span remote_index, const idx_t remote_index_base) { ATLAS_TRACE("atlas::util::locate_remote_index"); auto& comm = atlas::mpi::comm(mpi_comm); int mpi_size = comm.size(); std::size_t my_size = my_glb_idx.size(); std::size_t size = global_index.size(); std::vector> recv_gidx(mpi_size); { std::vector> send_gidx(mpi_size); std::vector send_counts(mpi_size,0); for (std::size_t j=0; j> recv_ridx(mpi_size); { std::vector> send_ridx(mpi_size); std::unordered_map gidx_to_ridx; for (idx_t i=0; isecond); } } comm.allToAll(send_ridx, recv_ridx); } std::vector c(mpi_size,0); for( idx_t j=0; j my_glb_idx, const gidx_t my_glb_idx_base, span my_ghost, fspan distribution, const int distribution_base, span global_index, const gidx_t global_index_base, span partition, const int partition_base, span remote_index, const idx_t remote_index_base) { ATLAS_TRACE("atlas::util::locate"); locate_partition(distribution, distribution_base, global_index, global_index_base, partition, partition_base); locate_remote_index(mpi_comm, my_glb_idx, my_glb_idx_base, my_ghost, global_index, global_index_base, partition, partition_base, remote_index, remote_index_base); } void Locator::locate( const std::string_view mpi_comm, span my_glb_idx, const gidx_t my_glb_idx_base, span my_ghost, span distribution, const int distribution_base, span global_index, const gidx_t global_index_base, span partition, const int partition_base, span remote_index, const idx_t remote_index_base) { ATLAS_TRACE("atlas::util::locate"); locate_partition(distribution, distribution_base, global_index, global_index_base, partition, partition_base); locate_remote_index(mpi_comm, my_glb_idx, my_glb_idx_base, my_ghost, global_index, global_index_base, partition, partition_base, remote_index, remote_index_base); } } // atlas::parallel atlas-0.46.0/src/atlas/mesh/0000775000175000017500000000000015160212070015712 5ustar alastairalastairatlas-0.46.0/src/atlas/mesh/MeshBuilder.h0000664000175000017500000002373015160212070020273 0ustar alastairalastair/* * (C) Copyright 2023 UCAR. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ #pragma once #include "eckit/config/Configuration.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/util/Config.h" #include "atlas/mdspan.h" #include "atlas/mesh/Mesh.h" #include #include namespace atlas { namespace mesh { //----------------------------------------------------------------------------- /** * \brief Construct a Mesh by importing external connectivity data * * Given a list of nodes and corresponding cell-to-node connectivity, sets up a Mesh. Not all Mesh * fields are initialized, but enough are to build halos and construct a NodeColumns FunctionSpace. * * Some limitations of the current implementation (but not inherent): * - can only set up triangle and quad cells. * - cannot import halos, i.e., cells owned by other MPI tasks; halos can still be subsequently * computed by calling the BuildMesh action. * - cannot import node-to-cell connectivity information. * * The Mesh's Grid can be initialized via the call operator's optional config argument. */ class MeshBuilder : public eckit::Owned { public: MeshBuilder(const eckit::Configuration& = util::NoConfig()) {} /** * \brief C-interface to construct a Mesh from external connectivity data * * The inputs lons, lats, ghosts, global_indices, remote_indices, and partitions are vectors of * size nb_nodes, ranging over the nodes locally owned by (or in the ghost nodes of) the MPI * task. The global index is a uniform labeling of the nodes across all MPI tasks; the remote * index is a remote_index_base-based vector index for the node on its owning task. * * The tri/quad connectivities (boundary_nodes and global_indices) are vectors ranging over the * cells owned by the MPI task. Each cell is defined by a list of nodes defining its boundary; * note that each boundary node must be locally known (whether as an owned of ghost node on the * MPI task), in other words, must be an element of the node global_indices. The boundary nodes * are ordered node-varies-fastest, element-varies-slowest order. The cell global index is, * here also, a uniform labeling over the of the cells across all MPI tasks. * * The config argument can be used to * - Request the Mesh's Grid to be constructed, usually from the config. If the Grid is either * an UnstructuredGrid or a Structured grid, the `validate` bool option can be used to trigger * a simple check that the grid is consistent with the lons/lats passed in to the MeshBuilder. * In the special case where `grid.type` is unstructured and the `grid.xy` coordinates are * _not_ given, then the grid is constructed from the lons/lats passed to the MeshBuilder. * - Select which MPI communicator to use. */ // operator (1) deprecated, use operator (2) [[deprecated("Use operator (2) with extra global_index_base argument")]] Mesh operator()(size_t nb_nodes, const double lon[], const double lat[], const int ghost[], const gidx_t global_index[], const idx_t remote_index[], const idx_t remote_index_base, const int partition[], size_t nb_triags, const gidx_t triag_nodes_global[], const gidx_t triag_global_index[], size_t nb_quads, const gidx_t quad_nodes_global[], const gidx_t quad_global_index[], const eckit::Configuration& config = util::NoConfig()) const; // operator (2) = slightly reordered arguments of (1) and extra global_index_base argument Mesh operator()(size_t nb_nodes, const gidx_t global_index[], const double lon[], const double lat[], const int ghost[], const int partition[], const idx_t remote_index[], const idx_t remote_index_base, size_t nb_triags, const gidx_t triag_nodes_global[], const gidx_t triag_global_index[], size_t nb_quads, const gidx_t quad_nodes_global[], const gidx_t quad_global_index[], gidx_t global_index_base, const eckit::Configuration& config = util::NoConfig()) const; // operator (3) deprecated, use (4) // Delegate to next operator with global index base = 1 [[deprecated("Use operator (4) with extra global_index_base argument")]] Mesh operator()(size_t nb_nodes, const gidx_t global_index[], const double x[], const double y[], size_t xstride, size_t ystride, const double lon[], const double lat[], size_t lonstride, size_t latstride, const int ghost[], const int partition[], const idx_t remote_index[], const idx_t remote_index_base, size_t nb_triags, const gidx_t triag_global_index[], const gidx_t triag_nodes_global[], size_t nb_quads, const gidx_t quad_global_index[], const gidx_t quad_nodes_global[], const eckit::Configuration& config = util::NoConfig()) const; // operator (4) = operator (3) with extra global_index_base argument Mesh operator()(size_t nb_nodes, const gidx_t global_index[], const double x[], const double y[], size_t xstride, size_t ystride, const double lon[], const double lat[], size_t lonstride, size_t latstride, const int ghost[], const int partition[], const idx_t remote_index[], const idx_t remote_index_base, size_t nb_triags, const gidx_t triag_global_index[], const gidx_t triag_nodes_global[], size_t nb_quads, const gidx_t quad_global_index[], const gidx_t quad_nodes_global[], gidx_t global_index_base, const eckit::Configuration& config = util::NoConfig()) const; template using span = mdspan,layout_right>; template using strided_span = mdspan,layout_stride>; // operator (5) Mesh operator()(span global_index, strided_span x, strided_span y, strided_span lon, strided_span lat, span ghost, span partition, span remote_index, const idx_t remote_index_base, span triag_global_index, mdspan> triag_nodes_global, span quad_global_index, mdspan> quad_nodes_global, const gidx_t global_index_base, const eckit::Configuration& config = util::NoConfig()) const; /** * \brief C++-interface to construct a Mesh from external connectivity data * * Provides a wrapper to the C-interface using STL containers. */ [[deprecated("Use mdspan based operator with extra global_index_base argument")]] Mesh operator()(const std::vector& lons, const std::vector& lats, const std::vector& ghosts, const std::vector& global_index, const std::vector& remote_index, const idx_t remote_index_base, const std::vector& partitions, const std::vector>& triag_nodes_global, const std::vector& triag_global_index, const std::vector>& quad_nodes_global, const std::vector& quad_global_index, const eckit::Configuration& config = util::NoConfig()) const; }; //----------------------------------------------------------------------------- class TriangularMeshBuilder { public: TriangularMeshBuilder(const eckit::Configuration& config = util::NoConfig()) : meshbuilder_(config) {} /** * \brief C-interface to construct a Triangular Mesh from external connectivity data * * The inputs x, y, lons, lats, ghost, global_index, remote_index, and partition are vectors of * size nb_nodes, ranging over the nodes locally owned by (or in the ghost nodes of) the MPI * task. The global index is a uniform labeling of the nodes across all MPI tasks; the remote * index is a remote_index_base-based vector index for the node on its owning task. * * The triangle connectivities (boundary_nodes and global_index) are vectors ranging over the * cells owned by the MPI task. Each cell is defined by a list of nodes defining its boundary; * note that each boundary node must be locally known (whether as an owned of ghost node on the * MPI task), in other words, must be an element of the node global_indices. The boundary nodes * are ordered node-varies-fastest, element-varies-slowest order. The cell global index is, * here also, a uniform labeling over the of the cells across all MPI tasks. */ [[deprecated("Use operator with extra global_index_base argument and strides")]] Mesh operator()(size_t nb_nodes, const gidx_t node_global_index[], const double x[], const double y[], const double lon[], const double lat[], size_t nb_triags, const gidx_t triangle_global_index[], const gidx_t triangle_nodes_global_index[]) const; Mesh operator()(size_t nb_nodes, const gidx_t node_global_index[], const double x[], const double y[], size_t xstride, size_t ystride, const double lon[], const double lat[], size_t lonstride, size_t latstride, size_t nb_triags, const gidx_t triangle_global_index[], const gidx_t triangle_nodes_global_index[], gidx_t global_index_base) const; private: MeshBuilder meshbuilder_; }; //----------------------------------------------------------------------------- } // namespace mesh } // namespace atlas atlas-0.46.0/src/atlas/mesh/Nodes.cc0000664000175000017500000002327215160212070017277 0ustar alastairalastair/* * (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/array.h" #include "atlas/field/Field.h" #include "atlas/mesh/Connectivity.h" #include "atlas/mesh/Nodes.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Log.h" #include "atlas/util/CoordinateEnums.h" using atlas::array::make_datatype; using atlas::array::make_shape; namespace atlas { namespace mesh { //------------------------------------------------------------------------------------------------------ Nodes::Nodes(): size_(0) { global_index_ = add(Field("glb_idx", make_datatype(), make_shape(size()))); remote_index_ = add(Field("remote_idx", make_datatype(), make_shape(size()))); partition_ = add(Field("partition", make_datatype(), make_shape(size()))); xy_ = add(Field("xy", make_datatype(), make_shape(size(), 2))); xy_.set_variables(2); lonlat_ = add(Field("lonlat", make_datatype(), make_shape(size(), 2))); lonlat_.set_variables(2); ghost_ = add(Field("ghost", make_datatype(), make_shape(size()))); flags_ = add(Field("flags", make_datatype(), make_shape(size()))); halo_ = add(Field("halo", make_datatype(), make_shape(size()))); edge_connectivity_ = &add(new Connectivity("edge")); cell_connectivity_ = &add(new Connectivity("cell")); } Nodes::Connectivity& Nodes::add(Connectivity* connectivity) { connectivities_[connectivity->name()] = connectivity; return *connectivity; } Field Nodes::add(const Field& field) { ATLAS_ASSERT(field); ATLAS_ASSERT(!field.name().empty()); if (has_field(field.name())) { std::stringstream msg; msg << "Trying to add field '" << field.name() << "' to Nodes, but Nodes already has a field with this name."; throw_Exception(msg.str(), Here()); } fields_[field.name()] = field; return field; } void Nodes::remove_field(const std::string& name) { if (!has_field(name)) { std::stringstream msg; msg << "Trying to remove field `" << name << "' in Nodes, but no field with this name is present in Nodes."; throw_Exception(msg.str(), Here()); } fields_.erase(name); } const Field& Nodes::field(const std::string& name) const { if (!has_field(name)) { std::stringstream msg; msg << "Trying to access field `" << name << "' in Nodes, but no field with this name is present in Nodes."; throw_Exception(msg.str(), Here()); } return fields_.find(name)->second; } Field& Nodes::field(const std::string& name) { return const_cast(static_cast(this)->field(name)); } void Nodes::resize(idx_t size) { if (size != size_) { idx_t previous_size = size_; size_ = size; for (FieldMap::iterator it = fields_.begin(); it != fields_.end(); ++it) { Field& field = it->second; array::ArrayShape shape = field.shape(); shape[0] = size_; field.resize(shape); } auto glb_idx = array::make_view(global_index()); auto part = array::make_view(partition()); auto flag = array::make_view(flags()); auto _halo = array::make_view(halo()); const int mpi_rank = static_cast(mpi::rank()); for (idx_t n = previous_size; n < size_; ++n) { glb_idx(n) = 1 + n; part(n) = mpi_rank; flag(n) = 0; _halo(n) = std::numeric_limits::max(); } } } const Field& Nodes::field(idx_t idx) const { ATLAS_ASSERT(idx < nb_fields()); idx_t c(0); for (FieldMap::const_iterator it = fields_.begin(); it != fields_.end(); ++it) { if (idx == c) { const Field& field = it->second; return field; } c++; } throw_Exception("Should not be here!", Here()); static Field f; return f; } Field& Nodes::field(idx_t idx) { return const_cast(static_cast(this)->field(idx)); } void Nodes::print(std::ostream& os) const { os << "Nodes[\n"; os << "\t size=" << size() << ",\n"; os << "\t fields=\n"; for (idx_t i = 0; i < nb_fields(); ++i) { os << "\t\t" << field(i); if (i != nb_fields() - 1) { os << ","; } os << "\n"; } os << "]"; } size_t Nodes::footprint() const { size_t size = sizeof(*this); for (FieldMap::const_iterator it = fields_.begin(); it != fields_.end(); ++it) { size += (*it).second.footprint(); } for (ConnectivityMap::const_iterator it = connectivities_.begin(); it != connectivities_.end(); ++it) { size += (*it).second->footprint(); } size += metadata_.footprint(); return size; } const IrregularConnectivity& Nodes::connectivity(const std::string& name) const { if ((connectivities_.find(name) == connectivities_.end())) { std::stringstream msg; msg << "Trying to access connectivity `" << name << "' in Nodes, but no connectivity with this name is present in " "Nodes."; throw_Exception(msg.str(), Here()); } return *connectivities_.find(name)->second; } IrregularConnectivity& Nodes::connectivity(const std::string& name) { if ((connectivities_.find(name) == connectivities_.end())) { std::stringstream msg; msg << "Trying to access connectivity `" << name << "' in Nodes, but no connectivity with this name is present in " "Nodes."; throw_Exception(msg.str(), Here()); } return *connectivities_.find(name)->second; } void Nodes::updateDevice() const { std::for_each(fields_.begin(), fields_.end(), [](const FieldMap::value_type& v) { v.second.updateDevice(); }); } void Nodes::updateHost() const { std::for_each(fields_.begin(), fields_.end(), [](const FieldMap::value_type& v) { v.second.updateHost(); }); } void Nodes::syncHostDevice() const { std::for_each(fields_.begin(), fields_.end(), [](const FieldMap::value_type& v) { v.second.syncHostDevice(); }); } //----------------------------------------------------------------------------- extern "C" { Nodes* atlas__mesh__Nodes__create() { return new Nodes(); } void atlas__mesh__Nodes__delete(Nodes* This) { ATLAS_ASSERT(This != nullptr); } idx_t atlas__mesh__Nodes__size(Nodes* This) { ATLAS_ASSERT(This != nullptr); return This->size(); } void atlas__mesh__Nodes__resize(Nodes* This, idx_t size) { ATLAS_ASSERT(This != nullptr); This->resize(size); } idx_t atlas__mesh__Nodes__nb_fields(Nodes* This) { ATLAS_ASSERT(This != nullptr); return This->nb_fields(); } void atlas__mesh__Nodes__add_field(Nodes* This, field::FieldImpl* field) { ATLAS_ASSERT(This != nullptr); ATLAS_ASSERT(field != nullptr); This->add(field); } void atlas__mesh__Nodes__remove_field(Nodes* This, char* name) { ATLAS_ASSERT(This != nullptr); This->remove_field(std::string(name)); } int atlas__mesh__Nodes__has_field(Nodes* This, char* name) { ATLAS_ASSERT(This != nullptr); return This->has_field(std::string(name)); } field::FieldImpl* atlas__mesh__Nodes__field_by_name(Nodes* This, char* name) { ATLAS_ASSERT(This != nullptr); return This->field(std::string(name)).get(); } field::FieldImpl* atlas__mesh__Nodes__field_by_idx(Nodes* This, idx_t idx) { ATLAS_ASSERT(This != nullptr); return This->field(idx).get(); } util::Metadata* atlas__mesh__Nodes__metadata(Nodes* This) { ATLAS_ASSERT(This != nullptr); return &This->metadata(); } void atlas__mesh__Nodes__str(Nodes* This, char*& str, int& size) { ATLAS_ASSERT(This != nullptr); std::stringstream ss; ss << *This; std::string s = ss.str(); size = static_cast(s.size()); str = new char[size + 1]; std::strncpy(str, s.c_str(), size + 1); } IrregularConnectivity* atlas__mesh__Nodes__edge_connectivity(Nodes* This) { ATLAS_ASSERT(This != nullptr); return &This->edge_connectivity(); } IrregularConnectivity* atlas__mesh__Nodes__cell_connectivity(Nodes* This) { ATLAS_ASSERT(This != nullptr); return &This->cell_connectivity(); } IrregularConnectivity* atlas__mesh__Nodes__connectivity(Nodes* This, char* name) { ATLAS_ASSERT(This != nullptr); return &This->connectivity(std::string(name)); } void atlas__mesh__Nodes__add_connectivity(Nodes* This, IrregularConnectivity* connectivity) { ATLAS_ASSERT(This != nullptr); ATLAS_ASSERT(connectivity != nullptr); This->add(connectivity); } field::FieldImpl* atlas__mesh__Nodes__xy(Nodes* This) { ATLAS_ASSERT(This != nullptr); return This->xy().get(); } field::FieldImpl* atlas__mesh__Nodes__lonlat(Nodes* This) { ATLAS_ASSERT(This != nullptr); return This->lonlat().get(); } field::FieldImpl* atlas__mesh__Nodes__global_index(Nodes* This) { ATLAS_ASSERT(This != nullptr); return This->global_index().get(); } field::FieldImpl* atlas__mesh__Nodes__remote_index(Nodes* This) { ATLAS_ASSERT(This != nullptr); return This->remote_index().get(); } field::FieldImpl* atlas__mesh__Nodes__partition(Nodes* This) { ATLAS_ASSERT(This != nullptr); return This->partition().get(); } field::FieldImpl* atlas__mesh__Nodes__ghost(Nodes* This) { ATLAS_ASSERT(This != nullptr); return This->ghost().get(); } } } // namespace mesh } // namespace atlas atlas-0.46.0/src/atlas/mesh/Mesh.cc0000664000175000017500000000557115160212070017125 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "atlas/mesh/Mesh.h" #include "atlas/grid/Distribution.h" #include "atlas/grid/Grid.h" #include "atlas/grid/Partitioner.h" #include "atlas/mesh/Nodes.h" #include "atlas/meshgenerator/MeshGenerator.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/runtime/Exception.h" namespace atlas { //---------------------------------------------------------------------------------------------------------------------- Mesh::Mesh(): Handle(new Implementation()) {} Mesh::Mesh(const Grid& grid, const eckit::Configuration& config): Handle([&]() { if (config.has("mpi_comm")) { mpi::scope::push(config.getString("mpi_comm")); } auto cfg = grid.meshgenerator() | util::Config(config); auto meshgenerator = MeshGenerator{grid.meshgenerator() | config}; auto mesh = meshgenerator.generate(grid, grid::Partitioner(grid.partitioner() | config)); if (config.has("mpi_comm")) { mpi::scope::pop(); } mesh.get()->attach(); return mesh.get(); }()) { get()->detach(); } Mesh::Mesh(const Grid& grid, const grid::Partitioner& partitioner, const eckit::Configuration& config): Handle([&]() { auto mpi_comm = partitioner.mpi_comm(); if (config.has("mpi_comm")) { mpi_comm = config.getString("mpi_comm"); ATLAS_ASSERT(mpi_comm == partitioner.mpi_comm()); } mpi::Scope mpi_scope(mpi_comm); auto meshgenerator = MeshGenerator{grid.meshgenerator() | config}; auto mesh = meshgenerator.generate(grid, partitioner); mesh.get()->attach(); return mesh.get(); }()) { get()->detach(); } Mesh::Mesh(const Grid& grid, const grid::Distribution& distribution, const eckit::Configuration& config): Handle([&]() { auto mpi_comm = mpi::comm().name(); if (config.has("mpi_comm")) { mpi_comm = config.getString("mpi_comm"); } mpi::Scope mpi_scope(mpi_comm); auto meshgenerator = MeshGenerator{grid.meshgenerator() | config}; auto mesh = meshgenerator.generate(grid, distribution); mesh.get()->attach(); return mesh.get(); }()) { get()->detach(); } Mesh::Mesh(eckit::Stream& stream): Handle(new Implementation(stream)) {} Mesh::operator bool() const { return get()->nodes().size() > 0; } //---------------------------------------------------------------------------------------------------------------------- } // namespace atlas atlas-0.46.0/src/atlas/mesh/MeshBuilder.cc0000664000175000017500000004575615160212070020445 0ustar alastairalastair/* * (C) Copyright 2023 UCAR. * * 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. */ #include "atlas/mesh/MeshBuilder.h" #include #include #include "atlas/array/MakeView.h" #include "atlas/grid/Grid.h" #include "atlas/grid/Iterator.h" #include "atlas/grid/StructuredGrid.h" #include "atlas/grid/UnstructuredGrid.h" #include "atlas/mesh/ElementType.h" #include "atlas/mesh/HybridElements.h" #include "atlas/mesh/Mesh.h" #include "atlas/mesh/Nodes.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/util/CoordinateEnums.h" namespace atlas { namespace detail { atlas::UnstructuredGrid assemble_unstructured_grid(size_t nb_nodes, const double lon[], const double lat[], const int ghost[], const eckit::mpi::Comm& comm) { // First serialize owned lons and lats into single vector const size_t nb_owned_nodes = std::count(ghost, ghost + nb_nodes, 0); std::vector owned_lonlat(2 * nb_owned_nodes); int counter = 0; for (size_t n = 0; n < nb_nodes; ++n) { if (ghost[n] == 0) { owned_lonlat[counter] = lon[n]; counter++; owned_lonlat[counter] = lat[n]; counter++; } } ATLAS_ASSERT(counter == 2 * nb_owned_nodes); // Gather points across MPI ranks size_t nb_nodes_global = 0; comm.allReduce(nb_owned_nodes, nb_nodes_global, eckit::mpi::sum()); std::vector global_lonlat(2 * nb_nodes_global); eckit::mpi::Buffer buffer(comm.size()); comm.allGatherv(owned_lonlat.begin(), owned_lonlat.end(), buffer); global_lonlat = std::move(buffer.buffer); std::vector points(nb_nodes_global); for (size_t n = 0; n < nb_nodes_global; ++n) { points[n] = atlas::PointXY({global_lonlat[2*n], global_lonlat[2*n + 1]}); } return atlas::UnstructuredGrid(new std::vector(points.begin(), points.end())); } // Check if grid points match mesh points -- not obviously true as the MeshBuilder sets the Grid // independently from the Mesh. This check can give confidence that the grid and mesh are specified // consistently with each other. // // This check makes a few assumptions // - the global_index passed to the MeshBuilder are a global_index_base-based contiguous index // - the Grid is one of StructuredGrid or UnstructedGrid. This restriction is because a // CubedSphereGrid doesn't present a simple interface for the coordinates at the N'th point. // - the Mesh grid points are the grid nodes, and not the grid cell-centers (e.g., HEALPix or CubedSphere meshes) void validate_grid_vs_mesh(const eckit::mpi::Comm& comm, const atlas::Grid& grid, size_t nb_nodes, const double lon[], const double lat[], size_t lonstride, size_t latstride, const int ghost[], const gidx_t global_index[], gidx_t global_index_base) { // Check assumption that global_index look like a global_index_based contiguous index const size_t nb_owned_nodes = std::count(ghost, ghost + nb_nodes, 0); size_t nb_nodes_global = 0; comm.allReduce(nb_owned_nodes, nb_nodes_global, eckit::mpi::sum()); for (size_t n = 0; n < nb_nodes; ++n) { if (ghost[n] == 0) { // Check global_index is consistent with a global_index_base contiguous index over nodes auto g = global_index[n] - global_index_base; ATLAS_ASSERT(g >= 0); ATLAS_ASSERT(g < nb_nodes_global); } } double lonlat[2]; const auto equal_within_roundoff = [](const double a, const double b) -> bool { return std::abs(a - b) <= 360.0 * 1.0e-16; }; // Check lonlats for each supported grid type if (grid.type() == "unstructured") { const atlas::UnstructuredGrid ugrid(grid); for (size_t n = 0; n < nb_nodes; ++n) { if (ghost[n] == 0) { ugrid.lonlat(global_index[n] - global_index_base, lonlat); if (!equal_within_roundoff(lonlat[0], lon[n]) || !equal_within_roundoff(lonlat[1], lat[n])) { throw_Exception("In MeshBuilder: UnstructuredGrid from config does not match mesh coordinates", Here()); } } } } else if (grid.type() == "structured") { const atlas::StructuredGrid sgrid(grid); for (size_t n = 0; n < nb_nodes; ++n) { if (ghost[n] == 0) { idx_t i, j; sgrid.index2ij(global_index[n] - global_index_base, i, j); sgrid.lonlat(i, j, lonlat); if (!equal_within_roundoff(lonlat[0], lon[n]) || !equal_within_roundoff(lonlat[1], lat[n])) { throw_Exception("In MeshBuilder: StructuredGrid from config does not match mesh coordinates", Here()); } } } } else { for (size_t n = 0; n < nb_nodes; ++n) { if (ghost[n] == 0) { auto point = *(grid.lonlat().begin() + static_cast(global_index[n] - global_index_base)); if (!equal_within_roundoff(point.lon(), lon[n]) || !equal_within_roundoff(point.lat(), lat[n])) { throw_Exception("In MeshBuilder: Grid from config does not match mesh coordinates", Here()); } } } } } } // namespace detail namespace mesh { //---------------------------------------------------------------------------------------------------------------------- Mesh MeshBuilder::operator()(const std::vector& lon, const std::vector& lat, const std::vector& ghost, const std::vector& global_index, const std::vector& remote_index, const idx_t remote_index_base, const std::vector& partition, const std::vector>& triag_nodes_global, const std::vector& triag_global_index, const std::vector>& quad_nodes_global, const std::vector& quad_global_index, const eckit::Configuration& config) const { constexpr gidx_t global_index_base = 1; return operator()( span{global_index.data(),global_index.size()}, span{lon.data(), lon.size()}, span{lat.data(), lat.size()}, span{lon.data(), lon.size()}, span{lat.data(), lat.size()}, span{ghost.data(), ghost.size()}, span{partition.data(), partition.size()}, span{remote_index.data(), remote_index.size()}, remote_index_base, span{triag_global_index.data(), triag_global_index.size()}, mdspan>{reinterpret_cast(triag_nodes_global.data()),triag_nodes_global.size()}, span{quad_global_index.data(), quad_global_index.size()}, mdspan>{reinterpret_cast(quad_nodes_global.data()),quad_nodes_global.size()}, global_index_base, config); } Mesh MeshBuilder::operator()(size_t nb_nodes, const double lon[], const double lat[], const int ghost[], const gidx_t global_index[], const idx_t remote_index[], const idx_t remote_index_base, const int partition[], size_t nb_triags, const gidx_t triag_nodes_global[], const gidx_t triag_global_index[], size_t nb_quads, const gidx_t quad_nodes_global[], const gidx_t quad_global_index[], const eckit::Configuration& config) const { constexpr gidx_t global_index_base = 1; return this->operator()( nb_nodes, global_index, lon, lat, ghost, partition, remote_index, remote_index_base, nb_triags, triag_global_index, triag_nodes_global, nb_quads, quad_global_index, quad_nodes_global, global_index_base, config ); } Mesh MeshBuilder::operator()( size_t nb_nodes, const gidx_t global_index[], const double lon[], const double lat[], const int ghost[], const int partition[], const idx_t remote_index[], const idx_t remote_index_base, size_t nb_triags, const gidx_t triag_nodes_global[], const gidx_t triag_global_index[], size_t nb_quads, const gidx_t quad_nodes_global[], const gidx_t quad_global_index[], gidx_t global_index_base, const eckit::Configuration& config) const { return this->operator()( nb_nodes, global_index, lon, lat, 1, 1, lon, lat, 1, 1, ghost, partition, remote_index, remote_index_base, nb_triags, triag_global_index, triag_nodes_global, nb_quads, quad_global_index, quad_nodes_global, global_index_base, config ); } Mesh MeshBuilder::operator()(size_t nb_nodes, const gidx_t global_index[], const double x[], const double y[], size_t xstride, size_t ystride, const double lon[], const double lat[], size_t lonstride, size_t latstride, const int ghost[], const int partitions[], const idx_t remote_index[], const idx_t remote_index_base, size_t nb_triags, const gidx_t triag_global_index[], const gidx_t triag_nodes_global[], size_t nb_quads, const gidx_t quad_global_index[], const gidx_t quad_nodes_global[], const eckit::Configuration& config) const { constexpr gidx_t global_index_base = 1; return this->operator()( nb_nodes, global_index, x, y, xstride, ystride, lon, lat, lonstride, latstride, ghost, partitions, remote_index, remote_index_base, nb_triags, triag_global_index, triag_nodes_global, nb_quads, quad_global_index, quad_nodes_global, global_index_base, config); } Mesh MeshBuilder::operator()(size_t nb_nodes, const gidx_t global_index[], const double x[], const double y[], size_t xstride, size_t ystride, const double lon[], const double lat[], size_t lonstride, size_t latstride, const int ghost[], const int partitions[], const idx_t remote_index[], const idx_t remote_index_base, size_t nb_triags, const gidx_t triag_global_index[], const gidx_t triag_nodes_global[], size_t nb_quads, const gidx_t quad_global_index[], const gidx_t quad_nodes_global[], gidx_t global_index_base, const eckit::Configuration& config) const { // Get MPI comm from config name or fall back to atlas default comm auto mpi_comm_name = [](const auto& config) { return config.getString("mpi_comm", atlas::mpi::comm().name()); }; Mesh mesh{}; mesh.metadata().set("mpi_comm", mpi_comm_name(config)); auto& comm = mpi::comm(mesh.mpi_comm()); // Setup a grid, if requested via config argument if (config.has("grid")) { atlas::Grid grid; if (config.has("grid.type") && (config.getString("grid.type") == "unstructured") && !config.has("grid.xy")) { // Assemble the unstructured grid by gathering input lons,lats across ranks grid = ::atlas::detail::assemble_unstructured_grid(nb_nodes, lon, lat, ghost, comm); } else { // Build grid directly from config grid = atlas::Grid(config.getSubConfiguration("grid")); const bool validate = config.getBool("validate", false); if (validate) { ::atlas::detail::validate_grid_vs_mesh(comm, grid, nb_nodes, lon, lat, lonstride, latstride, ghost, global_index, global_index_base); } } mesh.setGrid(grid); } // Populate node data mesh.nodes().resize(nb_nodes); auto xy = array::make_view(mesh.nodes().xy()); auto lonlat = array::make_view(mesh.nodes().lonlat()); auto ghostv = array::make_view(mesh.nodes().ghost()); auto gidx = array::make_view(mesh.nodes().global_index()); auto ridx = array::make_indexview(mesh.nodes().remote_index()); auto partition = array::make_view(mesh.nodes().partition()); auto halo = array::make_view(mesh.nodes().halo()); for (size_t i = 0; i < nb_nodes; ++i) { xy(i, size_t(XX)) = x[i*xstride]; xy(i, size_t(YY)) = y[i*ystride]; lonlat(i, size_t(LON)) = lon[i*lonstride]; lonlat(i, size_t(LAT)) = lat[i*latstride]; ghostv(i) = ghost[i]; gidx(i) = global_index[i] - global_index_base + 1; // Make 1-based! ridx(i) = remote_index[i] - remote_index_base; partition(i) = partitions[i]; } halo.assign(0); // Populate cell/element data // First, count how many cells of each type are on this processor // Then optimize away the element type if globally nb_triags or nb_quads is zero size_t sum_nb_triags = 0; comm.allReduce(nb_triags, sum_nb_triags, eckit::mpi::sum()); const bool add_triags = (sum_nb_triags > 0); size_t sum_nb_quads = 0; comm.allReduce(nb_quads, sum_nb_quads, eckit::mpi::sum()); const bool add_quads = (sum_nb_quads > 0); if (add_triags) { mesh.cells().add(mesh::ElementType::create("Triangle"), nb_triags); } if (add_quads) { mesh.cells().add(mesh::ElementType::create("Quadrilateral"), nb_quads); } atlas::mesh::HybridElements::Connectivity& node_connectivity = mesh.cells().node_connectivity(); auto cells_part = array::make_view(mesh.cells().partition()); auto cells_gidx = array::make_view(mesh.cells().global_index()); std::unordered_map global_to_local_index; global_to_local_index.reserve(nb_nodes); for (gidx_t j=0; j global_index, strided_span x, strided_span y, strided_span lon, strided_span lat, span ghost, span partition, span remote_index, const idx_t remote_index_base, span triag_global_index, mdspan> triag_nodes_global, span quad_global_index, mdspan> quad_nodes_global, const gidx_t global_index_base, const eckit::Configuration& config) const { const size_t nb_nodes = global_index.size(); const size_t nb_triags = triag_global_index.size(); const size_t nb_quads = quad_global_index.size(); ATLAS_ASSERT(nb_nodes == lon.size()); ATLAS_ASSERT(nb_nodes == lat.size()); ATLAS_ASSERT(nb_nodes == ghost.size()); ATLAS_ASSERT(nb_nodes == remote_index.size()); ATLAS_ASSERT(nb_nodes == partition.size()); ATLAS_ASSERT(nb_triags == triag_nodes_global.extent(0)); ATLAS_ASSERT(nb_quads == quad_nodes_global.extent(0)); return operator()( nb_nodes, global_index.data_handle(), x.data_handle(), y.data_handle(), x.stride(0), y.stride(0), lon.data_handle(), lat.data_handle(), lon.stride(0), lat.stride(0), ghost.data_handle(), partition.data_handle(), remote_index.data_handle(), remote_index_base, nb_triags, triag_global_index.data_handle(), triag_nodes_global.data_handle(), nb_quads, quad_global_index.data_handle(), quad_nodes_global.data_handle(), global_index_base, config); } //---------------------------------------------------------------------------------------------------------------------- Mesh TriangularMeshBuilder::operator()(size_t nb_nodes, const gidx_t nodes_global_index[], const double x[], const double y[], const double lon[], const double lat[], size_t nb_triags, const gidx_t triangle_global_index[], const gidx_t triangle_nodes_global_index[]) const { constexpr gidx_t global_index_base = 1; constexpr size_t stride_1 = 1; return this->operator()( nb_nodes, nodes_global_index, x, y, stride_1, stride_1, lon, lat, stride_1, stride_1, nb_triags, triangle_global_index, triangle_nodes_global_index, global_index_base ); } //---------------------------------------------------------------------------------------------------------------------- Mesh TriangularMeshBuilder::operator()(size_t nb_nodes, const gidx_t nodes_global_index[], const double x[], const double y[], size_t xstride, size_t ystride, const double lon[], const double lat[], size_t lonstride, size_t latstride, size_t nb_triags, const gidx_t triangle_global_index[], const gidx_t triangle_nodes_global_index[], gidx_t global_index_base) const { std::vector ghost(nb_nodes,0); std::vector partition(nb_nodes,0); std::vector remote_index(nb_nodes); idx_t remote_index_base = 0; std::iota(remote_index.begin(), remote_index.end(), remote_index_base); size_t nb_quads = 0; gidx_t quad_global_index[] = {}; gidx_t quad_nodes_global_index[] = {}; return meshbuilder_( nb_nodes, nodes_global_index, x, y, xstride, ystride, lon, lat, lonstride, latstride, ghost.data(), partition.data(), remote_index.data(), remote_index_base, nb_triags, triangle_global_index, triangle_nodes_global_index, nb_quads, quad_global_index, quad_nodes_global_index, global_index_base); } //---------------------------------------------------------------------------------------------------------------------- } // namespace mesh } // namespace atlas atlas-0.46.0/src/atlas/mesh/Mesh.h0000664000175000017500000001200315160212070016753 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include "atlas/library/config.h" #include "atlas/mesh/detail/MeshImpl.h" #include "atlas/util/ObjectHandle.h" //---------------------------------------------------------------------------------------------------------------------- // Forward declarations namespace atlas { class Projection; class Grid; } // namespace atlas namespace atlas { namespace grid { class Partitioner; class Distribution; } } // namespace atlas namespace atlas { namespace util { class Metadata; class PartitionPolygons; } // namespace util } // namespace atlas namespace atlas { namespace mesh { class MeshBuilder; class Nodes; class HybridElements; typedef HybridElements Edges; typedef HybridElements Cells; } // namespace mesh } // namespace atlas namespace atlas { namespace meshgenerator { class MeshGeneratorImpl; } } // namespace atlas //---------------------------------------------------------------------------------------------------------------------- namespace atlas { //---------------------------------------------------------------------------------------------------------------------- class Mesh : DOXYGEN_HIDE(public util::ObjectHandle) { public: using Nodes = mesh::Nodes; using Cells = mesh::Cells; using Edges = mesh::Edges; using HybridElements = mesh::HybridElements; using PartitionGraph = mesh::detail::PartitionGraph; using Polygon = mesh::PartitionPolygon; using Polygons = util::PartitionPolygons; public: using Handle::Handle; Mesh(); operator bool() const; /// @brief Generate a mesh from a Grid with recommended mesh generator and partitioner strategy Mesh(const Grid&, const eckit::Configuration& = util::NoConfig()); Mesh(const Grid&, const grid::Partitioner&, const eckit::Configuration& = util::NoConfig()); Mesh(const Grid&, const grid::Distribution&, const eckit::Configuration& = util::NoConfig()); /// @brief Construct a mesh from a Stream (serialization) explicit Mesh(eckit::Stream&); /// @brief Serialization to Stream void encode(eckit::Stream& s) const { return get()->encode(s); } const util::Metadata& metadata() const { return get()->metadata(); } util::Metadata& metadata() { return get()->metadata(); } const Nodes& nodes() const { return get()->nodes(); } Nodes& nodes() { return get()->nodes(); } const Cells& cells() const { return get()->cells(); } Cells& cells() { return get()->cells(); } const Edges& edges() const { return get()->edges(); } Edges& edges() { return get()->edges(); } const HybridElements& facets() const { return get()->facets(); } HybridElements& facets() { return get()->facets(); } const HybridElements& ridges() const { return get()->ridges(); } HybridElements& ridges() { return get()->ridges(); } const HybridElements& peaks() const { return get()->peaks(); } HybridElements& peaks() { return get()->peaks(); } bool generated() const { return get()->generated(); } /// @brief Return the memory footprint of the mesh size_t footprint() const { return get()->footprint(); } idx_t part() const { return get()->part(); } idx_t nb_parts() const { return get()->nb_parts(); } [[deprecated("Use 'atlas::mesh::Mesh::nb_parts() instead")]] // added in v0.35.0 idx_t nb_partitions() const { return nb_parts(); } std::string mpi_comm() const { return get()->mpi_comm(); } void updateDevice() const { get()->updateDevice(); } void updateHost() const { get()->updateHost(); } void syncHostDevice() const { get()->syncHostDevice(); } const Projection& projection() const { return get()->projection(); } const PartitionGraph& partitionGraph() const { return get()->partitionGraph(); } PartitionGraph::Neighbours nearestNeighbourPartitions() const { return get()->nearestNeighbourPartitions(); } const Polygon& polygon(idx_t halo = 0) const { return get()->polygon(halo); } const Polygons& polygons() const { return get()->polygons(); } const Grid grid() const { return get()->grid(); } private: // methods void print(std::ostream& out) const { get()->print(out); } friend std::ostream& operator<<(std::ostream& s, const Mesh& p) { p.print(s); return s; } friend class mesh::MeshBuilder; friend class meshgenerator::MeshGeneratorImpl; void setProjection(const Projection& p) { get()->setProjection(p); } void setGrid(const Grid& p) { get()->setGrid(p); } }; //---------------------------------------------------------------------------------------------------------------------- } // namespace atlas atlas-0.46.0/src/atlas/mesh/Halo.h0000664000175000017500000000161015160212070016744 0ustar alastairalastair/* * (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 namespace atlas { class Mesh; namespace mesh { namespace detail { class MeshImpl; } } // namespace mesh } // namespace atlas namespace atlas { namespace mesh { // ------------------------------------------------------------------- class Halo { public: Halo() {} Halo(const Mesh& mesh); Halo(const detail::MeshImpl& mesh); Halo(const int size): size_(size) {} int size() const; private: int size_{-1}; }; } // namespace mesh } // namespace atlas atlas-0.46.0/src/atlas/mesh/IsGhostNode.h0000664000175000017500000000202115160212070020244 0ustar alastairalastair/* * (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/array/ArrayView.h" #include "atlas/array/MakeView.h" #include "atlas/field/Field.h" #include "atlas/mesh/Nodes.h" #include "atlas/parallel/mpi/mpi.h" namespace atlas { namespace mesh { class IsGhostNode { public: IsGhostNode(const mesh::Nodes& nodes): flags_(array::make_view(nodes.flags())), ghost_(array::make_view(nodes.ghost())) {} bool operator()(idx_t idx) const { return Nodes::Topology::check(flags_(idx), Nodes::Topology::GHOST); } private: array::ArrayView flags_; array::ArrayView ghost_; }; } // namespace mesh } // namespace atlas atlas-0.46.0/src/atlas/mesh/PartitionPolygon.h0000664000175000017500000000363315160212070021411 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ /// @file PartitionPolygon.h /// @author Pedro Maciel /// @author Willem Deconinck /// @date September 2017 #pragma once #include #include "atlas/util/Object.h" #include "atlas/library/config.h" #include "atlas/projection/Projection.h" #include "atlas/util/Config.h" #include "atlas/util/Polygon.h" namespace atlas { namespace mesh { namespace detail { class MeshImpl; } } // namespace mesh } // namespace atlas namespace atlas { namespace mesh { /** * @brief Polygon class that holds the boundary of a mesh partition */ class PartitionPolygon : public util::PartitionPolygon { public: // methods //-- Constructors /// @brief Construct "size" polygon PartitionPolygon(const detail::MeshImpl& mesh, idx_t halo); //-- Accessors idx_t halo() const override { return halo_; } /// @brief Return the memory footprint of the Polygon size_t footprint() const override; void outputPythonScript(const eckit::PathName&, const eckit::Configuration& = util::NoConfig()) const override; PointsXY xy() const override; PointsXY lonlat() const override; void allGather(util::PartitionPolygons&) const override; private: void print(std::ostream&) const; friend std::ostream& operator<<(std::ostream& s, const PartitionPolygon& p) { p.print(s); return s; } private: const detail::MeshImpl& mesh_; idx_t halo_; }; //------------------------------------------------------------------------------------------------------ } // namespace mesh } // namespace atlas atlas-0.46.0/src/atlas/mesh/PartitionPolygon.cc0000664000175000017500000003066615160212070021555 0ustar alastairalastair/* * (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/exception/Exceptions.h" #include "atlas/array/MakeView.h" #include "atlas/field/Field.h" #include "atlas/mesh.h" #include "atlas/mesh/PartitionPolygon.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/runtime/Trace.h" #include "atlas/util/CoordinateEnums.h" namespace atlas { namespace mesh { namespace { util::Polygon::edge_set_t compute_edges(const detail::MeshImpl& mesh, idx_t halo) { ATLAS_TRACE("PartitionPolygon [halo=" + std::to_string(halo) + "]"); // extract partition boundary edges by always attempting first to` // remove a reversed edge from a neighbouring element, if any util::Polygon::edge_set_t edges; for (idx_t t = 0; t < mesh.cells().nb_types(); ++t) { const Elements& elements = mesh.cells().elements(t); const BlockConnectivity& conn = elements.node_connectivity(); auto field_flags = elements.view(elements.flags()); auto field_halo = elements.view(elements.halo()); constexpr bool include_patch = false; auto include = [&](idx_t e) { using util::Topology; auto topology = Topology::view(field_flags(e)); if (field_halo(e) > halo) { return false; } if (not include_patch) { if (topology.check(Topology::PATCH)) { return false; } } return true; }; const idx_t nb_nodes = elements.nb_nodes(); for (idx_t j = 0; j < elements.size(); ++j) { if (include(j)) { for (idx_t k = 0; k < nb_nodes; ++k) { util::Polygon::edge_t edge(conn(j, k), conn(j, (k + 1) % nb_nodes)); if (!edges.erase(edge.reverse())) { edges.insert(edge); } } } } } return edges; } } // namespace PartitionPolygon::PartitionPolygon(const detail::MeshImpl& mesh, idx_t halo): util::PartitionPolygon(compute_edges(mesh, halo)), mesh_(mesh), halo_(halo) {} size_t PartitionPolygon::footprint() const { size_t size = sizeof(*this); size += capacity() * sizeof(idx_t); return size; } void PartitionPolygon::outputPythonScript(const eckit::PathName& filepath, const eckit::Configuration& config) const { const auto& comm = mpi::comm(mesh_.mpi_comm()); int mpi_rank = int(comm.rank()); int mpi_size = int(comm.size()); std::string coordinates = config.getString("coordinates", "xy"); if (coordinates == "ij") { coordinates = "xy"; } auto points = coordinates == "xy" ? this->xy() : // coordinates == "ij" ? this->ij() : /* no member named `ij` in PartitionPolygon */ this->lonlat(); ATLAS_ASSERT(points.size() == size()); const auto nodes_xy = array::make_view(mesh_.nodes().field(coordinates)); const auto nodes_g = array::make_view(mesh_.nodes().global_index()); double xmin = std::numeric_limits::max(); double xmax = -std::numeric_limits::max(); for (const auto& p : points) { xmin = std::min(xmin, p[XX]); xmax = std::max(xmax, p[XX]); } comm.allReduceInPlace(xmin, eckit::mpi::min()); comm.allReduceInPlace(xmax, eckit::mpi::max()); idx_t count = mesh_.nodes().size(); idx_t count_all = count; comm.allReduceInPlace(count_all, eckit::mpi::sum()); bool plot_nodes = config.getBool("nodes", false); 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 (!f.is_open()) { throw eckit::CantOpenFile(filepath); } //clang-format off if (mpi_rank == 0) { f << "\n" "# Configuration option to plot nodes" "\n" "plot_nodes = " + std::string(plot_nodes ? "True" : "False") << "\n" "plot_node_numbers = False" "\n" "\n" "import matplotlib.pyplot as plt" "\n" "from matplotlib.path import Path" "\n" "import matplotlib.patches as patches" "\n" "\n" "from itertools import cycle" "\n" "import matplotlib.cm as cm" "\n" "import numpy as np" "\n" "colours = cycle([cm.Paired(i) for i in np.linspace(0,1,12,endpoint=True)])" "\n" "\n" "fig = plt.figure()" "\n" "ax = fig.add_subplot(111,aspect='equal')" "\n"; } f << "\n" "verts_" << r << " = ["; for (const auto& p : points) { f << "\n (" << p[XX] << ", " << p[YY] << "), "; } f << "\n]" "\n" "\n" "codes_" << r << " = [Path.MOVETO]" "\n" "codes_" << r << ".extend([Path.LINETO] * " << (size() - 2) << ")" "\n" "codes_" << r << ".extend([Path.CLOSEPOLY])" "\n" "\n" "count_" << r << " = " << count << "\n" "count_all_" << r << " = " << count_all << "\n"; if (plot_nodes) { f << "\n" "g_" << r << " = ["; for (idx_t i = 0; i < count; ++i) { f << nodes_g(i) << ", "; } f << "]" "\n" "x_" << r << " = ["; for (idx_t i = 0; i < count; ++i) { f << nodes_xy(i, XX) << ", "; } f << "]" "\n" "y_" << r << " = ["; for (idx_t i = 0; i < count; ++i) { f << nodes_xy(i, YY) << ", "; } f << "]"; } f << "\n" "\n" "c = next(colours)" "\n" "ax.add_patch(patches.PathPatch(Path(verts_" << r << ", codes_" << r << "), color=c, alpha=0.3, lw=1))"; if (plot_nodes) { f << "\n" "if plot_nodes:" "\n" " ax.scatter(x_" << r << ", y_" << r << ", color=c, marker='o')" "\n" " if plot_node_numbers:" "\n" " for (g,x,y) in zip(g_" << r << ", x_" << r << ", y_" << r << "):" "\n" " ax.text(x,y, str(g), color=c, fontsize=12)"; } f << "\n"; // clang-format on if (mpi_rank == mpi_size - 1) { auto xticks = [&]() -> std::string { std::vector xticks; xticks.push_back(std::floor(xmin - int(xmin) % 45)); while (xticks.back() < int(std::round(xmax))) { xticks.push_back(xticks.back() + 45); } std::stringstream xticks_ss; for (int i = 0; i < xticks.size(); ++i) { xticks_ss << (i == 0 ? "[" : ",") << xticks[i]; } xticks_ss << "]"; return xticks_ss.str(); }; // clang-format off if (mesh_.projection().units() == "degrees") { f << "\n" "ax.set_xlim( " << xmin << "-5, " << xmax << "+5)" "\n" "ax.set_ylim(-90-5, 90+5)" "\n" "ax.set_xticks(" << xticks() << ")" "\n" "ax.set_yticks([-90,-45,0,45,90])"; } else { f << "\n" "ax.autoscale()"; } f << "\n" "plt.grid()" "\n" "plt.show()"; // clang-format on } } comm.barrier(); } } void PartitionPolygon::allGather(util::PartitionPolygons& polygons) const { ATLAS_TRACE(); const auto& comm = mpi::comm(mesh_.mpi_comm()); const int mpi_size = int(comm.size()); polygons.clear(); polygons.reserve(mpi_size); auto& poly = *this; std::vector mypolygon; mypolygon.reserve(poly.size() * 2); auto points_xy = poly.xy(); ATLAS_ASSERT(points_xy.front() == points_xy.back()); for (auto& p : points_xy) { mypolygon.push_back(p[XX]); mypolygon.push_back(p[YY]); } ATLAS_ASSERT(mypolygon.size() >= 4); eckit::mpi::Buffer recv_polygons(mpi_size); comm.allGatherv(mypolygon.begin(), mypolygon.end(), recv_polygons); for (idx_t p = 0; p < mpi_size; ++p) { PointsXY recv_points; recv_points.reserve(recv_polygons.counts[p]); for (idx_t j = 0; j < recv_polygons.counts[p] / 2; ++j) { PointXY pxy(*(recv_polygons.begin() + recv_polygons.displs[p] + 2 * j + XX), *(recv_polygons.begin() + recv_polygons.displs[p] + 2 * j + YY)); recv_points.push_back(pxy); } polygons.emplace_back(new util::ExplicitPartitionPolygon(std::move(recv_points))); } } void PartitionPolygon::print(std::ostream& out) const { out << "polygon:{" << "halo:" << halo_ << ",size:" << size() << ",nodes:" << static_cast(*this) << "}"; } namespace { PartitionPolygon::PointsXY points(const Mesh::Implementation& mesh_, const Field& coords, const std::vector& polygon) { PartitionPolygon::PointsXY points_xy; points_xy.reserve(polygon.size()); auto xy_view = array::make_view(coords); auto flags = array::make_view(mesh_.nodes().flags()); bool domain_includes_north_pole = false; bool domain_includes_south_pole = false; if (mesh_.grid()) { if (mesh_.grid().domain()) { domain_includes_north_pole = mesh_.grid().domain().containsNorthPole(); domain_includes_south_pole = mesh_.grid().domain().containsSouthPole(); } } auto bc_north = [&flags, &domain_includes_north_pole](idx_t n) { if (domain_includes_north_pole) { using Topology = atlas::mesh::Nodes::Topology; return Topology::check(flags(n), Topology::BC | Topology::NORTH); } return false; }; auto bc_south = [&flags, &domain_includes_south_pole](idx_t n) { if (domain_includes_south_pole) { using Topology = atlas::mesh::Nodes::Topology; return Topology::check(flags(n), Topology::BC | Topology::SOUTH); } return false; }; if (coords.name() == "xy") { for (idx_t i : polygon) { double y = bc_north(i) ? 90. : bc_south(i) ? -90. : xy_view(i, idx_t(YY)); points_xy.emplace_back(xy_view(i, idx_t(XX)), y); } } else { for (idx_t i : polygon) { points_xy.emplace_back(xy_view(i, idx_t(XX)), xy_view(i, idx_t(YY))); } } return points_xy; } } // namespace PartitionPolygon::PointsXY PartitionPolygon::xy() const { return points(mesh_, mesh_.nodes().xy(), *this); } PartitionPolygon::PointsLonLat PartitionPolygon::lonlat() const { return points(mesh_, mesh_.nodes().lonlat(), *this); } } // namespace mesh } // namespace atlas atlas-0.46.0/src/atlas/mesh/Elements.h0000664000175000017500000002074415160212070017646 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ /// @file Elements.h /// @author Willem Deconinck /// @date October 2015 /// /// This file describes the Elements class for a Mesh. #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" namespace atlas { namespace mesh { class ElementType; } } // namespace atlas namespace atlas { namespace mesh { // ------------------------------------------------------------------------------------------------------ /// @brief Describe elements of a single type class Elements : public util::Object { public: //-- Constructors /// @brief Constructor that treats elements as sub-elements in HybridElements Elements(HybridElements& elements, idx_t type_idx); /// @brief Constructor that internally creates a HybridElements that owns the /// data Elements(ElementType*, idx_t nb_elements, const std::vector& node_connectivity); /// @brief Constructor that internally creates a HybridElements that owns the /// data Elements(ElementType*, idx_t nb_elements, const idx_t node_connectivity[], bool fortran_array = false); /// @brief Destructor virtual ~Elements(); //-- Accessors /// @brief Number of elements idx_t size() const; /// @brief Name of this element type const std::string& name() const; /// @brief Number of nodes for each element type idx_t nb_nodes() const; /// @brief Number of edges for each element type idx_t nb_edges() const; /// @brief Element to Node connectivity table const BlockConnectivity& node_connectivity() const; BlockConnectivity& node_connectivity(); /// @brief Element to Edge connectivity table const BlockConnectivity& edge_connectivity() const; BlockConnectivity& edge_connectivity(); /// @brief Element to Cell connectivity table const BlockConnectivity& cell_connectivity() const; BlockConnectivity& cell_connectivity(); /// @brief Element type of these Elements const ElementType& element_type() const; /// @brief Access hybrid_elements /// HybridElements can contain more Elements, and holds the data. // const HybridElements& hybrid_elements() const; /// @brief Index of Elements in hybrid_elements // idx_t type_idx() const; /// @brief Begin of elements in hybrid_elements idx_t begin() const; /// @brief End of elements in hybrid_elements idx_t end() const; const Field& field(const std::string& name) const { return hybrid_elements_->field(name); } Field& field(const std::string& name) { return hybrid_elements_->field(name); } bool has_field(const std::string& name) const { return hybrid_elements_->has_field(name); } const Field& field(idx_t idx) const { return hybrid_elements_->field(idx); } Field& field(idx_t idx) { return hybrid_elements_->field(idx); } idx_t nb_fields() const { return hybrid_elements_->nb_fields(); } const Field& global_index() const { return hybrid_elements_->global_index(); } Field& global_index() { return hybrid_elements_->global_index(); } const Field& remote_index() const { return hybrid_elements_->remote_index(); } Field& remote_index() { return hybrid_elements_->remote_index(); } const Field& partition() const { return hybrid_elements_->partition(); } Field& partition() { return hybrid_elements_->partition(); } const Field& halo() const { return hybrid_elements_->halo(); } Field& halo() { return hybrid_elements_->halo(); } const Field& flags() const { return hybrid_elements_->flags(); } Field& flags() { return hybrid_elements_->flags(); } template array::LocalView view(const Field&) const; 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: friend class HybridElements; void rebuild(); private: bool owns_; HybridElements* hybrid_elements_; idx_t size_; idx_t begin_; idx_t end_; idx_t type_idx_; idx_t nb_nodes_; idx_t nb_edges_; }; // ------------------------------------------------------------------------------------------------------ inline idx_t Elements::size() const { return size_; } inline idx_t Elements::nb_nodes() const { return nb_nodes_; } inline idx_t Elements::nb_edges() const { return nb_edges_; } // inline idx_t Elements::type_idx() const //{ // return type_idx_; //} // inline const HybridElements& Elements::hybrid_elements() const //{ // return *hybrid_elements_; //} inline const BlockConnectivity& Elements::node_connectivity() const { if (hybrid_elements_->node_connectivity().blocks()) { return hybrid_elements_->node_connectivity().block(type_idx_); } else { static BlockConnectivity dummy; return dummy; } } inline BlockConnectivity& Elements::node_connectivity() { if (hybrid_elements_->node_connectivity().blocks()) { return hybrid_elements_->node_connectivity().block(type_idx_); } else { static BlockConnectivity dummy; return dummy; } } inline const BlockConnectivity& Elements::edge_connectivity() const { if (hybrid_elements_->edge_connectivity().blocks()) { return hybrid_elements_->edge_connectivity().block(type_idx_); } else { static BlockConnectivity dummy; return dummy; } } inline BlockConnectivity& Elements::edge_connectivity() { if (hybrid_elements_->edge_connectivity().blocks()) { return hybrid_elements_->edge_connectivity().block(type_idx_); } else { static BlockConnectivity dummy; return dummy; } } inline const BlockConnectivity& Elements::cell_connectivity() const { if (hybrid_elements_->cell_connectivity().blocks()) { return hybrid_elements_->cell_connectivity().block(type_idx_); } else { static BlockConnectivity dummy; return dummy; } } inline BlockConnectivity& Elements::cell_connectivity() { if (hybrid_elements_->cell_connectivity().blocks()) { return hybrid_elements_->cell_connectivity().block(type_idx_); } else { static BlockConnectivity dummy; return dummy; } } inline const ElementType& Elements::element_type() const { return hybrid_elements_->element_type(type_idx_); } inline idx_t Elements::begin() const { return begin_; } inline idx_t Elements::end() const { return end_; } // ------------------------------------------------------------------------------------------------------ extern "C" { void atlas__mesh__Elements__delete(Elements* This); idx_t atlas__mesh__Elements__size(const Elements* This); idx_t atlas__mesh__Elements__begin(const Elements* This); idx_t atlas__mesh__Elements__end(const Elements* This); BlockConnectivity* atlas__mesh__Elements__node_connectivity(Elements* This); BlockConnectivity* atlas__mesh__Elements__edge_connectivity(Elements* This); BlockConnectivity* atlas__mesh__Elements__cell_connectivity(Elements* This); int atlas__mesh__Elements__has_field(const Elements* This, char* name); int atlas__mesh__Elements__nb_fields(const Elements* This); field::FieldImpl* atlas__mesh__Elements__field_by_idx(Elements* This, idx_t idx); field::FieldImpl* atlas__mesh__Elements__field_by_name(Elements* This, char* name); field::FieldImpl* atlas__mesh__Elements__global_index(Elements* This); field::FieldImpl* atlas__mesh__Elements__remote_index(Elements* This); field::FieldImpl* atlas__mesh__Elements__partition(Elements* This); field::FieldImpl* atlas__mesh__Elements__halo(Elements* This); const ElementType* atlas__mesh__Elements__element_type(const Elements* This); void atlas__mesh__Elements__add(Elements* This, idx_t nb_elements); } //------------------------------------------------------------------------------------------------------ } // namespace mesh } // namespace atlas atlas-0.46.0/src/atlas/mesh/ElementType.cc0000664000175000017500000000434015160212070020455 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "atlas/mesh/ElementType.h" #include "elementtypes/Line.h" #include "elementtypes/Triangle.h" #include "elementtypes/Quadrilateral.h" #include "elementtypes/Pentagon.h" #include "atlas/runtime/Exception.h" #include "atlas/util/CoordinateEnums.h" namespace atlas { namespace mesh { //------------------------------------------------------------------------------ ElementType* ElementType::create( const std::string& name ) { // currently naive implementation. This has to be replaced // with self-registration and factory mechanism if( name == "Triangle" ) { return new elementtypes::Triangle(); } else if( name == "Quadrilateral") { return new elementtypes::Quadrilateral(); } else if( name == "Line" ) { return new elementtypes::Line(); } else if( name == "Pentagon") { return new elementtypes::Pentagon(); } else { throw_NotImplemented("Element type ["+name+"] does not exist", Here()); } return nullptr; } ElementType::ElementType() = default; ElementType::~ElementType() = default; //----------------------------------------------------------------------------- extern "C" { void atlas__mesh__ElementType__delete(ElementType* This) { delete This; } ElementType* atlas__mesh__ElementType__create(const char* name) { return ElementType::create( std::string(name) ); } const char* atlas__mesh__ElementType__name(const ElementType* This) { ATLAS_ASSERT(This); return This->name().c_str(); } idx_t atlas__mesh__ElementType__nb_nodes(const ElementType* This) { ATLAS_ASSERT(This); return This->nb_nodes(); } idx_t atlas__mesh__ElementType__nb_edges(const ElementType* This) { ATLAS_ASSERT(This); return This->nb_edges(); } int atlas__mesh__ElementType__parametric(const ElementType* This) { ATLAS_ASSERT(This); return This->parametric(); } } } // namespace mesh } // namespace atlas atlas-0.46.0/src/atlas/mesh/ElementType.h0000664000175000017500000000413215160212070020316 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ /// @file ElementType.h /// @author Willem Deconinck /// @date October 2015 #pragma once #include #include "atlas/library/config.h" #include "atlas/util/Object.h" namespace atlas { namespace mesh { /** * \brief ElementType class (abstract) that provides access to geometric * information of an element */ class ElementType : public util::Object { public: // methods static ElementType* create(const std::string&); //-- Constructors ElementType(); ~ElementType() = 0; virtual const std::string& name() const = 0; virtual size_t dimensionality() const = 0; virtual size_t nb_vertices() const = 0; virtual size_t nb_edges() const = 0; virtual size_t nb_faces() const = 0; virtual size_t nb_nodes() const = 0; virtual bool parametric() const = 0; virtual bool simplex() const = 0; }; extern "C" { ElementType* atlas__mesh__ElementType__create(const char* name); void atlas__mesh__ElementType__delete(ElementType* This); idx_t atlas__mesh__ElementType__nb_nodes(const ElementType* This); idx_t atlas__mesh__ElementType__nb_edges(const ElementType* This); int atlas__mesh__ElementType__parametric(const ElementType* This); const char* atlas__mesh__ElementType__name(const ElementType* This); } //------------------------------------------------------------------------------------------------------ } // namespace mesh } // namespace atlas // Following includes are added for backwards compatibility with temporary elementtypes. // They contain deprecation warnings. // When deprecation is final, these lines can be removed. #include "atlas/mesh/elementtypes/Quadrilateral.h" #include "atlas/mesh/elementtypes/Triangle.h" #include "atlas/mesh/elementtypes/Line.h" atlas-0.46.0/src/atlas/mesh/elementtypes/0000775000175000017500000000000015160212070020430 5ustar alastairalastairatlas-0.46.0/src/atlas/mesh/elementtypes/Pentagon.h0000664000175000017500000000310615160212070022354 0ustar alastairalastair/* * (C) Copyright 1996-2016 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation nor * does it submit to any jurisdiction. */ /// @author Willem Deconinck /// @date October 2015 #pragma once #include "atlas/mesh/elementtypes/Classification.h" namespace atlas { namespace mesh { namespace elementtypes { //------------------------------------------------------------------------------------------------------ class Pentagon : public Face { public: enum { EDGES = 5 }; enum { VERTICES = 5 }; enum { FACETS = EDGES }; enum { RIDGES = VERTICES }; virtual ~Pentagon() {} virtual bool parametric() const { return false; } virtual bool simplex() const { return false; } virtual size_t nb_vertices() const { return VERTICES; } virtual size_t nb_edges() const { return EDGES; } virtual size_t nb_nodes() const { return VERTICES; } virtual size_t nb_facets() const { return FACETS; } virtual size_t nb_ridges() const { return RIDGES; } virtual const std::string& name() const { static std::string s("Pentagon"); return s; } }; //------------------------------------------------------------------------------------------------------ } // namespace elementtypes } // namespace mesh } // namespace atlas atlas-0.46.0/src/atlas/mesh/elementtypes/Triangle.h0000664000175000017500000000323515160212070022351 0ustar alastairalastair/* * (C) Copyright 1996-2016 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation nor * does it submit to any jurisdiction. */ /// @author Willem Deconinck /// @date October 2015 #pragma once #include "atlas/mesh/elementtypes/Classification.h" namespace atlas { namespace mesh { namespace elementtypes { //------------------------------------------------------------------------------------------------------ class Triangle : public Face { public: enum { EDGES=3 }; enum { VERTICES=3 }; enum { FACETS=EDGES }; enum { RIDGES=VERTICES }; virtual ~Triangle() {} virtual bool parametric() const { return true; } virtual bool simplex() const { return true; } virtual size_t nb_vertices() const { return VERTICES; } virtual size_t nb_edges() const { return EDGES; } virtual size_t nb_nodes() const { return VERTICES; } virtual size_t nb_facets() const { return FACETS; } virtual size_t nb_ridges() const { return RIDGES; } virtual const std::string& name() const { static std::string s("Triangle"); return s; } }; //------------------------------------------------------------------------------------------------------ } // namespace elementtypes namespace temporary { class Triangle : public elementtypes::Triangle { public: [[deprecated("Use 'atlas::mesh::ElementType::create(\"Triangle\")' instead")]] Triangle() {} }; } } // namespace mesh } // namespace atlas atlas-0.46.0/src/atlas/mesh/elementtypes/Classification.h0000664000175000017500000000373515160212070023544 0ustar alastairalastair/* * (C) Copyright 1996-2016 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation nor * does it submit to any jurisdiction. */ /// @author Willem Deconinck /// @date October 2015 #pragma once #include "atlas/mesh/ElementType.h" namespace atlas { namespace mesh { namespace elementtypes { //------------------------------------------------------------------- class Volume : public ElementType { public: enum { DIMENSIONALITY=3 }; virtual size_t dimensionality() const { return DIMENSIONALITY; } }; //------------------------------------------------------------------- class Face : public ElementType { public: enum { DIMENSIONALITY=2 }; enum { FACES=1 }; virtual size_t dimensionality() const { return DIMENSIONALITY; } virtual size_t nb_faces() const { return FACES; } }; //------------------------------------------------------------------- class Edge: public ElementType { public: enum { DIMENSIONALITY=1 }; enum { FACES=0 }; enum { EDGES=1 }; virtual size_t dimensionality() const { return DIMENSIONALITY; } virtual size_t nb_faces() const { return FACES; } virtual size_t nb_edges() const { return EDGES; } }; //------------------------------------------------------------------- class Vertex: public ElementType { public: enum { DIMENSIONALITY=0 }; enum { FACES=0 }; enum { EDGES=0 }; enum { VERTICES=1 }; virtual size_t dimensionality() const { return DIMENSIONALITY; } virtual size_t nb_faces() const { return FACES; } virtual size_t nb_edges() const { return EDGES; } virtual size_t nb_vertices() const { return VERTICES; } }; //------------------------------------------------------------------- } // namespace elementtypes } // namespace mesh } // namespace atlas atlas-0.46.0/src/atlas/mesh/elementtypes/Quadrilateral.h0000664000175000017500000000330215160212070023371 0ustar alastairalastair/* * (C) Copyright 1996-2016 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation nor * does it submit to any jurisdiction. */ /// @author Willem Deconinck /// @date October 2015 #pragma once #include "atlas/mesh/elementtypes/Classification.h" namespace atlas { namespace mesh { namespace elementtypes { //------------------------------------------------------------------------------------------------------ class Quadrilateral : public Face { public: enum { EDGES=4 }; enum { VERTICES=4 }; enum { FACETS=EDGES }; enum { RIDGES=VERTICES }; virtual ~Quadrilateral() {} virtual bool parametric() const { return true; } virtual bool simplex() const { return false; } virtual size_t nb_vertices() const { return VERTICES; } virtual size_t nb_edges() const { return EDGES; } virtual size_t nb_nodes() const { return VERTICES; } virtual size_t nb_facets() const { return FACETS; } virtual size_t nb_ridges() const { return RIDGES; } virtual const std::string& name() const { static std::string s("Quadrilateral"); return s; } }; //------------------------------------------------------------------------------------------------------ } // namespace elementtypes namespace temporary { class Quadrilateral : public elementtypes::Quadrilateral { public: [[deprecated("Use 'atlas::mesh::ElementType::create(\"Quadrilateral\")' instead")]] Quadrilateral() {} }; } } // namespace mesh } // namespace atlas atlas-0.46.0/src/atlas/mesh/elementtypes/Line.h0000664000175000017500000000307215160212070021472 0ustar alastairalastair/* * (C) Copyright 1996-2016 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation nor * does it submit to any jurisdiction. */ /// @author Willem Deconinck /// @date October 2015 #pragma once #include "atlas/mesh/elementtypes/Classification.h" namespace atlas { namespace mesh { namespace elementtypes { //------------------------------------------------------------------------------------------------------ class Line : public Edge { public: enum { VERTICES = 2 }; enum { FACETS = VERTICES }; enum { RIDGES = 0 }; virtual ~Line() {} virtual bool parametric() const { return true; } virtual bool simplex() const { return false; } virtual size_t nb_vertices() const { return VERTICES; } virtual size_t nb_edges() const { return EDGES; } virtual size_t nb_nodes() const { return VERTICES; } virtual size_t nb_facets() const { return FACETS; } virtual const std::string& name() const { static std::string s("Line"); return s; } }; //------------------------------------------------------------------------------------------------------ } // namespace elementtypes namespace temporary { class Line : public elementtypes::Line { public: [[deprecated("Use 'atlas::mesh::ElementType::create(\"Line\")' instead")]] Line() {} }; } } // namespace mesh } // namespace atlas atlas-0.46.0/src/atlas/mesh/Halo.cc0000664000175000017500000000154215160212070017106 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "atlas/mesh/Halo.h" #include "atlas/mesh/Mesh.h" #include "atlas/runtime/Exception.h" #include "atlas/util/Metadata.h" namespace atlas { namespace mesh { Halo::Halo(const Mesh& mesh) { size_ = 0; mesh.metadata().get("halo", size_); } Halo::Halo(const detail::MeshImpl& mesh) { size_ = 0; mesh.metadata().get("halo", size_); } int Halo::size() const { ATLAS_ASSERT(size_ >= 0); return size_; } } // namespace mesh } // namespace atlas atlas-0.46.0/src/atlas/mesh/Elements.cc0000664000175000017500000002216315160212070020001 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "atlas/mesh/Elements.h" #include "atlas/array/MakeView.h" #include "atlas/field/Field.h" #include "atlas/library/config.h" #include "atlas/mesh/ElementType.h" #include "atlas/runtime/Exception.h" namespace atlas { namespace mesh { //----------------------------------------------------------------------------- void Elements::rebuild() { size_ = hybrid_elements_->elements_size_[type_idx_]; nb_nodes_ = hybrid_elements_->element_type(type_idx_).nb_nodes(); nb_edges_ = hybrid_elements_->element_type(type_idx_).nb_edges(); begin_ = hybrid_elements_->elements_begin_[type_idx_]; end_ = hybrid_elements_->elements_begin_[type_idx_ + 1]; } Elements::Elements(HybridElements& elements, idx_t type_idx): owns_(false), hybrid_elements_(&elements), type_idx_(type_idx) { rebuild(); } Elements::Elements(ElementType* element_type, idx_t nb_elements, const std::vector& node_connectivity): owns_(true) { hybrid_elements_ = new HybridElements(); type_idx_ = hybrid_elements_->add(element_type, nb_elements, node_connectivity.data()); rebuild(); } Elements::Elements(ElementType* element_type, idx_t nb_elements, const idx_t node_connectivity[], bool fortran_array): owns_(true) { hybrid_elements_ = new HybridElements(); type_idx_ = hybrid_elements_->add(element_type, nb_elements, node_connectivity, fortran_array); rebuild(); } Elements::~Elements() { if (owns_) { delete hybrid_elements_; } } const std::string& Elements::name() const { return hybrid_elements_->element_type(type_idx_).name(); } template <> array::LocalView Elements::view(const Field& field) const { return array::make_host_view(field).slice(array::Range{begin(), begin() + size()}); } template <> array::LocalView Elements::view(const Field& field) const { return array::make_host_view(field).slice(array::Range{begin(), begin() + size()}); } template <> array::LocalView Elements::view(const Field& field) const { return array::make_host_view(field).slice(array::Range{begin(), begin() + size()}); } template <> array::LocalView Elements::view(const Field& field) const { return array::make_host_view(field).slice(array::Range{begin(), begin() + size()}); } template <> array::LocalView Elements::view(const Field& field) const { return array::make_host_view(field).slice(array::Range{begin(), begin() + size()}, array::Range::all()); } template <> array::LocalView Elements::view(const Field& field) const { return array::make_host_view(field).slice(array::Range{begin(), begin() + size()}, array::Range::all()); } template <> array::LocalView Elements::view(const Field& field) const { return array::make_host_view(field).slice(array::Range{begin(), begin() + size()}, array::Range::all()); } template <> array::LocalView Elements::view(const Field& field) const { return array::make_host_view(field).slice(array::Range{begin(), begin() + size()}, array::Range::all()); } // ---------------------------------------------------------------------------- template <> array::LocalView Elements::view(Field& field) const { return array::make_host_view(field).slice(array::Range{begin(), begin() + size()}); } template <> array::LocalView Elements::view(Field& field) const { return array::make_host_view(field).slice(array::Range{begin(), begin() + size()}); } template <> array::LocalView Elements::view(Field& field) const { return array::make_host_view(field).slice(array::Range{begin(), begin() + size()}); } template <> array::LocalView Elements::view(Field& field) const { return array::make_host_view(field).slice(array::Range{begin(), begin() + size()}); } template <> array::LocalView Elements::view(Field& field) const { return array::make_host_view(field).slice(array::Range{begin(), begin() + size()}, array::Range::all()); } template <> array::LocalView Elements::view(Field& field) const { return array::make_host_view(field).slice(array::Range{begin(), begin() + size()}, array::Range::all()); } template <> array::LocalView Elements::view(Field& field) const { return array::make_host_view(field).slice(array::Range{begin(), begin() + size()}, array::Range::all()); } template <> array::LocalView Elements::view(Field& field) const { return array::make_host_view(field).slice(array::Range{begin(), begin() + size()}, array::Range::all()); } idx_t Elements::add(const idx_t nb_elements) { idx_t position = size(); hybrid_elements_->insert(type_idx_, end(), 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" { void atlas__mesh__Elements__delete(Elements* This) { delete This; } idx_t atlas__mesh__Elements__size(const Elements* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Elements"); return This->size(); } idx_t atlas__mesh__Elements__begin(const Elements* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Elements"); return This->begin(); } idx_t atlas__mesh__Elements__end(const Elements* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Elements"); return This->end(); } BlockConnectivity* atlas__mesh__Elements__node_connectivity(Elements* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Elements"); return &This->node_connectivity(); } BlockConnectivity* atlas__mesh__Elements__edge_connectivity(Elements* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Elements"); return &This->edge_connectivity(); } BlockConnectivity* atlas__mesh__Elements__cell_connectivity(Elements* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Elements"); return &This->cell_connectivity(); } int atlas__mesh__Elements__has_field(const Elements* This, char* name) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Elements"); return This->has_field(std::string(name)); } int atlas__mesh__Elements__nb_fields(const Elements* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Elements"); return This->nb_fields(); } field::FieldImpl* atlas__mesh__Elements__field_by_idx(Elements* This, idx_t idx) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Elements"); return This->field(idx).get(); } field::FieldImpl* atlas__mesh__Elements__field_by_name(Elements* This, char* name) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Elements"); return This->field(std::string(name)).get(); } field::FieldImpl* atlas__mesh__Elements__global_index(Elements* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Elements"); return This->global_index().get(); } field::FieldImpl* atlas__mesh__Elements__remote_index(Elements* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Elements"); return This->remote_index().get(); } field::FieldImpl* atlas__mesh__Elements__partition(Elements* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Elements"); return This->partition().get(); } field::FieldImpl* atlas__mesh__Elements__halo(Elements* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Elements"); return This->halo().get(); } const ElementType* atlas__mesh__Elements__element_type(const Elements* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Elements"); return &This->element_type(); } void atlas__mesh__Elements__add(Elements* This, idx_t nb_elements) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Elements"); This->add(nb_elements); } } //----------------------------------------------------------------------------- } // namespace mesh } // namespace atlas #undef FORTRAN_BASE #undef TO_FORTRAN atlas-0.46.0/src/atlas/mesh/Connectivity.cc0000664000175000017500000007374415160212070020716 0ustar alastairalastair/* * (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/io/Buffer.h" #include "eckit/serialisation/Stream.h" #include "atlas/array.h" #include "atlas/array/DataType.h" #include "atlas/array/MakeView.h" #include "atlas/library/config.h" #include "atlas/mesh/Connectivity.h" #include "atlas/runtime/Exception.h" #if ATLAS_HAVE_FORTRAN #define FORTRAN_BASE 1 #define TO_FORTRAN +1 #else #define FORTRAN_BASE 0 #define TO_FORTRAN #endif namespace atlas { namespace mesh { // ----------------------------------------------------------------------------- IrregularConnectivityImpl::IrregularConnectivityImpl(const std::string& name): owns_(true), values_(0), displs_(1), counts_(1), missing_value_(std::numeric_limits::is_signed ? -1 : std::numeric_limits::max()), rows_(0), maxcols_(0), mincols_(std::numeric_limits::max()), ctxt_(nullptr), callback_update_(nullptr), callback_delete_(nullptr) { rename(name); displs_[0] = 0; counts_[0] = 0; } // ----------------------------------------------------------------------------- idx_t get_total_size_counts(idx_t rows, idx_t counts[]) { idx_t total_size = 0; for (idx_t j = 0; j < rows; ++j) { total_size += counts[j]; } return total_size; } // ----------------------------------------------------------------------------- IrregularConnectivityImpl::IrregularConnectivityImpl(idx_t values[], idx_t rows, idx_t displs[], idx_t counts[]): owns_(false), //TODO need to create clone if pointers are not cuda managed values_(values, get_total_size_counts(rows, counts)), displs_(displs, rows), counts_(counts, rows), missing_value_(std::numeric_limits::is_signed ? -1 : std::numeric_limits::max()), rows_(rows), ctxt_(nullptr), callback_update_(nullptr), callback_delete_(nullptr) { maxcols_ = 0; mincols_ = std::numeric_limits::max(); for (idx_t j = 0; j < rows; ++j) { maxcols_ = std::max(maxcols_, counts[j]); mincols_ = std::min(mincols_, counts[j]); } } //------------------------------------------------------------------------------------------------------ IrregularConnectivityImpl::IrregularConnectivityImpl(eckit::Stream& s) { decode_(s); } //------------------------------------------------------------------------------------------------------ IrregularConnectivityImpl::~IrregularConnectivityImpl() { on_delete(); //TODO owns is unsed ? } //------------------------------------------------------------------------------------------------------ void IrregularConnectivityImpl::clear() { //TODO clean this if (owns()) { values_.resize(0); displs_.resize(1); counts_.resize(1); displs_(0) = 0; counts_(0) = 0; } else { //TODO what to do here // data_[_values_] = nullptr; // data_[_displs_] = nullptr; // data_[_counts_] = nullptr; // std::for_each(data_.begin(), data_.end(), [](array::Array* a){ a=0;}); } rows_ = 0; maxcols_ = 0; mincols_ = std::numeric_limits::max(); on_update(); } //------------------------------------------------------------------------------------------------------ void IrregularConnectivityImpl::on_delete() { if (ctxt_ && callback_delete_) { callback_delete_(ctxt_); } } //------------------------------------------------------------------------------------------------------ void IrregularConnectivityImpl::on_update() { if (ctxt_ && callback_update_) { callback_update_(ctxt_); } } void IrregularConnectivityImpl::resize(idx_t old_size, idx_t new_size, bool initialize, const idx_t values[], bool fortran_array) { values_.resize(new_size); idx_t add_base = fortran_array ? 0 : FORTRAN_BASE; if (initialize) { for (idx_t j = 0, c = old_size; c < new_size; ++c, ++j) { values_[c] = values[j] + add_base; } } else { for (idx_t j = old_size; j < new_size; ++j) { values_[j] = missing_value() TO_FORTRAN; } } } //------------------------------------------------------------------------------------------------------ void IrregularConnectivityImpl::add(idx_t rows, idx_t cols, const idx_t values[], bool fortran_array) { ATLAS_ASSERT(owns_, "Connectivity must be owned to be resized directly"); idx_t old_size = values_.size(); if (rows_ == 0) { old_size = 0; } idx_t new_size = old_size + rows * cols; idx_t new_rows = rows_ + rows; //TODO what to do here // ATLAS_ASSERT( displs_] != nullptr ); // ATLAS_ASSERT( data_[_counts_] != nullptr ); displs_.resize(new_rows + 1); counts_.resize(new_rows); for (idx_t j = 0; rows_ < new_rows; ++rows_, ++j) { displs_[rows_ + 1] = displs_[rows_] + cols; counts_[rows_] = cols; } maxcols_ = std::max(maxcols_, cols); mincols_ = std::min(mincols_, cols); resize(old_size, new_size, true, values, fortran_array); on_update(); } //------------------------------------------------------------------------------------------------------ void IrregularConnectivityImpl::add(const BlockConnectivityImpl& block) { ATLAS_ASSERT(owns_, "Connectivity must be owned to be resized directly"); bool fortran_array = FORTRAN_BASE; const idx_t rows = block.rows(); const idx_t cols = block.cols(); const idx_t* values = block.data(); add(rows, cols, values, fortran_array); } //------------------------------------------------------------------------------------------------------ void IrregularConnectivityImpl::add(idx_t rows, const idx_t cols[]) { ATLAS_ASSERT(owns_, "Connectivity must be owned to be resized directly"); idx_t old_size = values_.size(); idx_t new_size = old_size; for (idx_t j = 0; j < rows; ++j) { new_size += cols[j]; } idx_t new_rows = rows_ + rows; displs_.resize(new_rows + 1); counts_.resize(new_rows); for (idx_t j = 0; rows_ < new_rows; ++rows_, ++j) { displs_[rows_ + 1] = displs_[rows_] + cols[j]; counts_[rows_] = cols[j]; maxcols_ = std::max(maxcols_, cols[j]); mincols_ = std::min(mincols_, cols[j]); } resize(old_size, new_size, false, nullptr, false); on_update(); } //------------------------------------------------------------------------------------------------------ void IrregularConnectivityImpl::add(idx_t rows, idx_t cols) { ATLAS_ASSERT(owns_, "Connectivity must be owned to be resized directly"); idx_t old_size = values_.size(); if (rows_ == 0) { old_size = 0; } idx_t new_size = old_size + rows * cols; idx_t new_rows = rows_ + rows; //TODO // ATLAS_ASSERT( data_[_displs_] != nullptr ); // ATLAS_ASSERT( data_[_counts_] != nullptr ); displs_.resize(new_rows + 1); counts_.resize(new_rows); for (idx_t j = 0; rows_ < new_rows; ++rows_, ++j) { displs_[rows_ + 1] = displs_[rows_] + cols; counts_[rows_] = cols; } maxcols_ = std::max(maxcols_, cols); mincols_ = std::min(mincols_, cols); const bool dummy_arg_fortran_array = false; const idx_t* dummy_arg_values = nullptr; resize(old_size, new_size, false, dummy_arg_values, dummy_arg_fortran_array); on_update(); } //------------------------------------------------------------------------------------------------------ void IrregularConnectivityImpl::insert(idx_t position, idx_t rows, idx_t cols, const idx_t values[], bool fortran_array) { ATLAS_ASSERT(owns_, "Connectivity must be owned to be resized directly"); idx_t position_displs = displs_[position]; displs_.insert(position, rows); counts_.insert(position, rows); displs_[position] = position_displs; for (idx_t jrow = position; jrow < position + rows; ++jrow) { counts_[jrow] = cols; } for (idx_t jrow = position; jrow < displs_.size() - 1; ++jrow) { displs_[jrow + 1] = displs_[jrow] + counts_[jrow]; } maxcols_ = std::max(maxcols_, cols); mincols_ = std::min(mincols_, cols); values_.insert(position_displs, rows * cols); if (values == nullptr) { for (idx_t c = position_displs; c < position_displs + rows * cols; ++c) { values_[c] = missing_value() TO_FORTRAN; } } else { idx_t add_base = fortran_array ? 0 : FORTRAN_BASE; for (idx_t c = position_displs; c < position_displs + rows * cols; ++c) { values_[c] = values[c - position_displs] + add_base; } } rows_ += rows; on_update(); } //------------------------------------------------------------------------------------------------------ void IrregularConnectivityImpl::insert(idx_t position, idx_t rows, idx_t cols) { IrregularConnectivityImpl::insert(position, rows, cols, nullptr, false); } //------------------------------------------------------------------------------------------------------ void IrregularConnectivityImpl::insert(idx_t position, idx_t rows, const idx_t cols[]) { ATLAS_ASSERT(owns_, "Connectivity must be owned to be resized directly"); idx_t position_displs = displs_[position]; if (rows_ == 0) { if (position > 1) { displs_.insert(position - 1, rows); counts_.insert(position - 1, rows); } } else { displs_.insert(position, rows); counts_.insert(position, rows); } displs_[position] = position_displs; for (idx_t jrow = position; jrow < position + rows; ++jrow) { counts_[jrow] = cols[jrow - position]; maxcols_ = std::max(maxcols_, counts_[jrow]); mincols_ = std::min(mincols_, counts_[jrow]); } for (idx_t jrow = position; jrow < displs_.size() - 1; ++jrow) { displs_[jrow + 1] = displs_[jrow] + counts_[jrow]; } idx_t insert_size(0); for (idx_t j = 0; j < rows; ++j) { insert_size += cols[j]; } values_.insert(position_displs, insert_size); for (idx_t c = position_displs; c < position_displs + insert_size; ++c) { values_[c] = missing_value() TO_FORTRAN; } rows_ += rows; on_update(); } size_t IrregularConnectivityImpl::footprint() const { size_t size = sizeof(*this); size += values_.footprint(); size += displs_.footprint(); size += counts_.footprint(); return size; } void IrregularConnectivityImpl::dump(std::ostream& os) const { //TODO dump } eckit::Stream& operator>>(eckit::Stream& s, array::SVector& x) { size_t size; s >> size; eckit::Buffer buffer(size * sizeof(idx_t)); s >> buffer; idx_t* data = static_cast(buffer.data()); idx_t N = static_cast(size); x.resize(N); for (idx_t i = 0; i < N; ++i) { x(i) = data[i]; } return s; } eckit::Stream& operator<<(eckit::Stream& s, const array::SVector& x) { size_t size = x.size(); s << size; eckit::Buffer buffer(reinterpret_cast(x.data()), size * sizeof(idx_t)); s << buffer; return s; } void BlockConnectivityImpl::print(std::ostream& out) const { out << "BlockConnectivity:{rows:" << rows_ << ",cols:" << cols_ << ",values:"; if (values_.size() == 0) { out << "null"; } else { out << "["; for (idx_t i = 0; i < values_.size(); ++i) { out << values_[i] << (i < values_.size() - 1 ? "," : "]"); } } out << "}"; } void IrregularConnectivityImpl::encode_(eckit::Stream& s) const { s << name(); s << values_; s << displs_; s << counts_; s << missing_value_; s << rows_; s << maxcols_; s << mincols_; } void IrregularConnectivityImpl::decode_(eckit::Stream& s) { std::string name; s >> name; s >> values_; s >> displs_; s >> counts_; s >> missing_value_; s >> rows_; s >> maxcols_; s >> mincols_; if (not name.empty()) { rename(name); } ctxt_ = nullptr; owns_ = true; } //------------------------------------------------------------------------------------------------------ /* } //------------------------------------------------------------------------------------------------------ MultiBlockConnectivity::MultiBlockConnectivity( idx_t values[], idx_t rows, idx_t displs[], idx_t counts[], idx_t blocks, idx_t block_displs[], idx_t block_cols[] ) : IrregularConnectivity(values,rows,displs,counts), blocks_(blocks), block_displs_(array::Array::wrap(block_displs, array::ArrayShape{blocks})), block_cols_(array::Array::wrap(block_cols, array::ArrayShape{blocks})), block_(blocks), block_displs_view_(array::make_view(*block_displs_)), block_cols_view_(array::make_view(*block_cols_)) { rebuild_block_connectivity(); } */ //------------------------------------------------------------------------------------------------------ MultiBlockConnectivityImpl::MultiBlockConnectivityImpl(const std::string& name): IrregularConnectivityImpl(name), blocks_(0), block_displs_(1), block_cols_(1), block_(0) { block_displs_(0) = 0; } MultiBlockConnectivityImpl::MultiBlockConnectivityImpl(eckit::Stream& s): IrregularConnectivityImpl(s) { decode_(s); } //------------------------------------------------------------------------------------------------------ MultiBlockConnectivityImpl::~MultiBlockConnectivityImpl() = default; //------------------------------------------------------------------------------------------------------ void MultiBlockConnectivityImpl::clear() { IrregularConnectivityImpl::clear(); if (owns()) { block_displs_.resize(1); block_cols_.resize(1); block_displs_(0) = 0ul; } blocks_ = 0; block_.clear(); } //------------------------------------------------------------------------------------------------------ void MultiBlockConnectivityImpl::add(idx_t rows, idx_t cols, const idx_t values[], bool fortran_array) { ATLAS_ASSERT(owns(), "MultiBlockConnectivity must be owned to be resized directly"); idx_t old_rows = this->rows(); IrregularConnectivityImpl::add(rows, cols, values, fortran_array); for (idx_t b = 0; b < blocks_; ++b) { ATLAS_ASSERT(block_[b].owns() == false); } block_displs_.insert(block_displs_.size(), 1); block_cols_.insert(block_cols_.size(), 1); blocks_++; block_displs_[block_displs_.size() - 1] = old_rows + rows; block_cols_[block_cols_.size() - 2] = cols; rebuild_block_connectivity(); } //------------------------------------------------------------------------------------------------------ void MultiBlockConnectivityImpl::add(const BlockConnectivityImpl& block) { ATLAS_ASSERT(owns(), "MultiBlockConnectivity must be owned to be resized directly"); IrregularConnectivityImpl::add(block); } //------------------------------------------------------------------------------------------------------ void MultiBlockConnectivityImpl::add(idx_t rows, idx_t cols) { ATLAS_ASSERT(owns(), "MultiBlockConnectivity must be owned to be resized directly"); idx_t old_rows = this->rows(); IrregularConnectivityImpl::add(rows, cols); for (idx_t b = 0; b < blocks_; ++b) { ATLAS_ASSERT(block_[b].owns() == false); } block_displs_.insert(block_displs_.size(), 1); block_cols_.insert(block_cols_.size(), 1); blocks_++; block_displs_[block_displs_.size() - 1] = old_rows + rows; block_cols_[block_cols_.size() - 2] = cols; rebuild_block_connectivity(); } //------------------------------------------------------------------------------------------------------ void MultiBlockConnectivityImpl::add(idx_t rows, const idx_t cols[]) { ATLAS_ASSERT(owns(), "MultiBlockConnectivity must be owned to be resized directly"); idx_t min = std::numeric_limits::max(); idx_t max = 0; idx_t old_rows = this->rows(); for (idx_t j = 0; j < rows; ++j) { min = std::min(min, cols[j]); max = std::min(max, cols[j]); } ATLAS_ASSERT(min == max, "MultiBlockConnectivity::add(rows,cols[]): " "all elements of cols[] must be identical"); IrregularConnectivityImpl::add(rows, cols); for (idx_t b = 0; b < blocks_; ++b) { ATLAS_ASSERT(block_[b].owns() == false); } block_displs_.insert(block_displs_.size(), 1); block_cols_.insert(block_cols_.size(), 1); blocks_++; block_displs_(block_displs_.size() - 1) = old_rows; block_cols_[block_cols_.size() - 2] = max; rebuild_block_connectivity(); } //------------------------------------------------------------------------------------------------------ void MultiBlockConnectivityImpl::insert(idx_t position, idx_t rows, idx_t cols, const idx_t values[], bool fortran_array) { ATLAS_ASSERT(owns(), "MultiBlockConnectivity must be owned to be resized directly"); ATLAS_ASSERT(blocks_); long blk_idx = blocks_; do { blk_idx--; } while (blk_idx >= 0l && block_displs_[blk_idx] >= position && cols != block_cols_[blk_idx]); ATLAS_ASSERT(blk_idx >= 0l); ATLAS_ASSERT(cols == block(blk_idx).cols()); for (idx_t jblk = blk_idx; jblk < blocks_; ++jblk) { block_displs_[jblk + 1] += rows; } IrregularConnectivityImpl::insert(position, rows, cols, values, fortran_array); rebuild_block_connectivity(); } //------------------------------------------------------------------------------------------------------ void MultiBlockConnectivityImpl::insert(idx_t position, idx_t rows, idx_t cols) { ATLAS_ASSERT(owns(), "MultiBlockConnectivity must be owned to be resized directly"); long blk_idx = blocks_; do { blk_idx--; } while (blk_idx >= 0l && block_displs_[blk_idx] >= position && cols != block_cols_[blk_idx]); ATLAS_ASSERT(blk_idx >= 0l); for (idx_t b = 0; b < blocks_; ++b) { ATLAS_ASSERT(block_[b].owns() == false); } IrregularConnectivityImpl::insert(position, rows, cols); for (idx_t jblk = blk_idx; jblk < blocks_; ++jblk) { block_displs_[jblk + 1] += rows; } rebuild_block_connectivity(); } //------------------------------------------------------------------------------------------------------ void MultiBlockConnectivityImpl::insert(idx_t position, idx_t rows, const idx_t cols[]) { ATLAS_ASSERT(owns(), "MultiBlockConnectivity must be owned to be resized directly"); idx_t min = std::numeric_limits::max(); idx_t max = 0; for (idx_t j = 0; j < rows; ++j) { min = std::min(min, cols[j]); max = std::min(max, cols[j]); } ATLAS_ASSERT(min == max, "MultiBlockConnectivity::add(rows,cols[]): " "all elements of cls[] must be identical"); long blk_idx = blocks_; do { blk_idx--; } while (blk_idx >= 0l && block_displs_[blk_idx] >= position && max != block_cols_[blk_idx]); ATLAS_ASSERT(blk_idx >= 0l); IrregularConnectivityImpl::insert(position, rows, cols); for (idx_t jblk = blk_idx; jblk < blocks_; ++jblk) { block_displs_[jblk + 1] += rows; } rebuild_block_connectivity(); } //------------------------------------------------------------------------------------------------------ void MultiBlockConnectivityImpl::rebuild_block_connectivity() { block_.resize(blocks_); for (idx_t b = 0; b < blocks_; ++b) { block_[b].rebuild(block_displs_[b + 1] - block_displs_[b], // rows block_cols_[b], // cols values_.data() + displs(block_displs_[b])); } } //------------------------------------------------------------------------------------------------------ size_t MultiBlockConnectivityImpl::footprint() const { size_t size = IrregularConnectivityImpl::footprint(); size += block_displs_.footprint(); size += block_cols_.footprint(); for (idx_t j = 0; j < block_.size(); ++j) { size += block_[j].footprint(); } return size; } //------------------------------------------------------------------------------------------------------ void MultiBlockConnectivityImpl::encode_(eckit::Stream& s) const { s << blocks_; s << block_displs_; s << block_cols_; } //------------------------------------------------------------------------------------------------------ void MultiBlockConnectivityImpl::decode_(eckit::Stream& s) { s >> blocks_; s >> block_displs_; s >> block_cols_; rebuild_block_connectivity(); } //------------------------------------------------------------------------------------------------------ BlockConnectivityImpl::BlockConnectivityImpl(): owns_(true), values_(0), rows_(0), cols_(0), missing_value_(std::numeric_limits::is_signed ? -1 : std::numeric_limits::max()) {} //------------------------------------------------------------------------------------------------------ BlockConnectivityImpl::BlockConnectivityImpl(idx_t rows, idx_t cols, const std::initializer_list& values): owns_(true), values_(rows * cols), rows_(rows), cols_(cols), missing_value_(std::numeric_limits::is_signed ? -1 : std::numeric_limits::max()) { idx_t add_base = FORTRAN_BASE; auto v = values.begin(); for (idx_t i = 0; i < rows_; ++i) { for (idx_t j = 0; j < cols_; ++j) { values_[index(i, j)] = *(v++) + add_base; } } ATLAS_ASSERT(v == values.end()); } //------------------------------------------------------------------------------------------------------ BlockConnectivityImpl::BlockConnectivityImpl(idx_t rows, idx_t cols, idx_t values[]): owns_(true), values_(rows * cols), rows_(rows), cols_(cols), missing_value_(std::numeric_limits::is_signed ? -1 : std::numeric_limits::max()) { if (values_.size()) { idx_t add_base = FORTRAN_BASE; idx_t* v = &values[0]; for (idx_t i = 0; i < rows_; ++i) { for (idx_t j = 0; j < cols_; ++j) { values_[index(i, j)] = *(v++) + add_base; } } } } BlockConnectivityImpl::BlockConnectivityImpl(eckit::Stream& s) { decode(s); } //------------------------------------------------------------------------------------------------------ BlockConnectivityImpl::BlockConnectivityImpl(idx_t rows, idx_t cols, idx_t values[], bool /*dummy*/): owns_(false), values_(values, rows * cols), rows_(rows), cols_(cols), missing_value_(std::numeric_limits::is_signed ? -1 : std::numeric_limits::max()) {} //------------------------------------------------------------------------------------------------------ BlockConnectivityImpl::BlockConnectivityImpl(BlockConnectivityImpl&& other): owns_(other.owns_), values_(std::move(other.values_)), rows_(other.rows_), cols_(other.cols_), missing_value_(other.missing_value_) { other.owns_ = false; rows_ = 0; cols_ = 0; } BlockConnectivityImpl& BlockConnectivityImpl::operator=(BlockConnectivityImpl&& other) { owns_ = other.owns_; values_ = std::move(other.values_); rows_ = other.rows_; cols_ = other.cols_; missing_value_ = other.missing_value_; other.owns_ = false; other.rows_ = 0; other.cols_ = 0; return *this; } BlockConnectivityImpl::~BlockConnectivityImpl() { values_.clear(); } //------------------------------------------------------------------------------------------------------ void BlockConnectivityImpl::rebuild(idx_t rows, idx_t cols, idx_t values[]) { owns_ = false; rows_ = rows; cols_ = cols; values_ = array::SVector(values, rows * cols); } //------------------------------------------------------------------------------------------------------ void BlockConnectivityImpl::add(idx_t rows, idx_t cols, const idx_t values[], bool fortran_array) { ATLAS_ASSERT(owns(), "BlockConnectivity must be owned to be resized directly"); if (cols_ != 0 && cols_ != cols) { ATLAS_ASSERT(false, "Cannot add values with different cols than " "already existing in BlockConnectivity"); } values_.resize((rows_ + rows) * cols); const idx_t oldrows = rows_; idx_t add_base = fortran_array ? 0 : FORTRAN_BASE; rows_ += rows; cols_ = cols; for (idx_t i = 0; i < rows; ++i) { for (idx_t j = 0; j < cols; ++j) { values_[index(i + oldrows, j)] = values[i * cols + j] + add_base; } } } void BlockConnectivityImpl::encode(eckit::Stream& s) const { s << values_; s << rows_; s << cols_; s << missing_value_; } void BlockConnectivityImpl::decode(eckit::Stream& s) { owns_ = true; s >> values_; s >> rows_; s >> cols_; s >> missing_value_; } //------------------------------------------------------------------------------------------------------ size_t BlockConnectivityImpl::footprint() const { size_t size = sizeof(*this); if (owns()) { size += values_.footprint(); } return size; } //------------------------------------------------------------------------------------------------------ class ConnectivityPrivateAccess { private: using ctxt_t = Connectivity::ctxt_t; using callback_t = Connectivity::callback_t; public: ConnectivityPrivateAccess(Connectivity& connectivity): connectivity_(connectivity) {} ctxt_t& ctxt() { return connectivity_.ctxt_; } callback_t& callback_update() { return connectivity_.callback_update_; } callback_t& callback_delete() { return connectivity_.callback_delete_; } idx_t* values() { return connectivity_.values_.data(); } idx_t* displs() { return connectivity_.displs_.data(); } idx_t* counts() { return connectivity_.counts_.data(); } const char* name() { return connectivity_.name_; } private: Connectivity& connectivity_; }; //------------------------------------------------------------------------------------------------------ extern "C" { Connectivity* atlas__Connectivity__create() { Connectivity* connectivity = nullptr; connectivity = new Connectivity(); return connectivity; } void atlas__Connectivity__delete(Connectivity* This) { delete This; } void atlas__connectivity__register_ctxt(Connectivity* This, Connectivity::ctxt_t ctxt) { ConnectivityPrivateAccess access(*This); access.ctxt() = ctxt; } int atlas__connectivity__ctxt(Connectivity* This, Connectivity::ctxt_t* ctxt) { ConnectivityPrivateAccess access(*This); *ctxt = access.ctxt(); return bool(access.ctxt()); } void atlas__connectivity__register_update(Connectivity* This, Connectivity::callback_t callback) { ConnectivityPrivateAccess access(*This); access.callback_update() = callback; } void atlas__connectivity__register_delete(Connectivity* This, Connectivity::callback_t callback) { ConnectivityPrivateAccess access(*This); access.callback_delete() = callback; } void atlas__Connectivity__displs(Connectivity* This, idx_t*& displs, idx_t& size) { ConnectivityPrivateAccess access(*This); displs = access.displs(); size = This->rows() + 1; } void atlas__Connectivity__counts(Connectivity* This, idx_t*& counts, idx_t& size) { ConnectivityPrivateAccess access(*This); counts = access.counts(); size = This->rows(); } void atlas__Connectivity__values(Connectivity* This, idx_t*& values, idx_t& size) { ConnectivityPrivateAccess access(*This); values = access.values(); size = This->rows() ? access.displs()[This->rows()] + 1 : 0; } void atlas__Connectivity__add_values(Connectivity* This, idx_t rows, idx_t cols, idx_t values[]) { This->add(rows, cols, values, true); } void atlas__Connectivity__add_missing(Connectivity* This, idx_t rows, idx_t cols) { This->add(rows, cols); } idx_t atlas__Connectivity__rows(const Connectivity* This) { return This->rows(); } idx_t atlas__Connectivity__missing_value(const Connectivity* This) { return This->missing_value() TO_FORTRAN; } MultiBlockConnectivity* atlas__MultiBlockConnectivity__create() { MultiBlockConnectivity* connectivity = nullptr; connectivity = new MultiBlockConnectivity(); return connectivity; } idx_t atlas__MultiBlockConnectivity__blocks(const MultiBlockConnectivity* This) { return This->blocks(); } BlockConnectivityImpl* atlas__MultiBlockConnectivity__block(MultiBlockConnectivity* This, idx_t block_idx) { ATLAS_ASSERT(This != nullptr); BlockConnectivityImpl* block = &This->block(block_idx); ATLAS_ASSERT(block != nullptr); return block; } void atlas__BlockConnectivity__delete(BlockConnectivityImpl* This) { delete This; } idx_t atlas__BlockConnectivity__rows(const BlockConnectivityImpl* This) { ATLAS_ASSERT(This != nullptr); return This->rows(); } idx_t atlas__BlockConnectivity__cols(const BlockConnectivityImpl* This) { ATLAS_ASSERT(This != nullptr); return This->cols(); } idx_t atlas__BlockConnectivity__missing_value(const BlockConnectivityImpl* This) { ATLAS_ASSERT(This != nullptr); return This->missing_value(); } void atlas__BlockConnectivity__data(BlockConnectivityImpl* This, idx_t*& data, idx_t& rows, idx_t& cols) { ATLAS_ASSERT(This != nullptr); data = This->data(); rows = This->rows(); cols = This->cols(); } const char* atlas__Connectivity__name(Connectivity* This) { ATLAS_ASSERT(This); return ConnectivityPrivateAccess(*This).name(); } void atlas__Connectivity__rename(Connectivity* This, const char* name) { ATLAS_ASSERT(This); This->rename(std::string(name)); } } } // namespace mesh } // namespace atlas atlas-0.46.0/src/atlas/mesh/detail/0000775000175000017500000000000015160212070017154 5ustar alastairalastairatlas-0.46.0/src/atlas/mesh/detail/MeshIntf.cc0000664000175000017500000000432015160212070021177 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "atlas/mesh/detail/MeshIntf.h" #include "atlas/mesh/Nodes.h" #include "atlas/runtime/Exception.h" namespace atlas { namespace mesh { //---------------------------------------------------------------------------------------------------------------------- // C wrapper interfaces to C++ routines Mesh::Implementation* atlas__Mesh__new() { return new Mesh::Implementation(); } void atlas__Mesh__delete(Mesh::Implementation* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialisd atlas_Mesh"); delete This; } Nodes* atlas__Mesh__nodes(Mesh::Implementation* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialisd atlas_Mesh"); return &This->nodes(); } Edges* atlas__Mesh__edges(Mesh::Implementation* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialisd atlas_Mesh"); return &This->edges(); } Cells* atlas__Mesh__cells(Mesh::Implementation* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialisd atlas_Mesh"); return &This->cells(); } size_t atlas__Mesh__footprint(Mesh::Implementation* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialisd atlas_Mesh"); return This->footprint(); } void atlas__Mesh__update_device(Mesh::Implementation* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialisd atlas_Mesh"); This->updateDevice(); } void atlas__Mesh__update_host(Mesh::Implementation* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialisd atlas_Mesh"); This->updateHost(); } void atlas__Mesh__sync_host_device(Mesh::Implementation* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialisd atlas_Mesh"); This->syncHostDevice(); } //---------------------------------------------------------------------------------------------------------------------- } // namespace mesh } // namespace atlas atlas-0.46.0/src/atlas/mesh/detail/PartitionGraph.h0000664000175000017500000000332115160212070022257 0ustar alastairalastair/* * (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/Object.h" //---------------------------------------------------------------------------------------------------------------------- namespace atlas { namespace mesh { namespace detail { class MeshImpl; //---------------------------------------------------------------------------------------------------------------------- class PartitionGraph : public util::Object { public: using Neighbours = std::vector; public: PartitionGraph(); PartitionGraph(idx_t values[], idx_t rows, idx_t displs[], idx_t counts[]); size_t footprint() const; idx_t size() const; Neighbours nearestNeighbours(const idx_t partition) const; idx_t maximumNearestNeighbours() const; operator bool() const; private: void print(std::ostream&) const; friend std::ostream& operator<<(std::ostream& s, const PartitionGraph& p); private: std::vector counts_; std::vector displs_; std::vector values_; idx_t maximum_nearest_neighbours_; }; PartitionGraph* build_partition_graph(const MeshImpl& mesh); //---------------------------------------------------------------------------------------------------------------------- } // namespace detail } // namespace mesh } // namespace atlas atlas-0.46.0/src/atlas/mesh/detail/MeshBuilderIntf.h0000664000175000017500000000254115160212070022353 0ustar alastairalastair/* * (C) Copyright 2023 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/MeshBuilder.h" //---------------------------------------------------------------------------------------------------------------------- namespace atlas { namespace mesh { // C wrapper interfaces to C++ routines extern "C" { TriangularMeshBuilder* atlas__TriangularMeshBuilder__new(); void atlas__TriangularMeshBuilder__delete(TriangularMeshBuilder* This); Mesh::Implementation* atlas__TriangularMeshBuilder__operator(TriangularMeshBuilder* This, size_t nb_nodes, const gidx_t node_global_index[], const double x[], const double y[], size_t xstride, size_t ystride, const double lon[], const double lat[], size_t lonstride, size_t latstride, size_t nb_triags, const gidx_t triangle_global_index[], const gidx_t triangle_nodes_global_index[], gidx_t global_index_base); } //---------------------------------------------------------------------------------------------------------------------- } // namespace mesh } // namespace atlas atlas-0.46.0/src/atlas/mesh/detail/AccumulateFacets.cc0000664000175000017500000003220015160212070022671 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "atlas/mesh/detail/AccumulateFacets.h" #include "atlas/mesh/Elements.h" #include "atlas/mesh/HybridElements.h" #include "atlas/mesh/Nodes.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Trace.h" namespace atlas { namespace mesh { namespace detail { void accumulate_facets(const mesh::HybridElements& cells, const mesh::Nodes& nodes, std::vector& facet_nodes_data, // shape(nb_facets,nb_nodes_per_facet) std::vector& connectivity_facet_to_elem, idx_t& nb_facets, idx_t& nb_inner_facets, idx_t& missing_value) { ATLAS_TRACE(); missing_value = -1; std::vector> node_to_facet(nodes.size()); for (auto& facet : node_to_facet) { facet.reserve(6); } nb_facets = 0; nb_inner_facets = 0; if (connectivity_facet_to_elem.size() == 0) { connectivity_facet_to_elem.reserve(6 * cells.size()); } if (facet_nodes_data.size() == 0) { facet_nodes_data.reserve(6 * cells.size()); } for (idx_t t = 0; t < cells.nb_types(); ++t) { const mesh::Elements& elements = cells.elements(t); const mesh::BlockConnectivity& elem_nodes = elements.node_connectivity(); auto elem_flags = elements.view(elements.flags()); auto patch = [&elem_flags](idx_t e) { using Topology = atlas::mesh::Nodes::Topology; return Topology::check(elem_flags(e), Topology::PATCH); }; idx_t nb_elems = elements.size(); idx_t nb_nodes_in_facet = 2; std::vector> facet_node_numbering; idx_t nb_facets_in_elem; if (elements.name() == "Pentagon") { nb_facets_in_elem = 5; facet_node_numbering.resize(nb_facets_in_elem, std::vector(nb_nodes_in_facet)); facet_node_numbering[0][0] = 0; facet_node_numbering[0][1] = 1; facet_node_numbering[1][0] = 1; facet_node_numbering[1][1] = 2; facet_node_numbering[2][0] = 2; facet_node_numbering[2][1] = 3; facet_node_numbering[3][0] = 3; facet_node_numbering[3][1] = 4; facet_node_numbering[4][0] = 4; facet_node_numbering[4][1] = 0; } else if (elements.name() == "Quadrilateral") { nb_facets_in_elem = 4; facet_node_numbering.resize(nb_facets_in_elem, std::vector(nb_nodes_in_facet)); facet_node_numbering[0][0] = 0; facet_node_numbering[0][1] = 1; facet_node_numbering[1][0] = 1; facet_node_numbering[1][1] = 2; facet_node_numbering[2][0] = 2; facet_node_numbering[2][1] = 3; facet_node_numbering[3][0] = 3; facet_node_numbering[3][1] = 0; } else if (elements.name() == "Triangle") { nb_facets_in_elem = 3; facet_node_numbering.resize(nb_facets_in_elem, std::vector(nb_nodes_in_facet)); facet_node_numbering[0][0] = 0; facet_node_numbering[0][1] = 1; facet_node_numbering[1][0] = 1; facet_node_numbering[1][1] = 2; facet_node_numbering[2][0] = 2; facet_node_numbering[2][1] = 0; } else { throw_Exception(elements.name() + " is not \"Pentagon\", \"Quadrilateral\", or \"Triangle\"", Here()); } std::vector facet_nodes(nb_nodes_in_facet); for (idx_t e = 0; e < nb_elems; ++e) { if (patch(e)) { continue; } for (idx_t f = 0; f < nb_facets_in_elem; ++f) { bool found_face = false; for (idx_t jnode = 0; jnode < nb_nodes_in_facet; ++jnode) { facet_nodes[jnode] = elem_nodes(e, facet_node_numbering[f][jnode]); } idx_t node = facet_nodes[0]; for (const idx_t face : node_to_facet[node]) { idx_t nb_matched_nodes = 0; if (nb_nodes_in_facet > 1) // 2D or 3D { for (idx_t jnode = 0; jnode < nb_nodes_in_facet; ++jnode) { idx_t other_node = facet_nodes[jnode]; for (const idx_t other_face : node_to_facet[other_node]) { if (other_face == face) { ++nb_matched_nodes; break; } } } if (nb_matched_nodes == nb_nodes_in_facet) { connectivity_facet_to_elem[2 * face + 1] = e + elements.begin(); ++nb_inner_facets; found_face = true; break; } } } if (found_face == false) { connectivity_facet_to_elem.emplace_back(elements.begin() + e); // if 2nd element stays missing_value, it is a bdry face connectivity_facet_to_elem.emplace_back(missing_value); for (idx_t n = 0; n < nb_nodes_in_facet; ++n) { node_to_facet[facet_nodes[n]].emplace_back(nb_facets); facet_nodes_data.emplace_back(facet_nodes[n]); } ++nb_facets; } } } } } void accumulate_facets_in_range(std::vector& range, const mesh::HybridElements& cells, const mesh::Nodes& /*nodes*/, std::vector& facet_nodes_data, // shape(nb_facets,nb_nodes_per_facet) std::vector& connectivity_facet_to_elem, idx_t& nb_facets, idx_t& nb_inner_facets, idx_t& missing_value, std::vector>& node_to_facet) { ATLAS_TRACE(); if (connectivity_facet_to_elem.size() == 0) { connectivity_facet_to_elem.reserve(6 * cells.size()); } if (facet_nodes_data.size() == 0) { facet_nodes_data.reserve(6 * cells.size()); } for (idx_t t = 0; t < cells.nb_types(); ++t) { const mesh::Elements& elements = cells.elements(t); const mesh::BlockConnectivity& elem_nodes = elements.node_connectivity(); auto elem_flags = elements.view(elements.flags()); auto patch = [&elem_flags](idx_t e) { using Topology = atlas::mesh::Nodes::Topology; return Topology::check(elem_flags(e), Topology::PATCH); }; idx_t nb_nodes_in_facet = 2; std::vector> facet_node_numbering; idx_t nb_facets_in_elem; if (elements.name() == "Pentagon") { nb_facets_in_elem = 5; facet_node_numbering.resize(nb_facets_in_elem, std::vector(nb_nodes_in_facet)); facet_node_numbering[0][0] = 0; facet_node_numbering[0][1] = 1; facet_node_numbering[1][0] = 1; facet_node_numbering[1][1] = 2; facet_node_numbering[2][0] = 2; facet_node_numbering[2][1] = 3; facet_node_numbering[3][0] = 3; facet_node_numbering[3][1] = 4; facet_node_numbering[4][0] = 4; facet_node_numbering[4][1] = 0; } else if (elements.name() == "Quadrilateral") { nb_facets_in_elem = 4; facet_node_numbering.resize(nb_facets_in_elem, std::vector(nb_nodes_in_facet)); facet_node_numbering[0][0] = 0; facet_node_numbering[0][1] = 1; facet_node_numbering[1][0] = 1; facet_node_numbering[1][1] = 2; facet_node_numbering[2][0] = 2; facet_node_numbering[2][1] = 3; facet_node_numbering[3][0] = 3; facet_node_numbering[3][1] = 0; } else if (elements.name() == "Triangle") { nb_facets_in_elem = 3; facet_node_numbering.resize(nb_facets_in_elem, std::vector(nb_nodes_in_facet)); facet_node_numbering[0][0] = 0; facet_node_numbering[0][1] = 1; facet_node_numbering[1][0] = 1; facet_node_numbering[1][1] = 2; facet_node_numbering[2][0] = 2; facet_node_numbering[2][1] = 0; } else { throw_Exception(elements.name() + " is not \"Pentagon\", \"Quadrilateral\", or \"Triangle\"", Here()); } std::vector facet_nodes(nb_nodes_in_facet); const idx_t e_start = range[t].start(); const idx_t e_end = range[t].end(); for (idx_t e = e_start; e < e_end; ++e) { if (patch(e)) { continue; } for (idx_t f = 0; f < nb_facets_in_elem; ++f) { bool found_face = false; for (idx_t jnode = 0; jnode < nb_nodes_in_facet; ++jnode) { facet_nodes[jnode] = elem_nodes(e, facet_node_numbering[f][jnode]); } int node = facet_nodes[0]; for (idx_t face : node_to_facet[node]) { idx_t nb_matched_nodes = 0; if (nb_nodes_in_facet > 1) // 2D or 3D { for (idx_t jnode = 0; jnode < nb_nodes_in_facet; ++jnode) { idx_t other_node = facet_nodes[jnode]; for (idx_t other_face : node_to_facet[other_node]) { if (other_face == face) { ++nb_matched_nodes; break; } } } if (nb_matched_nodes == nb_nodes_in_facet) { connectivity_facet_to_elem[2 * face + 1] = e + elements.begin(); ++nb_inner_facets; found_face = true; break; } } } if (found_face == false) { connectivity_facet_to_elem.emplace_back(elements.begin() + e); // if 2nd element stays missing_value, it is a bdry face connectivity_facet_to_elem.emplace_back(missing_value); for (idx_t n = 0; n < nb_nodes_in_facet; ++n) { node_to_facet[facet_nodes[n]].emplace_back(nb_facets); facet_nodes_data.emplace_back(facet_nodes[n]); } ++nb_facets; } } } } } void accumulate_facets_ordered_by_halo(const mesh::HybridElements& cells, const mesh::Nodes& nodes, std::vector& facet_nodes_data, // shape(nb_facets,nb_nodes_per_facet) std::vector& connectivity_facet_to_elem, idx_t& nb_facets, idx_t& nb_inner_facets, idx_t& missing_value, std::vector& halo_offsets) { ATLAS_TRACE(); static int MAXHALO = 50; std::vector> ranges(MAXHALO, std::vector(cells.nb_types())); int maxhalo{0}; for (idx_t t = 0; t < cells.nb_types(); ++t) { const mesh::Elements& elements = cells.elements(t); auto elem_halo = elements.view(elements.halo()); idx_t nb_elems = elements.size(); int halo{0}; int begin{0}; int end{0}; for (idx_t e = 0; e < nb_elems; ++e) { ATLAS_ASSERT(elem_halo(e) >= halo); if (elem_halo(e) > halo) { end = e; ranges[halo][t] = array::Range{begin, end}; begin = end; ++halo; } } end = nb_elems; ranges[halo][t] = array::Range{begin, end}; maxhalo = std::max(halo, maxhalo); } missing_value = -1; std::vector> node_to_facet(nodes.size()); for (auto& facets : node_to_facet) { facets.reserve(6); } nb_facets = 0; nb_inner_facets = 0; halo_offsets = std::vector{0}; for (int h = 0; h <= maxhalo; ++h) { accumulate_facets_in_range(ranges[h], cells, nodes, facet_nodes_data, connectivity_facet_to_elem, nb_facets, nb_inner_facets, missing_value, node_to_facet); halo_offsets.emplace_back(nb_facets); } } } // namespace detail } // namespace mesh } // namespace atlas atlas-0.46.0/src/atlas/mesh/detail/PartitionGraph.cc0000664000175000017500000001361415160212070022423 0ustar alastairalastair/* * (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 "eckit/log/Bytes.h" #include "eckit/types/FloatCompare.h" #include "atlas/mesh/Elements.h" #include "atlas/mesh/HybridElements.h" #include "atlas/mesh/Mesh.h" #include "atlas/mesh/Nodes.h" #include "atlas/mesh/detail/MeshImpl.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/runtime/Log.h" #include "atlas/util/Unique.h" namespace atlas { namespace mesh { namespace detail { //---------------------------------------------------------------------------------------------------------------------- PartitionGraph* build_partition_graph(const MeshImpl& mesh) { ATLAS_TRACE("build_partition_graph..."); const auto& comm = mpi::comm(mesh.mpi_comm()); const int mpi_size = int(comm.size()); const util::Polygon& poly = mesh.polygon(); std::vector polygon; polygon.reserve(poly.size() * 2); auto xy = array::make_view(mesh.nodes().xy()); for (idx_t node : poly) { polygon.push_back(xy(node, XX)); polygon.push_back(xy(node, YY)); } eckit::mpi::Buffer recv_polygons(mpi_size); comm.allGatherv(polygon.begin(), polygon.end(), recv_polygons); using PolygonXY = std::vector; std::vector polygons(mpi_size); for (idx_t p = 0; p < mpi_size; ++p) { for (idx_t j = 0; j < recv_polygons.counts[p] / 2; ++j) { PointXY pxy(*(recv_polygons.begin() + recv_polygons.displs[p] + 2 * j + XX), *(recv_polygons.begin() + recv_polygons.displs[p] + 2 * j + YY)); polygons[p].push_back(pxy); } } std::map> uid_2_parts; idx_t jpart = 0; for (const PolygonXY& _polygon : polygons) { for (const PointXY& pxy : _polygon) { PointLonLat pll = pxy; if (eckit::types::is_strictly_greater(0., pll.lon())) { pll.lon() += 360.; } if (eckit::types::is_approximately_greater_or_equal(pll.lon(), 360.)) { pll.lon() -= 360.; } uidx_t uid = util::unique_lonlat(pll.data()); uid_2_parts[uid].insert(jpart); } ++jpart; } std::vector> graph(mpi_size); for (const auto& u2p : uid_2_parts) { const std::set& parts = u2p.second; for (idx_t jpart : parts) { for (idx_t ipart : parts) { if (jpart != ipart) { graph[jpart].insert(ipart); } } } } std::vector counts(mpi_size); std::vector displs(mpi_size); idx_t values_size = 0; for (idx_t jpart = 0; jpart < mpi_size; ++jpart) { counts[jpart] = graph[jpart].size(); displs[jpart] = values_size; values_size += counts[jpart]; } std::vector values; values.reserve(values_size); for (const std::set& graph_node : graph) { for (idx_t v : graph_node) { values.push_back(v); } } return new PartitionGraph(values.data(), mpi_size, displs.data(), counts.data()); } size_t PartitionGraph::footprint() const { size_t size = sizeof(*this); size += sizeof(idx_t) * displs_.capacity(); size += sizeof(idx_t) * counts_.capacity(); size += sizeof(idx_t) * values_.capacity(); return size; } idx_t PartitionGraph::size() const { return displs_.size(); } PartitionGraph::Neighbours PartitionGraph::nearestNeighbours(const idx_t partition) const { return Neighbours(values_.data() + displs_[partition], values_.data() + displs_[partition] + counts_[partition]); } PartitionGraph::PartitionGraph() = default; PartitionGraph::PartitionGraph(idx_t values[], idx_t rows, idx_t displs[], idx_t counts[]) { displs_.assign(displs, displs + rows); counts_.assign(counts, counts + rows); values_.assign(values, values + displs[rows - 1] + counts[rows - 1]); for (idx_t jpart = 0; jpart < rows; ++jpart) { for (idx_t neighbour : nearestNeighbours(jpart)) { bool found(false); for (idx_t nextneighbour : nearestNeighbours(neighbour)) { if (nextneighbour == jpart) { found = true; } } if (not found) { values_.insert(values_.begin() + displs_[neighbour] + counts_[neighbour], jpart); counts_[neighbour]++; for (idx_t j = neighbour + 1; j < rows; ++j) { displs_[j]++; } } } } maximum_nearest_neighbours_ = 0; for (idx_t n : counts_) { maximum_nearest_neighbours_ = std::max(n, maximum_nearest_neighbours_); } } idx_t PartitionGraph::maximumNearestNeighbours() const { return maximum_nearest_neighbours_; } void PartitionGraph::print(std::ostream& os) const { for (idx_t jpart = 0; jpart < size(); ++jpart) { os << std::setw(3) << jpart << " : "; for (idx_t v : nearestNeighbours(jpart)) { os << std::setw(3) << v << " "; } os << '\n'; } os << "partition graph maximum neighbours = " << maximumNearestNeighbours() << '\n'; os << "partition graph footprint = " << eckit::Bytes(footprint()); } PartitionGraph::operator bool() const { return size(); } std::ostream& operator<<(std::ostream& s, const PartitionGraph& p) { p.print(s); return s; } //---------------------------------------------------------------------------------------------------------------------- } // namespace detail } // namespace mesh } // namespace atlas atlas-0.46.0/src/atlas/mesh/detail/MeshBuilderIntf.cc0000664000175000017500000000421715160212070022513 0ustar alastairalastair/* * (C) Copyright 2023 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "atlas/mesh/detail/MeshBuilderIntf.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Log.h" namespace atlas { namespace mesh { //---------------------------------------------------------------------------------------------------------------------- // C wrapper interfaces to C++ routines TriangularMeshBuilder* atlas__TriangularMeshBuilder__new() { return new TriangularMeshBuilder(); } void atlas__TriangularMeshBuilder__delete(TriangularMeshBuilder* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialisd atlas_TriangularMeshBuilder"); delete This; } Mesh::Implementation* atlas__TriangularMeshBuilder__operator(TriangularMeshBuilder* This, size_t nb_nodes, const gidx_t node_global_index[], const double x[], const double y[], size_t xstride, size_t ystride, const double lon[], const double lat[], size_t lonstride, size_t latstride, size_t nb_triags, const gidx_t triangle_global_index[], const gidx_t triangle_nodes_global_index[], gidx_t global_index_base) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialisd atlas_TriangularMeshBuilder"); Mesh::Implementation* m; { Mesh mesh = This->operator()(nb_nodes, node_global_index, x, y, xstride, ystride, lon, lat, lonstride, latstride, nb_triags, triangle_global_index, triangle_nodes_global_index, global_index_base); mesh.get()->attach(); m = mesh.get(); } m->detach(); return m; } //---------------------------------------------------------------------------------------------------------------------- } // namespace mesh } // namespace atlas atlas-0.46.0/src/atlas/mesh/detail/AccumulateFacets.h0000664000175000017500000000316515160212070022543 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include "atlas/library/config.h" namespace atlas { namespace mesh { class HybridElements; } } // namespace atlas namespace atlas { namespace mesh { class Nodes; } } // namespace atlas namespace atlas { namespace mesh { namespace detail { // currently only supports 2D meshes. Little work needed for 3D. void accumulate_facets(const mesh::HybridElements& cells, const mesh::Nodes& nodes, std::vector& facet_nodes_data, // shape(nb_facets,nb_nodes_per_facet) std::vector& connectivity_facet_to_elem, idx_t& nb_facets, idx_t& nb_inner_facets, idx_t& missing_value); // currently only supports 2D meshes. Little work needed for 3D. void accumulate_facets_ordered_by_halo(const mesh::HybridElements& cells, const mesh::Nodes& nodes, std::vector& facet_nodes_data, // shape(nb_facets,nb_nodes_per_facet) std::vector& connectivity_facet_to_elem, idx_t& nb_facets, idx_t& nb_inner_facets, idx_t& missing_value, std::vector& halo_offsets); } // namespace detail } // namespace mesh } // namespace atlas atlas-0.46.0/src/atlas/mesh/detail/MeshIntf.h0000664000175000017500000000244715160212070021051 0ustar alastairalastair/* * (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/Mesh.h" //---------------------------------------------------------------------------------------------------------------------- namespace atlas { namespace mesh { // C wrapper interfaces to C++ routines extern "C" { Mesh::Implementation* atlas__Mesh__new(); void atlas__Mesh__delete(Mesh::Implementation* This); Nodes* atlas__Mesh__nodes(Mesh::Implementation* This); Edges* atlas__Mesh__edges(Mesh::Implementation* This); Cells* atlas__Mesh__cells(Mesh::Implementation* This); size_t atlas__Mesh__footprint(Mesh::Implementation* This); void atlas__Mesh__update_device(Mesh::Implementation* This); void atlas__Mesh__update_host(Mesh::Implementation* This); void atlas__Mesh__sync_host_device(Mesh::Implementation* This); } //---------------------------------------------------------------------------------------------------------------------- } // namespace mesh } // namespace atlas atlas-0.46.0/src/atlas/mesh/detail/MeshImpl.cc0000664000175000017500000001355215160212070021207 0ustar alastairalastair/* * (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 "eckit/types/FloatCompare.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/detail/MeshImpl.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/runtime/Exception.h" using atlas::Grid; using atlas::Projection; namespace atlas { namespace mesh { namespace detail { //---------------------------------------------------------------------------------------------------------------------- MeshImpl::MeshImpl(eckit::Stream&) { ATLAS_NOTIMPLEMENTED; } void MeshImpl::encode(eckit::Stream&) const { ATLAS_NOTIMPLEMENTED; } MeshImpl::MeshImpl(): nodes_(new mesh::Nodes()), dimensionality_(2) { metadata_.set("mpi_comm",mpi::comm().name()); createElements(); } MeshImpl::~MeshImpl() { while (mesh_observers_.size()) { MeshObserver* o = mesh_observers_.back(); o->onMeshDestruction(*this); o->unregisterMesh(*this); // will also delete observer from mesh } } void MeshImpl::print(std::ostream&) const {} size_t MeshImpl::footprint() const { size_t size = sizeof(*this); size += metadata_.footprint(); if (nodes_) { size += nodes_->footprint(); } if (cells_) { size += cells_->footprint(); } if (facets_) { size += facets_->footprint(); } if (ridges_) { size += ridges_->footprint(); } if (peaks_) { size += peaks_->footprint(); } if (partition_graph_) { size += partition_graph_->footprint(); } for (const auto& polygon : polygons_) { if (polygon) { size += polygon->footprint(); } } return size; } void MeshImpl::createElements() { cells_.reset(new HybridElements()); facets_.reset(new HybridElements()); ridges_.reset(new HybridElements()); peaks_.reset(new HybridElements()); if (dimensionality_ == 2) { edges_ = facets_; } else if (dimensionality_ == 3) { edges_ = ridges_; } else { throw_Exception("Invalid Mesh dimensionality", Here()); } ATLAS_ASSERT(edges_.owners() == 2); } bool MeshImpl::generated() const { return !(cells_->size() == 0 && facets_->size() == 0 && ridges_->size() == 0 && peaks_->size() == 0); } void MeshImpl::setProjection(const Projection& projection) { projection_ = projection; } void MeshImpl::setGrid(const Grid& grid) { grid_ = grid; if (not projection_) { projection_ = grid_.projection(); } } idx_t MeshImpl::nb_parts() const { idx_t n; if (not metadata().get("nb_parts", n)) { n = mpi::comm(mpi_comm()).size(); } return n; } idx_t MeshImpl::part() const { idx_t p; if (not metadata().get("part", p)) { p = mpi::comm(mpi_comm()).rank(); } return p; } std::string MeshImpl::mpi_comm() const { return metadata().getString("mpi_comm"); } void MeshImpl::updateDevice() const { if (nodes_) { nodes_->updateDevice(); } if (cells_) { cells_->updateDevice(); } if (facets_) { facets_->updateDevice(); } if (ridges_) { ridges_->updateDevice(); } if (peaks_) { peaks_->updateDevice(); } } void MeshImpl::updateHost() const { if (nodes_) { nodes_->updateHost(); } if (cells_) { cells_->updateHost(); } if (facets_) { facets_->updateHost(); } if (ridges_) { ridges_->updateHost(); } if (peaks_) { peaks_->updateHost(); } } void MeshImpl::syncHostDevice() const { if (nodes_) { nodes_->syncHostDevice(); } if (cells_) { cells_->syncHostDevice(); } if (facets_) { facets_->syncHostDevice(); } if (ridges_) { ridges_->syncHostDevice(); } if (peaks_) { peaks_->syncHostDevice(); } } const PartitionGraph& MeshImpl::partitionGraph() const { if (not partition_graph_) { partition_graph_.reset(build_partition_graph(*this)); } return *partition_graph_; } PartitionGraph::Neighbours MeshImpl::nearestNeighbourPartitions() const { return partitionGraph().nearestNeighbours(part()); } const PartitionPolygon& MeshImpl::polygon(idx_t halo) const { if (halo >= static_cast(polygons_.size())) { polygons_.resize(halo + 1); } if (not polygons_[halo]) { int mesh_halo = 0; metadata().get("halo", mesh_halo); if (halo > mesh_halo) { throw_Exception("Mesh does not contain a halo of size " + std::to_string(halo) + ".", Here()); } polygons_[halo].reset(new PartitionPolygon(*this, halo)); } return *polygons_[halo]; } const util::PartitionPolygons& MeshImpl::polygons() const { if (all_polygons_.size() == 0) { polygon().allGather(all_polygons_); } return all_polygons_; } void MeshImpl::attachObserver(MeshObserver& observer) const { if (std::find(mesh_observers_.begin(), mesh_observers_.end(), &observer) == mesh_observers_.end()) { mesh_observers_.push_back(&observer); } } void MeshImpl::detachObserver(MeshObserver& observer) const { mesh_observers_.erase(std::remove(mesh_observers_.begin(), mesh_observers_.end(), &observer), mesh_observers_.end()); } //---------------------------------------------------------------------------------------------------------------------- } // namespace detail } // namespace mesh } // namespace atlas atlas-0.46.0/src/atlas/mesh/detail/MeshImpl.h0000664000175000017500000001306515160212070021050 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include #include "atlas/grid/Grid.h" #include "atlas/mesh/PartitionPolygon.h" #include "atlas/mesh/detail/PartitionGraph.h" #include "atlas/projection/Projection.h" #include "atlas/util/Metadata.h" #include "atlas/util/Object.h" #include "atlas/util/ObjectHandle.h" //---------------------------------------------------------------------------------------------------------------------- namespace eckit { class Stream; } namespace atlas { namespace util { class PartitionPolygons; } class Mesh; namespace mesh { class PartitionPolygon; class Nodes; class HybridElements; typedef HybridElements Edges; typedef HybridElements Cells; } // namespace mesh } // namespace atlas //---------------------------------------------------------------------------------------------------------------------- namespace atlas { namespace mesh { namespace detail { //---------------------------------------------------------------------------------------------------------------------- class MeshObserver; class MeshImpl : public util::Object { public: // methods /// @brief Construct a empty MeshImpl explicit MeshImpl(); /// @brief Construct a mesh from a Stream (serialization) explicit MeshImpl(eckit::Stream&); /// @brief Serialization to Stream void encode(eckit::Stream& s) const; /// Destructor /// @note No need to be virtual since this is not a base class. ~MeshImpl(); util::Metadata& metadata() { return metadata_; } const util::Metadata& metadata() const { return metadata_; } void print(std::ostream&) const; const Nodes& nodes() const { return *nodes_; } Nodes& nodes() { return *nodes_; } const Cells& cells() const { return *cells_; } Cells& cells() { return *cells_; } const Edges& edges() const { return *edges_; } Edges& edges() { return *edges_; } const HybridElements& facets() const { return *facets_; } HybridElements& facets() { return *facets_; } const HybridElements& ridges() const { return *ridges_; } HybridElements& ridges() { return *ridges_; } const HybridElements& peaks() const { return *peaks_; } HybridElements& peaks() { return *peaks_; } bool generated() const; /// @brief Return the memory footprint of the mesh size_t footprint() const; idx_t part() const; idx_t nb_parts() const; std::string mpi_comm() const; void updateDevice() const; void updateHost() const; void syncHostDevice() const; const Projection& projection() const { return projection_; } const PartitionGraph& partitionGraph() const; PartitionGraph::Neighbours nearestNeighbourPartitions() const; const PartitionPolygon& polygon(idx_t halo = 0) const; const util::PartitionPolygons& polygons() const; const Grid grid() const { return grid_; } void attachObserver(MeshObserver&) const; void detachObserver(MeshObserver&) const; private: // methods friend class ::atlas::Mesh; friend std::ostream& operator<<(std::ostream& s, const MeshImpl& p) { p.print(s); return s; } void createElements(); void setProjection(const Projection&); void setGrid(const Grid&); private: // members util::Metadata metadata_; util::ObjectHandle nodes_; // dimensionality : 2D | 3D // -------- util::ObjectHandle cells_; // 2D | 3D util::ObjectHandle facets_; // 1D | 2D util::ObjectHandle ridges_; // 0D | 1D util::ObjectHandle peaks_; // NA | 0D util::ObjectHandle edges_; // alias to facets of 2D mesh, ridges of 3D mesh idx_t dimensionality_; Projection projection_; Grid grid_; mutable util::ObjectHandle partition_graph_; mutable std::vector> polygons_; mutable util::PartitionPolygons all_polygons_; // from all partitions mutable std::vector mesh_observers_; }; //---------------------------------------------------------------------------------------------------------------------- class MeshObserver { private: std::vector registered_meshes_; public: void registerMesh(const MeshImpl& mesh) { if (std::find(registered_meshes_.begin(), registered_meshes_.end(), &mesh) == registered_meshes_.end()) { registered_meshes_.push_back(&mesh); mesh.attachObserver(*this); } } void unregisterMesh(const MeshImpl& mesh) { auto found = std::find(registered_meshes_.begin(), registered_meshes_.end(), &mesh); if (found != registered_meshes_.end()) { registered_meshes_.erase(found); mesh.detachObserver(*this); } } virtual ~MeshObserver() { for (auto mesh : registered_meshes_) { mesh->detachObserver(*this); } } virtual void onMeshDestruction(MeshImpl&) = 0; }; //---------------------------------------------------------------------------------------------------------------------- } // namespace detail } // namespace mesh } // namespace atlas atlas-0.46.0/src/atlas/mesh/Connectivity.cu0000664000175000017500000000003315160212070020715 0ustar alastairalastair#include "Connectivity.cc" atlas-0.46.0/src/atlas/mesh/HybridElements.h0000664000175000017500000002424715160212070021012 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ /// @file HybridElements.h /// @author Willem Deconinck /// @date October 2015 /// /// This file describes the HybridElements class for a Mesh. #pragma once #include #include "atlas/util/Object.h" #include "atlas/util/ObjectHandle.h" #include "atlas/field/Field.h" #include "atlas/util/Metadata.h" #include "atlas/mesh/Connectivity.h" namespace atlas { namespace field { class FieldImpl; } } // namespace atlas namespace atlas { class Mesh; } namespace atlas { namespace mesh { class ElementType; } } // namespace atlas namespace atlas { namespace mesh { class Elements; } } // namespace atlas namespace atlas { namespace mesh { template class ConnectivityInterface; class MultiBlockConnectivityImpl; using MultiBlockConnectivity = ConnectivityInterface; } // namespace mesh } // namespace atlas namespace atlas { namespace mesh { // ------------------------------------------------------------------------------- /// @brief HybridElements class that describes elements of different types class HybridElements : public util::Object { friend class Elements; public: typedef MultiBlockConnectivity Connectivity; public: // methods //-- Constructors HybridElements(); virtual ~HybridElements(); //-- Accessors /// @brief Number of elements idx_t size() const; /// @brief Number of nodes for given element idx_t nb_nodes(idx_t elem_idx) const; /// @brief Number of edges for given element idx_t nb_edges(idx_t elem_idx) const; /// @brief Element type index for given element idx_t type_idx(idx_t elem_idx) const; /// @brief Element type name for given element const std::string& name(idx_t elem_idx) const; /// @brief Element to Node connectivity table const HybridElements::Connectivity& node_connectivity() const; HybridElements::Connectivity& node_connectivity(); /// @brief Element to Edge connectivity table const HybridElements::Connectivity& edge_connectivity() const; HybridElements::Connectivity& edge_connectivity(); /// @brief Element to Cell connectivity table const HybridElements::Connectivity& cell_connectivity() const; HybridElements::Connectivity& cell_connectivity(); /// @brief Number of types present in HybridElements idx_t nb_types() const; /// @brief The element_type description for given type const ElementType& element_type(idx_t type_idx) const; /// @brief Sub-elements convenience class for given type /// This allows optimized access to connectivities and loops. const Elements& elements(idx_t type_idx) const; Elements& elements(idx_t type_idx); const Field& field(const std::string& name) const; Field& field(const std::string& name); bool has_field(const std::string& name) const { return (fields_.find(name) != fields_.end()); } const Field& field(idx_t) const; Field& field(idx_t); idx_t nb_fields() const { return static_cast(fields_.size()); } const util::Metadata& metadata() const { return metadata_; } util::Metadata& metadata() { return metadata_; } const Field& global_index() const { return field("glb_idx"); } Field& global_index() { return field("glb_idx"); } const Field& remote_index() const { return field("remote_idx"); } Field& remote_index() { return field("remote_idx"); } const Field& partition() const { return field("partition"); } Field& partition() { return field("partition"); } const Field& halo() const { return field("halo"); } Field& halo() { return field("halo"); } const Field& flags() const { return field("flags"); } Field& flags() { return field("flags"); } // -- Modifiers /// @brief Add a new element type with given number of elements /// @return type_idx of the added element type idx_t add(const ElementType*, idx_t nb_elements); /// @brief Add a new element type with given number of elements and /// node-connectivity /// @return type_idx of the added element type idx_t add(const ElementType*, idx_t nb_elements, const std::vector& node_connectivity); /// @brief Add a new element type with given number of elements and /// node-connectivity /// @return type_idx of the added element type idx_t add(const ElementType*, idx_t nb_elements, const idx_t node_connectivity[]); /// @brief Add a new element type with given number of elements and /// node-connectivity /// @return type_idx of the added element type idx_t add(const ElementType*, idx_t nb_elements, const idx_t node_connectivity[], bool fortran_array); /// @brief Add a new element type from existing Elements. /// Data will be copied. /// @return type_idx of the added element type idx_t add(const Elements&); Field add(const Field& field); void remove_field(const std::string& name); void insert(idx_t type_idx, idx_t position, idx_t nb_elements = 1); void updateDevice() const; void updateHost() const; void syncHostDevice() const; void clear(); /// @brief Return the memory footprint of the elements size_t footprint() const; private: // -- types typedef std::map FieldMap; typedef std::map> ConnectivityMap; private: // -- methods void resize(idx_t size); idx_t elemtype_nb_nodes(idx_t elem_idx) const; idx_t elemtype_nb_edges(idx_t elem_idx) const; Connectivity& add(Connectivity*); private: // -- Data // -- Total number of elements idx_t size_; // -- Data: one value per type std::vector elements_size_; std::vector elements_begin_; std::vector> element_types_; // -- Data: one value per element std::vector type_idx_; // -- Sub elements std::vector> elements_; // -- Fields and connectivities FieldMap fields_; ConnectivityMap connectivities_; // -- Metadata util::Metadata metadata_; // -- Cached shortcuts to specific connectivities in connectivities_ Connectivity* node_connectivity_; Connectivity* edge_connectivity_; Connectivity* cell_connectivity_; }; // ----------------------------------------------------------------------------------------------------- inline idx_t HybridElements::size() const { return size_; } inline idx_t HybridElements::nb_types() const { return static_cast(element_types_.size()); } inline const ElementType& HybridElements::element_type(idx_t type_idx) const { return *element_types_[type_idx].get(); } inline const HybridElements::Connectivity& HybridElements::node_connectivity() const { return *node_connectivity_; } inline HybridElements::Connectivity& HybridElements::node_connectivity() { return *node_connectivity_; } inline const HybridElements::Connectivity& HybridElements::edge_connectivity() const { return *edge_connectivity_; } inline HybridElements::Connectivity& HybridElements::edge_connectivity() { return *edge_connectivity_; } inline const HybridElements::Connectivity& HybridElements::cell_connectivity() const { return *cell_connectivity_; } inline HybridElements::Connectivity& HybridElements::cell_connectivity() { return *cell_connectivity_; } inline const Elements& HybridElements::elements(idx_t type_idx) const { return *elements_[type_idx].get(); } inline Elements& HybridElements::elements(idx_t type_idx) { return *elements_[type_idx].get(); } inline idx_t HybridElements::nb_nodes(idx_t elem_idx) const { return node_connectivity_->rows() ? node_connectivity_->cols(elem_idx) : elemtype_nb_nodes(elem_idx); } inline idx_t HybridElements::nb_edges(idx_t elem_idx) const { return edge_connectivity_->rows() ? edge_connectivity_->cols(elem_idx) : elemtype_nb_edges(elem_idx); } inline idx_t HybridElements::type_idx(idx_t elem_idx) const { return type_idx_[elem_idx]; } // ------------------------------------------------------------------------------------------------------ extern "C" { HybridElements* atlas__mesh__HybridElements__create(); void atlas__mesh__HybridElements__delete(HybridElements* This); MultiBlockConnectivity* atlas__mesh__HybridElements__node_connectivity(HybridElements* This); MultiBlockConnectivity* atlas__mesh__HybridElements__edge_connectivity(HybridElements* This); MultiBlockConnectivity* atlas__mesh__HybridElements__cell_connectivity(HybridElements* This); idx_t atlas__mesh__HybridElements__size(const HybridElements* This); void atlas__mesh__HybridElements__add_elements(HybridElements* This, ElementType* elementtype, idx_t nb_elements); void atlas__mesh__HybridElements__add_elements_with_nodes(HybridElements* This, ElementType* elementtype, idx_t nb_elements, idx_t node_connectivity[], int fortran_array); void atlas__mesh__HybridElements__add_field(HybridElements* This, field::FieldImpl* field); int atlas__mesh__HybridElements__has_field(const HybridElements* This, char* name); int atlas__mesh__HybridElements__nb_fields(const HybridElements* This); int atlas__mesh__HybridElements__nb_types(const HybridElements* This); field::FieldImpl* atlas__mesh__HybridElements__field_by_name(HybridElements* This, char* name); field::FieldImpl* atlas__mesh__HybridElements__field_by_idx(HybridElements* This, idx_t idx); field::FieldImpl* atlas__mesh__HybridElements__global_index(HybridElements* This); field::FieldImpl* atlas__mesh__HybridElements__remote_index(HybridElements* This); field::FieldImpl* atlas__mesh__HybridElements__partition(HybridElements* This); field::FieldImpl* atlas__mesh__HybridElements__halo(HybridElements* This); Elements* atlas__mesh__HybridElements__elements(HybridElements* This, idx_t idx); } } // namespace mesh } // namespace atlas atlas-0.46.0/src/atlas/mesh/Connectivity.h0000664000175000017500000006036215160212070020550 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ /// @file Connectivity.h /// @author Willem Deconinck /// @date October 2015 /// @details /// This file contains Connectivity classes /// - IrregularConnectivity /// - BlockConnectivity /// - MultiBlockConnectivity /// /// It is important to note that connectivity access functions are /// inlined for optimal performance. The connectivity itself is internally /// stored with base 1, to be compatible with Fortran access. /// C++ access operators however convert the resulting connectivity to base 0. #pragma once #include #include #include #include "atlas/array.h" #include "atlas/array/ArrayView.h" #include "atlas/array/DataType.h" #include "atlas/array/IndexView.h" #include "atlas/array/SVector.h" #include "atlas/array_fwd.h" #include "atlas/library/config.h" #include "atlas/util/Object.h" namespace eckit { class Stream; } namespace atlas { namespace mesh { constexpr size_t MAX_STRING_SIZE() { return 60; } template class ConnectivityInterface : public util::Object, DOXYGEN_HIDE(public ConnectivityImpl) { using ConnectivityImpl::ConnectivityImpl; using util::Object::Object; }; // Classes defined in this file: class IrregularConnectivityImpl; class BlockConnectivityImpl; class MultiBlockConnectivityImpl; // -------------------------------------------------------------------------- #ifndef DOXYGEN_SHOULD_SKIP_THIS #if ATLAS_HAVE_FORTRAN #define INDEX_DEREF Index #define INDEX_REF Index #define FROM_FORTRAN -1 #define TO_FORTRAN +1 #else #define INDEX_DEREF * #define INDEX_REF idx_t& #define FROM_FORTRAN #define TO_FORTRAN #endif #endif /// @brief Irregular Connectivity Table /// @author Willem Deconinck /// /// Container for irregular connectivity tables. This is e.g. the case /// for a node-to-X connectivity. /// connectivity = [ /// 1 2 3 4 5 6 # node 1 /// 7 8 # node 2 /// 9 10 11 12 # node 3 /// 13 14 15 # node 4 /// 16 17 18 19 20 21 # node 5 /// ] /// There are 2 modes of construction: /// - It wraps existing raw data /// - It owns the connectivity data /// /// In case ATLAS_HAVE_FORTRAN is defined (which is usually the case), /// the raw data will be stored with base 1 for Fortran interoperability. /// The operator(row,col) will then do the conversion to base 0. /// /// In the first mode of construction, the connectivity table cannot be resized. /// In the second mode of construction, resizing is possible namespace detail { // FortranIndex: // Helper class that does +1 and -1 operations on stored values class ConnectivityIndex { public: enum { BASE = 1 }; public: ATLAS_HOST_DEVICE ConnectivityIndex(const ConnectivityIndex& other) { set(other.get()); } ATLAS_HOST_DEVICE ConnectivityIndex(idx_t* idx): idx_(idx) {} ATLAS_HOST_DEVICE void set(const idx_t& value) { *(idx_) = value + BASE; } ATLAS_HOST_DEVICE idx_t get() const { return *(idx_)-BASE; } ATLAS_HOST_DEVICE void operator=(const idx_t& value) { set(value); } ATLAS_HOST_DEVICE ConnectivityIndex& operator+(const idx_t& value) { *(idx_) += value; return *this; } ATLAS_HOST_DEVICE ConnectivityIndex& operator-(const idx_t& value) { *(idx_) -= value; return *this; } ATLAS_HOST_DEVICE ConnectivityIndex& operator--() { --(*(idx_)); return *this; } ATLAS_HOST_DEVICE ConnectivityIndex& operator++() { ++(*(idx_)); return *this; } // implicit conversion ATLAS_HOST_DEVICE operator idx_t() const { return get(); } private: idx_t* idx_; }; } // namespace detail class ConnectivityRow { #if ATLAS_HAVE_FORTRAN using Index = detail::ConnectivityIndex; #else using Index = idx_t&; #endif public: ATLAS_HOST_DEVICE ConnectivityRow(idx_t* data, idx_t size): data_(data), size_(size) {} template ATLAS_HOST_DEVICE idx_t operator()(Int i) const { return data_[i] FROM_FORTRAN; } template ATLAS_HOST_DEVICE INDEX_REF operator()(Int i) { return INDEX_DEREF(data_ + i); } ATLAS_HOST_DEVICE idx_t size() const { return size_; } private: idx_t* data_; idx_t size_; }; class IrregularConnectivityImpl { public: typedef ConnectivityRow Row; public: //-- Constructors /// @brief Construct connectivity table that needs resizing a-posteriori /// Data is owned IrregularConnectivityImpl(const std::string& name = ""); /// @brief Construct connectivity table wrapping existing raw data. /// No resizing can be performed as data is not owned. IrregularConnectivityImpl(idx_t values[], idx_t rows, idx_t displs[], idx_t counts[]); #if ATLAS_HIC_COMPILER /// @brief Copy ctr (only to be used when calling a cuda kernel) // This ctr has to be defined in the header, since __CUDACC__ will identify // whether // it is compiled it for a GPU kernel IrregularConnectivityImpl(const IrregularConnectivityImpl& other): owns_(false), values_(other.values_), displs_(other.displs_), counts_(other.counts_), missing_value_(other.missing_value_), rows_(other.rows_), maxcols_(other.maxcols_), mincols_(other.mincols_), ctxt_(nullptr) {} #endif /// @brief Construct a mesh from a Stream (serialization) explicit IrregularConnectivityImpl(eckit::Stream&); virtual ~IrregularConnectivityImpl(); //-- Accessors /// @brief Name associated to this Connetivity const std::string name() const { return std::string(name_); } /// @brief Rename this Connectivity void rename(const std::string& name) { std::strncpy(name_, name.c_str(), std::max(name.size(), MAX_STRING_SIZE())); } /// @brief Number of rows in the connectivity table ATLAS_HOST_DEVICE idx_t rows() const { return rows_; } /// @brief Number of columns for specified row in the connectivity table ATLAS_HOST_DEVICE idx_t cols(idx_t row_idx) const { return counts_[row_idx]; } /// @brief Maximum value for number of columns over all rows ATLAS_HOST_DEVICE idx_t maxcols() const { return maxcols_; } /// @brief Minimum value for number of columns over all rows ATLAS_HOST_DEVICE idx_t mincols() const { return mincols_; } /// @brief Access to connectivity table elements for given row and column /// The returned index has base 0 regardless if ATLAS_HAVE_FORTRAN is defined. ATLAS_HOST_DEVICE idx_t operator()(idx_t row_idx, idx_t col_idx) const; idx_t size() const { return values_.size(); } ATLAS_HOST_DEVICE idx_t missing_value() const { return missing_value_; } ATLAS_HOST_DEVICE Row row(idx_t row_idx) const; // -- Modifiers /// @brief Modify row with given values. Values must be given with base 0 void set(idx_t row_idx, const idx_t column_values[]); /// @brief Modify (row,col) with given value. Value must be given with base 0 void set(idx_t row_idx, idx_t col_idx, const idx_t value); /// @brief Resize connectivity /// @note Can only be used when data is owned. virtual void resize(idx_t old_size, idx_t size, bool initialize, const idx_t values[], bool fortran_array); /// @brief Resize connectivity, and add given rows /// @note Can only be used when data is owned. virtual void add(idx_t rows, idx_t cols, const idx_t values[], bool fortran_array = false); /// @brief Resize connectivity, and add given rows with missing values /// @note Can only be used when data is owned. virtual void add(idx_t rows, idx_t cols); /// @brief Resize connectivity, and add given rows with missing values /// @note Can only be used when data is owned. virtual void add(idx_t rows, const idx_t cols[]); /// @brief Resize connectivity, and copy from a BlockConnectivity virtual void add(const BlockConnectivityImpl& block); /// @brief Resize connectivity, and insert given rows /// @note Can only be used when data is owned. virtual void insert(idx_t position, idx_t rows, idx_t cols, const idx_t values[], bool fortran_array = false); /// @brief Resize connectivity, and insert given rows with missing values /// @note Can only be used when data is owned. virtual void insert(idx_t position, idx_t rows, idx_t cols); /// @brief Resize connectivity, and insert given rows with missing values /// @note Can only be used when data is owned. virtual void insert(idx_t position, idx_t rows, const idx_t cols[]); virtual void clear(); virtual size_t footprint() const; idx_t displs(const idx_t row) const { return displs_[row]; } void dump(std::ostream& os) const; friend std::ostream& operator<<(std::ostream& os, const IrregularConnectivityImpl& p) { p.dump(os); return os; } virtual void encode(eckit::Stream& s) const { encode_(s); } virtual void decode(eckit::Stream& s) { decode_(s); } protected: bool owns() { return owns_; } const idx_t* displs() const { return displs_.data(); } const idx_t* counts() const { return counts_.data(); } /// @brief Serialization to Stream void encode_(eckit::Stream&) const; /// @brief Serialization from Stream void decode_(eckit::Stream&); friend eckit::Stream& operator<<(eckit::Stream& s, const IrregularConnectivityImpl& x) { x.encode(s); return s; } friend eckit::Stream& operator>>(eckit::Stream& s, IrregularConnectivityImpl& x) { x.decode(s); return s; } private: void on_delete(); void on_update(); private: char name_[MAX_STRING_SIZE()]; bool owns_; protected: array::SVector values_; array::SVector displs_; array::SVector counts_; idx_t missing_value_; idx_t rows_; idx_t maxcols_; idx_t mincols_; public: typedef void* ctxt_t; typedef void (*callback_t)(ctxt_t); private: friend class ConnectivityPrivateAccess; ctxt_t ctxt_; callback_t callback_update_; callback_t callback_delete_; }; // ---------------------------------------------------------------------------------------------- /// @brief Connectivity contiguously composed of multiple BlockConnectivityImpl /// @author Willem Deconinck /// /// Container for connectivity tables that are layed out in memory as multiple /// BlockConnectivities stitched together. /// This is e.g. the case for a element-node connectivity, with element-types /// grouped together: /// @code{.sh} /// connectivity = [ /// # triangles (block 0) /// 1 2 3 /// 4 5 6 /// 7 8 9 /// 10 11 12 /// # quadrilaterals (block 1) /// 13 14 15 16 /// 17 18 19 20 /// 21 22 23 24 /// ] /// @endcode /// /// This class can also be interpreted as a atlas::IrregularConnectivity without /// distinction between blocks /// /// There are 2 modes of construction: /// - It wraps existing raw data /// - It owns the connectivity data /// /// In case ATLAS_HAVE_FORTRAN is defined (which is usually the case), /// the raw data will be stored with base 1 for Fortran interoperability. /// The operator(row,col) will then do the conversion to base 0. /// /// In the first mode of construction, the connectivity table cannot be resized. /// In the second mode of construction, resizing is possible class MultiBlockConnectivityImpl : public IrregularConnectivityImpl { public: //-- Constructors /// @brief Construct connectivity table that needs resizing a-posteriori /// Data is owned MultiBlockConnectivityImpl(const std::string& name = ""); /// @brief Construct a mesh from a Stream (serialization) explicit MultiBlockConnectivityImpl(eckit::Stream&); virtual ~MultiBlockConnectivityImpl(); //-- Accessors /// @brief Number of blocks ATLAS_HOST_DEVICE idx_t blocks() const { return blocks_; } /// @brief Access to a block connectivity ATLAS_HOST_DEVICE const BlockConnectivityImpl& block(idx_t block_idx) const { return block_[block_idx]; } ATLAS_HOST_DEVICE BlockConnectivityImpl& block(idx_t block_idx) { return block_[block_idx]; } /// @brief Access to connectivity table elements for given row and column /// The row_idx counts up from 0, from block 0, as in IrregularConnectivity /// The returned index has base 0 regardless if ATLAS_HAVE_FORTRAN is defined. ATLAS_HOST_DEVICE idx_t operator()(idx_t row_idx, idx_t col_idx) const; /// @brief Access to connectivity table elements for given row and column /// The block_row_idx counts up from zero for every block_idx. /// The returned index has base 0 regardless if ATLAS_HAVE_FORTRAN is defined. ATLAS_HOST_DEVICE idx_t operator()(idx_t block_idx, idx_t block_row_idx, idx_t block_col_idx) const; //-- Modifiers /// @brief Resize connectivity, and add given rows as a new block /// @note Can only be used when data is owned. virtual void add(idx_t rows, idx_t cols, const idx_t values[], bool fortran_array = false); /// @brief Resize connectivity, and copy from a BlockConnectivity to a new /// block /// @note Can only be used when data is owned. virtual void add(const BlockConnectivityImpl&); /// @brief Resize connectivity, and add given rows with missing values /// @note Can only be used when data is owned. virtual void add(idx_t rows, idx_t cols); /// @brief Resize connectivity, and add given rows with missing values /// @note Can only be used when data is owned. virtual void add(idx_t rows, const idx_t cols[]); /// @brief Resize connectivity, and insert given rows /// @note Can only be used when data is owned. virtual void insert(idx_t position, idx_t rows, idx_t cols, const idx_t values[], bool fortran_array = false); /// @brief Resize connectivity, and insert given rows with missing values /// @note Can only be used when data is owned. virtual void insert(idx_t position, idx_t rows, idx_t cols); /// @brief Resize connectivity, and insert given rows with missing values /// @note Can only be used when data is owned. virtual void insert(idx_t position, idx_t rows, const idx_t cols[]); virtual void clear(); virtual size_t footprint() const; virtual void encode(eckit::Stream& s) const { IrregularConnectivityImpl::encode_(s); encode_(s); } virtual void decode(eckit::Stream& s) { IrregularConnectivityImpl::decode_(s); decode_(s); } protected: /// @brief Serialization to Stream void encode_(eckit::Stream&) const; /// @brief Serialization from Stream void decode_(eckit::Stream&); friend eckit::Stream& operator<<(eckit::Stream& s, const MultiBlockConnectivityImpl& x) { x.encode(s); return s; } friend eckit::Stream& operator>>(eckit::Stream& s, MultiBlockConnectivityImpl& x) { x.decode(s); return s; } private: void rebuild_block_connectivity(); private: idx_t blocks_; array::SVector block_displs_; array::SVector block_cols_; array::SVector block_; }; // ----------------------------------------------------------------------------------------------------- /// @brief Block Connectivity table /// @author Willem Deconinck /// Container for connectivity tables that are layed out in memory as a block. /// Every row has the same /// number of columns. /// /// There are 2 modes of construction: /// - It wraps existing raw data /// - It owns the connectivity data /// /// In case ATLAS_HAVE_FORTRAN is defined (which is usually the case), /// the raw data will be stored with base 1 for Fortran interoperability. /// The operator(row,col) will then do the conversion to base 0. /// /// In the first mode of construction, the connectivity table cannot be resized. /// In the second mode of construction, resizing is possible class BlockConnectivityImpl { private: friend class IrregularConnectivityImpl; friend class MultiBlockConnectivityImpl; BlockConnectivityImpl(idx_t rows, idx_t cols, idx_t values[], bool dummy); public: //-- Constructors /// @brief Construct connectivity table that needs resizing a-posteriori /// Data is owned BlockConnectivityImpl(); BlockConnectivityImpl(idx_t rows, idx_t cols, const std::initializer_list&); /// @brief Construct connectivity table wrapping existing raw data. /// No resizing can be performed as data is not owned. BlockConnectivityImpl(idx_t rows, idx_t cols, idx_t values[]); #if ATLAS_HIC_COMPILER /// @brief Copy ctr (only to be used when calling a cuda kernel) // This ctr has to be defined in the header, since __CUDACC__ will identify // whether it is compiled it for a GPU kernel BlockConnectivityImpl(const BlockConnectivityImpl& other): owns_(false), values_(other.values_), rows_(other.rows_), cols_(other.cols_), missing_value_(other.missing_value_) {} #endif BlockConnectivityImpl(BlockConnectivityImpl&& other); BlockConnectivityImpl& operator=(const BlockConnectivityImpl& other); BlockConnectivityImpl& operator=(BlockConnectivityImpl&& other); /// @brief Construct a mesh from a Stream (serialization) explicit BlockConnectivityImpl(eckit::Stream&); /// @brief Destructor ~BlockConnectivityImpl(); ATLAS_HOST_DEVICE idx_t index(idx_t i, idx_t j) const; //-- Accessors /// @brief Access to connectivity table elements for given row and column /// The returned index has base 0 regardless if ATLAS_HAVE_FORTRAN is defined. ATLAS_HOST_DEVICE idx_t operator()(idx_t row_idx, idx_t col_idx) const; /// @brief Number of rows ATLAS_HOST_DEVICE idx_t rows() const { return rows_; } /// @brief Number of columns ATLAS_HOST_DEVICE idx_t cols() const { return cols_; } /// @brief Access to raw data. /// Note that the connectivity base is 1 in case ATLAS_HAVE_FORTRAN is /// defined. ATLAS_HOST_DEVICE const idx_t* data() const { return values_.data(); } ATLAS_HOST_DEVICE idx_t* data() { return values_.data(); } ATLAS_HOST_DEVICE idx_t missing_value() const { return missing_value_; } size_t footprint() const; //-- Modifiers /// @brief Modify row with given values. Values must be given with base 0 ATLAS_HOST_DEVICE void set(idx_t row_idx, const idx_t column_values[]); /// @brief Modify (row,col) with given value. Value must be given with base 0 ATLAS_HOST_DEVICE void set(idx_t row_idx, idx_t col_idx, const idx_t value); /// @brief Resize connectivity, and add given rows /// @note Can only be used when data is owned. void add(idx_t rows, idx_t cols, const idx_t values[], bool fortran_array = false); bool owns() const { return owns_; } friend std::ostream& operator<<(std::ostream& out, const BlockConnectivityImpl& x) { x.print(out); return out; } protected: /// @brief Wrap existing and set owns_ = false void rebuild(idx_t rows, idx_t cols, idx_t values[]); /// @brief Serialization to Stream void encode(eckit::Stream&) const; /// @brief Serialization from Stream void decode(eckit::Stream&); friend eckit::Stream& operator<<(eckit::Stream& s, const BlockConnectivityImpl& x) { x.encode(s); return s; } friend eckit::Stream& operator>>(eckit::Stream& s, BlockConnectivityImpl& x) { x.decode(s); return s; } void print(std::ostream&) const; private: bool owns_; array::SVector values_; idx_t rows_; idx_t cols_; idx_t missing_value_; }; using IrregularConnectivity = ConnectivityInterface; using MultiBlockConnectivity = ConnectivityInterface; using BlockConnectivity = BlockConnectivityImpl; using Connectivity = IrregularConnectivity; // ----------------------------------------------------------------------------------------------------- ATLAS_HOST_DEVICE inline idx_t IrregularConnectivityImpl::operator()(idx_t row_idx, idx_t col_idx) const { assert(counts_[row_idx] > (col_idx)); return values_[displs_[row_idx] + col_idx] FROM_FORTRAN; } inline void IrregularConnectivityImpl::set(idx_t row_idx, const idx_t column_values[]) { const idx_t N = counts_[row_idx]; for (idx_t n = 0; n < N; ++n) { values_[displs_[row_idx] + n] = column_values[n] TO_FORTRAN; } } inline void IrregularConnectivityImpl::set(idx_t row_idx, idx_t col_idx, const idx_t value) { assert(col_idx < counts_[row_idx]); values_[displs_[row_idx] + col_idx] = value TO_FORTRAN; } ATLAS_HOST_DEVICE inline IrregularConnectivityImpl::Row IrregularConnectivityImpl::row(idx_t row_idx) const { return IrregularConnectivityImpl::Row(const_cast(values_.data()) + displs_(row_idx), counts_(row_idx)); } // ----------------------------------------------------------------------------------------------------- ATLAS_HOST_DEVICE inline idx_t MultiBlockConnectivityImpl::operator()(idx_t row_idx, idx_t col_idx) const { return IrregularConnectivityImpl::operator()(row_idx, col_idx); } ATLAS_HOST_DEVICE inline idx_t MultiBlockConnectivityImpl::operator()(idx_t block_idx, idx_t block_row_idx, idx_t block_col_idx) const { return block(block_idx)(block_row_idx, block_col_idx); } // ----------------------------------------------------------------------------------------------------- ATLAS_HOST_DEVICE inline idx_t BlockConnectivityImpl::operator()(idx_t row_idx, idx_t col_idx) const { return values_[index(row_idx, col_idx)] FROM_FORTRAN; } ATLAS_HOST_DEVICE inline void BlockConnectivityImpl::set(idx_t row_idx, const idx_t column_values[]) { for (idx_t n = 0; n < cols_; ++n) { values_[index(row_idx, n)] = column_values[n] TO_FORTRAN; } } ATLAS_HOST_DEVICE inline void BlockConnectivityImpl::set(idx_t row_idx, idx_t col_idx, const idx_t value) { values_[index(row_idx, col_idx)] = value TO_FORTRAN; } ATLAS_HOST_DEVICE inline idx_t BlockConnectivityImpl::index(idx_t i, idx_t j) const { return i * cols_ + j; } // ------------------------------------------------------------------------------------------------------ extern "C" { Connectivity* atlas__Connectivity__create(); MultiBlockConnectivity* atlas__MultiBlockConnectivity__create(); const char* atlas__Connectivity__name(Connectivity* This); void atlas__Connectivity__rename(Connectivity* This, const char* name); void atlas__Connectivity__delete(Connectivity* This); void atlas__Connectivity__displs(Connectivity* This, idx_t*& displs, idx_t& size); void atlas__Connectivity__counts(Connectivity* This, idx_t*& counts, idx_t& size); void atlas__Connectivity__values(Connectivity* This, idx_t*& values, idx_t& size); idx_t atlas__Connectivity__rows(const Connectivity* This); void atlas__Connectivity__add_values(Connectivity* This, idx_t rows, idx_t cols, idx_t values[]); void atlas__Connectivity__add_missing(Connectivity* This, idx_t rows, idx_t cols); idx_t atlas__Connectivity__missing_value(const Connectivity* This); idx_t atlas__MultiBlockConnectivity__blocks(const MultiBlockConnectivity* This); BlockConnectivityImpl* atlas__MultiBlockConnectivity__block(MultiBlockConnectivity* This, idx_t block_idx); idx_t atlas__BlockConnectivity__rows(const BlockConnectivityImpl* This); idx_t atlas__BlockConnectivity__cols(const BlockConnectivityImpl* This); idx_t atlas__BlockConnectivity__missing_value(const BlockConnectivityImpl* This); void atlas__BlockConnectivity__data(BlockConnectivityImpl* This, idx_t*& data, idx_t& rows, idx_t& cols); void atlas__BlockConnectivity__delete(BlockConnectivityImpl* This); } #undef FROM_FORTRAN #undef TO_FORTRAN #undef INDEX_DEREF //------------------------------------------------------------------------------------------------------ } // namespace mesh namespace array { // TODO HACK // template<> inline DataType::kind_t // DataType::kind() { return KIND_INT32; } } } // namespace atlas atlas-0.46.0/src/atlas/mesh/HybridElements.cc0000664000175000017500000003433315160212070021145 0ustar alastairalastair/* * (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 "eckit/log/Bytes.h" #include "atlas/array/MakeView.h" #include "atlas/field/Field.h" #include "atlas/library/config.h" #include "atlas/mesh/ElementType.h" #include "atlas/mesh/Elements.h" #include "atlas/mesh/HybridElements.h" #include "atlas/mesh/Mesh.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Log.h" #if ATLAS_HAVE_FORTRAN #define FORTRAN_BASE 1 #define TO_FORTRAN +1 #else #define FORTRAN_BASE 0 #endif using atlas::array::ArrayView; using atlas::array::IndexView; using atlas::array::make_datatype; using atlas::array::make_indexview; using atlas::array::make_shape; using atlas::array::make_view; namespace atlas { namespace mesh { //------------------------------------------------------------------------------------------------------ namespace { static void set_uninitialized_fields_to_zero(HybridElements& elems, idx_t begin) { ArrayView global_index = make_view(elems.global_index()); IndexView remote_index = make_indexview(elems.remote_index()); ArrayView partition = make_view(elems.partition()); ArrayView halo = make_view(elems.halo()); ArrayView flags = make_view(elems.flags()); for (idx_t j = begin; j < elems.size(); ++j) { global_index(j) = 0; remote_index(j) = 0; partition(j) = 0; halo(j) = 0; flags(j) = 0; } } } // namespace //------------------------------------------------------------------------------------------------------ HybridElements::HybridElements(): size_(0), elements_size_(), elements_begin_(1, 0ul), type_idx_() { add(Field("glb_idx", make_datatype(), make_shape(size()))); add(Field("remote_idx", make_datatype(), make_shape(size()))); add(Field("partition", make_datatype(), make_shape(size()))); add(Field("halo", make_datatype(), make_shape(size()))); add(Field("flags", make_datatype(), make_shape(size()))); set_uninitialized_fields_to_zero(*this, 0); node_connectivity_ = &add(new Connectivity("node")); edge_connectivity_ = &add(new Connectivity("edge")); cell_connectivity_ = &add(new Connectivity("cell")); } HybridElements::~HybridElements() = default; Field HybridElements::add(const Field& field) { ATLAS_ASSERT(field); ATLAS_ASSERT(!field.name().empty()); if (has_field(field.name())) { std::stringstream msg; msg << "Trying to add field '" << field.name() << "' to HybridElements, but HybridElements already has a field with " "this name."; throw_Exception(msg.str(), Here()); } fields_[field.name()] = field; return field; } void HybridElements::resize(idx_t size) { idx_t old_size = size_; size_ = size; for (FieldMap::iterator it = fields_.begin(); it != fields_.end(); ++it) { Field& field = it->second; array::ArrayShape shape = field.shape(); shape[0] = size_; field.resize(shape); } set_uninitialized_fields_to_zero(*this, old_size); } void HybridElements::remove_field(const std::string& name) { if (!has_field(name)) { std::stringstream msg; msg << "Trying to remove field `" << name << "' in HybridElements, but no field with this name is present in HybridElements."; throw_Exception(msg.str(), Here()); } fields_.erase(name); } const Field& HybridElements::field(const std::string& name) const { if (!has_field(name)) { std::stringstream msg; msg << "Trying to access field `" << name << "' in HybridElements, but no field with this name is present in HybridElements."; throw_Exception(msg.str(), Here()); } return fields_.find(name)->second; } Field& HybridElements::field(const std::string& name) { return const_cast(static_cast(this)->field(name)); } const Field& HybridElements::field(idx_t idx) const { ATLAS_ASSERT(idx < nb_fields()); idx_t c(0); for (FieldMap::const_iterator it = fields_.begin(); it != fields_.end(); ++it) { if (idx == c) { const Field& field = it->second; return field; } c++; } throw_Exception("Should not be here!", Here()); } Field& HybridElements::field(idx_t idx) { return const_cast(static_cast(this)->field(idx)); } HybridElements::Connectivity& HybridElements::add(Connectivity* connectivity) { connectivities_[connectivity->name()] = connectivity; return *connectivity; } idx_t HybridElements::add(const ElementType* element_type, idx_t nb_elements, const std::vector& connectivity) { return add(element_type, nb_elements, connectivity.data()); } idx_t HybridElements::add(const ElementType* element_type, idx_t nb_elements, const idx_t connectivity[]) { return add(element_type, nb_elements, connectivity, false); } idx_t HybridElements::add(const ElementType* element_type, idx_t nb_elements, const idx_t connectivity[], bool fortran_array) { util::ObjectHandle etype(element_type); idx_t old_size = size(); idx_t new_size = old_size + nb_elements; idx_t nb_nodes = etype->nb_nodes(); type_idx_.resize(new_size); for (idx_t e = old_size; e < new_size; ++e) { type_idx_[e] = element_types_.size(); } elements_begin_.push_back(new_size); elements_size_.push_back(nb_elements); element_types_.push_back(etype); elements_.resize(element_types_.size()); for (idx_t t = 0; t < nb_types(); ++t) { if (elements_[t]) { elements_[t]->rebuild(); } else { elements_[t] = util::ObjectHandle(new Elements(*this, t)); } } node_connectivity_->add(nb_elements, nb_nodes, connectivity, fortran_array); resize(new_size); return element_types_.size() - 1; } idx_t HybridElements::add(const ElementType* element_type, idx_t nb_elements) { util::ObjectHandle etype(element_type); idx_t old_size = size(); idx_t new_size = old_size + nb_elements; idx_t nb_nodes = etype->nb_nodes(); type_idx_.resize(new_size); for (idx_t e = old_size; e < new_size; ++e) { type_idx_[e] = element_types_.size(); } elements_begin_.emplace_back(new_size); elements_size_.emplace_back(nb_elements); element_types_.emplace_back(etype); elements_.emplace_back(new Elements(*this, elements_.size())); node_connectivity_->add(nb_elements, nb_nodes); resize(new_size); return element_types_.size() - 1; } idx_t HybridElements::add(const Elements& elems) { bool fortran_array = true; return add(&elems.element_type(), elems.size(), elems.node_connectivity().data(), fortran_array); } const std::string& HybridElements::name(idx_t elem_idx) const { return element_types_[type_idx_[elem_idx]]->name(); } idx_t HybridElements::elemtype_nb_nodes(idx_t elem_idx) const { return element_type(type_idx(elem_idx)).nb_nodes(); } idx_t HybridElements::elemtype_nb_edges(idx_t elem_idx) const { return element_type(type_idx(elem_idx)).nb_edges(); } void HybridElements::insert(idx_t type_idx, idx_t position, idx_t nb_elements) { type_idx_.insert(type_idx_.begin() + position, nb_elements, type_idx); elements_size_[type_idx] += nb_elements; for (idx_t jtype = type_idx + 1; jtype < nb_types() + 1; ++jtype) { elements_begin_[jtype] += nb_elements; } for (idx_t t = 0; t < nb_types(); ++t) { elements_[t]->rebuild(); } node_connectivity_->insert(position, nb_elements, element_types_[type_idx]->nb_nodes()); size_ += nb_elements; for (FieldMap::iterator it = fields_.begin(); it != fields_.end(); ++it) { Field& field = it->second; field.insert(position, nb_elements); } } //----------------------------------------------------------------------------- void HybridElements::clear() { resize(0); for (ConnectivityMap::iterator it = connectivities_.begin(); it != connectivities_.end(); ++it) { it->second->clear(); } size_ = 0; elements_size_.clear(); elements_begin_.resize(1); elements_begin_[0] = 0; element_types_.clear(); type_idx_.clear(); elements_.clear(); } //----------------------------------------------------------------------------- void HybridElements::updateDevice() const { std::for_each(fields_.begin(), fields_.end(), [](const FieldMap::value_type& v) { v.second.updateDevice(); }); } void HybridElements::updateHost() const { std::for_each(fields_.begin(), fields_.end(), [](const FieldMap::value_type& v) { v.second.updateHost(); }); } void HybridElements::syncHostDevice() const { std::for_each(fields_.begin(), fields_.end(), [](const FieldMap::value_type& v) { v.second.syncHostDevice(); }); } size_t HybridElements::footprint() const { size_t size = sizeof(*this); for (FieldMap::const_iterator it = fields_.begin(); it != fields_.end(); ++it) { size += (*it).second.footprint(); } for (ConnectivityMap::const_iterator it = connectivities_.begin(); it != connectivities_.end(); ++it) { size += (*it).second->footprint(); } size += elements_size_.capacity() * sizeof(idx_t); size += elements_begin_.capacity() * sizeof(idx_t); size += metadata_.footprint(); return size; } //----------------------------------------------------------------------------- extern "C" { HybridElements* atlas__mesh__HybridElements__create() { return new HybridElements(); } void atlas__mesh__HybridElements__delete(HybridElements* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_mesh_HybridElements"); delete This; } MultiBlockConnectivity* atlas__mesh__HybridElements__node_connectivity(HybridElements* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_mesh_HybridElements"); return &This->node_connectivity(); } MultiBlockConnectivity* atlas__mesh__HybridElements__edge_connectivity(HybridElements* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_mesh_HybridElements"); return &This->edge_connectivity(); } MultiBlockConnectivity* atlas__mesh__HybridElements__cell_connectivity(HybridElements* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_mesh_HybridElements"); return &This->cell_connectivity(); } idx_t atlas__mesh__HybridElements__size(const HybridElements* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_mesh_HybridElements"); return This->size(); } void atlas__mesh__HybridElements__add_elements(HybridElements* This, ElementType* elementtype, idx_t nb_elements) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_mesh_HybridElements"); This->add(elementtype, nb_elements); } void atlas__mesh__HybridElements__add_elements_with_nodes(HybridElements* This, ElementType* elementtype, idx_t nb_elements, idx_t node_connectivity[], int fortran_array) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_mesh_HybridElements"); ATLAS_ASSERT(elementtype != nullptr, "Cannot access uninitialised atlas_mesh_ElementType"); This->add(elementtype, nb_elements, node_connectivity, fortran_array); } int atlas__mesh__HybridElements__has_field(const HybridElements* This, char* name) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_mesh_HybridElements"); return This->has_field(std::string(name)); } int atlas__mesh__HybridElements__nb_fields(const HybridElements* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_mesh_HybridElements"); return This->nb_fields(); } int atlas__mesh__HybridElements__nb_types(const HybridElements* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_mesh_HybridElements"); return This->nb_types(); } field::FieldImpl* atlas__mesh__HybridElements__field_by_idx(HybridElements* This, idx_t idx) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_mesh_HybridElements"); return This->field(idx).get(); } field::FieldImpl* atlas__mesh__HybridElements__field_by_name(HybridElements* This, char* name) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_mesh_HybridElements"); return This->field(std::string(name)).get(); } field::FieldImpl* atlas__mesh__HybridElements__global_index(HybridElements* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_mesh_HybridElements"); return This->global_index().get(); } field::FieldImpl* atlas__mesh__HybridElements__remote_index(HybridElements* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_mesh_HybridElements"); return This->remote_index().get(); } field::FieldImpl* atlas__mesh__HybridElements__partition(HybridElements* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_mesh_HybridElements"); return This->partition().get(); } field::FieldImpl* atlas__mesh__HybridElements__halo(HybridElements* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_mesh_HybridElements"); return This->halo().get(); } Elements* atlas__mesh__HybridElements__elements(HybridElements* This, idx_t idx) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_mesh_HybridElements"); return &This->elements(idx); } void atlas__mesh__HybridElements__add_field(HybridElements* This, field::FieldImpl* field) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_mesh_HybridElements"); This->add(field); } } //----------------------------------------------------------------------------- } // namespace mesh } // namespace atlas #undef FORTRAN_BASE atlas-0.46.0/src/atlas/mesh/Nodes.h0000664000175000017500000001405515160212070017140 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ /// @file Nodes.h /// @author Willem Deconinck /// @date August 2015 #pragma once #include #include #include "atlas/util/Object.h" #include "atlas/util/ObjectHandle.h" #include "atlas/field/Field.h" #include "atlas/util/Metadata.h" #include "atlas/util/Topology.h" namespace atlas { namespace mesh { template class ConnectivityInterface; class IrregularConnectivityImpl; using IrregularConnectivity = ConnectivityInterface; } // namespace mesh } // namespace atlas namespace atlas { namespace mesh { /** * \brief Nodes class that owns a collection of fields defined in nodes of the * mesh */ class Nodes : public util::Object { public: using Connectivity = IrregularConnectivity; using Topology = util::Topology; public: // methods //-- Constructors /// @brief Construct "size" nodes Nodes(); // Nodes(idx_t size); //-- Accessors const Field& field(const std::string& name) const; Field& field(const std::string& name); bool has_field(const std::string& name) const { return (fields_.find(name) != fields_.end()); } const Field& field(idx_t) const; Field& field(idx_t); idx_t nb_fields() const { return static_cast(fields_.size()); } const util::Metadata& metadata() const { return metadata_; } util::Metadata& metadata() { return metadata_; } const Field& global_index() const { return global_index_; } Field& global_index() { return global_index_; } const Field& remote_index() const { return remote_index_; } Field& remote_index() { return remote_index_; } const Field& partition() const { return partition_; } Field& partition() { return partition_; } const Field& xy() const { return xy_; } Field& xy() { return xy_; } const Field& lonlat() const { return lonlat_; } Field& lonlat() { return lonlat_; } const Field& ghost() const { return ghost_; } Field& ghost() { return ghost_; } const Field& flags() const { return flags_; } Field& flags() { return flags_; } const Field& halo() const { return halo_; } Field& halo() { return halo_; } /// @brief Node to Edge connectivity table const Connectivity& edge_connectivity() const; Connectivity& edge_connectivity(); /// @brief Node to Cell connectivity table const Connectivity& cell_connectivity() const; Connectivity& cell_connectivity(); const Connectivity& connectivity(const std::string& name) const; Connectivity& connectivity(const std::string& name); bool has_connectivity(std::string name) const { return connectivities_.count(name); } idx_t size() const { return size_; } // -- Modifiers Field add(const Field&); void resize(idx_t); void remove_field(const std::string& name); Connectivity& add(Connectivity*); /// @brief Return the memory footprint of the Nodes size_t footprint() const; void updateDevice() const; void updateHost() const; void syncHostDevice() const; private: void print(std::ostream&) const; friend std::ostream& operator<<(std::ostream& s, const Nodes& p) { p.print(s); return s; } private: typedef std::map FieldMap; typedef std::map> ConnectivityMap; private: idx_t size_; FieldMap fields_; ConnectivityMap connectivities_; util::Metadata metadata_; // Cached shortcuts to specific fields in fields_ Field global_index_; Field remote_index_; Field partition_; Field xy_; Field lonlat_; Field ghost_; Field flags_; Field halo_; // Cached shortcuts to specific connectivities in connectivities_ Connectivity* edge_connectivity_; Connectivity* cell_connectivity_; }; inline const Nodes::Connectivity& Nodes::edge_connectivity() const { return *edge_connectivity_; } inline Nodes::Connectivity& Nodes::edge_connectivity() { return *edge_connectivity_; } inline const Nodes::Connectivity& Nodes::cell_connectivity() const { return *cell_connectivity_; } inline Nodes::Connectivity& Nodes::cell_connectivity() { return *cell_connectivity_; } extern "C" { Nodes* atlas__mesh__Nodes__create(); void atlas__mesh__Nodes__delete(Nodes* This); idx_t atlas__mesh__Nodes__size(Nodes* This); void atlas__mesh__Nodes__resize(Nodes* This, idx_t size); idx_t atlas__mesh__Nodes__nb_fields(Nodes* This); void atlas__mesh__Nodes__add_field(Nodes* This, field::FieldImpl* field); void atlas__mesh__Nodes__remove_field(Nodes* This, char* name); int atlas__mesh__Nodes__has_field(Nodes* This, char* name); field::FieldImpl* atlas__mesh__Nodes__field_by_name(Nodes* This, char* name); field::FieldImpl* atlas__mesh__Nodes__field_by_idx(Nodes* This, idx_t idx); util::Metadata* atlas__mesh__Nodes__metadata(Nodes* This); void atlas__mesh__Nodes__str(Nodes* This, char*& str, int& size); IrregularConnectivity* atlas__mesh__Nodes__edge_connectivity(Nodes* This); IrregularConnectivity* atlas__mesh__Nodes__cell_connectivity(Nodes* This); IrregularConnectivity* atlas__mesh__Nodes__connectivity(Nodes* This, char* name); void atlas__mesh__Nodes__add_connectivity(Nodes* This, IrregularConnectivity* connectivity); field::FieldImpl* atlas__mesh__Nodes__xy(Nodes* This); field::FieldImpl* atlas__mesh__Nodes__lonlat(Nodes* This); field::FieldImpl* atlas__mesh__Nodes__global_index(Nodes* This); field::FieldImpl* atlas__mesh__Nodes__remote_index(Nodes* This); field::FieldImpl* atlas__mesh__Nodes__partition(Nodes* This); field::FieldImpl* atlas__mesh__Nodes__ghost(Nodes* This); } //------------------------------------------------------------------------------------------------------ } // namespace mesh } // namespace atlas atlas-0.46.0/src/atlas/mesh/actions/0000775000175000017500000000000015160212070017352 5ustar alastairalastairatlas-0.46.0/src/atlas/mesh/actions/BuildDualMesh.cc0000664000175000017500000004434415160212070022354 0ustar alastairalastair/* * (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/array/ArrayView.h" #include "atlas/array/IndexView.h" #include "atlas/field/Field.h" #include "atlas/functionspace/EdgeColumns.h" #include "atlas/functionspace/NodeColumns.h" #include "atlas/grid/StructuredGrid.h" #include "atlas/library/config.h" #include "atlas/mesh/HybridElements.h" #include "atlas/mesh/Mesh.h" #include "atlas/mesh/Nodes.h" #include "atlas/mesh/actions/BuildDualMesh.h" #include "atlas/parallel/Checksum.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Trace.h" #include "atlas/util/CoordinateEnums.h" #include "atlas/util/Unique.h" using atlas::functionspace::NodeColumns; using atlas::mesh::Halo; namespace atlas { namespace mesh { namespace actions { namespace { void global_bounding_box(const mesh::Nodes& nodes, double min[2], double max[2]) { ATLAS_TRACE(); auto xy = array::make_view(nodes.xy()); const int nb_nodes = nodes.size(); min[XX] = std::numeric_limits::max(); min[YY] = std::numeric_limits::max(); max[XX] = -std::numeric_limits::max(); max[YY] = -std::numeric_limits::max(); for (int node = 0; node < nb_nodes; ++node) { min[XX] = std::min(min[XX], xy(node, XX)); min[YY] = std::min(min[YY], xy(node, YY)); max[XX] = std::max(max[XX], xy(node, XX)); max[YY] = std::max(max[YY], xy(node, YY)); } ATLAS_TRACE_MPI(ALLREDUCE) { mpi::comm().allReduceInPlace(min, 2, eckit::mpi::min()); mpi::comm().allReduceInPlace(max, 2, eckit::mpi::max()); } } struct Node { Node() = default; Node(gidx_t gid, idx_t idx) { g = gid; i = idx; } gidx_t g; idx_t i; bool operator<(const Node& other) const { return (g < other.g); } }; } // namespace array::Array* build_centroids_xy(const mesh::HybridElements&, const Field& xy); void add_centroid_dual_volume_contribution(Mesh& mesh, array::ArrayView& dual_volumes); void add_median_dual_volume_contribution_cells(const mesh::HybridElements& cells, const mesh::HybridElements& edges, const mesh::Nodes& nodes, array::Array& array_dual_volumes); void add_median_dual_volume_contribution_poles(const mesh::HybridElements& edges, const mesh::Nodes& nodes, array::Array& array_dual_volumes); void build_dual_normals(Mesh& mesh); void make_dual_normals_outward(Mesh& mesh); void build_median_dual_mesh(Mesh& mesh) { ATLAS_TRACE(); bool median_dual_mesh = false; mesh.metadata().get("median_dual_mesh", median_dual_mesh); if (median_dual_mesh) { return; } mesh.metadata().set("median_dual_mesh", true); mesh::Nodes& nodes = mesh.nodes(); mesh::HybridElements& edges = mesh.edges(); Field dual_volumes = nodes.add(Field("dual_volumes", array::make_datatype(), array::make_shape(nodes.size()))); if (!mesh.cells().has_field("centroids_xy")) { mesh.cells().add(Field("centroids_xy", build_centroids_xy(mesh.cells(), mesh.nodes().xy()))); } if (!mesh.edges().has_field("centroids_xy")) { mesh.edges().add(Field("centroids_xy", build_centroids_xy(mesh.edges(), mesh.nodes().xy()))); } array::ArrayView v = array::make_view(dual_volumes); v.assign(0.); add_median_dual_volume_contribution_cells(mesh.cells(), mesh.edges(), mesh.nodes(), dual_volumes); add_median_dual_volume_contribution_poles(mesh.edges(), mesh.nodes(), dual_volumes); build_dual_normals(mesh); Field skewness = mesh.edges().add(Field("skewness", array::make_datatype(), array::make_shape(mesh.edges().size()))); Field alpha = mesh.edges().add(Field("alpha", array::make_datatype(), array::make_shape(mesh.edges().size()))); array::make_view(skewness).assign(0.); array::make_view(alpha).assign(0.5); functionspace::NodeColumns nodes_fs(mesh); { ATLAS_TRACE("halo-exchange dual_volumes"); nodes_fs.haloExchange(nodes.field("dual_volumes")); } functionspace::EdgeColumns edges_fs(mesh); { ATLAS_TRACE("halo-exchange dual_normals"); edges_fs.haloExchange(edges.field("dual_normals")); } make_dual_normals_outward(mesh); } array::Array* build_centroids_xy(const mesh::HybridElements& elements, const Field& field_xy) { auto xy = array::make_view(field_xy); array::Array* array_centroids = array::Array::create(array::make_shape(elements.size(), 2)); array::ArrayView centroids = array::make_view(*array_centroids); idx_t nb_elems = elements.size(); const mesh::HybridElements::Connectivity& elem_nodes = elements.node_connectivity(); for (idx_t e = 0; e < nb_elems; ++e) { centroids(e, XX) = 0.; centroids(e, YY) = 0.; const idx_t nb_nodes_per_elem = elem_nodes.cols(e); const double average_coefficient = 1. / static_cast(nb_nodes_per_elem); for (idx_t n = 0; n < nb_nodes_per_elem; ++n) { centroids(e, XX) += xy(elem_nodes(e, n), XX); centroids(e, YY) += xy(elem_nodes(e, n), YY); } centroids(e, XX) *= average_coefficient; centroids(e, YY) *= average_coefficient; } return array_centroids; } void add_median_dual_volume_contribution_cells(const mesh::HybridElements& cells, const mesh::HybridElements& edges, const mesh::Nodes& nodes, array::Array& array_dual_volumes) { ATLAS_TRACE(); auto dual_volumes = array::make_view(array_dual_volumes); auto xy = array::make_view(nodes.xy()); auto cell_centroids = array::make_view(cells.field("centroids_xy")); auto edge_centroids = array::make_view(edges.field("centroids_xy")); const mesh::HybridElements::Connectivity& cell_edge_connectivity = cells.edge_connectivity(); const mesh::HybridElements::Connectivity& edge_node_connectivity = edges.node_connectivity(); auto field_flags = array::make_view(cells.flags()); auto patch = [&field_flags](idx_t e) { using Topology = atlas::mesh::Nodes::Topology; return Topology::check(field_flags(e), Topology::PATCH); }; // special ordering for bit-identical results idx_t nb_cells = cells.size(); std::vector ordering(nb_cells); for (idx_t jcell = 0; jcell < nb_cells; ++jcell) { ordering[jcell] = Node(util::unique_lonlat(cell_centroids(jcell, XX), cell_centroids(jcell, YY)), jcell); } std::sort(ordering.data(), ordering.data() + nb_cells); for (idx_t jcell = 0; jcell < nb_cells; ++jcell) { idx_t icell = ordering[jcell].i; if (patch(icell)) { continue; } double x0 = cell_centroids(icell, XX); double y0 = cell_centroids(icell, YY); for (idx_t jedge = 0; jedge < cell_edge_connectivity.cols(icell); ++jedge) { idx_t iedge = cell_edge_connectivity(icell, jedge); double x1 = edge_centroids(iedge, XX); double y1 = edge_centroids(iedge, YY); for (idx_t jnode = 0; jnode < 2; ++jnode) { idx_t inode = edge_node_connectivity(iedge, jnode); double x2 = xy(inode, XX); double y2 = xy(inode, YY); double triag_area = std::abs(x0 * (y1 - y2) + x1 * (y2 - y0) + x2 * (y0 - y1)) * 0.5; dual_volumes(inode) += triag_area; } } } } void add_median_dual_volume_contribution_poles(const mesh::HybridElements& edges, const mesh::Nodes& nodes, array::Array& array_dual_volumes) { ATLAS_TRACE(); array::ArrayView dual_volumes = array::make_view(array_dual_volumes); auto xy = array::make_view(nodes.xy()); auto edge_centroids = array::make_view(edges.field("centroids_xy")); const mesh::HybridElements::Connectivity& edge_node_connectivity = edges.node_connectivity(); const mesh::HybridElements::Connectivity& edge_cell_connectivity = edges.cell_connectivity(); const idx_t nb_edges = edges.size(); std::map> node_to_bdry_edge; for (idx_t jedge = 0; jedge < nb_edges; ++jedge) { if (edge_cell_connectivity(jedge, 0) != edge_cell_connectivity.missing_value() && edge_cell_connectivity(jedge, 1) == edge_cell_connectivity.missing_value()) { node_to_bdry_edge[edge_node_connectivity(jedge, 0)].push_back(jedge); node_to_bdry_edge[edge_node_connectivity(jedge, 1)].push_back(jedge); } } const double tol = 1.e-6; double min[2], max[2]; global_bounding_box(nodes, min, max); std::map>::iterator it; for (it = node_to_bdry_edge.begin(); it != node_to_bdry_edge.end(); ++it) { const idx_t jnode = (*it).first; std::vector& bdry_edges = (*it).second; const double x0 = xy(jnode, XX); const double y0 = xy(jnode, YY); double x1, y1, y2; for (idx_t jedge = 0; jedge < static_cast(bdry_edges.size()); ++jedge) { const idx_t iedge = bdry_edges[jedge]; x1 = edge_centroids(iedge, XX); y1 = edge_centroids(iedge, YY); y2 = 0.; if (std::abs(y1 - max[YY]) < tol) { y2 = 90.; } else if (std::abs(y1 - min[YY]) < tol) { y2 = -90.; } if (y2 != 0) { const double quad_area = std::abs((x1 - x0) * (y2 - y0)); dual_volumes(jnode) += quad_area; } } } } void build_dual_normals(Mesh& mesh) { ATLAS_TRACE(); array::ArrayView elem_centroids = array::make_view(mesh.cells().field("centroids_xy")); mesh::Nodes& nodes = mesh.nodes(); mesh::HybridElements& edges = mesh.edges(); const idx_t nb_edges = edges.size(); array::ArrayView node_xy = array::make_view(nodes.xy()); double min[2], max[2]; global_bounding_box(nodes, min, max); double tol = 1.e-6; double xl, yl, xr, yr; array::ArrayView edge_centroids = array::make_view(edges.field("centroids_xy")); array::ArrayView dual_normals = array::make_view( edges.add(Field("dual_normals", array::make_datatype(), array::make_shape(nb_edges, 2)))); const mesh::HybridElements::Connectivity& edge_node_connectivity = edges.node_connectivity(); const mesh::HybridElements::Connectivity& edge_cell_connectivity = edges.cell_connectivity(); std::map> node_to_bdry_edge; for (idx_t jedge = 0; jedge < nb_edges; ++jedge) { if (edge_cell_connectivity(jedge, 0) != edge_cell_connectivity.missing_value() && edge_cell_connectivity(jedge, 1) == edge_cell_connectivity.missing_value()) { node_to_bdry_edge[edge_node_connectivity(jedge, 0)].push_back(jedge); node_to_bdry_edge[edge_node_connectivity(jedge, 1)].push_back(jedge); } } for (idx_t edge = 0; edge < nb_edges; ++edge) { if (edge_cell_connectivity(edge, 0) == edge_cell_connectivity.missing_value()) { // this is a pole edge // only compute for one node for (idx_t n = 0; n < 2; ++n) { idx_t node = edge_node_connectivity(edge, n); std::vector& bdry_edges = node_to_bdry_edge[node]; double x[2]; idx_t cnt = 0; const idx_t nb_bdry_edges = static_cast(bdry_edges.size()); for (idx_t jedge = 0; jedge < nb_bdry_edges; ++jedge) { idx_t bdry_edge = bdry_edges[jedge]; if (std::abs(edge_centroids(bdry_edge, YY) - max[YY]) < tol) { edge_centroids(edge, YY) = 90.; x[cnt] = edge_centroids(bdry_edge, XX); ++cnt; } else if (std::abs(edge_centroids(bdry_edge, YY) - min[YY]) < tol) { edge_centroids(edge, YY) = -90.; x[cnt] = edge_centroids(bdry_edge, XX); ++cnt; } } if (cnt == 2) { dual_normals(edge, XX) = 0; if (node_xy(node, YY) < 0.) { dual_normals(edge, YY) = -std::abs(x[1] - x[0]); } else if (node_xy(node, YY) > 0.) { dual_normals(edge, YY) = std::abs(x[1] - x[0]); } // std::cout << "pole dual_normal = " << dual_normals(YY,edge) << // std::endl; break; } } } else { idx_t left_elem = edge_cell_connectivity(edge, 0); idx_t right_elem = edge_cell_connectivity(edge, 1); xl = elem_centroids(left_elem, XX); yl = elem_centroids(left_elem, YY); if (right_elem == edge_cell_connectivity.missing_value()) { xr = edge_centroids(edge, XX); yr = edge_centroids(edge, YY); ; if (std::abs(yr - max[YY]) < tol) { yr = 90.; } else if (std::abs(yr - min[YY]) < tol) { yr = -90.; } } else { xr = elem_centroids(right_elem, XX); yr = elem_centroids(right_elem, YY); } dual_normals(edge, XX) = yl - yr; dual_normals(edge, YY) = -xl + xr; } } } void make_dual_normals_outward(Mesh& mesh) { ATLAS_TRACE(); mesh::Nodes& nodes = mesh.nodes(); array::ArrayView node_xy = array::make_view(nodes.xy()); mesh::HybridElements& edges = mesh.edges(); const mesh::HybridElements::Connectivity& edge_cell_connectivity = edges.cell_connectivity(); const mesh::HybridElements::Connectivity& edge_node_connectivity = edges.node_connectivity(); array::ArrayView dual_normals = array::make_view(edges.field("dual_normals")); const idx_t nb_edges = edges.size(); for (idx_t edge = 0; edge < nb_edges; ++edge) { if (edge_cell_connectivity(edge, 0) != edge_cell_connectivity.missing_value()) { // Make normal point from node 1 to node 2 const idx_t ip1 = edge_node_connectivity(edge, 0); const idx_t ip2 = edge_node_connectivity(edge, 1); double dx = node_xy(ip2, XX) - node_xy(ip1, XX); double dy = node_xy(ip2, YY) - node_xy(ip1, YY); if (dx * dual_normals(edge, XX) + dy * dual_normals(edge, YY) < 0) { dual_normals(edge, XX) = -dual_normals(edge, XX); dual_normals(edge, YY) = -dual_normals(edge, YY); } } } } void build_brick_dual_mesh(const Grid& grid, Mesh& mesh) { auto g = StructuredGrid(grid); if (g) { if (mpi::size() != 1) { throw_Exception("Cannot build_brick_dual_mesh with more than 1 task", Here()); } mesh::Nodes& nodes = mesh.nodes(); auto xy = array::make_view(nodes.xy()); auto dual_volumes = array::make_view( nodes.add(Field("dual_volumes", array::make_datatype(), array::make_shape(nodes.size())))); auto gidx = array::make_view(nodes.global_index()); int c = 0; int n = 0; for (idx_t jlat = 0; jlat < g.ny(); ++jlat) { double lat = g.y(jlat); double latN = (jlat == 0) ? 90. : 0.5 * (lat + g.y(jlat - 1)); double latS = (jlat == g.ny() - 1) ? -90. : 0.5 * (lat + g.y(jlat + 1)); double dlat = (latN - latS); double dlon = 360. / static_cast(g.nx(jlat)); for (idx_t jlon = 0; jlon < g.nx(jlat); ++jlon) { while (gidx(c) != n + 1) { c++; } ATLAS_ASSERT(xy(c, XX) == g.x(jlon, jlat)); ATLAS_ASSERT(xy(c, YY) == lat); dual_volumes(c) = dlon * dlat; ++n; } } functionspace::NodeColumns nodes_fs(mesh); nodes_fs.haloExchange(nodes.field("dual_volumes")); } else { throw_Exception("Cannot build_brick_dual_mesh with mesh provided grid type", Here()); } } void build_centroid_dual_mesh(Mesh&) { ATLAS_NOTIMPLEMENTED; // This requires code below which has not been ported yet } // ------------------------------------------------------------------ // C wrapper interfaces to C++ routines void atlas__build_median_dual_mesh(Mesh::Implementation* mesh) { ATLAS_ASSERT(mesh != nullptr, "Cannot access uninitialised atlas_Mesh"); Mesh m(mesh); build_median_dual_mesh(m); } void atlas__build_centroid_dual_mesh(Mesh::Implementation* mesh) { ATLAS_ASSERT(mesh != nullptr, "Cannot access uninitialised atlas_Mesh"); Mesh m(mesh); build_centroid_dual_mesh(m); } // ------------------------------------------------------------------ } // namespace actions } // namespace mesh } // namespace atlas atlas-0.46.0/src/atlas/mesh/actions/ExtendNodesGlobal.h0000664000175000017500000000165215160212070023070 0ustar alastairalastair/* * (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 namespace atlas { class Mesh; class Grid; } // namespace atlas namespace atlas { namespace mesh { namespace actions { /// Adds virtual nodes to the mesh that aren't contained in the Grid Domain class ExtendNodesGlobal { public: explicit ExtendNodesGlobal(const std::string& gridname = DEFAULT_GRIDNAME); void operator()(const Grid&, Mesh&) const; private: static const std::string DEFAULT_GRIDNAME; std::string gridname_; }; } // namespace actions } // namespace mesh } // namespace atlas atlas-0.46.0/src/atlas/mesh/actions/BuildPeriodicBoundaries.h0000664000175000017500000000213515160212070024256 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once namespace atlas { class Mesh; namespace mesh { namespace actions { /* * Make the mesh periodic * - Find out which nodes at the WEST-BC are master nodes * of nodes at the EAST-BC * - The remote_idx and partition of the EAST-BC nodes * are set to the master nodes at WEST-BC, whereas the * global index remains unchanged */ void build_periodic_boundaries(Mesh& mesh); // ------------------------------------------------------------------ // C wrapper interfaces to C++ routines extern "C" { void atlas__build_periodic_boundaries(Mesh::Implementation* mesh); } // ------------------------------------------------------------------ } // namespace actions } // namespace mesh } // namespace atlas atlas-0.46.0/src/atlas/mesh/actions/BuildCellCentres.cc0000664000175000017500000001172515160212070023052 0ustar alastairalastair/* * (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 "eckit/types/FloatCompare.h" #include "atlas/array/MakeView.h" #include "atlas/field/Field.h" #include "atlas/mesh/HybridElements.h" #include "atlas/mesh/Mesh.h" #include "atlas/mesh/Nodes.h" #include "atlas/mesh/actions/BuildCellCentres.h" #include "atlas/runtime/Trace.h" #include "atlas/util/CoordinateEnums.h" namespace atlas { namespace mesh { namespace actions { //---------------------------------------------------------------------------------------------------------------------- BuildCellCentres::BuildCellCentres(const std::string& field_name, bool force_recompute): field_name_(field_name), force_recompute_(force_recompute), flatten_virtual_elements_(true) {} BuildCellCentres::BuildCellCentres(eckit::Configuration& config): field_name_(config.getString("name", "centre")), force_recompute_(config.getBool("force_recompute", false)), flatten_virtual_elements_(config.getBool("flatten_virtual_elements", true)) {} Field& BuildCellCentres::operator()(Mesh& mesh) const { bool recompute = force_recompute_; if (!mesh.cells().has_field(field_name_)) { mesh.cells().add(Field(field_name_, array::make_datatype(), array::make_shape(mesh.cells().size(), 3))); recompute = true; } if (recompute) { ATLAS_TRACE("BuildCellCentres"); mesh::Nodes& nodes = mesh.nodes(); array::ArrayView coords = array::make_view(nodes.field("xyz")); idx_t firstVirtualPoint = std::numeric_limits::max(); if (nodes.metadata().has("NbRealPts")) { firstVirtualPoint = nodes.metadata().get("NbRealPts"); } idx_t nb_cells = mesh.cells().size(); auto centroids = array::make_view(mesh.cells().field(field_name_)); const mesh::HybridElements::Connectivity& cell_node_connectivity = mesh.cells().node_connectivity(); for (idx_t e = 0; e < nb_cells; ++e) { centroids(e, XX) = 0.; centroids(e, YY) = 0.; centroids(e, ZZ) = 0.; const idx_t nb_cell_nodes = cell_node_connectivity.cols(e); // check for degenerate elements (less than three unique nodes) // NOTE: this is not a proper check but it is very robust eckit::types::CompareApproximatelyEqual approx(1.e-9); idx_t nb_equal_nodes = 0; for (idx_t ni = 0; ni < nb_cell_nodes - 1; ++ni) { idx_t i = cell_node_connectivity(e, ni); Point3 Pi(coords(i, XX), coords(i, YY), coords(i, ZZ)); for (idx_t nj = ni + 1; nj < nb_cell_nodes; ++nj) { idx_t j = cell_node_connectivity(e, nj); Point3 Pj(coords(j, XX), coords(j, YY), coords(j, ZZ)); if (approx(Pi[XX], Pj[XX]) && approx(Pi[YY], Pj[YY]) && approx(Pi[ZZ], Pj[ZZ])) { ++nb_equal_nodes; } } } idx_t nb_unique_nodes = idx_t(nb_cell_nodes) - nb_equal_nodes; if (nb_unique_nodes < 3) { continue; } if (flatten_virtual_elements_) { // calculate centroid by averaging coordinates (uses only "real" nodes) idx_t nb_real_nodes = 0; for (idx_t n = 0; n < nb_cell_nodes; ++n) { const idx_t i = cell_node_connectivity(e, n); if (i < firstVirtualPoint) { ++nb_real_nodes; centroids(e, XX) += coords(i, XX); centroids(e, YY) += coords(i, YY); centroids(e, ZZ) += coords(i, ZZ); } } if (nb_real_nodes > 1) { const double average_coefficient = 1. / static_cast(nb_real_nodes); centroids(e, XX) *= average_coefficient; centroids(e, YY) *= average_coefficient; centroids(e, ZZ) *= average_coefficient; } } else { const double average_coefficient = 1. / static_cast(nb_cell_nodes); for (idx_t n = 0; n < nb_cell_nodes; ++n) { const idx_t i = cell_node_connectivity(e, n); for (idx_t d = 0; d < 3; ++d) { centroids(e, d) += coords(i, d) * average_coefficient; } } } } } return mesh.cells().field(field_name_); } } // namespace actions } // namespace mesh } // namespace atlas atlas-0.46.0/src/atlas/mesh/actions/ReorderReverseCuthillMckee.h0000664000175000017500000000302615160212070024754 0ustar alastairalastair/* * (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 atlas-0.46.0/src/atlas/mesh/actions/WriteLoadBalanceReport.h0000664000175000017500000000171315160212070024061 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once namespace atlas { class Mesh; namespace mesh { namespace actions { void write_load_balance_report(const Mesh& mesh, std::ostream& ofs); void write_load_balance_report(const Mesh& mesh, const std::string& filename); // ------------------------------------------------------------------ // C wrapper interfaces to C++ routines extern "C" { void atlas__write_load_balance_report(Mesh::Implementation* mesh, char* filename); } // ------------------------------------------------------------------ } // namespace actions } // namespace mesh } // namespace atlas atlas-0.46.0/src/atlas/mesh/actions/ExtendNodesGlobal.cc0000664000175000017500000000675415160212070023236 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "atlas/mesh/actions/ExtendNodesGlobal.h" #include "atlas/array.h" #include "atlas/field/Field.h" #include "atlas/grid/Grid.h" #include "atlas/grid/Iterator.h" #include "atlas/mesh/Mesh.h" #include "atlas/mesh/Nodes.h" #include "atlas/runtime/Exception.h" #include "atlas/util/CoordinateEnums.h" #include "atlas/util/Earth.h" namespace atlas { namespace mesh { namespace actions { const std::string ExtendNodesGlobal::DEFAULT_GRIDNAME = "O16"; ExtendNodesGlobal::ExtendNodesGlobal(const std::string& gridname): gridname_(gridname.empty() ? DEFAULT_GRIDNAME : gridname) { ATLAS_ASSERT(!gridname_.empty()); } void ExtendNodesGlobal::operator()(const Grid& grid, Mesh& mesh) const { if (grid.domain().global()) { return; // don't add virtual points to global domains } Grid extension_grid(gridname_); // virtual points std::vector extended_pts; extended_pts.reserve(grid.size()); // loop over the point and keep the ones that *don't* fall in the domain for (const PointLonLat& lonlat : extension_grid.lonlat()) { PointXY xy = grid.projection().xy(lonlat); if (not grid.domain().contains(xy)) { extended_pts.push_back(xy); } } mesh::Nodes& nodes = mesh.nodes(); const idx_t nb_real_pts = nodes.size(); const idx_t nb_extension_pts = extended_pts.size(); idx_t new_size = nodes.size() + extended_pts.size(); nodes.resize(new_size); // resizes the fields const idx_t nb_total_pts = nodes.size(); ATLAS_ASSERT(nb_total_pts == nb_real_pts + nb_extension_pts); nodes.metadata().set("NbRealPts", nb_real_pts); nodes.metadata().set("NbVirtualPts", nb_extension_pts); array::ArrayView xyz = array::make_view(nodes.field("xyz")); array::ArrayView xy = array::make_view(nodes.xy()); array::ArrayView lonlat = array::make_view(nodes.lonlat()); array::ArrayView gidx = array::make_view(nodes.global_index()); array::ArrayView ghost = array::make_view(nodes.ghost()); array::ArrayView partition = array::make_view(nodes.partition()); array::ArrayView flags = array::make_view(nodes.flags()); for (idx_t i = 0; i < nb_extension_pts; ++i) { const idx_t n = nb_real_pts + i; const PointLonLat pLL = grid.projection().lonlat(extended_pts[i]); PointXYZ pXYZ; util::Earth::convertSphericalToCartesian(pLL, pXYZ); xyz(n, XX) = pXYZ.x(); xyz(n, YY) = pXYZ.y(); xyz(n, ZZ) = pXYZ.z(); xy(n, XX) = extended_pts[i].x(); xy(n, YY) = extended_pts[i].y(); lonlat(n, LON) = pLL.lon(); lonlat(n, LAT) = pLL.lat(); gidx(n) = n + 1; ghost(n) = false; partition(n) = 0; util::Topology::view(flags(n)).reset(util::Topology::NONE); // util::Topology::view(flags(n)).set(util::Topology::GHOST); } } } // namespace actions } // namespace mesh } // namespace atlas atlas-0.46.0/src/atlas/mesh/actions/BuildDualMesh.h0000664000175000017500000000361415160212070022211 0ustar alastairalastair/* * (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/library/config.h" #include "atlas/mesh/Mesh.h" namespace atlas { class Grid; namespace mesh { namespace actions { /* * Build median-dual mesh by connecting cell centres with edge centres * - dual_volumes field * - dual_normals field */ void build_median_dual_mesh(Mesh& mesh); /* * Build centroid-dual mesh by connecting cell centres with cell centres * - dual_volumes field * - dual_normals field * - skewness field value 1 at ip1, value -1 at ip2 * - alpha field value 1 at ip1, value 0 at ip2 */ void build_centroid_dual_mesh(Mesh& mesh); /* * Build brick-dual mesh * - dual_volumes field * @note Only works for reduced grids with 1 MPI task * This was urgently coded to show the resolution as IFS sees it. * * # | # | # | # | * | | | | * ----+-+-----+-+-----+-+-----+-+ * | | | | * # | # | # | # | * | | | | * -+----+--+----+--+----+--+----+ * | | | | * | # | # | # | # */ void build_brick_dual_mesh(const Grid& grid, Mesh& mesh); // ------------------------------------------------------------------ // C wrapper interfaces to C++ routines extern "C" { void atlas__build_median_dual_mesh(Mesh::Implementation* mesh); void atlas__build_centroid_dual_mesh(Mesh::Implementation* mesh); } // ------------------------------------------------------------------ } // namespace actions } // namespace mesh } // namespace atlas atlas-0.46.0/src/atlas/mesh/actions/BuildPeriodicBoundaries.cc0000664000175000017500000002113015160212070024410 0ustar alastairalastair/* * (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 "atlas/array.h" #include "atlas/array/ArrayView.h" #include "atlas/array/IndexView.h" #include "atlas/mesh/Mesh.h" #include "atlas/mesh/Nodes.h" #include "atlas/mesh/actions/BuildPeriodicBoundaries.h" #include "atlas/parallel/mpi/Statistics.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/runtime/Exception.h" #include "atlas/util/CoordinateEnums.h" #include "atlas/util/LonLatMicroDeg.h" #include "atlas/util/PeriodicTransform.h" using Topology = atlas::mesh::Nodes::Topology; using atlas::util::LonLatMicroDeg; using atlas::util::PeriodicTransform; namespace atlas { namespace mesh { namespace actions { using uid_t = gidx_t; void build_periodic_boundaries(Mesh& mesh) { ATLAS_TRACE(); bool periodic = false; mesh.metadata().get("periodic", periodic); if (!periodic) { mpi::Scope mpi_scope(mesh.mpi_comm()); auto mpi_size = mpi::size(); auto mypart = mpi::rank(); mesh::Nodes& nodes = mesh.nodes(); auto flags = array::make_view(nodes.flags()); auto ridx = array::make_indexview(nodes.remote_index()); auto part = array::make_view(nodes.partition()); auto ghost = array::make_view(nodes.ghost()); int nb_nodes = nodes.size(); auto xy = array::make_view(nodes.xy()); // Identify my master and slave nodes on own partition // master nodes are at x=0, slave nodes are at x=2pi std::map master_lookup; std::map slave_lookup; std::vector master_nodes; master_nodes.reserve(3 * nb_nodes); std::vector slave_nodes; slave_nodes.reserve(3 * nb_nodes); for (idx_t jnode = 0; jnode < nodes.size(); ++jnode) { if (Topology::check_all(flags(jnode), Topology::BC | Topology::WEST)) { Topology::set(flags(jnode), Topology::PERIODIC); if (part(jnode) == mypart) { LonLatMicroDeg ll(xy(jnode, XX), xy(jnode, YY)); master_lookup[ll.unique()] = jnode; master_nodes.push_back(ll.lon()); master_nodes.push_back(ll.lat()); master_nodes.push_back(jnode); } } else if (Topology::check(flags(jnode), Topology::BC | Topology::EAST)) { Topology::set(flags(jnode), Topology::PERIODIC); Topology::set(flags(jnode), Topology::GHOST); ghost(jnode) = 1; LonLatMicroDeg ll(xy(jnode, XX), xy(jnode, YY)); slave_lookup[ll.unique()] = jnode; slave_nodes.push_back(ll.lon()); slave_nodes.push_back(ll.lat()); slave_nodes.push_back(jnode); ridx(jnode) = -1; } } std::vector> found_master(mpi_size); std::vector> send_slave_idx(mpi_size); // Find masters on other tasks to send to me { int sendcnt = slave_nodes.size(); std::vector recvcounts(mpi_size); ATLAS_TRACE_MPI(ALLGATHER) { mpi::comm().allGather(sendcnt, recvcounts.begin(), recvcounts.end()); } std::vector recvdispls(mpi_size); recvdispls[0] = 0; int recvcnt = recvcounts[0]; for (idx_t jproc = 1; jproc < mpi_size; ++jproc) { recvdispls[jproc] = recvdispls[jproc - 1] + recvcounts[jproc - 1]; recvcnt += recvcounts[jproc]; } std::vector recvbuf(recvcnt); ATLAS_TRACE_MPI(ALLGATHER) { mpi::comm().allGatherv(slave_nodes.begin(), slave_nodes.end(), recvbuf.begin(), recvcounts.data(), recvdispls.data()); } PeriodicTransform transform; for (idx_t jproc = 0; jproc < mpi_size; ++jproc) { found_master.reserve(master_nodes.size()); send_slave_idx.reserve(master_nodes.size()); array::LocalView recv_slave(recvbuf.data() + recvdispls[jproc], array::make_shape(recvcounts[jproc] / 3, 3)); for (idx_t jnode = 0; jnode < recv_slave.shape(0); ++jnode) { LonLatMicroDeg slave(recv_slave(jnode, LON), recv_slave(jnode, LAT)); transform(slave, -1); uid_t slave_uid = slave.unique(); if (master_lookup.count(slave_uid)) { int master_idx = master_lookup[slave_uid]; int slave_idx = recv_slave(jnode, 2); found_master[jproc].push_back(master_idx); send_slave_idx[jproc].push_back(slave_idx); } } } } // Fill in data to communicate std::vector> recv_slave_idx(mpi_size); std::vector> send_master_part(mpi_size); std::vector> recv_master_part(mpi_size); std::vector> send_master_ridx(mpi_size); std::vector> recv_master_ridx(mpi_size); // std::vector< std::vector > send_slave_part( mpi_size ); // std::vector< std::vector > recv_slave_part( mpi_size ); // std::vector< std::vector > send_slave_ridx( mpi_size ); // std::vector< std::vector > recv_slave_ridx( mpi_size ); { for (idx_t jproc = 0; jproc < mpi_size; ++jproc) { idx_t nb_found_master = static_cast(found_master[jproc].size()); send_master_part[jproc].resize(nb_found_master); send_master_ridx[jproc].resize(nb_found_master); for (idx_t jnode = 0; jnode < nb_found_master; ++jnode) { int loc_idx = found_master[jproc][jnode]; send_master_part[jproc][jnode] = part(loc_idx); send_master_ridx[jproc][jnode] = loc_idx; } // int nb_found_slaves = found_slave[jproc].size(); // send_slave_glb_idx[jproc].resize(nb_found_slaves); // send_slave_part [jproc].resize(nb_found_slaves); // send_slave_ridx [jproc].resize(nb_found_slaves); // for( int jnode=0; jnode(recv_slave_idx[jproc].size()); for (idx_t jnode = 0; jnode < nb_recv; ++jnode) { idx_t slave_idx = recv_slave_idx[jproc][jnode]; part(slave_idx) = recv_master_part[jproc][jnode]; ridx(slave_idx) = recv_master_ridx[jproc][jnode]; } } } mesh.metadata().set("periodic", true); } // ------------------------------------------------------------------ // C wrapper interfaces to C++ routines void atlas__build_periodic_boundaries(Mesh::Implementation* mesh) { ATLAS_ASSERT(mesh != nullptr, "Cannot access uninitialised atlas_Mesh"); Mesh m(mesh); build_periodic_boundaries(m); } // ------------------------------------------------------------------ } // namespace actions } // namespace mesh } // namespace atlas atlas-0.46.0/src/atlas/mesh/actions/WriteLoadBalanceReport.cc0000664000175000017500000002273015160212070024221 0ustar alastairalastair/* * (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/filesystem/PathName.h" #include "atlas/mesh/HybridElements.h" #include "atlas/mesh/IsGhostNode.h" #include "atlas/mesh/Mesh.h" #include "atlas/mesh/Nodes.h" #include "atlas/mesh/actions/WriteLoadBalanceReport.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/runtime/Exception.h" #include "atlas/library/FloatingPointExceptions.h" using atlas::mesh::IsGhostNode; namespace atlas { namespace mesh { namespace actions { void write_load_balance_report(const Mesh& mesh, const std::string& filename) { std::ofstream ofs; if (mpi::rank() == 0) { eckit::PathName path(filename); ofs.open(path.localPath(), std::ofstream::out); } write_load_balance_report(mesh, ofs); if (mpi::rank() == 0) { ofs.close(); } } void write_load_balance_report(const Mesh& mesh, std::ostream& ofs) { idx_t npart = mpi::size(); idx_t root = 0; std::vector nb_total_nodes(npart, 0); std::vector nb_owned_nodes(npart, 0); std::vector nb_ghost_nodes(npart, 0); std::vector ghost_ratio_nodes(npart, 0); std::vector nb_total_edges(npart, 0); std::vector nb_owned_edges(npart, 0); std::vector nb_ghost_edges(npart, 0); std::vector nb_ghost_ratio_edges(npart, 0); { const mesh::Nodes& nodes = mesh.nodes(); IsGhostNode is_ghost(nodes); idx_t nb_nodes = nodes.size(); idx_t nowned(0); idx_t nghost(0); for (idx_t n = 0; n < nb_nodes; ++n) { if (is_ghost(n)) { ++nghost; } else { ++nowned; } } /// @note this could be improved by packing the 3 integers in a vector, and /// doing only comm() call ATLAS_TRACE_MPI(GATHER) { mpi::comm().gather(nb_nodes, nb_total_nodes, root); mpi::comm().gather(nowned, nb_owned_nodes, root); mpi::comm().gather(nghost, nb_ghost_nodes, root); } bool disabled_fpe = library::disable_floating_point_exception(FE_INVALID); for (idx_t p = 0; p < npart; ++p) { if (nb_owned_nodes[p]) { ghost_ratio_nodes[p] = static_cast(nb_ghost_nodes[p]) / static_cast(nb_owned_nodes[p]); } else { ghost_ratio_nodes[p] = -1; } } if (disabled_fpe) { library::enable_floating_point_exception(FE_INVALID); } } bool has_edges = mesh.edges().size(); if (has_edges) { const mesh::Nodes& nodes = mesh.nodes(); IsGhostNode is_ghost(nodes); const mesh::HybridElements::Connectivity& edge_nodes = mesh.edges().node_connectivity(); idx_t nb_edges = mesh.edges().size(); idx_t nowned(0); idx_t nghost(0); for (idx_t j = 0; j < nb_edges; ++j) { if (is_ghost(edge_nodes(j, 0))) { ++nghost; } else { ++nowned; } } /// @note this could be improved by packing the 3 integers in a vector, and /// doing only comm() call mpi::comm().gather(nb_edges, nb_total_edges, root); mpi::comm().gather(nowned, nb_owned_edges, root); mpi::comm().gather(nghost, nb_ghost_nodes, root); } if (mpi::rank() == 0) { int idt = 10; ofs << "# STATISTICS\n"; ofs << std::setw(1) << "#" << std::setw(5) << ""; ofs << std::setw(idt) << "nodes"; ofs << std::setw(idt) << "owned"; ofs << std::setw(idt) << "ghost"; ofs << std::setw(idt) << "ratio(%)"; if (has_edges) { ofs << std::setw(idt) << "edges"; ofs << std::setw(idt) << "oedges"; ofs << std::setw(idt) << "gedges"; } ofs << "\n"; ofs << std::setw(6) << "# tot "; ofs << std::setw(idt) << std::accumulate(nb_total_nodes.data(), nb_total_nodes.data() + npart, 0); ofs << std::setw(idt) << std::accumulate(nb_owned_nodes.data(), nb_owned_nodes.data() + npart, 0); ofs << std::setw(idt) << std::accumulate(nb_ghost_nodes.data(), nb_ghost_nodes.data() + npart, 0); ofs << std::setw(idt) << "/"; if (has_edges) { ofs << std::setw(idt) << std::accumulate(nb_total_edges.data(), nb_total_edges.data() + npart, 0); ofs << std::setw(idt) << std::accumulate(nb_owned_edges.data(), nb_owned_edges.data() + npart, 0); ofs << std::setw(idt) << std::accumulate(nb_ghost_edges.data(), nb_ghost_edges.data() + npart, 0); } ofs << "\n"; ofs << std::setw(6) << "# max "; ofs << std::setw(idt) << *std::max_element(nb_total_nodes.data(), nb_total_nodes.data() + npart); ofs << std::setw(idt) << *std::max_element(nb_owned_nodes.data(), nb_owned_nodes.data() + npart); ofs << std::setw(idt) << *std::max_element(nb_ghost_nodes.data(), nb_ghost_nodes.data() + npart); ofs << std::setw(idt) << std::setw(idt) << std::fixed << std::setprecision(2) << *std::max_element(ghost_ratio_nodes.data(), ghost_ratio_nodes.data() + npart) * 100.; if (has_edges) { ofs << std::setw(idt) << *std::max_element(nb_total_edges.data(), nb_total_edges.data() + npart); ofs << std::setw(idt) << *std::max_element(nb_owned_edges.data(), nb_owned_edges.data() + npart); ofs << std::setw(idt) << *std::max_element(nb_ghost_edges.data(), nb_ghost_edges.data() + npart); } ofs << "\n"; ofs << std::setw(6) << "# min "; ofs << std::setw(idt) << *std::min_element(nb_total_nodes.data(), nb_total_nodes.data() + npart); ofs << std::setw(idt) << *std::min_element(nb_owned_nodes.data(), nb_owned_nodes.data() + npart); ofs << std::setw(idt) << *std::min_element(nb_ghost_nodes.data(), nb_ghost_nodes.data() + npart); ofs << std::setw(idt) << std::fixed << std::setprecision(2) << *std::min_element(ghost_ratio_nodes.data(), ghost_ratio_nodes.data() + npart) * 100.; if (has_edges) { ofs << std::setw(idt) << *std::min_element(nb_total_edges.data(), nb_total_edges.data() + npart); ofs << std::setw(idt) << *std::min_element(nb_owned_edges.data(), nb_owned_edges.data() + npart); ofs << std::setw(idt) << *std::min_element(nb_ghost_edges.data(), nb_ghost_edges.data() + npart); } ofs << "\n"; ofs << std::setw(6) << "# avg "; ofs << std::setw(idt) << std::accumulate(nb_total_nodes.data(), nb_total_nodes.data() + npart, 0) / npart; ofs << std::setw(idt) << std::accumulate(nb_owned_nodes.data(), nb_owned_nodes.data() + npart, 0) / npart; ofs << std::setw(idt) << std::accumulate(nb_ghost_nodes.data(), nb_ghost_nodes.data() + npart, 0) / npart; ofs << std::setw(idt) << std::fixed << std::setprecision(2) << std::accumulate(ghost_ratio_nodes.data(), ghost_ratio_nodes.data() + npart, 0.) / static_cast(npart) * 100.; if (has_edges) { ofs << std::setw(idt) << std::accumulate(nb_total_edges.data(), nb_total_edges.data() + npart, 0) / npart; ofs << std::setw(idt) << std::accumulate(nb_owned_edges.data(), nb_owned_edges.data() + npart, 0) / npart; ofs << std::setw(idt) << std::accumulate(nb_ghost_edges.data(), nb_ghost_edges.data() + npart, 0) / npart; } ofs << "\n"; ofs << "#----------------------------------------------------\n"; ofs << "# PER TASK\n"; ofs << std::setw(6) << "# part"; ofs << std::setw(idt) << "nodes"; ofs << std::setw(idt) << "owned"; ofs << std::setw(idt) << "ghost"; ofs << std::setw(idt) << "ratio(%)"; if (has_edges) { ofs << std::setw(idt) << "edges"; ofs << std::setw(idt) << "oedges"; ofs << std::setw(idt) << "gedges"; } ofs << "\n"; for (idx_t jpart = 0; jpart < npart; ++jpart) { ofs << std::setw(6) << jpart; ofs << std::setw(idt) << nb_total_nodes[jpart]; ofs << std::setw(idt) << nb_owned_nodes[jpart]; ofs << std::setw(idt) << nb_ghost_nodes[jpart]; ofs << std::setw(idt) << std::fixed << std::setprecision(2) << ghost_ratio_nodes[jpart] * 100.; if (has_edges) { ofs << std::setw(idt) << nb_total_edges[jpart]; ofs << std::setw(idt) << nb_owned_edges[jpart]; ofs << std::setw(idt) << nb_ghost_edges[jpart]; } ofs << "\n"; } } } // ------------------------------------------------------------------ // C wrapper interfaces to C++ routines void atlas__write_load_balance_report(Mesh::Implementation* mesh, char* filename) { ATLAS_ASSERT(mesh != nullptr, "Cannot access uninitialised atlas_Mesh"); Mesh m(mesh); write_load_balance_report(m, std::string(filename)); } // ------------------------------------------------------------------ } // namespace actions } // namespace mesh } // namespace atlas atlas-0.46.0/src/atlas/mesh/actions/GetCubedSphereNodalArea.h0000664000175000017500000000111315160212070024117 0ustar alastairalastair/* * (C) Crown Copyright 2024 Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ #pragma once #include "atlas/field/Field.h" #include "atlas/mesh/Mesh.h" #include "atlas/mesh/Nodes.h" namespace atlas { class Mesh; class Field; namespace mesh { namespace actions { /// Provide the area around nodes of cubed sphere mesh class GetCubedSphereNodalArea { public: Field& operator()(Mesh&); }; } // namespace actions } // namespace mesh } // namespace atlas atlas-0.46.0/src/atlas/mesh/actions/BuildEdges.cc0000664000175000017500000006067115160212070021702 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "atlas/mesh/actions/BuildEdges.h" #include #include #include #include #include #include #include #include "eckit/types/FloatCompare.h" #include "atlas/array.h" #include "atlas/array/ArrayView.h" #include "atlas/array/IndexView.h" #include "atlas/domain.h" #include "atlas/field/Field.h" #include "atlas/grid/StructuredGrid.h" #include "atlas/library/config.h" #include "atlas/mesh/ElementType.h" #include "atlas/mesh/Elements.h" #include "atlas/mesh/HybridElements.h" #include "atlas/mesh/Mesh.h" #include "atlas/mesh/Nodes.h" #include "atlas/mesh/detail/AccumulateFacets.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Log.h" #include "atlas/util/CoordinateEnums.h" #include "atlas/util/LonLatMicroDeg.h" #include "atlas/util/MicroDeg.h" #include "atlas/util/Unique.h" using atlas::mesh::detail::accumulate_facets_ordered_by_halo; using Topology = atlas::mesh::Nodes::Topology; using atlas::util::microdeg; using atlas::util::UniqueLonLat; namespace atlas { namespace mesh { namespace actions { //---------------------------------------------------------------------------------------------------------------------- namespace { // anonymous struct Sort { Sort() = default; Sort(gidx_t gid, idx_t idx) { g = gid; i = idx; } gidx_t g; idx_t i; bool operator<(const Sort& other) const { return (g < other.g); } }; } // anonymous namespace void build_element_to_edge_connectivity(Mesh& mesh) { ATLAS_TRACE(); mesh::HybridElements::Connectivity& cell_edge_connectivity = mesh.cells().edge_connectivity(); cell_edge_connectivity.clear(); // Allocate cell_edge_connectivity for (idx_t t = 0; t < mesh.cells().nb_types(); ++t) { idx_t nb_elements = mesh.cells().elements(t).size(); idx_t nb_edges_per_elem = mesh.cells().element_type(t).nb_edges(); std::vector init(mesh.cells().elements(t).size() * nb_edges_per_elem, cell_edge_connectivity.missing_value()); cell_edge_connectivity.add(nb_elements, nb_edges_per_elem, init.data()); } idx_t nb_edges = mesh.edges().size(); const mesh::HybridElements::Connectivity& edge_cell_connectivity = mesh.edges().cell_connectivity(); const mesh::HybridElements::Connectivity& edge_node_connectivity = mesh.edges().node_connectivity(); auto edge_flags = array::make_view(mesh.edges().flags()); auto is_pole_edge = [&](idx_t e) { return Topology::check(edge_flags(e), Topology::POLE); }; // Sort edges for bit-reproducibility std::vector edge_sort; edge_sort.reserve(nb_edges); { UniqueLonLat compute_uid(mesh); for (idx_t jedge = 0; jedge < nb_edges; ++jedge) { edge_sort.emplace_back(compute_uid(edge_node_connectivity.row(jedge)), jedge); } std::sort(edge_sort.data(), edge_sort.data() + nb_edges); } // Fill in cell_edge_connectivity std::vector edge_cnt(mesh.cells().size()); for (idx_t jedge = 0; jedge < nb_edges; ++jedge) { int iedge = edge_sort[jedge].i; for (idx_t j = 0; j < 2; ++j) { idx_t elem = edge_cell_connectivity(iedge, j); if (elem != edge_cell_connectivity.missing_value()) { ATLAS_ASSERT(edge_cnt[elem] < cell_edge_connectivity.cols(elem)); cell_edge_connectivity.set(elem, edge_cnt[elem]++, iedge); } else { if (not is_pole_edge(iedge)) { if (j == 0) { auto node_gidx = array::make_view(mesh.nodes().global_index()); std::stringstream ss; ss << "Edge [" << node_gidx(edge_node_connectivity(jedge, 0)) << ", " << node_gidx(edge_node_connectivity(jedge, 1)) << "] " << "has no element connected."; Log::error() << ss.str() << std::endl; throw_Exception(ss.str(), Here()); } } } } } // Verify that all edges have been found auto field_flags = array::make_view(mesh.cells().flags()); auto patch = [&field_flags](idx_t e) { using Topology = atlas::mesh::Nodes::Topology; return Topology::check(field_flags(e), Topology::PATCH); }; for (idx_t jcell = 0; jcell < mesh.cells().size(); ++jcell) { if (patch(jcell)) { continue; } for (idx_t jcol = 0; jcol < cell_edge_connectivity.cols(jcell); ++jcol) { if (cell_edge_connectivity(jcell, jcol) == cell_edge_connectivity.missing_value()) { const array::ArrayView gidx = array::make_view(mesh.nodes().global_index()); std::stringstream msg; msg << "Could not find edge " << jcol << " for " << mesh.cells().name(jcell) << " elem " << jcell << " with nodes ( "; for (idx_t jnode = 0; jnode < mesh.cells().node_connectivity().cols(jcell); ++jnode) { msg << gidx(mesh.cells().node_connectivity()(jcell, jnode)) << " "; } msg << ")"; throw_Exception(msg.str(), Here()); } } } } void build_node_to_edge_connectivity(Mesh& mesh) { mesh::Nodes& nodes = mesh.nodes(); const idx_t nb_edges = mesh.edges().size(); mesh::Nodes::Connectivity& node_to_edge = nodes.edge_connectivity(); node_to_edge.clear(); const mesh::HybridElements::Connectivity& edge_node_connectivity = mesh.edges().node_connectivity(); std::vector to_edge_size(nodes.size(), 0); for (idx_t jedge = 0; jedge < nb_edges; ++jedge) { for (idx_t j = 0; j < 2; ++j) { ++to_edge_size[edge_node_connectivity(jedge, j)]; } } node_to_edge.add(nodes.size(), to_edge_size.data()); for (idx_t jnode = 0; jnode < nodes.size(); ++jnode) { to_edge_size[jnode] = 0; } UniqueLonLat compute_uid(mesh); std::vector edge_sort(nb_edges); for (idx_t jedge = 0; jedge < nb_edges; ++jedge) { edge_sort[jedge] = Sort(compute_uid(edge_node_connectivity.row(jedge)), jedge); } std::stable_sort(edge_sort.data(), edge_sort.data() + nb_edges); for (idx_t jedge = 0; jedge < nb_edges; ++jedge) { idx_t iedge = edge_sort[jedge].i; ATLAS_ASSERT(iedge < nb_edges); for (idx_t j = 0; j < 2; ++j) { idx_t node = edge_node_connectivity(iedge, j); node_to_edge.set(node, to_edge_size[node]++, iedge); } } } class AccumulatePoleEdges { enum { NORTH = 0, SOUTH = 1 }; const array::ArrayView xy; const array::ArrayView flags; const array::ArrayView part; const array::ArrayView halo; const idx_t nb_nodes; std::vector> pole_nodes; public: AccumulatePoleEdges(mesh::Nodes& nodes): xy(array::make_view(nodes.xy())), flags(array::make_view(nodes.flags())), part(array::make_view(nodes.partition())), halo(array::make_view(nodes.halo())), nb_nodes(nodes.size()), pole_nodes(2) { double min[2], max[2]; min[XX] = std::numeric_limits::max(); min[YY] = std::numeric_limits::max(); max[XX] = -std::numeric_limits::max(); max[YY] = -std::numeric_limits::max(); for (idx_t node = 0; node < nb_nodes; ++node) { min[XX] = std::min(min[XX], xy(node, XX)); min[YY] = std::min(min[YY], xy(node, YY)); max[XX] = std::max(max[XX], xy(node, XX)); max[YY] = std::max(max[YY], xy(node, YY)); } ATLAS_TRACE_MPI(ALLREDUCE) { mpi::comm().allReduceInPlace(min, 2, eckit::mpi::min()); mpi::comm().allReduceInPlace(max, 2, eckit::mpi::max()); } double tol = 1e-6; // Collect all nodes closest to poles for (idx_t node = 0; node < nb_nodes; ++node) { if (std::abs(xy(node, YY) - max[YY]) < tol) { pole_nodes[NORTH].insert(node); } else if (std::abs(xy(node, YY) - min[YY]) < tol) { pole_nodes[SOUTH].insert(node); } } // Sanity check { for (idx_t NS = 0; NS < 2; ++NS) { int npart = -1; for (std::set::iterator it = pole_nodes[NS].begin(); it != pole_nodes[NS].end(); ++it) { int node = *it; if (npart == -1) { npart = part(node); } else if (part(node) != npart) { // Not implemented yet, when pole-lattitude is split. std::stringstream msg; msg << "Split pole-latitude is not supported yet... node " << node << "[p" << part(node) << "] should belong to part " << npart; throw_NotImplemented(msg.str(), Here()); } } } } } void compute_pole_edges(int _halo, std::vector& pole_edge_nodes, idx_t& nb_pole_edges) { // Create connections over the poles and store in pole_edge_nodes nb_pole_edges = 0; for (idx_t NS = 0; NS < 2; ++NS) { for (std::set::iterator it = pole_nodes[NS].begin(); it != pole_nodes[NS].end(); ++it) { int node = *it; if (!Topology::check(flags(node), Topology::PERIODIC | Topology::GHOST)) { int x2 = microdeg(xy(node, XX) + 180.); for (std::set::iterator itr = pole_nodes[NS].begin(); itr != pole_nodes[NS].end(); ++itr) { int other_node = *itr; if (microdeg(xy(other_node, XX)) == x2) { if (!Topology::check(flags(other_node), Topology::PERIODIC)) { if (halo(node) == _halo && halo(other_node) == _halo) { pole_edge_nodes.push_back(node); pole_edge_nodes.push_back(other_node); ++nb_pole_edges; } } } } } } } } }; struct ComputeUniquePoleEdgeIndex { // Already assumes that the edges cross the pole ComputeUniquePoleEdgeIndex(const mesh::Nodes& nodes): xy(array::make_view(nodes.xy())) {} gidx_t operator()(const mesh::Connectivity::Row& edge_nodes) const { double centroid[2]; centroid[XX] = 0.; centroid[YY] = 0.; for (idx_t jnode = 0; jnode < 2; ++jnode) { centroid[XX] += xy(edge_nodes(jnode), XX); centroid[YY] += xy(edge_nodes(jnode), YY); } centroid[XX] /= 2.; centroid[YY] /= 2.; if (centroid[YY] > 0) { centroid[YY] = 90.; } else { centroid[YY] = -90.; } /// FIXME make this into `util::unique_lonlat(centroid)` but this causes /// weird parallel behavior return util::detail::unique32(microdeg(centroid[XX]), microdeg(centroid[YY])); } array::ArrayView xy; }; void build_edges(Mesh& mesh) { build_edges(mesh, util::NoConfig()); } void build_edges(Mesh& mesh, const eckit::Configuration& config) { ATLAS_TRACE("BuildEdges"); mpi::Scope mpi_scope(mesh.mpi_comm()); int mesh_halo(0); mesh.metadata().get("halo", mesh_halo); if (mesh.metadata().has("built_edges_for_halo")) { int edges_halo = mesh.metadata().getInt("built_edges_for_halo"); if (edges_halo == mesh_halo) { // Nothing to be done here return; } } bool pole_edges{false}; if (StructuredGrid grid = mesh.grid()) { if (RectangularDomain domain = grid.domain()) { if (domain.global()) { double ymax = std::max(std::abs(grid.y().front()), std::abs(grid.y().back())); pole_edges = not eckit::types::is_approximately_equal(ymax, domain.ymax()); } } } 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()); idx_t nb_nodes = nodes.size(); mesh.edges().clear(); idx_t edge_start{0}; idx_t edge_end{0}; // storage for edge-to-node-connectivity shape=(nb_edges,2) std::vector edge_nodes_data; std::vector edge_to_elem_data; std::vector edge_halo_offsets; idx_t nb_edges; idx_t nb_inner_edges; idx_t missing_value; accumulate_facets_ordered_by_halo(mesh.cells(), mesh.nodes(), edge_nodes_data, edge_to_elem_data, nb_edges, nb_inner_edges, missing_value, edge_halo_offsets); 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; if (halo+1 < edge_halo_offsets.size()) { 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(mesh::ElementType::create("Line"), (edge_end - edge_start), edge_nodes_data.data() + edge_halo_offsets[halo] * 2); auto& edge_nodes = mesh.edges().node_connectivity(); const auto& cell_nodes = mesh.cells().node_connectivity(); UniqueLonLat compute_uid(mesh); auto edge_ridx = array::make_indexview(mesh.edges().remote_index()); auto edge_part = array::make_view(mesh.edges().partition()); auto edge_glb_idx = array::make_view(mesh.edges().global_index()); auto edge_halo = array::make_view(mesh.edges().halo()); auto edge_flags = array::make_view(mesh.edges().flags()); ATLAS_ASSERT(cell_nodes.missing_value() == missing_value); for (idx_t edge = edge_start; edge < edge_end; ++edge) { const idx_t iedge = edge_halo_offsets[halo] + (edge - edge_start); const int ip1 = edge_nodes(edge, 0); const int ip2 = edge_nodes(edge, 1); if (compute_uid(ip1) > compute_uid(ip2)) { idx_t swapped[2] = {ip2, ip1}; edge_nodes.set(edge, swapped); } ATLAS_ASSERT(idx_t(edge_nodes(edge, 0)) < nb_nodes); ATLAS_ASSERT(idx_t(edge_nodes(edge, 1)) < nb_nodes); edge_glb_idx(edge) = compute_uid(edge_nodes.row(edge)); edge_part(edge) = std::min(node_part(edge_nodes(edge, 0)), node_part(edge_nodes(edge, 1))); edge_ridx(edge) = edge; edge_halo(edge) = halo; edge_flags(edge) = 0; const idx_t e1 = edge_to_elem_data[2 * iedge + 0]; const idx_t e2 = edge_to_elem_data[2 * iedge + 1]; ATLAS_ASSERT(e1 != cell_nodes.missing_value()); if (e2 == cell_nodes.missing_value()) { // do nothing } else if (compute_uid(cell_nodes.row(e1)) > compute_uid(cell_nodes.row(e2))) { edge_to_elem_data[iedge * 2 + 0] = e2; edge_to_elem_data[iedge * 2 + 1] = e1; } } mesh.edges().cell_connectivity().add((edge_end - edge_start), 2, edge_to_elem_data.data() + edge_halo_offsets[halo] * 2); if (pole_edges) { auto pole_edge_accumulator = std::make_shared(nodes); idx_t nb_pole_edges; std::vector pole_edge_nodes; pole_edge_accumulator->compute_pole_edges(halo, pole_edge_nodes, nb_pole_edges); if (nb_pole_edges) { edge_start = edge_end; edge_end += nb_pole_edges; mesh.edges().add(mesh::ElementType::create("Line"), nb_pole_edges, pole_edge_nodes.data()); auto edge_ridx = array::make_indexview(mesh.edges().remote_index()); auto edge_part = array::make_view(mesh.edges().partition()); auto edge_glb_idx = array::make_view(mesh.edges().global_index()); auto edge_halo = array::make_view(mesh.edges().halo()); auto edge_flags = array::make_view(mesh.edges().flags()); auto set_pole_edge = [&edge_flags](idx_t e) { Topology::set(edge_flags(e), Topology::POLE); }; auto& edge_nodes = mesh.edges().node_connectivity(); mesh.edges().cell_connectivity().add(nb_pole_edges, 2); idx_t cnt = 0; ComputeUniquePoleEdgeIndex compute_uid(nodes); for (idx_t edge = edge_start; edge < edge_end; ++edge) { idx_t ip1 = pole_edge_nodes[cnt++]; idx_t ip2 = pole_edge_nodes[cnt++]; std::array enodes{ip1, ip2}; edge_nodes.set(edge, enodes.data()); edge_glb_idx(edge) = compute_uid(edge_nodes.row(edge)); edge_part(edge) = std::min(node_part(edge_nodes(edge, 0)), node_part(edge_nodes(edge, 1))); edge_ridx(edge) = edge; edge_halo(edge) = halo; set_pole_edge(edge); } } } } mesh.edges().metadata().set("pole_edges", pole_edges); build_element_to_edge_connectivity(mesh); mesh::HybridElements::Connectivity& cell_edges = mesh.cells().edge_connectivity(); auto cell_halo = array::make_view(mesh.cells().halo()); auto cell_flags = array::make_view(mesh.cells().flags()); auto cell_patch = [&cell_flags](idx_t e) { using Topology = atlas::mesh::Nodes::Topology; return Topology::check(cell_flags(e), Topology::PATCH); }; auto edge_halo = array::make_view(mesh.edges().halo()); int max_halo = 0; for (idx_t jcell = 0; jcell < mesh.cells().size(); ++jcell) { if (not cell_patch(jcell)) { int halo = cell_halo(jcell); max_halo = std::max(halo, max_halo); for (idx_t jedge = 0; jedge < cell_edges.cols(jcell); ++jedge) { auto iedge = cell_edges(jcell, jedge); ATLAS_ASSERT(edge_halo(iedge) <= halo); } } } std::vector nb_edges_including_halo(max_halo + 1); { int nb_edges = mesh.edges().size(); for (int jedge = 0; jedge < nb_edges; ++jedge) { nb_edges_including_halo[edge_halo(jedge)] = jedge + 1; if (jedge > 0) { ATLAS_ASSERT(edge_halo(jedge) >= edge_halo(jedge - 1)); } } } for (int i = 0; i <= max_halo; ++i) { if (i > 0) { ATLAS_ASSERT(nb_edges_including_halo[i] > nb_edges_including_halo[i - 1]); } std::stringstream ss; ss << "nb_edges_including_halo[" << i << "]"; mesh.metadata().set(ss.str(), nb_edges_including_halo[i]); } for (int i = max_halo + 1; i <= mesh_halo; ++i) { std::stringstream ss; ss << "nb_edges_including_halo[" << i << "]"; mesh.metadata().set(ss.str(), nb_edges_including_halo.back()); } mesh.metadata().set("built_edges_for_halo", mesh_halo); // Backwards compatibility for code that reads "is_pole_edge" field instead of checking the flags, only Fortran would do it { if (pole_edges) { if (!mesh.edges().has_field("is_pole_edge")) { mesh.edges().add( Field("is_pole_edge", array::make_datatype(), array::make_shape(mesh.edges().size()))); } auto edge_flags = array::make_view(mesh.edges().flags()); auto is_pole_edge = array::make_view(mesh.edges().field("is_pole_edge")); int nb_edges = mesh.edges().size(); for (int jedge = 0; jedge < nb_edges; ++jedge) { is_pole_edge(jedge) = Topology::check(edge_flags(jedge), Topology::POLE); } } } } void build_pole_edges(Mesh&) { ATLAS_TRACE(); Log::info() << "ATLAS_WARNING: Deprecation warning: build_pole_edges is no longer required.\n" << "It is automatically inferred within atlas_build_edges" << std::endl; Log::info() << "The 'build_pole_edges' function will be removed in a future version" << std::endl; } //---------------------------------------------------------------------------------------------------------------------- // C wrapper interfaces to C++ routines extern "C" { void atlas__build_edges(Mesh::Implementation* mesh) { ATLAS_ASSERT(mesh != nullptr, "Cannot access uninitialised atlas_Mesh"); Mesh m(mesh); build_edges(m); } void atlas__build_pole_edges(Mesh::Implementation*) { Log::info() << "ATLAS_WARNING: Deprecation warning: atlas_build_pole_edges is no longer required.\n" << "It is automatically inferred within atlas_build_edges" << std::endl; Log::info() << "The 'atlas_build_pole_edges' function will be removed in a future version" << std::endl; } void atlas__build_node_to_edge_connectivity(Mesh::Implementation* mesh) { ATLAS_ASSERT(mesh != nullptr, "Cannot access uninitialised atlas_Mesh"); Mesh m(mesh); build_node_to_edge_connectivity(m); } } //---------------------------------------------------------------------------------------------------------------------- } // namespace actions } // namespace mesh } // namespace atlas atlas-0.46.0/src/atlas/mesh/actions/ReorderHilbert.h0000664000175000017500000000332415160212070022441 0ustar alastairalastair/* * (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 atlas-0.46.0/src/atlas/mesh/actions/BuildEdges.h0000664000175000017500000000272115160212070021534 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once namespace eckit { class Configuration; } // namespace eckit namespace atlas { class Mesh; } // namespace atlas namespace atlas { namespace mesh { namespace actions { void build_edges(Mesh& mesh); void build_edges(Mesh& mesh, const eckit::Configuration&); void build_pole_edges(Mesh& mesh); void build_element_to_edge_connectivity(Mesh& mesh); void build_node_to_edge_connectivity(Mesh& mesh); // ------------------------------------------------------------------ } // namespace actions } // namespace mesh } // namespace atlas // ------------------------------------------------------------------ namespace atlas { namespace mesh { namespace detail { class MeshImpl; } // namespace detail } // namespace mesh } // namespace atlas // C wrapper interfaces to C++ routines extern "C" { void atlas__build_edges(atlas::mesh::detail::MeshImpl* mesh); void atlas__build_pole_edges(atlas::mesh::detail::MeshImpl* mesh); void atlas__build_node_to_edge_connectivity(atlas::mesh::detail::MeshImpl* mesh); } // ------------------------------------------------------------------ atlas-0.46.0/src/atlas/mesh/actions/Build2DCellCentres.h0000664000175000017500000000152015160212070023072 0ustar alastairalastair/* * (C) Crown Copyright 2021 Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ #pragma once #include #include "atlas/util/Config.h" namespace atlas { class Mesh; class Field; namespace mesh { namespace actions { /// Generates the cell centres on each cell class Build2DCellCentres { public: Build2DCellCentres(const std::string& field_name = "centre", bool force_recompute = false); Build2DCellCentres(eckit::Configuration&); /// @note Correct only for Linear Triangles and Quadrilaterals Field& operator()(Mesh&) const; private: std::string field_name_; bool force_recompute_; bool flatten_virtual_elements_; }; } // namespace actions } // namespace mesh } // namespace atlas atlas-0.46.0/src/atlas/mesh/actions/BuildTorusXYZField.cc0000664000175000017500000000533515160212070023342 0ustar alastairalastair/* * (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/array/ArrayView.h" #include "atlas/domain/detail/GlobalDomain.h" #include "atlas/domain/detail/RectangularDomain.h" #include "atlas/field/Field.h" #include "atlas/mesh/Mesh.h" #include "atlas/mesh/Nodes.h" #include "atlas/mesh/actions/BuildTorusXYZField.h" #include "atlas/runtime/Exception.h" namespace atlas { namespace mesh { namespace actions { //---------------------------------------------------------------------------------------------------------------------- BuildTorusXYZField::BuildTorusXYZField(const std::string& name): name_(name) {} Field& BuildTorusXYZField::operator()(Mesh& mesh, const Domain& dom, double r0, double r1, idx_t nx, idx_t ny) const { return operator()(mesh.nodes(), dom, r0, r1, nx, ny); } Field& BuildTorusXYZField::operator()(mesh::Nodes& nodes, const Domain& dom, double r0, double r1, idx_t nx, idx_t ny) const { // fill xyz with torus coordinates. r0 and r1 are large and small radii, // respectively. auto domain = RectangularDomain(dom); ATLAS_ASSERT(domain); const double xmin = domain.xmin(); const double xmax = domain.xmax(); const double ymin = domain.ymin(); const double ymax = domain.ymax(); if (!nodes.has_field(name_)) { const idx_t npts = nodes.size(); const array::ArrayView lonlat = array::make_view(nodes.xy()); array::ArrayView xyz = array::make_view( nodes.add(Field(name_, array::make_datatype(), array::make_shape(npts, 3)))); const double pi = M_PI; const double c1 = 2. * pi / double(nx) * (nx - 1) / (xmax - xmin); const double c2 = 2. * pi / double(ny) * (ny - 1) / (ymax - ymin); for (idx_t n = 0; n < npts; ++n) { double lon = -pi + c1 * (lonlat(n, 0) - xmin); double lat = -pi + c2 * (lonlat(n, 1) - ymin); xyz(n, 0) = std::cos(lon) * (r0 + r1 * std::cos(lat)); xyz(n, 1) = std::sin(lon) * (r0 + r1 * std::cos(lat)); xyz(n, 2) = r1 * std::sin(lat); } } return nodes.field(name_); } //---------------------------------------------------------------------------------------------------------------------- } // namespace actions } // namespace mesh } // namespace atlas atlas-0.46.0/src/atlas/mesh/actions/Build2DCellCentres.cc0000664000175000017500000001152615160212070023237 0ustar alastairalastair/* * (C) Crown Copyright 2021 Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ #include #include "eckit/types/FloatCompare.h" #include "atlas/array/MakeView.h" #include "atlas/field/Field.h" #include "atlas/mesh/HybridElements.h" #include "atlas/mesh/Mesh.h" #include "atlas/mesh/Nodes.h" #include "atlas/mesh/actions/Build2DCellCentres.h" #include "atlas/runtime/Trace.h" #include "atlas/util/CoordinateEnums.h" namespace atlas { namespace mesh { namespace actions { //---------------------------------------------------------------------------------------------------------------------- Build2DCellCentres::Build2DCellCentres(const std::string& field_name, bool force_recompute): field_name_(field_name), force_recompute_(force_recompute), flatten_virtual_elements_(true) {} Build2DCellCentres::Build2DCellCentres(eckit::Configuration& config): field_name_(config.getString("name", "centre")), force_recompute_(config.getBool("force_recompute", false)), flatten_virtual_elements_(config.getBool("flatten_virtual_elements", true)) {} Field& Build2DCellCentres::operator()(Mesh& mesh) const { bool recompute = force_recompute_; if (!mesh.cells().has_field(field_name_)) { mesh.cells().add(Field(field_name_, array::make_datatype(), array::make_shape(mesh.cells().size(), 2))); recompute = true; } if (recompute) { ATLAS_TRACE("Build2DCellCentres"); mesh::Nodes& nodes = mesh.nodes(); array::ArrayView coords = array::make_view(nodes.field("lonlat")); idx_t firstVirtualPoint = std::numeric_limits::max(); if (nodes.metadata().has("NbRealPts")) { firstVirtualPoint = nodes.metadata().get("NbRealPts"); } idx_t nb_cells = mesh.cells().size(); std::vector lons(nodes.size()); for (idx_t e = 0; e < nodes.size(); ++e) { lons[e] = coords(e, LON); //while (lons[e] > 360.0) { lons[e] -= 360.0; } //while (lons[e] < 0.0) { lons[e] += 360.0; } } auto centroids = array::make_view(mesh.cells().field(field_name_)); const mesh::HybridElements::Connectivity& cell_node_connectivity = mesh.cells().node_connectivity(); for (idx_t e = 0; e < nb_cells; ++e) { centroids(e, LON) = 0.; centroids(e, LAT) = 0.; const idx_t nb_cell_nodes = cell_node_connectivity.cols(e); // check for degenerate elements (less than three unique nodes) // NOTE: this is not a proper check but it is very robust eckit::types::CompareApproximatelyEqual approx(1.e-9); idx_t nb_equal_nodes = 0; for (idx_t ni = 0; ni < nb_cell_nodes - 1; ++ni) { idx_t i = cell_node_connectivity(e, ni); Point2 Pi(lons[i], coords(i, LAT)); for (idx_t nj = ni + 1; nj < nb_cell_nodes; ++nj) { idx_t j = cell_node_connectivity(e, nj); Point2 Pj(lons[j], coords(j, LAT)); if (approx(Pi[LON], Pj[LON]) && approx(Pi[LAT], Pj[LAT])) { ++nb_equal_nodes; } } } idx_t nb_unique_nodes = idx_t(nb_cell_nodes) - nb_equal_nodes; if (nb_unique_nodes < 3) { continue; } if (flatten_virtual_elements_) { // calculate centroid by averaging coordinates (uses only "real" nodes) idx_t nb_real_nodes = 0; for (idx_t n = 0; n < nb_cell_nodes; ++n) { const idx_t i = cell_node_connectivity(e, n); if (i < firstVirtualPoint) { ++nb_real_nodes; centroids(e, LON) += lons[i]; centroids(e, LAT) += coords(i, LAT); } } if (nb_real_nodes > 1) { const double average_coefficient = 1. / static_cast(nb_real_nodes); centroids(e, LON) *= average_coefficient; centroids(e, LAT) *= average_coefficient; } } else { const double average_coefficient = 1. / static_cast(nb_cell_nodes); for (idx_t n = 0; n < nb_cell_nodes; ++n) { const idx_t i = cell_node_connectivity(e, n); centroids(e, LON) += lons[i] * average_coefficient; centroids(e, LAT) += coords(i, LAT) * average_coefficient; } } } } return mesh.cells().field(field_name_); } } // namespace actions } // namespace mesh } // namespace atlas atlas-0.46.0/src/atlas/mesh/actions/Reorder.cc0000664000175000017500000002144615160212070021272 0ustar alastairalastair/* * (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" // For static linking #include "atlas/mesh/actions/ReorderHilbert.h" #include "atlas/mesh/actions/ReorderReverseCuthillMckee.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 { // ------------------------------------------------------------------ struct force_link { template void load_builder() { ReorderBuilder("tmp"); } force_link() { load_builder(); load_builder(); } }; // ------------------------------------------------------------------ const ReorderImpl* ReorderFactory::build(const eckit::Parametrisation& config) { static force_link static_linking; 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() == static_cast(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 atlas-0.46.0/src/atlas/mesh/actions/GetCubedSphereNodalArea.cc0000664000175000017500000000351415160212070024264 0ustar alastairalastair/* * (C) Crown Copyright 2024 Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ #include "atlas/array/MakeView.h" #include "atlas/grid/CubedSphereGrid.h" #include "atlas/mesh/actions/GetCubedSphereNodalArea.h" #include "atlas/util/NormaliseLongitude.h" #include "atlas/util/Point.h" namespace atlas { namespace mesh { namespace actions { Field& GetCubedSphereNodalArea::operator()(Mesh& mesh) { if (mesh.nodes().has_field("grid_cell_areas")) { return mesh.nodes().field("grid_cell_areas"); } else { constexpr auto deg2rad = M_PI / 180.; const auto& proj = mesh.projection(); auto lonlat = array::make_view(mesh.nodes().lonlat()); auto gcell_area_field = Field("grid_cell_areas", make_datatype(), array::make_shape(mesh.nodes().size())); auto gcell_area_fview = array::make_view(gcell_area_field); double gcell_area_cs = [&] { ATLAS_ASSERT(CubedSphereGrid(mesh.grid())); // (grid_res * grid_res) = no. of cells on a tile auto grid_res = CubedSphereGrid(mesh.grid()).N(); // area of a grid cell (cubed-sphere coord. system) return M_PI/(2*grid_res) * M_PI/(2*grid_res); }(); for (size_t i = 0; i < gcell_area_fview.size(); ++i) { PointLonLat loc = PointLonLat(lonlat(i, atlas::LON), lonlat(i, atlas::LAT)); double cos_lat = std::cos(deg2rad * loc.lat()); double grid_jac_det = 1/proj.jacobian(loc).determinant(); // area of a grid cell (geographic coord. system) gcell_area_fview(i) = grid_jac_det * gcell_area_cs * cos_lat; } mesh.nodes().add(gcell_area_field); return mesh.nodes().field("grid_cell_areas"); } } } // namespace actions } // namespace mesh } // namespace atlas atlas-0.46.0/src/atlas/mesh/actions/BuildXYZField.cc0000664000175000017500000000406715160212070022306 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "atlas/mesh/actions/BuildXYZField.h" #include "atlas/array/MakeView.h" #include "atlas/field/Field.h" #include "atlas/mesh/Mesh.h" #include "atlas/mesh/Nodes.h" #include "atlas/runtime/Trace.h" #include "atlas/util/Earth.h" #include "atlas/util/Point.h" namespace atlas { namespace mesh { namespace actions { //---------------------------------------------------------------------------------------------------------------------- BuildXYZField::BuildXYZField(const std::string& name, bool force_recompute): name_(name), force_recompute_(force_recompute) {} Field& BuildXYZField::operator()(Mesh& mesh) const { return operator()(mesh.nodes()); } Field& BuildXYZField::operator()(mesh::Nodes& nodes) const { bool recompute = force_recompute_; if (!nodes.has_field(name_)) { nodes.add(Field(name_, array::make_datatype(), array::make_shape(nodes.size(), 3))); recompute = true; } if (recompute) { ATLAS_TRACE("BuildXYZField"); array::ArrayView lonlat = array::make_view(nodes.lonlat()); array::ArrayView xyz = array::make_view(nodes.field(name_)); PointXYZ p2; for (idx_t n = 0; n < nodes.size(); ++n) { const PointLonLat p1(lonlat(n, 0), lonlat(n, 1)); util::Earth::convertSphericalToCartesian(p1, p2); xyz(n, 0) = p2.x(); xyz(n, 1) = p2.y(); xyz(n, 2) = p2.z(); } } return nodes.field(name_); } //---------------------------------------------------------------------------------------------------------------------- } // namespace actions } // namespace mesh } // namespace atlas atlas-0.46.0/src/atlas/mesh/actions/BuildParallelFields.h0000664000175000017500000000457715160212070023403 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ /// @file BuildParallelFields.h /// @author Willem Deconinck /// @date June 2014 #pragma once #include "atlas/library/config.h" #include "atlas/mesh/Mesh.h" namespace atlas { namespace mesh { class Nodes; } } // namespace atlas namespace atlas { namespace mesh { namespace actions { /* * Build all parallel fields in the mesh * - calls build_nodes_parallel_fields() */ void build_parallel_fields(Mesh& mesh); /* * Build parallel fields for the "nodes" function space if they don't exist. * - glb_idx: create unique indices for non-positive values * - partition: set to mpi::rank() for negative values * - remote_idx: rebuild from scratch */ void build_nodes_parallel_fields(Mesh& mesh); void build_nodes_parallel_fields(mesh::Nodes& nodes); // deprecated (WARNING: does not change MPI scope) /* * Build parallel fields for the "edges" function space if they don't exist. * - glb_idx: create unique indices for non-positive values * - partition: set to partition of node with lowest glb_idx * - remote_idx: rebuild from scratch * * TODO: Make sure that the edge-partition is at least one of the partition * numbers of the * neighbouring elements. * Because of this problem, the size of the halo should be set to 2 * instead of 1!!! */ void build_edges_parallel_fields(Mesh& mesh); void build_cells_parallel_fields(Mesh& mesh); void renumber_nodes_glb_idx(Mesh& mesh); void renumber_nodes_glb_idx(mesh::Nodes& nodes); // deprecated (WARNING: does not change MPI scope) // ------------------------------------------------------------------ // C wrapper interfaces to C++ routines extern "C" { void atlas__build_parallel_fields(Mesh::Implementation* mesh); void atlas__build_nodes_parallel_fields(mesh::Nodes* nodes); void atlas__build_edges_parallel_fields(Mesh::Implementation* mesh); void atlas__renumber_nodes_glb_idx(mesh::Nodes* nodes); } // ------------------------------------------------------------------ } // namespace actions } // namespace mesh } // namespace atlas atlas-0.46.0/src/atlas/mesh/actions/BuildParallelFields.cc0000664000175000017500000013045015160212070023527 0ustar alastairalastair/* * (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/array/ArrayView.h" #include "atlas/array/IndexView.h" #include "atlas/field/Field.h" #include "atlas/mesh/HybridElements.h" #include "atlas/mesh/Mesh.h" #include "atlas/mesh/Nodes.h" #include "atlas/mesh/actions/BuildParallelFields.h" #include "atlas/parallel/GatherScatter.h" #include "atlas/parallel/mpi/Buffer.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/PeriodicTransform.h" #include "atlas/util/Unique.h" #define EDGE(jedge) \ "Edge(" << node_gidx(edge_nodes(jedge, 0)) << "[p" << node_part(edge_nodes(jedge, 0)) << "] " \ << node_gidx(edge_nodes(jedge, 1)) << "[p" << node_part(edge_nodes(jedge, 1)) << "])" //#define DEBUGGING_PARFIELDS #ifdef DEBUGGING_PARFIELDS #define own1 2419089 #define own2 2423185 #define OWNED_EDGE(jedge) \ ((node_gidx(edge_nodes(jedge, 0)) == own1 && node_gidx(edge_nodes(jedge, 1)) == own2) || \ (node_gidx(edge_nodes(jedge, 0)) == own2 && node_gidx(edge_nodes(jedge, 1)) == own1)) #define per1 -1 #define per2 -1 #define PERIODIC_EDGE(jedge) \ ((node_gidx(edge_nodes(jedge, 0)) == per1 && node_gidx(edge_nodes(jedge, 1)) == per2) || \ (node_gidx(edge_nodes(jedge, 0)) == per2 && node_gidx(edge_nodes(jedge, 1)) == per1)) #define find1 39 // 1697 #define find2 41 // 1698 #define FIND_EDGE(jedge) \ ((node_gidx(edge_nodes(jedge, 0)) == find1 && node_gidx(edge_nodes(jedge, 1)) == find2) || \ (node_gidx(edge_nodes(jedge, 0)) == find2 && node_gidx(edge_nodes(jedge, 1)) == find1)) #define find_gidx 689849552510167040 #define FIND_GIDX(UID) ((UID) == find_gidx) #endif using Topology = atlas::mesh::Nodes::Topology; using atlas::util::PeriodicTransform; using atlas::util::UniqueLonLat; namespace atlas { namespace mesh { namespace actions { Field& build_nodes_partition(mesh::Nodes& nodes); Field& build_nodes_remote_idx(mesh::Nodes& nodes); Field& build_nodes_global_idx(mesh::Nodes& nodes); Field& build_edges_partition(Mesh& mesh); Field& build_edges_remote_idx(Mesh& mesh); Field& build_edges_global_idx(Mesh& mesh); //---------------------------------------------------------------------------------------------------------------------- using uid_t = gidx_t; namespace { struct Node { Node(gidx_t gid, idx_t idx) { g = gid; i = idx; } gidx_t g; idx_t i; bool operator<(const Node& other) const { return (g < other.g); } }; } // namespace //---------------------------------------------------------------------------------------------------------------------- void build_parallel_fields(Mesh& mesh) { ATLAS_TRACE(); build_nodes_parallel_fields(mesh); } //---------------------------------------------------------------------------------------------------------------------- void build_nodes_parallel_fields(Mesh& mesh) { mpi::Scope mpi_scope(mesh.mpi_comm()); build_nodes_parallel_fields(mesh.nodes()); } void build_nodes_parallel_fields(mesh::Nodes& nodes) { ATLAS_TRACE(); bool parallel = false; nodes.metadata().get("parallel", parallel); if (!parallel) { build_nodes_partition(nodes); build_nodes_remote_idx(nodes); build_nodes_global_idx(nodes); } nodes.metadata().set("parallel", true); } //---------------------------------------------------------------------------------------------------------------------- void build_edges_parallel_fields(Mesh& mesh) { ATLAS_TRACE(); mpi::Scope mpi_scope(mesh.mpi_comm()); build_edges_partition(mesh); build_edges_remote_idx(mesh); /* * We turn following off. It is expensive and we don't really care about a nice * contiguous * ordering. */ // build_edges_global_idx( mesh ); } //---------------------------------------------------------------------------------------------------------------------- Field& build_nodes_global_idx(mesh::Nodes& nodes) { ATLAS_TRACE(); array::ArrayView glb_idx = array::make_view(nodes.global_index()); UniqueLonLat compute_uid(nodes); for (idx_t jnode = 0; jnode < glb_idx.shape(0); ++jnode) { if (glb_idx(jnode) <= 0) { glb_idx(jnode) = compute_uid(jnode); } } return nodes.global_index(); } void renumber_nodes_glb_idx(Mesh& mesh) { mpi::Scope mpi_scope(mesh.mpi_comm()); renumber_nodes_glb_idx(mesh.nodes()); } void renumber_nodes_glb_idx(mesh::Nodes& nodes) { bool human_readable(false); nodes.global_index().metadata().get("human_readable", human_readable); if (human_readable) { /* nothing to be done */ return; } ATLAS_TRACE(); // TODO: ATLAS-14: fix renumbering of EAST periodic boundary points // --> Those specific periodic points at the EAST boundary are not checked for // uid, // and could receive different gidx for different tasks UniqueLonLat compute_uid(nodes); // unused // int mypart = mpi::rank(); int nparts = mpi::size(); idx_t root = 0; array::ArrayView glb_idx = array::make_view(nodes.global_index()); /* * Sorting following gidx will define global order of * gathered fields. Special care needs to be taken for * pole edges, as their centroid might coincide with * other edges */ int nb_nodes = glb_idx.shape(0); for (int jnode = 0; jnode < nb_nodes; ++jnode) { if (glb_idx(jnode) <= 0) { glb_idx(jnode) = compute_uid(jnode); } } // 1) Gather all global indices, together with location array::ArrayT loc_id_arr(nb_nodes); array::ArrayView loc_id = array::make_view(loc_id_arr); for (int jnode = 0; jnode < nb_nodes; ++jnode) { loc_id(jnode) = glb_idx(jnode); } std::vector recvcounts(mpi::size()); std::vector recvdispls(mpi::size()); ATLAS_TRACE_MPI(GATHER) { mpi::comm().gather(nb_nodes, recvcounts, root); } recvdispls[0] = 0; for (int jpart = 1; jpart < nparts; ++jpart) { // start at 1 recvdispls[jpart] = recvcounts[jpart - 1] + recvdispls[jpart - 1]; } int glb_nb_nodes = std::accumulate(recvcounts.begin(), recvcounts.end(), 0); array::ArrayT glb_id_arr(glb_nb_nodes); array::ArrayView glb_id = array::make_view(glb_id_arr); ATLAS_TRACE_MPI(GATHER) { mpi::comm().gatherv(loc_id.data(), loc_id.size(), glb_id.data(), recvcounts.data(), recvdispls.data(), root); } // 2) Sort all global indices, and renumber from 1 to glb_nb_edges std::vector node_sort; 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.emplace_back(glb_id(jnode), jnode); } std::sort(node_sort.begin(), node_sort.end()); } // Assume edge gid start uid_t gid = 0; const idx_t nb_sorted_nodes = static_cast(node_sort.size()); for (idx_t jnode = 0; jnode < nb_sorted_nodes; ++jnode) { if (jnode == 0) { ++gid; } else if (node_sort[jnode].g != node_sort[jnode - 1].g) { ++gid; } idx_t inode = node_sort[jnode].i; glb_id(inode) = gid; } // 3) Scatter renumbered back ATLAS_TRACE_MPI(SCATTER) { mpi::comm().scatterv(glb_id.data(), recvcounts.data(), recvdispls.data(), loc_id.data(), loc_id.size(), root); } for (int jnode = 0; jnode < nb_nodes; ++jnode) { glb_idx(jnode) = loc_id(jnode); } nodes.global_index().metadata().set("human_readable", true); } //---------------------------------------------------------------------------------------------------------------------- Field& build_nodes_remote_idx(mesh::Nodes& nodes) { ATLAS_TRACE(); idx_t mypart = static_cast(mpi::rank()); idx_t nparts = static_cast(mpi::size()); std::vector proc(nparts); for (idx_t jpart = 0; jpart < nparts; ++jpart) { proc[jpart] = jpart; } auto ridx = array::make_indexview(nodes.remote_index()); const auto part = array::make_view(nodes.partition()); const auto flags = array::make_view(nodes.flags()); const PeriodicTransform transform_periodic_east(-360.); const PeriodicTransform transform_periodic_west(+360.); const UniqueLonLat compute_uid_lonlat(nodes); auto compute_uid = [&](idx_t jnode) { constexpr int PERIODIC = util::Topology::PERIODIC; constexpr int EAST = util::Topology::EAST; constexpr int WEST = util::Topology::WEST; const auto flags_view = util::Bitflags::view(flags(jnode)); if (flags_view.check(PERIODIC | EAST)) { return compute_uid_lonlat(jnode, transform_periodic_east); } if (flags_view.check(PERIODIC | WEST)) { return compute_uid_lonlat(jnode, transform_periodic_west); } return compute_uid_lonlat(jnode); }; idx_t nb_nodes = nodes.size(); constexpr idx_t varsize = 2; std::vector> send_needed(mpi::size()); std::vector> recv_needed(mpi::size()); std::map lookup; for (idx_t jnode = 0; jnode < nb_nodes; ++jnode) { uid_t uid = compute_uid(jnode); if (idx_t(part(jnode)) == mypart) { lookup[uid] = jnode; ridx(jnode) = jnode; } else { ATLAS_ASSERT(jnode < part.shape(0)); if (part(jnode) >= static_cast(proc.size())) { std::stringstream msg; msg << "Assertion [part(" << jnode << ") < proc.size()] failed\n" << "part(" << jnode << ") = " << part(jnode) << "\n" << "proc.size() = " << proc.size(); throw_AssertionFailed(msg.str(), Here()); } ATLAS_ASSERT(part(jnode) < (idx_t)proc.size()); ATLAS_ASSERT((size_t)proc[part(jnode)] < send_needed.size()); send_needed[proc[part(jnode)]].push_back(uid); send_needed[proc[part(jnode)]].push_back(jnode); } } ATLAS_TRACE_MPI(ALLTOALL) { mpi::comm().allToAll(send_needed, recv_needed); } std::vector> send_found(mpi::size()); std::vector> recv_found(mpi::size()); for (idx_t jpart = 0; jpart < nparts; ++jpart) { const std::vector& recv_node = recv_needed[proc[jpart]]; const idx_t nb_recv_nodes = idx_t(recv_node.size()) / varsize; for (idx_t jnode = 0; jnode < nb_recv_nodes; ++jnode) { uid_t uid = recv_node[jnode * varsize + 0]; int inode = recv_node[jnode * varsize + 1]; send_found[proc[jpart]].push_back(inode); send_found[proc[jpart]].push_back(lookup.count(uid) ? lookup[uid] : -1); } } ATLAS_TRACE_MPI(ALLTOALL) { mpi::comm().allToAll(send_found, recv_found); } std::stringstream errstream; size_t failed{0}; const auto gidx = array::make_view(nodes.global_index()); for (idx_t jpart = 0; jpart < nparts; ++jpart) { const std::vector& recv_node = recv_found[proc[jpart]]; const idx_t nb_recv_nodes = recv_node.size() / 2; // array::ArrayView recv_node( recv_found[ proc[jpart] ].data(), // array::make_shape(recv_found[ proc[jpart] ].size()/2,2) ); for (idx_t jnode = 0; jnode < nb_recv_nodes; ++jnode) { idx_t inode = recv_node[jnode * 2 + 0]; idx_t ridx_inode = recv_node[jnode * 2 + 1]; if (ridx_inode >= 0) { ridx(recv_node[jnode * 2 + 0]) = ridx_inode; } else { ++failed; errstream << "\n[" << mpi::rank() << "] " << "Node with global index " << gidx(inode) << " not found on part [" << part(inode) << "]"; } } } if (failed) { throw_AssertionFailed(errstream.str(), Here()); } return nodes.remote_index(); } //---------------------------------------------------------------------------------------------------------------------- Field& build_nodes_partition(mesh::Nodes& nodes) { ATLAS_TRACE(); return nodes.partition(); } //---------------------------------------------------------------------------------------------------------------------- Field& build_edges_partition(Mesh& mesh) { ATLAS_TRACE(); const mesh::Nodes& nodes = mesh.nodes(); idx_t mypart = mpi::rank(); mesh::HybridElements& edges = mesh.edges(); auto edge_part = array::make_view(edges.partition()); //const auto edge_glb_idx = array::make_view( edges.global_index() ); const auto& edge_nodes = edges.node_connectivity(); const auto& edge_to_elem = edges.cell_connectivity(); const auto node_part = array::make_view(nodes.partition()); const auto xy = array::make_view(nodes.xy()); const auto flags = array::make_view(nodes.flags()); const auto node_gidx = array::make_view(nodes.global_index()); const auto elem_gidx = array::make_view(mesh.cells().global_index()); const auto elem_part = array::make_view(mesh.cells().partition()); //const auto elem_halo = array::make_view( mesh.cells().halo() ); auto check_flags = [&](idx_t jedge, int flag) { idx_t ip1 = edge_nodes(jedge, 0); idx_t ip2 = edge_nodes(jedge, 1); return Topology::check(flags(ip1), flag) && Topology::check(flags(ip2), flag); }; auto domain_bdry = [&](idx_t jedge) { if (check_flags(jedge, Topology::BC | Topology::NORTH)) { return true; } if (check_flags(jedge, Topology::BC | Topology::SOUTH)) { return true; } if (check_flags(jedge, Topology::BC | Topology::WEST)) { return true; } if (check_flags(jedge, Topology::BC | Topology::EAST)) { return true; } return false; }; auto periodic_east = [&](idx_t jedge) { if (check_flags(jedge, Topology::PERIODIC | Topology::EAST)) { return true; } return false; }; auto periodic_west = [&](idx_t jedge) { if (check_flags(jedge, Topology::PERIODIC | Topology::WEST)) { return true; } return false; }; auto periodic_west_bdry = [&](idx_t jedge) { if (check_flags(jedge, Topology::PERIODIC | Topology::WEST | Topology::BC)) { return true; } return false; }; idx_t nb_edges = edges.size(); std::vector bdry_edges; bdry_edges.reserve(nb_edges); std::map global_to_local; PeriodicTransform transform_periodic_east(-360.); PeriodicTransform transform_periodic_west(+360.); UniqueLonLat _compute_uid(mesh); auto compute_uid = [&](idx_t jedge) -> gidx_t { if (periodic_east(jedge)) { return -_compute_uid(edge_nodes.row(jedge), transform_periodic_east); } else if (periodic_west_bdry(jedge)) { return _compute_uid(edge_nodes.row(jedge)); } else if (periodic_west(jedge)) { return -_compute_uid(edge_nodes.row(jedge), transform_periodic_west); } else { return _compute_uid(edge_nodes.row(jedge)); } }; // should be unit-test { ATLAS_ASSERT(util::unique_lonlat(360., 0., transform_periodic_east) == util::unique_lonlat(0., 0.)); ATLAS_ASSERT(util::unique_lonlat(0., 0., transform_periodic_west) == util::unique_lonlat(360., 0.)); } for (idx_t jedge = 0; jedge < nb_edges; ++jedge) { gidx_t edge_gidx = compute_uid(jedge); global_to_local[edge_gidx] = jedge; #ifdef DEBUGGING_PARFIELDS if (FIND_EDGE(jedge)) { std::cout << "[" << mypart << "] " << EDGE(jedge) << " has gidx " << edge_gidx << std::endl; if (periodic_east(jedge)) { std::cout << "[" << mypart << "] " << EDGE(jedge) << " is periodic_east " << std::endl; } else if (periodic_west(jedge)) { std::cout << "[" << mypart << "] " << EDGE(jedge) << " is periodic_west " << std::endl; } else { std::cout << "[" << mypart << "] " << EDGE(jedge) << " is not periodic" << std::endl; } } if (FIND_GIDX(edge_gidx)) std::cout << "[" << mypart << "] " << "has " << EDGE(jedge) << " with gidx " << edge_gidx << std::endl; #endif idx_t ip1 = edge_nodes(jedge, 0); idx_t ip2 = edge_nodes(jedge, 1); int pn1 = node_part(ip1); int pn2 = node_part(ip2); int y1 = util::microdeg(xy(ip1, YY)); int y2 = util::microdeg(xy(ip2, YY)); int p; if (y1 == y2) { int x1 = util::microdeg(xy(ip1, XX)); int x2 = util::microdeg(xy(ip2, XX)); p = (x1 < x2) ? pn1 : pn2; } else { p = (y1 > y2) ? pn1 : pn2; } idx_t elem1 = edge_to_elem(jedge, 0); idx_t elem2 = edge_to_elem(jedge, 1); idx_t missing = edge_to_elem.missing_value(); if (elem1 == missing && elem2 == missing) { // Don't attempt to change p } else if (elem1 == missing) { ATLAS_NOTIMPLEMENTED; } else if (elem2 == missing) { if (pn1 == pn2) { p = pn1; } else if (periodic_east(jedge)) { #ifdef DEBUGGING_PARFIELDS if (FIND_EDGE(jedge)) std::cout << "[" << mypart << "] " << "periodic_east" << std::endl; #endif bdry_edges.push_back(edge_gidx); p = -1; } else if (periodic_west_bdry(jedge)) { #ifdef DEBUGGING_PARFIELDS if (FIND_EDGE(jedge)) std::cout << "[" << mypart << "] " << "periodic_west_bdry" << std::endl; #endif p = elem_part(elem1); } else if (periodic_west(jedge)) { #ifdef DEBUGGING_PARFIELDS if (FIND_EDGE(jedge)) std::cout << "[" << mypart << "] " << "periodic_west" << std::endl; #endif bdry_edges.push_back(edge_gidx); p = -1; } else if (domain_bdry(jedge)) { #ifdef DEBUGGING_PARFIELDS if (FIND_EDGE(jedge)) std::cout << "[" << mypart << "] " << "domain_bdry" << std::endl; #endif p = elem_part(elem1); } else { bdry_edges.push_back(edge_gidx); p = -1; } } else if (p != elem_part(elem1) && p != elem_part(elem2)) { p = (p == pn1) ? pn2 : pn1; if (p != elem_part(elem1) and p != elem_part(elem2)) { std::stringstream msg; msg << "[" << mpi::rank() << "] " << EDGE(jedge) << " has nodes and elements of different rank: elem1[p" << elem_part(elem1) << "] elem2[p" << elem_part(elem2) << "]"; throw_Exception(msg.str(), Here()); } } edge_part(jedge) = p; } std::sort(bdry_edges.begin(), bdry_edges.end()); auto is_bdry_edge = [&bdry_edges](gidx_t gid) { std::vector::iterator it = std::lower_bound(bdry_edges.begin(), bdry_edges.end(), gid); bool found = !(it == bdry_edges.end() || gid < *it); return found; }; int mpi_size = mpi::size(); mpi::Buffer recv_bdry_edges_from_parts(mpi_size); std::vector> send_gidx(mpi_size); std::vector> send_part(mpi_size); std::vector> send_bdry_gidx(mpi_size); std::vector> send_bdry_elem_part(mpi_size); std::vector> send_bdry_elem_gidx(mpi_size); mpi::comm().allGatherv(bdry_edges.begin(), bdry_edges.end(), recv_bdry_edges_from_parts); for (int p = 0; p < mpi_size; ++p) { auto view = recv_bdry_edges_from_parts[p]; for (size_t j = 0; j < view.size(); ++j) { gidx_t gidx = view[j]; gidx_t master_gidx = std::abs(gidx); if (global_to_local.count(master_gidx)) { idx_t iedge = global_to_local[master_gidx]; #ifdef DEBUGGING_PARFIELDS if (FIND_GIDX(master_gidx)) std::cout << "[" << mypart << "] found " << EDGE(iedge) << std::endl; #endif if (not is_bdry_edge(master_gidx)) { send_gidx[p].push_back(gidx); send_part[p].push_back(edge_part(iedge)); #ifdef DEBUGGING_PARFIELDS if (FIND_EDGE(iedge)) { std::cout << "[" << mypart << "] found " << EDGE(iedge) " for part " << p << std::endl; } #endif } else { // boundary edge with nodes of different rank idx_t ielem = (edge_to_elem(iedge, 0) != edge_to_elem.missing_value() ? edge_to_elem(iedge, 0) : edge_to_elem(iedge, 1)); send_bdry_gidx[p].push_back(gidx); send_bdry_elem_part[p].push_back(elem_part(ielem)); send_bdry_elem_gidx[p].push_back(elem_gidx(ielem)); } } } } std::vector> recv_gidx(mpi_size); std::vector> recv_part(mpi_size); mpi::comm().allToAll(send_gidx, recv_gidx); mpi::comm().allToAll(send_part, recv_part); for (int p = 0; p < mpi_size; ++p) { const auto& recv_gidx_p = recv_gidx[p]; const auto& recv_part_p = recv_part[p]; for (size_t j = 0; j < recv_gidx_p.size(); ++j) { idx_t iedge = global_to_local[recv_gidx_p[j]]; // int prev = edge_part( iedge ); edge_part(iedge) = recv_part_p[j]; // if( edge_part(iedge) != prev ) // Log::error() << EDGE(iedge) << " part " << prev << " --> " << // edge_part(iedge) << std::endl; } } std::vector> recv_bdry_gidx(mpi_size); std::vector> recv_bdry_elem_part(mpi_size); std::vector> recv_bdry_elem_gidx(mpi_size); mpi::comm().allToAll(send_bdry_gidx, recv_bdry_gidx); mpi::comm().allToAll(send_bdry_elem_part, recv_bdry_elem_part); mpi::comm().allToAll(send_bdry_elem_gidx, recv_bdry_elem_gidx); for (int p = 0; p < mpi_size; ++p) { const auto& recv_bdry_gidx_p = recv_bdry_gidx[p]; const auto& recv_bdry_elem_part_p = recv_bdry_elem_part[p]; const auto& recv_bdry_elem_gidx_p = recv_bdry_elem_gidx[p]; for (size_t j = 0; j < recv_bdry_gidx_p.size(); ++j) { idx_t iedge = global_to_local[recv_bdry_gidx_p[j]]; idx_t e1 = (edge_to_elem(iedge, 0) != edge_to_elem.missing_value() ? edge_to_elem(iedge, 0) : edge_to_elem(iedge, 1)); if (elem_gidx(e1) != recv_bdry_elem_gidx_p[j]) { idx_t ip1 = edge_nodes(iedge, 0); idx_t ip2 = edge_nodes(iedge, 1); int pn1 = node_part(ip1); int pn2 = node_part(ip2); int y1 = util::microdeg(xy(ip1, YY)); int y2 = util::microdeg(xy(ip2, YY)); int ped; if (y1 == y2) { int x1 = util::microdeg(xy(ip1, XX)); int x2 = util::microdeg(xy(ip2, XX)); ped = (x1 < x2) ? pn1 : pn2; } else { ped = (y1 > y2) ? pn1 : pn2; } int pe1 = elem_part(e1); int pe2 = recv_bdry_elem_part_p[j]; if (ped != pe1 && ped != pe2) { ped = (ped == pn1) ? pn2 : pn1; if (ped != pe1 && p != pe2) { std::stringstream msg; msg << "[" << mpi::rank() << "] " << EDGE(iedge) << " has nodes and elements of different rank: elem1[p" << pe1 << "] elem2[p" << pe2 << "]"; throw_Exception(msg.str(), Here()); } } edge_part(iedge) = ped; } } } // Sanity check auto edge_flags = array::make_view(edges.flags()); auto is_pole_edge = [&](idx_t e) { return Topology::check(edge_flags(e), Topology::POLE); }; bool has_pole_edges = false; mesh.edges().metadata().get("pole_edges", has_pole_edges); int insane = 0; for (idx_t jedge = 0; jedge < nb_edges; ++jedge) { idx_t ip1 = edge_nodes(jedge, 0); idx_t ip2 = edge_nodes(jedge, 1); idx_t elem1 = edge_to_elem(jedge, 0); idx_t elem2 = edge_to_elem(jedge, 1); int p = edge_part(jedge); int pn1 = node_part(ip1); int pn2 = node_part(ip2); if (has_pole_edges && is_pole_edge(jedge)) { if (p != pn1 || p != pn2) { Log::error() << "pole edge " << EDGE(jedge) << " [p" << p << "] is not correct" << std::endl; insane = 1; } } else { if (elem1 == edge_to_elem.missing_value() && elem2 == edge_to_elem.missing_value()) { Log::error() << EDGE(jedge) << " has no neighbouring elements" << std::endl; insane = 1; } } bool edge_is_partition_boundary = (elem1 == edge_to_elem.missing_value() || elem2 == edge_to_elem.missing_value()); bool edge_partition_is_same_as_one_of_nodes = (p == pn1 || p == pn2); if (edge_is_partition_boundary) { if (not edge_partition_is_same_as_one_of_nodes) { gidx_t edge_gidx = compute_uid(jedge); if (elem1 != edge_to_elem.missing_value()) { Log::error() << "[" << mypart << "] " << EDGE(jedge) << " " << edge_gidx << " [p" << p << "] at partition_boundary is not correct. elem1[p" << elem_part(elem1) << "]" << std::endl; } else { Log::error() << "[" << mypart << "] " << EDGE(jedge) << " " << edge_gidx << " [p" << p << "] at partition_boundary is not correct elem2[p" << elem_part(elem2) << "]" << std::endl; } insane = 1; } } else { int pe1 = elem_part(elem1); int pe2 = elem_part(elem2); bool edge_partition_is_same_as_one_of_elems = (p == pe1 || p == pe2); if (not edge_partition_is_same_as_one_of_elems and not edge_partition_is_same_as_one_of_nodes) { Log::error() << EDGE(jedge) << " is not correct elem1[p" << pe1 << "] elem2[p" << pe2 << "]" << std::endl; insane = 1; } } } mpi::comm().allReduceInPlace(insane, eckit::mpi::max()); if (insane && mpi::rank() == 0) { throw_Exception("Sanity check failed", Here()); } //#ifdef DEBUGGING_PARFIELDS // if( OWNED_EDGE(jedge) ) // DEBUG( EDGE(jedge) << " --> part " << edge_part(jedge)); //#endif //#ifdef DEBUGGING_PARFIELDS_disable // if( PERIODIC_EDGE(jedge) ) // DEBUG_VAR( " the part is " << edge_part(jedge) ); //#endif // } return edges.partition(); } Field& build_edges_remote_idx(Mesh& mesh) { ATLAS_TRACE(); const mesh::Nodes& nodes = mesh.nodes(); UniqueLonLat compute_uid(mesh); idx_t mypart = mpi::rank(); idx_t nparts = mpi::size(); mesh::HybridElements& edges = mesh.edges(); auto edge_ridx = array::make_indexview(edges.remote_index()); const array::ArrayView edge_part = array::make_view(edges.partition()); const mesh::HybridElements::Connectivity& edge_nodes = edges.node_connectivity(); array::ArrayView xy = array::make_view(nodes.xy()); array::ArrayView flags = array::make_view(nodes.flags()); #ifdef DEBUGGING_PARFIELDS array::ArrayView node_gidx = array::make_view(nodes.global_index()); array::ArrayView node_part = array::make_view(nodes.partition()); #endif auto edge_flags = array::make_view(edges.flags()); auto is_pole_edge = [&](idx_t e) { return Topology::check(edge_flags(e), Topology::POLE); }; bool has_pole_edges = false; mesh.edges().metadata().get("pole_edges", has_pole_edges); const int nb_edges = edges.size(); double centroid[2]; std::vector> send_needed(mpi::size()); std::vector> recv_needed(mpi::size()); std::map lookup; PeriodicTransform transform; for (int jedge = 0; jedge < nb_edges; ++jedge) { int ip1 = edge_nodes(jedge, 0); int ip2 = edge_nodes(jedge, 1); centroid[XX] = 0.5 * (xy(ip1, XX) + xy(ip2, XX)); centroid[YY] = 0.5 * (xy(ip1, YY) + xy(ip2, YY)); if (has_pole_edges && is_pole_edge(jedge)) { centroid[YY] = centroid[YY] > 0 ? 90. : -90.; } bool needed(false); if ((Topology::check(flags(ip1), Topology::PERIODIC) && !Topology::check(flags(ip1), Topology::BC | Topology::WEST) && Topology::check(flags(ip2), Topology::PERIODIC) && !Topology::check(flags(ip2), Topology::BC | Topology::WEST)) || (Topology::check(flags(ip1), Topology::PERIODIC) && !Topology::check(flags(ip1), Topology::BC | Topology::WEST) && Topology::check(flags(ip2), Topology::BC | Topology::WEST)) || (Topology::check(flags(ip1), Topology::BC | Topology::WEST) && Topology::check(flags(ip2), Topology::PERIODIC) && !Topology::check(flags(ip2), Topology::BC | Topology::WEST))) { needed = true; if (Topology::check(flags(ip1), Topology::EAST)) { transform(centroid, -1); } else { transform(centroid, +1); } } uid_t uid = util::unique_lonlat(centroid); if (idx_t(edge_part(jedge)) == mypart && !needed) // All interior edges fall here { lookup[uid] = jedge; edge_ridx(jedge) = jedge; #ifdef DEBUGGING_PARFIELDS if (FIND_EDGE(jedge)) { ATLAS_DEBUG("Found " << EDGE(jedge)); } #endif } else // All ghost edges PLUS the periodic edges identified edges above // fall here { send_needed[edge_part(jedge)].push_back(uid); send_needed[edge_part(jedge)].push_back(jedge); #ifdef DEBUGGING_PARFIELDS send_needed[edge_part(jedge)].push_back(node_gidx(ip1)); send_needed[edge_part(jedge)].push_back(node_gidx(ip2)); send_needed[edge_part(jedge)].push_back(node_part(ip1)); send_needed[edge_part(jedge)].push_back(node_part(ip2)); #endif } } idx_t varsize = 2; #ifdef DEBUGGING_PARFIELDS varsize = 6; #endif ATLAS_TRACE_MPI(ALLTOALL) { mpi::comm().allToAll(send_needed, recv_needed); } std::vector> send_found(mpi::size()); std::vector> recv_found(mpi::size()); std::map::iterator found; for (idx_t jpart = 0; jpart < nparts; ++jpart) { const std::vector& recv_edge = recv_needed[jpart]; const idx_t nb_recv_edges = idx_t(recv_edge.size()) / varsize; // array::ArrayView recv_edge( recv_needed[ jpart ].data(), // array::make_shape(recv_needed[ jpart ].size()/varsize,varsize) ); for (idx_t jedge = 0; jedge < nb_recv_edges; ++jedge) { uid_t recv_uid = recv_edge[jedge * varsize + 0]; int recv_idx = recv_edge[jedge * varsize + 1]; found = lookup.find(recv_uid); if (found != lookup.end()) { send_found[jpart].push_back(recv_idx); send_found[jpart].push_back(found->second); } else { std::stringstream msg; #ifdef DEBUGGING_PARFIELDS msg << "Edge(" << recv_edge[jedge * varsize + 2] << "[p" << recv_edge[jedge * varsize + 4] << "] " << recv_edge[jedge * varsize + 3] << "[p" << recv_edge[jedge * varsize + 5] << "])"; #else msg << "Edge with uid " << recv_uid; #endif msg << " requested by rank [" << jpart << "]"; msg << " that should be owned by " << mpi::rank() << " is not found. This could be because no " "halo was built."; // throw_Exception(msg.str(),Here()); Log::warning() << msg.str() << " @ " << Here() << std::endl; } } } ATLAS_TRACE_MPI(ALLTOALL) { mpi::comm().allToAll(send_found, recv_found); } for (idx_t jpart = 0; jpart < nparts; ++jpart) { const std::vector& recv_edge = recv_found[jpart]; const idx_t nb_recv_edges = recv_edge.size() / 2; // array::ArrayView recv_edge( recv_found[ jpart ].data(), // array::make_shape(recv_found[ jpart ].size()/2,2) ); for (idx_t jedge = 0; jedge < nb_recv_edges; ++jedge) { edge_ridx(recv_edge[jedge * 2 + 0]) = recv_edge[jedge * 2 + 1]; } } return edges.remote_index(); } Field& build_edges_global_idx(Mesh& mesh) { ATLAS_TRACE(); UniqueLonLat compute_uid(mesh); int nparts = mpi::size(); idx_t root = 0; mesh::HybridElements& edges = mesh.edges(); array::make_view(edges.global_index()).assign(-1); array::ArrayView edge_gidx = array::make_view(edges.global_index()); const mesh::HybridElements::Connectivity& edge_nodes = edges.node_connectivity(); array::ArrayView xy = array::make_view(mesh.nodes().xy()); auto edge_flags = array::make_view(edges.flags()); auto is_pole_edge = [&](idx_t e) { return Topology::check(edge_flags(e), Topology::POLE); }; bool has_pole_edges = false; mesh.edges().metadata().get("pole_edges", has_pole_edges); /* * Sorting following edge_gidx will define global order of * gathered fields. Special care needs to be taken for * pole edges, as their centroid might coincide with * other edges */ double centroid[2]; int nb_edges = edges.size(); for (int jedge = 0; jedge < nb_edges; ++jedge) { if (edge_gidx(jedge) <= 0) { centroid[XX] = 0.5 * (xy(edge_nodes(jedge, 0), XX) + xy(edge_nodes(jedge, 1), XX)); centroid[YY] = 0.5 * (xy(edge_nodes(jedge, 0), YY) + xy(edge_nodes(jedge, 1), YY)); if (has_pole_edges && is_pole_edge(jedge)) { centroid[YY] = centroid[YY] > 0 ? 90. : -90.; } edge_gidx(jedge) = util::unique_lonlat(centroid); } } /* * REMOTE INDEX BASE = 1 */ // 1) Gather all global indices, together with location array::ArrayT loc_edge_id_arr(nb_edges); array::ArrayView loc_edge_id = array::make_view(loc_edge_id_arr); for (int jedge = 0; jedge < nb_edges; ++jedge) { loc_edge_id(jedge) = edge_gidx(jedge); } std::vector recvcounts(mpi::size()); std::vector recvdispls(mpi::size()); ATLAS_TRACE_MPI(GATHER) { mpi::comm().gather(nb_edges, recvcounts, root); } recvdispls[0] = 0; for (int jpart = 1; jpart < nparts; ++jpart) // start at 1 { recvdispls[jpart] = recvcounts[jpart - 1] + recvdispls[jpart - 1]; } int glb_nb_edges = std::accumulate(recvcounts.begin(), recvcounts.end(), 0); array::ArrayT glb_edge_id_arr(glb_nb_edges); array::ArrayView glb_edge_id = array::make_view(glb_edge_id_arr); ATLAS_TRACE_MPI(GATHER) { mpi::comm().gatherv(loc_edge_id.data(), loc_edge_id.size(), glb_edge_id.data(), recvcounts.data(), recvdispls.data(), root); } // 2) Sort all global indices, and renumber from 1 to glb_nb_edges std::vector edge_sort; edge_sort.reserve(glb_nb_edges); for (idx_t jedge = 0; jedge < glb_edge_id.shape(0); ++jedge) { edge_sort.emplace_back(glb_edge_id(jedge), jedge); } std::sort(edge_sort.begin(), edge_sort.end()); // Assume edge gid start uid_t gid(0); for (size_t jedge = 0; jedge < edge_sort.size(); ++jedge) { if (jedge == 0) { ++gid; } else if (edge_sort[jedge].g != edge_sort[jedge - 1].g) { ++gid; } int iedge = edge_sort[jedge].i; glb_edge_id(iedge) = gid; } // 3) Scatter renumbered back ATLAS_TRACE_MPI(SCATTER) { mpi::comm().scatterv(glb_edge_id.data(), recvcounts.data(), recvdispls.data(), loc_edge_id.data(), loc_edge_id.size(), root); } for (int jedge = 0; jedge < nb_edges; ++jedge) { edge_gidx(jedge) = loc_edge_id(jedge); } return edges.global_index(); } //---------------------------------------------------------------------------------------------------------------------- Field& build_cells_remote_idx(mesh::Cells& cells, const mesh::Nodes& nodes) { ATLAS_TRACE(); idx_t mypart = static_cast(mpi::rank()); idx_t nparts = static_cast(mpi::size()); // 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()); const auto part = array::make_view(cells.partition()); const auto gidx = array::make_view(cells.global_index()); const auto flags = array::make_view(cells.flags()); const auto& element_nodes = cells.node_connectivity(); idx_t nb_cells = cells.size(); const PeriodicTransform transform_periodic_east(-360.); const PeriodicTransform transform_periodic_west(+360.); const UniqueLonLat compute_uid_lonlat(nodes); auto compute_uid = [&](idx_t jcell) { constexpr int PERIODIC = util::Topology::PERIODIC; constexpr int EAST = util::Topology::EAST; constexpr int WEST = util::Topology::WEST; const auto flags_view = util::Bitflags::view(flags(jcell)); if (flags_view.check(PERIODIC | EAST)) { return compute_uid_lonlat(element_nodes.row(jcell), transform_periodic_east); } if (flags_view.check(PERIODIC | WEST)) { return compute_uid_lonlat(element_nodes.row(jcell), transform_periodic_west); } return compute_uid_lonlat(element_nodes.row(jcell)); }; idx_t varsize = 2; std::vector> send_needed(mpi::size()); std::vector> recv_needed(mpi::size()); std::map lookup; for (idx_t jcell = 0; jcell < nb_cells; ++jcell) { uid_t uid = compute_uid(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); } } ATLAS_TRACE_MPI(ALLTOALL) { mpi::comm().allToAll(send_needed, recv_needed); } std::vector> send_found(mpi::size()); std::vector> recv_found(mpi::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; 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]; send_found[proc[jpart]].push_back(icell); send_found[proc[jpart]].push_back(lookup.count(uid) ? lookup[uid] : -1); } } ATLAS_TRACE_MPI(ALLTOALL) { mpi::comm().allToAll(send_found, recv_found); } std::stringstream errstream; size_t failed{0}; 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) { idx_t icell = recv_cell[jcell * 2 + 0]; idx_t ridx_icell = recv_cell[jcell * 2 + 1]; if (ridx_icell >= 0) { ridx(icell) = ridx_icell; } else { ++failed; errstream << "\n[" << mpi::rank() << "] " << "Cell " << gidx(icell) << " not found on part [" << part(icell) << "]"; } } } if (failed) { throw_AssertionFailed(errstream.str(), Here()); } return cells.remote_index(); } void build_cells_parallel_fields(Mesh& mesh) { mpi::Scope mpi_scope(mesh.mpi_comm()); bool parallel = false; mesh.cells().metadata().get("parallel", parallel); if (!parallel) { build_cells_remote_idx(mesh.cells(), mesh.nodes()); } mesh.cells().metadata().set("parallel", true); } //---------------------------------------------------------------------------------------------------------------------- // C wrapper interfaces to C++ routines void atlas__build_parallel_fields(Mesh::Implementation* mesh) { ATLAS_ASSERT(mesh != nullptr, "Cannot access uninitialised atlas_Mesh"); Mesh m(mesh); build_parallel_fields(m); } void atlas__build_nodes_parallel_fields(mesh::Nodes* nodes) { ATLAS_ASSERT(nodes != nullptr, "Cannot access uninitialised atlas_mesh_Nodes"); build_nodes_parallel_fields(*nodes); } void atlas__build_edges_parallel_fields(Mesh::Implementation* mesh) { ATLAS_ASSERT(mesh != nullptr, "Cannot access uninitialised atlas_Mesh"); Mesh m(mesh); build_edges_parallel_fields(m); } void atlas__renumber_nodes_glb_idx(mesh::Nodes* nodes) { ATLAS_ASSERT(nodes != nullptr, "Cannot access uninitialised atlas_mesh_Nodes"); renumber_nodes_glb_idx(*nodes); } //---------------------------------------------------------------------------------------------------------------------- } // namespace actions } // namespace mesh } // namespace atlas atlas-0.46.0/src/atlas/mesh/actions/BuildNode2CellConnectivity.cc0000664000175000017500000000776015160212070025021 0ustar alastairalastair/* * (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 "BuildNode2CellConnectivity.h" #include "atlas/array.h" #include "atlas/array/ArrayView.h" #include "atlas/array/IndexView.h" #include "atlas/domain.h" #include "atlas/field/Field.h" #include "atlas/grid/StructuredGrid.h" #include "atlas/library/config.h" #include "atlas/mesh/ElementType.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/BuildEdges.h" #include "atlas/mesh/detail/AccumulateFacets.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Log.h" #include "atlas/util/CoordinateEnums.h" #include "atlas/util/LonLatMicroDeg.h" #include "atlas/util/MicroDeg.h" #include "atlas/util/Unique.h" using atlas::mesh::detail::accumulate_facets_ordered_by_halo; using Topology = atlas::mesh::Nodes::Topology; using atlas::util::microdeg; using atlas::util::UniqueLonLat; namespace atlas { namespace mesh { namespace actions { //---------------------------------------------------------------------------------------------------------------------- namespace { // anonymous struct Sort { Sort() = default; Sort(gidx_t gid, idx_t idx) { g = gid; i = idx; } gidx_t g; idx_t i; bool operator<(const Sort& other) const { return (g < other.g); } }; } // anonymous namespace void BuildNode2CellConnectivity::operator()() { mesh::Nodes& nodes = mesh_.nodes(); const idx_t nb_cells = mesh_.cells().size(); mesh::Nodes::Connectivity& node_to_cell = nodes.cell_connectivity(); node_to_cell.clear(); const mesh::HybridElements::Connectivity& cell_node_connectivity = mesh_.cells().node_connectivity(); std::vector to_cell_size(nodes.size(), 0); for (idx_t jcell = 0; jcell < nb_cells; ++jcell) { for (idx_t j = 0; j < cell_node_connectivity.cols(jcell); ++j) { ++to_cell_size[cell_node_connectivity(jcell, j)]; } } node_to_cell.add(nodes.size(), to_cell_size.data()); for (idx_t jnode = 0; jnode < nodes.size(); ++jnode) { to_cell_size[jnode] = 0; } UniqueLonLat compute_uid(mesh_); std::vector cell_sort(nb_cells); for (idx_t jcell = 0; jcell < nb_cells; ++jcell) { cell_sort[jcell] = Sort(compute_uid(cell_node_connectivity.row(jcell)), jcell); } std::stable_sort(cell_sort.data(), cell_sort.data() + nb_cells); for (idx_t jcell = 0; jcell < nb_cells; ++jcell) { idx_t icell = cell_sort[jcell].i; ATLAS_ASSERT(icell < nb_cells); for (idx_t j = 0; j < cell_node_connectivity.cols(icell); ++j) { idx_t node = cell_node_connectivity(icell, j); node_to_cell.set(node, to_cell_size[node]++, icell); } } } BuildNode2CellConnectivity::BuildNode2CellConnectivity(Mesh& mesh): mesh_(mesh) {} //---------------------------------------------------------------------------------------------------------------------- // C wrapper interfaces to C++ routines extern "C" { void atlas__build_node_to_cell_connectivity(Mesh::Implementation* mesh) { ATLAS_ASSERT(mesh != nullptr, "Cannot access uninitialised atlas_Mesh"); auto m = Mesh(mesh); auto build_node_to_cell_connectivity = BuildNode2CellConnectivity(m); build_node_to_cell_connectivity(); } } //---------------------------------------------------------------------------------------------------------------------- } // namespace actions } // namespace mesh } // namespace atlas atlas-0.46.0/src/atlas/mesh/actions/ReorderReverseCuthillMckee.cc0000664000175000017500000001345215160212070025116 0ustar alastairalastair/* * (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 (size_t 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()) { idx_t 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(), [&](idx_t i, idx_t 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(); idx_t size = static_cast(cuthill.size()); idx_t n = size; if (n % 2 == 0) { n -= 1; } n = n / 2; for (idx_t i = 0; i <= n; i++) { idx_t 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 atlas-0.46.0/src/atlas/mesh/actions/BuildConvexHull3D.h0000664000175000017500000000152715160212070022766 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include "eckit/config/Parametrisation.h" #include "atlas/util/Config.h" namespace atlas { class Mesh; namespace mesh { namespace actions { /// Creates a 3D convex-hull on the mesh points class BuildConvexHull3D { public: BuildConvexHull3D(const eckit::Parametrisation& = util::NoConfig()); void operator()(Mesh&) const; private: bool remove_duplicate_points_; }; } // namespace actions } // namespace mesh } // namespace atlas atlas-0.46.0/src/atlas/mesh/actions/BuildHalo.cc0000664000175000017500000015623315160212070021536 0ustar alastairalastair/* * (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 #include #include "atlas/array.h" #include "atlas/array/IndexView.h" #include "atlas/field/Field.h" #include "atlas/library/config.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/BuildHalo.h" #include "atlas/mesh/actions/BuildParallelFields.h" #include "atlas/mesh/detail/AccumulateFacets.h" #include "atlas/parallel/mpi/Buffer.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/parallel/omp/omp.h" #include "atlas/parallel/omp/sort.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Log.h" #include "atlas/runtime/Trace.h" #include "atlas/util/CoordinateEnums.h" #include "atlas/util/LonLatMicroDeg.h" #include "atlas/util/MicroDeg.h" #include "atlas/util/PeriodicTransform.h" #include "atlas/util/Unique.h" //#define DEBUG_OUTPUT #ifdef DEBUG_OUTPUT #include "atlas/mesh/actions/BuildParallelFields.h" #include "atlas/mesh/actions/BuildXYZField.h" #include "atlas/output/Gmsh.h" #endif // #define ATLAS_103 // #define ATLAS_103_SORT using atlas::mesh::detail::accumulate_facets; using atlas::util::LonLatMicroDeg; using atlas::util::microdeg; using atlas::util::PeriodicTransform; using atlas::util::UniqueLonLat; using Topology = atlas::mesh::Nodes::Topology; namespace atlas { namespace mesh { namespace actions { struct Entity { Entity(gidx_t gid, idx_t idx) { g = gid; i = idx; } gidx_t g; idx_t i; bool operator<(const Entity& other) const { return (g < other.g); } }; void make_nodes_global_index_human_readable(const mesh::actions::BuildHalo& build_halo, mesh::Nodes& nodes, bool do_all) { ATLAS_TRACE(); // TODO: ATLAS-14: fix renumbering of EAST periodic boundary points // --> Those specific periodic points at the EAST boundary are not checked for // uid, // and could receive different gidx for different tasks // unused // int mypart = mpi::rank(); int nparts = mpi::size(); size_t root = 0; array::ArrayView nodes_glb_idx = array::make_view(nodes.global_index()); // nodes_glb_idx.dump( Log::info() ); // ATLAS_DEBUG( "min = " << nodes.global_index().metadata().getLong("min") ); // ATLAS_DEBUG( "max = " << nodes.global_index().metadata().getLong("max") ); // ATLAS_DEBUG( "human_readable = " << // nodes.global_index().metadata().getBool("human_readable") ); gidx_t glb_idx_max = 0; std::vector points_to_edit; if (do_all) { points_to_edit.resize(nodes_glb_idx.size()); for (idx_t i = 0; i < nodes_glb_idx.size(); ++i) { points_to_edit[i] = i; } } else { glb_idx_max = nodes.global_index().metadata().getLong("max", 0); points_to_edit.resize(build_halo.periodic_points_local_index_.size()); for (size_t i = 0; i < points_to_edit.size(); ++i) { points_to_edit[i] = build_halo.periodic_points_local_index_[i]; } } std::vector glb_idx(points_to_edit.size()); int nb_nodes = static_cast(glb_idx.size()); for (idx_t i = 0; i < nb_nodes; ++i) { glb_idx[i] = nodes_glb_idx(points_to_edit[i]); } // ATLAS_DEBUG_VAR( points_to_edit ); // ATLAS_DEBUG_VAR( points_to_edit.size() ); /* * Sorting following gidx will define global order of * gathered fields. Special care needs to be taken for * pole edges, as their centroid might coincide with * other edges */ // Try to recover // { // UniqueLonLat compute_uid(nodes); // for( int jnode=0; jnode recvcounts(mpi::size()); std::vector recvdispls(mpi::size()); ATLAS_TRACE_MPI(GATHER) { mpi::comm().gather(nb_nodes, recvcounts, root); } idx_t glb_nb_nodes = std::accumulate(recvcounts.begin(), recvcounts.end(), 0); recvdispls[0] = 0; for (int jpart = 1; jpart < nparts; ++jpart) { // start at 1 recvdispls[jpart] = recvcounts[jpart - 1] + recvdispls[jpart - 1]; } std::vector glb_idx_gathered(glb_nb_nodes); ATLAS_TRACE_MPI(GATHER) { mpi::comm().gatherv(glb_idx.data(), glb_idx.size(), glb_idx_gathered.data(), recvcounts.data(), recvdispls.data(), root); } // 2) Sort all global indices, and renumber from 1 to glb_nb_edges std::vector node_sort; 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.emplace_back(glb_idx_gathered[jnode], jnode); } ATLAS_TRACE_SCOPE("sort on rank 0") { std::sort(node_sort.begin(), node_sort.end()); } gidx_t gid = glb_idx_max + 1; const idx_t nb_node_sort = static_cast(node_sort.size()); for (idx_t jnode = 0; jnode < nb_node_sort; ++jnode) { if (jnode > 0 && node_sort[jnode].g != node_sort[jnode - 1].g) { ++gid; } int inode = node_sort[jnode].i; glb_idx_gathered[inode] = gid; } // 3) Scatter renumbered back ATLAS_TRACE_MPI(SCATTER) { mpi::comm().scatterv(glb_idx_gathered.data(), recvcounts.data(), recvdispls.data(), glb_idx.data(), static_cast(glb_idx.size()), root); } for (int jnode = 0; jnode < nb_nodes; ++jnode) { nodes_glb_idx(points_to_edit[jnode]) = glb_idx[jnode]; } // nodes_glb_idx.dump( Log::info() ); // Log::info() << std::endl; nodes.global_index().metadata().set("human_readable", true); } // ------------------------------------------------------------------------------------- void make_cells_global_index_human_readable(const mesh::actions::BuildHalo& build_halo, mesh::Cells& cells, bool do_all) { ATLAS_TRACE(); int nparts = static_cast(mpi::size()); idx_t root = 0; array::ArrayView cells_glb_idx = array::make_view(cells.global_index()); // ATLAS_DEBUG( "min = " << cells.global_index().metadata().getLong("min") ); // ATLAS_DEBUG( "max = " << cells.global_index().metadata().getLong("max") ); // ATLAS_DEBUG( "human_readable = " << // cells.global_index().metadata().getBool("human_readable") ); gidx_t glb_idx_max = 0; std::vector cells_to_edit; if (do_all) { cells_to_edit.resize(cells_glb_idx.size()); for (idx_t i = 0; i < cells_glb_idx.size(); ++i) { cells_to_edit[i] = i; } } else { idx_t nb_cells_to_edit(0); for (const auto& new_cells : build_halo.periodic_cells_local_index_) { nb_cells_to_edit += new_cells.size(); } cells_to_edit.resize(nb_cells_to_edit); int c{0}; int i{0}; for (int t = 0; t < cells.nb_types(); ++t) { for (idx_t p : build_halo.periodic_cells_local_index_[t]) { cells_to_edit[i++] = c + p; } c += cells.elements(t).size(); } glb_idx_max = cells.global_index().metadata().getLong("max", 0); } std::vector glb_idx(cells_to_edit.size()); const int nb_cells = static_cast(glb_idx.size()); for (int i = 0; i < nb_cells; ++i) { glb_idx[i] = cells_glb_idx(cells_to_edit[i]); } // 1) Gather all global indices, together with location std::vector recvcounts(mpi::size()); std::vector recvdispls(mpi::size()); ATLAS_TRACE_MPI(GATHER) { mpi::comm().gather(nb_cells, recvcounts, root); } idx_t glb_nb_cells = std::accumulate(recvcounts.begin(), recvcounts.end(), 0); recvdispls[0] = 0; for (int jpart = 1; jpart < nparts; ++jpart) { // start at 1 recvdispls[jpart] = recvcounts[jpart - 1] + recvdispls[jpart - 1]; } std::vector glb_idx_gathered(glb_nb_cells); ATLAS_TRACE_MPI(GATHER) { mpi::comm().gatherv(glb_idx.data(), glb_idx.size(), glb_idx_gathered.data(), recvcounts.data(), recvdispls.data(), root); } // 2) Sort all global indices, and renumber from 1 to glb_nb_edges std::vector cell_sort; cell_sort.reserve(glb_nb_cells); for (idx_t jcell = 0; jcell < glb_nb_cells; ++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()); } gidx_t gid = glb_idx_max + 1; for (idx_t jcell = 0; jcell < glb_nb_cells; ++jcell) { if (jcell > 0 && cell_sort[jcell].g != cell_sort[jcell - 1].g) { ++gid; } idx_t icell = cell_sort[jcell].i; glb_idx_gathered[icell] = gid; } // 3) Scatter renumbered back ATLAS_TRACE_MPI(SCATTER) { mpi::comm().scatterv(glb_idx_gathered.data(), recvcounts.data(), recvdispls.data(), glb_idx.data(), static_cast(glb_idx.size()), root); } for (int jcell = 0; jcell < nb_cells; ++jcell) { cells_glb_idx(cells_to_edit[jcell]) = glb_idx[jcell]; } // nodes_glb_idx.dump( Log::info() ); // Log::info() << std::endl; cells.global_index().metadata().set("human_readable", true); } // ------------------------------------------------------------------------------------- using uid_t = gidx_t; // ------------------------------------------------------------------ class BuildHaloHelper; void increase_halo(Mesh& mesh); void increase_halo_interior(BuildHaloHelper&); class EastWest : public util::PeriodicTransform { public: EastWest() { x_translation_ = -360.; } }; class WestEast : public util::PeriodicTransform { public: WestEast() { x_translation_ = 360.; } }; using Node2Elem = std::vector>; void build_lookup_node2elem(const Mesh& mesh, Node2Elem& node2elem) { ATLAS_TRACE(); const mesh::Nodes& nodes = mesh.nodes(); node2elem.resize(nodes.size()); for (idx_t jnode = 0; jnode < nodes.size(); ++jnode) { node2elem[jnode].clear(); node2elem[jnode].reserve(12); } const mesh::HybridElements::Connectivity& elem_nodes = mesh.cells().node_connectivity(); auto field_flags = array::make_view(mesh.cells().flags()); auto patched = [&field_flags](idx_t e) { using Topology = atlas::mesh::Nodes::Topology; return Topology::check(field_flags(e), Topology::PATCH); }; idx_t nb_elems = mesh.cells().size(); for (idx_t elem = 0; elem < nb_elems; ++elem) { if (not patched(elem)) { for (idx_t n = 0; n < elem_nodes.cols(elem); ++n) { int node = elem_nodes(elem, n); node2elem[node].push_back(elem); } } } } void accumulate_partition_bdry_nodes_old(Mesh& mesh, std::vector& bdry_nodes) { ATLAS_TRACE(); std::set bdry_nodes_set; std::vector facet_nodes; std::vector connectivity_facet_to_elem; facet_nodes.reserve(mesh.nodes().size() * 4); connectivity_facet_to_elem.reserve(facet_nodes.capacity() * 2); idx_t nb_facets(0); idx_t nb_inner_facets(0); idx_t missing_value; accumulate_facets( /*in*/ mesh.cells(), /*in*/ mesh.nodes(), /*out*/ facet_nodes, // shape(nb_facets,nb_nodes_per_facet) /*out*/ connectivity_facet_to_elem, /*out*/ nb_facets, /*out*/ nb_inner_facets, /*out*/ missing_value); for (idx_t jface = 0; jface < nb_facets; ++jface) { if (connectivity_facet_to_elem[jface * 2 + 1] == missing_value) { for (idx_t jnode = 0; jnode < 2; ++jnode) // 2 nodes per face { bdry_nodes_set.insert(facet_nodes[jface * 2 + jnode]); } } } bdry_nodes = std::vector(bdry_nodes_set.begin(), bdry_nodes_set.end()); } void accumulate_partition_bdry_nodes(Mesh& mesh, idx_t halo, std::vector& bdry_nodes) { #ifndef ATLAS_103 /* deprecated */ accumulate_partition_bdry_nodes_old(mesh, bdry_nodes); #else ATLAS_TRACE(); const Mesh::Polygon& polygon = mesh.polygon(halo); bdry_nodes = std::vector(polygon.begin(), polygon.end()); #endif #ifdef ATLAS_103_SORT /* not required */ std::sort(bdry_nodes.begin(), bdry_nodes.end()); #endif } template std::vector filter_nodes(std::vector nodes, const Predicate& predicate) { std::vector filtered; filtered.reserve(nodes.size()); for (idx_t inode : nodes) { if (predicate(inode)) { filtered.push_back(inode); } } return filtered; } class Notification { public: void add_error(const std::string& note, const eckit::CodeLocation& loc) { notes.push_back(note + " @ " + std::string(loc)); } void add_error(const std::string& note) { notes.push_back(note); } bool error() const { return notes.size() > 0; } void reset() { notes.clear(); } std::string str() const { std::stringstream stream; for (size_t jnote = 0; jnote < notes.size(); ++jnote) { if (jnote > 0) { stream << "\n"; } stream << notes[jnote]; } return stream.str(); } operator std::string() const { return str(); } private: friend std::ostream& operator<<(std::ostream& s, const Notification& notes) { s << notes.str(); return s; } private: std::vector notes; }; using Uid2Node = std::unordered_map; void build_lookup_uid2node(Mesh& mesh, Uid2Node& uid2node) { ATLAS_TRACE(); Notification notes; mesh::Nodes& nodes = mesh.nodes(); array::ArrayView xy = array::make_view(nodes.xy()); array::ArrayView glb_idx = array::make_view(nodes.global_index()); idx_t nb_nodes = nodes.size(); UniqueLonLat compute_uid(mesh); uid2node.clear(); for (idx_t jnode = 0; jnode < nb_nodes; ++jnode) { uid_t uid = compute_uid(jnode); bool inserted = uid2node.insert(std::make_pair(uid, jnode)).second; if (not inserted) { int other = uid2node[uid]; std::stringstream msg; msg << std::setprecision(10) << std::fixed << "Node uid: " << uid << " " << glb_idx(jnode) << " xy(" << xy(jnode, XX) << "," << xy(jnode, YY) << ")"; if (nodes.has_field("ij")) { auto ij = array::make_view(nodes.field("ij")); msg << " ij(" << ij(jnode, XX) << "," << ij(jnode, YY) << ")"; } msg << " has already been added as node " << glb_idx(other) << " (" << xy(other, XX) << "," << xy(other, YY) << ")"; if (nodes.has_field("ij")) { auto ij = array::make_view(nodes.field("ij")); msg << " ij(" << ij(other, XX) << "," << ij(other, YY) << ")"; } notes.add_error(msg.str()); } } if (notes.error()) { throw_Exception(notes.str(), Here()); } } /// For given nodes, find new connected elements void accumulate_elements(const Mesh& mesh, const mpi::BufferView& request_node_uid, const Uid2Node& uid2node, const Node2Elem& node2elem, std::vector& found_elements, std::set& new_nodes_uid) { ATLAS_TRACE(); const mesh::HybridElements::Connectivity& elem_nodes = mesh.cells().node_connectivity(); const auto elem_part = array::make_view(mesh.cells().partition()); const idx_t nb_nodes = mesh.nodes().size(); const idx_t nb_request_nodes = static_cast(request_node_uid.size()); const int mpi_rank = static_cast(mpi::rank()); std::unordered_set found_elements_set; found_elements_set.reserve(nb_request_nodes*2); for (idx_t jnode = 0; jnode < nb_request_nodes; ++jnode) { uid_t uid = request_node_uid(jnode); idx_t inode = -1; // search and get node index for uid auto found = uid2node.find(uid); if (found != uid2node.end()) { inode = found->second; } if (inode != -1 && inode < nb_nodes) { for (const idx_t e : node2elem[inode]) { if (elem_part(e) == mpi_rank) { found_elements_set.insert(e); } } } } // found_bdry_elements_set now contains elements for the nodes found_elements = std::vector(found_elements_set.begin(), found_elements_set.end()); UniqueLonLat compute_uid(mesh); // Collect all nodes new_nodes_uid.clear(); for (const idx_t e : found_elements) { idx_t nb_elem_nodes = elem_nodes.cols(e); for (idx_t n = 0; n < nb_elem_nodes; ++n) { new_nodes_uid.insert(compute_uid(elem_nodes(e, n))); } } // Remove nodes we already have in the request-buffer for (idx_t jnode = 0; jnode < nb_request_nodes; ++jnode) { new_nodes_uid.erase(request_node_uid(jnode)); } } class BuildHaloHelper { public: struct Buffers { std::vector> node_part; std::vector> node_ridx; std::vector> node_flags; std::vector> node_glb_idx; std::vector> node_xy; std::vector> elem_glb_idx; std::vector> elem_nodes_id; std::vector> elem_nodes_displs; std::vector> elem_part; std::vector> elem_ridx; std::vector> elem_flags; std::vector> elem_type; Buffers(Mesh&) { const idx_t mpi_size = static_cast(mpi::size()); node_part.resize(mpi_size); node_ridx.resize(mpi_size); node_flags.resize(mpi_size); node_glb_idx.resize(mpi_size); node_xy.resize(mpi_size); elem_glb_idx.resize(mpi_size); 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); } void print(std::ostream& os) const { const idx_t mpi_size = static_cast(mpi::size()); os << "Nodes\n" << "-----\n"; idx_t n(0); for (idx_t jpart = 0; jpart < mpi_size; ++jpart) { for (idx_t jnode = 0; jnode < node_glb_idx[jpart].size(); ++jnode ) { auto g = node_glb_idx[jpart][jnode]; auto p = node_part[jpart][jnode]; os << std::setw(4) << n++ << " : [ p" << p << " ] " << g << "\n"; } } os << std::flush; os << "Cells\n" << "-----\n"; idx_t e(0); for (idx_t jpart = 0; jpart < mpi_size; ++jpart) { const idx_t nb_elem = static_cast(elem_glb_idx[jpart].size()); for (idx_t jelem = 0; jelem < nb_elem; ++jelem) { os << std::setw(4) << e++ << " : [ t" << elem_type[jpart][jelem] << " -- p" << elem_part[jpart][jelem] << "] " << elem_glb_idx[jpart][jelem] << "\n"; } } os << std::flush; } friend std::ostream& operator<<(std::ostream& out, const Buffers& b) { b.print(out); return out; } }; static void all_to_all(Buffers& send, Buffers& recv) { ATLAS_TRACE(); const eckit::mpi::Comm& comm = mpi::comm(); ATLAS_TRACE_MPI(ALLTOALL) { comm.allToAll(send.node_glb_idx, recv.node_glb_idx); comm.allToAll(send.node_part, recv.node_part); comm.allToAll(send.node_ridx, recv.node_ridx); comm.allToAll(send.node_flags, recv.node_flags); comm.allToAll(send.node_xy, recv.node_xy); 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); } } struct Status { std::vector new_periodic_ghost_points; std::vector> new_periodic_ghost_cells; } status; public: BuildHalo& builder_; Mesh& mesh; array::ArrayView xy; array::ArrayView lonlat; array::ArrayView glb_idx; array::ArrayView part; array::IndexView ridx; array::ArrayView flags; array::ArrayView halo; 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; std::vector bdry_nodes; Node2Elem node_to_elem; Uid2Node uid2node; UniqueLonLat compute_uid; idx_t halosize; public: BuildHaloHelper(BuildHalo& builder, Mesh& _mesh): builder_(builder), mesh(_mesh), xy(array::make_view(mesh.nodes().xy())), lonlat(array::make_view(mesh.nodes().lonlat())), glb_idx(array::make_view(mesh.nodes().global_index())), part(array::make_view(mesh.nodes().partition())), ridx(array::make_indexview(mesh.nodes().remote_index())), flags(array::make_view(mesh.nodes().flags())), halo(array::make_view(mesh.nodes().halo())), 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) { halosize = 0; mesh.metadata().get("halo", halosize); // update(); } void update() { compute_uid.update(); mesh::Nodes& nodes = mesh.nodes(); xy = array::make_view(nodes.xy()); lonlat = array::make_view(nodes.lonlat()); glb_idx = array::make_view(nodes.global_index()); part = array::make_view(nodes.partition()); ridx = array::make_indexview(nodes.remote_index()); flags = array::make_view(nodes.flags()); ghost = array::make_view(nodes.ghost()); halo = array::make_view(nodes.halo()); 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()); } template void fill_sendbuffer(Buffers& buf, const NodeContainer& nodes_uid, const ElementContainer& elems, const int p) { ATLAS_TRACE(); idx_t nb_nodes = static_cast(nodes_uid.size()); buf.node_glb_idx[p].resize(nb_nodes); buf.node_part[p].resize(nb_nodes); buf.node_ridx[p].resize(nb_nodes); buf.node_flags[p].resize(nb_nodes, Topology::NONE); buf.node_xy[p].resize(2 * nb_nodes); idx_t jnode = 0; typename NodeContainer::const_iterator it; for (it = nodes_uid.begin(); it != nodes_uid.end(); ++it, ++jnode) { uid_t uid = *it; auto found = uid2node.find(uid); if (found != uid2node.end()) // Point exists inside domain { idx_t node = found->second; buf.node_glb_idx[p][jnode] = glb_idx(node); buf.node_part[p][jnode] = part(node); buf.node_ridx[p][jnode] = ridx(node); buf.node_xy[p][jnode * 2 + XX] = xy(node, XX); buf.node_xy[p][jnode * 2 + YY] = xy(node, YY); Topology::set(buf.node_flags[p][jnode], flags(node) | Topology::GHOST); } else { Log::warning() << "Node with uid " << uid << " needed by [" << p << "] was not found in [" << mpi::rank() << "]." << std::endl; ATLAS_ASSERT(false); } } idx_t nb_elems = static_cast(elems.size()); idx_t nb_elem_nodes(0); for (idx_t jelem = 0; jelem < nb_elems; ++jelem) { idx_t ielem = elems[jelem]; nb_elem_nodes += elem_nodes->cols(ielem); } 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); buf.elem_nodes_displs[p].resize(nb_elems); idx_t jelemnode(0); for (idx_t jelem = 0; jelem < nb_elems; ++jelem) { buf.elem_nodes_displs[p][jelem] = jelemnode; idx_t ielem = elems[jelem]; 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) { buf.elem_nodes_id[p][jelemnode++] = compute_uid((*elem_nodes)(ielem, jnode)); } Topology::set(buf.elem_flags[p][jelem], Topology::GHOST); } } template void fill_sendbuffer(Buffers& buf, const NodeContainer& nodes_uid, const ElementContainer& elems, const util::PeriodicTransform& transform, int newflags, const int p) { // ATLAS_TRACE(); idx_t nb_nodes = static_cast(nodes_uid.size()); buf.node_glb_idx[p].resize(nb_nodes); buf.node_part[p].resize(nb_nodes); buf.node_ridx[p].resize(nb_nodes); buf.node_flags[p].resize(nb_nodes, Topology::NONE); buf.node_xy[p].resize(2 * nb_nodes); int jnode = 0; typename NodeContainer::const_iterator it; for (it = nodes_uid.begin(); it != nodes_uid.end(); ++it, ++jnode) { uid_t uid = *it; auto found = uid2node.find(uid); if (found != uid2node.end()) // Point exists inside domain { int node = found->second; buf.node_part[p][jnode] = part(node); buf.node_ridx[p][jnode] = ridx(node); buf.node_xy[p][jnode * 2 + XX] = xy(node, XX); buf.node_xy[p][jnode * 2 + YY] = xy(node, YY); transform(&buf.node_xy[p][jnode * 2], -1); // Global index of node is based on UID of destination buf.node_glb_idx[p][jnode] = util::unique_lonlat(&buf.node_xy[p][jnode * 2]); Topology::set(buf.node_flags[p][jnode], newflags); } else { Log::warning() << "Node with uid " << uid << " needed by [" << p << "] was not found in [" << mpi::rank() << "]." << std::endl; ATLAS_ASSERT(false); } } idx_t nb_elems = static_cast(elems.size()); idx_t nb_elem_nodes(0); for (idx_t jelem = 0; jelem < nb_elems; ++jelem) { idx_t ielem = elems[jelem]; nb_elem_nodes += elem_nodes->cols(ielem); } 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); buf.elem_nodes_displs[p].resize(nb_elems); idx_t jelemnode(0); for (idx_t jelem = 0; jelem < nb_elems; ++jelem) { 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); for (idx_t jnode = 0; jnode < elem_nodes->cols(ielem); ++jnode) { double crd[] = {xy((*elem_nodes)(ielem, jnode), XX), xy((*elem_nodes)(ielem, jnode), YY)}; transform(crd, -1); buf.elem_nodes_id[p][jelemnode++] = util::unique_lonlat(crd); crds[jnode * 2 + XX] = crd[XX]; crds[jnode * 2 + YY] = crd[YY]; } // Global index of element is based on UID of destination buf.elem_glb_idx[p][jelem] = -util::unique_lonlat(crds.data(), elem_nodes->cols(ielem)); } } void add_nodes(Buffers& buf) { ATLAS_TRACE(); const idx_t mpi_size = static_cast(mpi::size()); mesh::Nodes& nodes = mesh.nodes(); int nb_nodes = nodes.size(); // Nodes might be duplicated from different Tasks. We need to identify // unique entries std::vector node_uid(nb_nodes); std::set new_node_uid; { ATLAS_TRACE("compute node_uid"); for (int jnode = 0; jnode < nb_nodes; ++jnode) { node_uid[jnode] = compute_uid(jnode); } std::sort(node_uid.begin(), node_uid.end()); } auto node_already_exists = [&node_uid, &new_node_uid](uid_t uid) { std::vector::iterator it = std::lower_bound(node_uid.begin(), node_uid.end(), uid); bool not_found = (it == node_uid.end() || uid < *it); if (not_found) { bool inserted = new_node_uid.insert(uid).second; return not inserted; } else { return true; } }; std::vector> rfn_idx(mpi_size); for (idx_t jpart = 0; jpart < mpi_size; ++jpart) { rfn_idx[jpart].reserve(buf.node_glb_idx[jpart].size()); } int nb_new_nodes = 0; for (idx_t jpart = 0; jpart < mpi_size; ++jpart) { const idx_t nb_nodes_on_part = static_cast(buf.node_glb_idx[jpart].size()); for (idx_t n = 0; n < nb_nodes_on_part; ++n) { std::array crd{buf.node_xy[jpart][n * 2 + XX], buf.node_xy[jpart][n * 2 + YY]}; mesh.projection().xy2lonlat(crd.data()); if (not node_already_exists(util::unique_lonlat(crd))) { rfn_idx[jpart].push_back(n); } } nb_new_nodes += rfn_idx[jpart].size(); } // Resize nodes // ------------ nodes.resize(nb_nodes + nb_new_nodes); flags = array::make_view(nodes.flags()); halo = array::make_view(nodes.halo()); glb_idx = array::make_view(nodes.global_index()); part = array::make_view(nodes.partition()); ridx = array::make_indexview(nodes.remote_index()); xy = array::make_view(nodes.xy()); lonlat = array::make_view(nodes.lonlat()); ghost = array::make_view(nodes.ghost()); compute_uid.update(); // Add new nodes // ------------- int new_node = 0; for (idx_t jpart = 0; jpart < mpi_size; ++jpart) { for (size_t n = 0; n < rfn_idx[jpart].size(); ++n) { int loc_idx = nb_nodes + new_node; halo(loc_idx) = halosize + 1; Topology::reset(flags(loc_idx), buf.node_flags[jpart][rfn_idx[jpart][n]]); ghost(loc_idx) = Topology::check(flags(loc_idx), Topology::GHOST); glb_idx(loc_idx) = buf.node_glb_idx[jpart][rfn_idx[jpart][n]]; part(loc_idx) = buf.node_part[jpart][rfn_idx[jpart][n]]; ridx(loc_idx) = buf.node_ridx[jpart][rfn_idx[jpart][n]]; PointXY pxy(&buf.node_xy[jpart][rfn_idx[jpart][n] * 2]); xy(loc_idx, XX) = pxy.x(); xy(loc_idx, YY) = pxy.y(); PointLonLat pll = mesh.projection().lonlat(pxy); lonlat(loc_idx, XX) = pll.lon(); lonlat(loc_idx, YY) = pll.lat(); if (Topology::check(flags(loc_idx), Topology::PERIODIC) and not Topology::check(flags(loc_idx), Topology::BC)) { status.new_periodic_ghost_points.push_back(loc_idx); } // make sure new node was not already there { uid_t uid = compute_uid(loc_idx); auto found = uid2node.find(uid); if (found != uid2node.end()) { int other = found->second; std::stringstream msg; msg << "New node loc " << loc_idx << " with uid " << uid << ":\n" << glb_idx(loc_idx) << "(" << xy(loc_idx, XX) << "," << xy(loc_idx, YY) << ")\n"; msg << "Existing already loc " << other << " : " << glb_idx(other) << "(" << xy(other, XX) << "," << xy(other, YY) << ")\n"; throw_Exception(msg.str(), Here()); } uid2node[uid] = nb_nodes + new_node; } ++new_node; } } } void add_elements(Buffers& buf) { ATLAS_TRACE(); const idx_t mpi_size = static_cast(mpi::size()); auto cell_gidx = array::make_view(mesh.cells().global_index()); // Elements might be duplicated from different Tasks. We need to identify // unique entries int nb_elems = mesh.cells().size(); // std::set elem_uid; std::vector elem_uid(2 * nb_elems); std::set new_elem_uid; { ATLAS_TRACE("compute elem_uid"); atlas_omp_parallel_for (int jelem = 0; jelem < nb_elems; ++jelem) { elem_uid[jelem * 2 + 0] = -compute_uid(elem_nodes->row(jelem)); elem_uid[jelem * 2 + 1] = cell_gidx(jelem); } omp::sort(elem_uid.begin(), elem_uid.end()); } auto element_already_exists = [&elem_uid, &new_elem_uid](uid_t uid) -> bool { std::vector::iterator it = std::lower_bound(elem_uid.begin(), elem_uid.end(), uid); bool not_found = (it == elem_uid.end() || uid < *it); if (not_found) { bool inserted = new_elem_uid.insert(uid).second; return not inserted; } else { return true; } }; if (not status.new_periodic_ghost_cells.size()) { status.new_periodic_ghost_cells.resize(mesh.cells().nb_types()); } std::vector> received_new_elems(mpi_size); for (idx_t jpart = 0; jpart < mpi_size; ++jpart) { received_new_elems[jpart].reserve(buf.elem_glb_idx[jpart].size()); } for (idx_t jpart = 0; jpart < mpi_size; ++jpart) { 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].emplace_back(e); } } } std::vector>> elements_of_type(mesh.cells().nb_types(), std::vector>(mpi_size)); std::vector nb_elements_of_type(mesh.cells().nb_types(), 0); 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].emplace_back(ielem); ++nb_elements_of_type[buf.elem_type[jpart][ielem]]; } } for (idx_t t = 0; t < mesh.cells().nb_types(); ++t) { const std::vector>& elems = elements_of_type[t]; mesh::Elements& elements = mesh.cells().elements(t); // Add new elements BlockConnectivity& node_connectivity = elements.node_connectivity(); if (nb_elements_of_type[t] == 0) { continue; } idx_t old_size = elements.size(); idx_t new_elems_pos = elements.add(nb_elements_of_type[t]); 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()); // Copy information in new elements idx_t new_elem(0); for (idx_t jpart = 0; jpart < mpi_size; ++jpart) { for (const idx_t jelem : elems[jpart]) { 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) { node_connectivity.set( loc_idx, n, uid2node[buf.elem_nodes_id[jpart][buf.elem_nodes_displs[jpart][jelem] + n]]); } if (Topology::check(elem_type_flags(loc_idx), Topology::PERIODIC)) { status.new_periodic_ghost_cells[t].push_back(old_size + new_elem); } ++new_elem; } } } } std::vector> gather_nb_elements() { idx_t nb_types = mesh.cells().nb_types(); idx_t mpi_size = mpi::comm().size(); std::vector elems_per_type(nb_types); for (idx_t t = 0; t < nb_types; ++t) { elems_per_type[t] = mesh.cells().elements(t).size(); } atlas::mpi::Buffer recv(mpi_size); ATLAS_TRACE_MPI(ALLGATHER) { mpi::comm().allGatherv(elems_per_type.begin(), elems_per_type.end(), recv); } std::vector> result(nb_types, std::vector(mpi_size)); for (idx_t p = 0; p < mpi_size; ++p) { auto recv_p = recv[p]; for (idx_t t = 0; t < nb_types; ++t) { result[t][p] = recv_p[t]; } } return result; }; void add_buffers(Buffers& buf) { add_nodes(buf); auto nb_elements_pre = gather_nb_elements(); add_elements(buf); auto nb_elements_post = gather_nb_elements(); // Renumber remote_index as the number of elements per type might have grown { auto nb_partitions = mpi::comm().size(); auto nb_types = mesh.cells().nb_types(); std::vector> accumulated_diff(nb_types, std::vector(nb_partitions)); for (idx_t t = 1; t < nb_types; ++t) { for (idx_t p = 0; p < nb_partitions; ++p) { accumulated_diff[t][p] = accumulated_diff[t - 1][p] + nb_elements_post[t - 1][p] - nb_elements_pre[t - 1][p]; } } for (idx_t t = 0; t < nb_types; ++t) { auto& elements = mesh.cells().elements(t); auto partition = elements.view(mesh.cells().partition()); auto ridx = elements.indexview(mesh.cells().remote_index()); auto& accumulated_diff_t = accumulated_diff[t]; for (idx_t elem = 0; elem < elements.size(); ++elem) { ridx(elem) += accumulated_diff_t[partition(elem)]; } } } update(); } }; namespace { void gather_bdry_nodes(const BuildHaloHelper& helper, const std::vector& send, atlas::mpi::Buffer& recv, bool periodic = false) { auto& comm = mpi::comm(); #ifndef ATLAS_103 /* deprecated */ ATLAS_TRACE("gather_bdry_nodes old way"); { ATLAS_TRACE_MPI(ALLGATHER) { comm.allGatherv(send.begin(), send.end(), recv); } } #else ATLAS_TRACE(); Mesh::PartitionGraph::Neighbours neighbours = helper.mesh.nearestNeighbourPartitions(); if (periodic) { // add own rank to neighbours to allow periodicity with self (pole caps) idx_t rank = comm.rank(); neighbours.insert(std::upper_bound(neighbours.begin(), neighbours.end(), rank), rank); } const idx_t mpi_size = comm.size(); const int counts_tag = 0; const int buffer_tag = 1; std::vector counts_requests; counts_requests.reserve(neighbours.size()); std::vector buffer_requests; buffer_requests.reserve(neighbours.size()); int sendcnt = send.size(); ATLAS_TRACE_MPI(ISEND) { for (idx_t to : neighbours) { counts_requests.push_back(comm.iSend(sendcnt, to, counts_tag)); } } recv.counts.assign(0, mpi_size); ATLAS_TRACE_MPI(IRECEIVE) { for (idx_t from : neighbours) { counts_requests.push_back(comm.iReceive(recv.counts[from], from, counts_tag)); } } ATLAS_TRACE_MPI(ISEND) { for (idx_t to : neighbours) { buffer_requests.push_back(comm.iSend(send.data(), send.size(), to, buffer_tag)); } } ATLAS_TRACE_MPI(WAIT) { for (auto request : counts_requests) { comm.wait(request); } } recv.displs[0] = 0; recv.cnt = recv.counts[0]; for (idx_t jpart = 1; jpart < mpi_size; ++jpart) { recv.displs[jpart] = recv.displs[jpart - 1] + recv.counts[jpart - 1]; recv.cnt += recv.counts[jpart]; } recv.buffer.resize(recv.cnt); ATLAS_TRACE_MPI(IRECEIVE) { for (idx_t from : neighbours) { buffer_requests.push_back( comm.iReceive(recv.buffer.data() + recv.displs[from], recv.counts[from], from, buffer_tag)); } } ATLAS_TRACE_MPI(WAIT) { for (auto request : buffer_requests) { comm.wait(request); } } #endif } } // namespace void increase_halo_interior(BuildHaloHelper& helper) { helper.update(); if (helper.node_to_elem.size() == 0) { build_lookup_node2elem(helper.mesh, helper.node_to_elem); } if (helper.uid2node.size() == 0) { build_lookup_uid2node(helper.mesh, helper.uid2node); } // All buffers needed to move elements and nodes BuildHaloHelper::Buffers sendmesh(helper.mesh); BuildHaloHelper::Buffers recvmesh(helper.mesh); // 1) Find boundary nodes of this partition: accumulate_partition_bdry_nodes(helper.mesh, helper.halosize, helper.bdry_nodes); const std::vector& bdry_nodes = helper.bdry_nodes; const idx_t nb_bdry_nodes = static_cast(bdry_nodes.size()); // 2) Communicate uid of these boundary nodes to other partitions std::vector send_bdry_nodes_uid(bdry_nodes.size()); for (idx_t jnode = 0; jnode < nb_bdry_nodes; ++jnode) { send_bdry_nodes_uid[jnode] = helper.compute_uid(bdry_nodes[jnode]); } idx_t mpi_size = mpi::size(); atlas::mpi::Buffer recv_bdry_nodes_uid_from_parts(mpi_size); gather_bdry_nodes(helper, send_bdry_nodes_uid, recv_bdry_nodes_uid_from_parts); { runtime::trace::Barriers set_barriers(false); runtime::trace::Logging set_logging(false); #ifndef ATLAS_103 /* deprecated */ atlas_omp_parallel_for (idx_t jpart = 0; jpart < mpi_size; ++jpart) #else const Mesh::PartitionGraph::Neighbours neighbours = helper.mesh.nearestNeighbourPartitions(); for (idx_t jpart : neighbours) #endif { // 3) Find elements and nodes completing these elements in // other tasks that have my nodes through its UID mpi::BufferView recv_bdry_nodes_uid = recv_bdry_nodes_uid_from_parts[jpart]; std::vector found_bdry_elems; std::set found_bdry_nodes_uid; accumulate_elements(helper.mesh, recv_bdry_nodes_uid, helper.uid2node, helper.node_to_elem, found_bdry_elems, found_bdry_nodes_uid); // 4) Fill node and element buffers to send back helper.fill_sendbuffer(sendmesh, found_bdry_nodes_uid, found_bdry_elems, jpart); } } // 5) Now communicate all buffers helper.all_to_all(sendmesh, recvmesh); // 6) Adapt mesh #ifdef DEBUG_OUTPUT Log::debug() << "increase_halo_interior -- recv: \n" << recvmesh << std::endl; #endif helper.add_buffers(recvmesh); } class PeriodicPoints { public: PeriodicPoints(Mesh& mesh, int flag, idx_t N): flags_(array::make_view(mesh.nodes().flags())) { flag_ = flag; N_ = N; } bool operator()(int j) const { if (j >= N_) { return false; } if (Topology::check(flags_(j), flag_)) { return true; } return false; } private: int N_; int flag_; array::ArrayView flags_; friend std::ostream& operator<<(std::ostream& os, const PeriodicPoints& periodic_points) { os << "["; for (idx_t j = 0; j < periodic_points.flags_.shape(0); ++j) { if (periodic_points(j)) { os << " " << j + 1; } } os << " ]"; return os; } }; void increase_halo_periodic(BuildHaloHelper& helper, const PeriodicPoints& periodic_points, const util::PeriodicTransform& transform, int newflags) { helper.update(); // if (helper.node_to_elem.size() == 0 ) !!! NOT ALLOWED !!! (atlas_test_halo // will fail) build_lookup_node2elem(helper.mesh, helper.node_to_elem); // if( helper.uid2node.size() == 0 ) !!! NOT ALLOWED !!! (atlas_test_halo will // fail) build_lookup_uid2node(helper.mesh, helper.uid2node); // All buffers needed to move elements and nodes BuildHaloHelper::Buffers sendmesh(helper.mesh); BuildHaloHelper::Buffers recvmesh(helper.mesh); // 1) Find boundary nodes of this partition: if (!helper.bdry_nodes.size()) { accumulate_partition_bdry_nodes(helper.mesh, helper.halosize, helper.bdry_nodes); } std::vector bdry_nodes = filter_nodes(helper.bdry_nodes, periodic_points); const idx_t nb_bdry_nodes = static_cast(bdry_nodes.size()); // 2) Compute transformed uid of these boundary nodes and send to other // partitions std::vector send_bdry_nodes_uid(nb_bdry_nodes); for (idx_t jnode = 0; jnode < nb_bdry_nodes; ++jnode) { double crd[] = {helper.xy(bdry_nodes[jnode], XX), helper.xy(bdry_nodes[jnode], YY)}; transform(crd, +1); // Log::info() << " crd " << crd[0] << " " << crd[1] << " uid " << // util::unique_lonlat(crd) << std::endl; send_bdry_nodes_uid[jnode] = util::unique_lonlat(crd); } idx_t mpi_size = mpi::size(); atlas::mpi::Buffer recv_bdry_nodes_uid_from_parts(mpi_size); gather_bdry_nodes(helper, send_bdry_nodes_uid, recv_bdry_nodes_uid_from_parts, /* periodic = */ true); { runtime::trace::Barriers set_barriers(false); runtime::trace::Logging set_logging(false); #ifndef ATLAS_103 /* deprecated */ atlas_omp_parallel_for (idx_t jpart = 0; jpart < mpi_size; ++jpart) #else Mesh::PartitionGraph::Neighbours neighbours = helper.mesh.nearestNeighbourPartitions(); // add own rank to neighbours to allow periodicity with self (pole caps) idx_t rank = mpi::rank(); neighbours.insert(std::upper_bound(neighbours.begin(), neighbours.end(), rank), rank); for (idx_t jpart : neighbours) #endif { // 3) Find elements and nodes completing these elements in // other tasks that have my nodes through its UID atlas::mpi::BufferView recv_bdry_nodes_uid = recv_bdry_nodes_uid_from_parts[jpart]; std::vector found_bdry_elems; std::set found_bdry_nodes_uid; accumulate_elements(helper.mesh, recv_bdry_nodes_uid, helper.uid2node, helper.node_to_elem, found_bdry_elems, found_bdry_nodes_uid); // 4) Fill node and element buffers to send back helper.fill_sendbuffer(sendmesh, found_bdry_nodes_uid, found_bdry_elems, transform, newflags, jpart); } } // 5) Now communicate all buffers helper.all_to_all(sendmesh, recvmesh); // 6) Adapt mesh #ifdef DEBUG_OUTPUT Log::debug() << "increase_halo_periodic -- recv: \n" << recvmesh << std::endl; #endif helper.add_buffers(recvmesh); } BuildHalo::BuildHalo(Mesh& mesh): mesh_(mesh), periodic_cells_local_index_(mesh.cells().nb_types()) {} void BuildHalo::operator()(int nb_elems) { ATLAS_TRACE("BuildHalo"); mpi::Scope mpi_scope(mesh_.mpi_comm()); int halo = 0; mesh_.metadata().get("halo", halo); // Locked halos must be set at mesh generation. bool haloLocked = false; if (mesh_.metadata().get("halo_locked", haloLocked)) { if (haloLocked && halo < nb_elems) { const auto errMsg = "Error: Halo size locked. Please set MeshGenerator " "parameter \"halo\" to at least " + std::to_string(nb_elems) + ".\n" "Current halo size is " + std::to_string(halo) + ".\n"; throw_Exception(errMsg, Here()); } } if (halo == nb_elems) { return; } ATLAS_TRACE("Increasing mesh halo"); for (int jhalo = halo; jhalo < nb_elems; ++jhalo) { Log::debug() << "Increase halo " << jhalo + 1 << std::endl; idx_t nb_nodes_before_halo_increase = mesh_.nodes().size(); BuildHaloHelper helper(*this, mesh_); ATLAS_TRACE_SCOPE("increase_halo_interior") { increase_halo_interior(helper); } PeriodicPoints westpts(mesh_, Topology::PERIODIC | Topology::WEST, nb_nodes_before_halo_increase); #ifdef DEBUG_OUTPUT Log::debug() << " periodic west : " << westpts << std::endl; #endif ATLAS_TRACE_SCOPE("increase_halo_periodic West") { increase_halo_periodic(helper, westpts, WestEast(), Topology::PERIODIC | Topology::WEST | Topology::GHOST); } PeriodicPoints eastpts(mesh_, Topology::PERIODIC | Topology::EAST, nb_nodes_before_halo_increase); #ifdef DEBUG_OUTPUT Log::debug() << " periodic east : " << eastpts << std::endl; #endif ATLAS_TRACE_SCOPE("increase_halo_periodic East") { increase_halo_periodic(helper, eastpts, EastWest(), Topology::PERIODIC | Topology::EAST | Topology::GHOST); } for (idx_t p : helper.status.new_periodic_ghost_points) { periodic_points_local_index_.push_back(p); } for (int t = 0; t < mesh_.cells().nb_types(); ++t) { for (idx_t p : helper.status.new_periodic_ghost_cells[t]) { periodic_cells_local_index_[t].push_back(p); } } { 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); #ifdef DEBUG_OUTPUT output::Gmsh gmsh2d("build-halo-mesh2d.msh", util::Config("ghost", true)("coordinates", "xy")); output::Gmsh gmsh3d("build-halo-mesh3d.msh", util::Config("ghost", true)("coordinates", "xyz")); renumber_nodes_glb_idx(mesh_.nodes()); BuildXYZField("xyz", true)(mesh_.nodes()); mesh_.metadata().set("halo", jhalo + 1); gmsh2d.write(mesh_); gmsh3d.write(mesh_); #endif } make_nodes_global_index_human_readable(*this, mesh_.nodes(), /*do_all*/ false); make_cells_global_index_human_readable(*this, mesh_.cells(), /*do_all*/ false); // renumber_nodes_glb_idx (mesh_.nodes()); } // ------------------------------------------------------------------ // C wrapper interfaces to C++ routines void atlas__build_halo(Mesh::Implementation* mesh, int nb_elems) { ATLAS_ASSERT(mesh != nullptr, "Cannot access uninitialised atlas_Mesh"); Mesh m(mesh); build_halo(m, nb_elems); } // ------------------------------------------------------------------ } // namespace actions } // namespace mesh } // namespace atlas atlas-0.46.0/src/atlas/mesh/actions/BuildNode2CellConnectivity.h0000664000175000017500000000230415160212070024650 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once namespace atlas { class Mesh; } // namespace atlas namespace atlas { namespace mesh { namespace actions { class BuildNode2CellConnectivity { public: BuildNode2CellConnectivity(Mesh& mesh); void operator()(); private: Mesh& mesh_; }; inline void build_node_to_cell_connectivity(Mesh& mesh) { BuildNode2CellConnectivity{mesh}(); } } // namespace actions } // namespace mesh } // namespace atlas // ------------------------------------------------------------------ namespace atlas { namespace mesh { namespace detail { class MeshImpl; } } // namespace mesh } // namespace atlas // C wrapper interfaces to C++ routines extern "C" { void atlas__build_node_to_cell_connectivity(atlas::mesh::detail::MeshImpl* mesh); } // ------------------------------------------------------------------ atlas-0.46.0/src/atlas/mesh/actions/BuildStatistics.cc0000664000175000017500000002342015160212070022774 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ // file deepcode ignore MissingOpenCheckOnFile: False positive #include #include #include #include #include #include #include #include #include "eckit/exception/Exceptions.h" #include "eckit/filesystem/PathName.h" #include "atlas/array/ArrayView.h" #include "atlas/array/MakeView.h" #include "atlas/field/Field.h" #include "atlas/library/config.h" #include "atlas/mesh/ElementType.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/BuildDualMesh.h" #include "atlas/parallel/Checksum.h" #include "atlas/runtime/Exception.h" #include "atlas/util/Constants.h" #include "atlas/util/CoordinateEnums.h" #include "atlas/util/Earth.h" #include "atlas/util/Point.h" #include "atlas/util/UnitSphere.h" namespace atlas { namespace mesh { namespace actions { namespace { void tri_quality(double& eta, double& rho, const PointLonLat& p1, const PointLonLat& p2, const PointLonLat& p3) { // see // http://www.gidhome.com/component/manual/referencemanual/preprocessing/mesh_menu/mesh_quality double l12 = util::Constants::radiansToDegrees() * util::Earth::centralAngle(p1, p2); double l23 = util::Constants::radiansToDegrees() * util::Earth::centralAngle(p2, p3); double l31 = util::Constants::radiansToDegrees() * util::Earth::centralAngle(p3, p1); double s = 0.5 * (l12 + l23 + l31); double area = std::sqrt(s * (s - l12) * (s - l23) * (s - l31)); eta = (4 * area * std::sqrt(3.)) / (std::pow(l12, 2) + std::pow(l23, 2) + std::pow(l31, 2)); double min_length = std::min(std::min(l12, l23), l31); double max_length = std::max(std::max(l12, l23), l31); rho = min_length / max_length; } void quad_quality(double& eta, double& rho, const PointLonLat& p1, const PointLonLat& p2, const PointLonLat& p3, const PointLonLat& p4) { // see http://geuz.org/gmsh/doc/preprints/gmsh_quad_preprint.pdf PointXYZ xyz[4]; util::UnitSphere::convertSphericalToCartesian(p1, xyz[0]); util::UnitSphere::convertSphericalToCartesian(p2, xyz[1]); util::UnitSphere::convertSphericalToCartesian(p3, xyz[2]); util::UnitSphere::convertSphericalToCartesian(p4, xyz[3]); PointXYZ l2m1(PointXYZ::sub(xyz[1], xyz[0])); PointXYZ l3m2(PointXYZ::sub(xyz[2], xyz[1])); PointXYZ l4m3(PointXYZ::sub(xyz[3], xyz[2])); PointXYZ l1m4(PointXYZ::sub(xyz[0], xyz[3])); double norm_l2m1 = PointXYZ::norm(l2m1); double norm_l3m2 = PointXYZ::norm(l3m2); double norm_l4m3 = PointXYZ::norm(l4m3); double norm_l1m4 = PointXYZ::norm(l1m4); double dot_l4m1_l2m1 = -PointXYZ::dot(l1m4, l2m1); double dot_l1m2_l3m2 = -PointXYZ::dot(l2m1, l3m2); double dot_l2m3_l4m3 = -PointXYZ::dot(l3m2, l4m3); double dot_l3m4_l1m4 = -PointXYZ::dot(l4m3, l1m4); // Angles at each quad corner double a1 = std::acos(dot_l4m1_l2m1 / (norm_l1m4 * norm_l2m1)); double a2 = std::acos(dot_l1m2_l3m2 / (norm_l2m1 * norm_l3m2)); double a3 = std::acos(dot_l2m3_l4m3 / (norm_l3m2 * norm_l4m3)); double a4 = std::acos(dot_l3m4_l1m4 / (norm_l4m3 * norm_l1m4)); double max_inner = std::max(std::max(std::max(std::abs(M_PI_2 - a1), std::abs(M_PI_2 - a2)), std::abs(M_PI_2 - a3)), std::abs(M_PI_2 - a4)); eta = std::max(1. - M_2_PI * max_inner, 0.); double l12 = util::Earth::centralAngle(p1, p2); double l23 = util::Earth::centralAngle(p2, p3); double l34 = util::Earth::centralAngle(p3, p4); double l41 = util::Earth::centralAngle(p4, p1); double min_length = std::min(std::min(std::min(l12, l23), l34), l41); double max_length = std::max(std::max(std::max(l12, l23), l34), l41); rho = min_length / max_length; } } // namespace void build_statistics(Mesh& mesh) { mesh::Nodes& nodes = mesh.nodes(); array::ArrayView lonlat = array::make_view(nodes.lonlat()); if (mesh.edges().size()) { if (!mesh.edges().has_field("arc_length")) { mesh.edges().add( Field("arc_length", array::make_datatype(), array::make_shape(mesh.edges().size()))); } array::ArrayView dist = array::make_view(mesh.edges().field("arc_length")); const mesh::HybridElements::Connectivity& edge_nodes = mesh.edges().node_connectivity(); const int nb_edges = mesh.edges().size(); for (int jedge = 0; jedge < nb_edges; ++jedge) { int ip1 = edge_nodes(jedge, 0); int ip2 = edge_nodes(jedge, 1); PointLonLat p1(lonlat(ip1, LON), lonlat(ip1, LAT)); PointLonLat p2(lonlat(ip2, LON), lonlat(ip2, LAT)); dist(jedge) = util::Earth::distance(p1, p2) * 1e-3; } } std::ofstream ofs; eckit::PathName stats_path("stats.txt"); int idt = 10; if (mpi::size() == 1) { ofs.open(stats_path.localPath(), std::ofstream::out); if (!ofs.is_open()) { throw eckit::CantOpenFile(stats_path); } ofs << "# STATISTICS rho (min_length/max_length), eta (quality) \n"; ofs << std::setw(idt) << "# rho"; ofs << std::setw(idt) << "eta"; ofs << "\n"; } // Cell statistics { array::ArrayView rho = array::make_view(mesh.cells().add( Field("stats_rho", array::make_datatype(), array::make_shape(mesh.cells().size())))); array::ArrayView eta = array::make_view(mesh.cells().add( Field("stats_eta", array::make_datatype(), array::make_shape(mesh.cells().size())))); for (idx_t jtype = 0; jtype < mesh.cells().nb_types(); ++jtype) { const mesh::Elements& elements = mesh.cells().elements(jtype); const BlockConnectivity& elem_nodes = elements.node_connectivity(); const idx_t nb_elems = elements.size(); if (elements.element_type().name() == "Triangle") { for (idx_t jelem = 0; jelem < nb_elems; ++jelem) { idx_t ielem = elements.begin() + jelem; idx_t ip1 = elem_nodes(jelem, 0); idx_t ip2 = elem_nodes(jelem, 1); idx_t ip3 = elem_nodes(jelem, 2); PointLonLat p1(lonlat(ip1, LON), lonlat(ip1, LAT)); PointLonLat p2(lonlat(ip2, LON), lonlat(ip2, LAT)); PointLonLat p3(lonlat(ip3, LON), lonlat(ip3, LAT)); tri_quality(eta(ielem), rho(ielem), p1, p2, p3); if (mpi::size() == 1) { ofs << std::setw(idt) << rho(ielem) << std::setw(idt) << eta(ielem) << "\n"; } } } if (elements.element_type().name() == "Quadrilateral") { for (idx_t jelem = 0; jelem < nb_elems; ++jelem) { idx_t ielem = elements.begin() + jelem; idx_t ip1 = elem_nodes(jelem, 0); idx_t ip2 = elem_nodes(jelem, 1); idx_t ip3 = elem_nodes(jelem, 2); idx_t ip4 = elem_nodes(jelem, 3); PointLonLat p1(lonlat(ip1, LON), lonlat(ip1, LAT)); PointLonLat p2(lonlat(ip2, LON), lonlat(ip2, LAT)); PointLonLat p3(lonlat(ip3, LON), lonlat(ip3, LAT)); PointLonLat p4(lonlat(ip4, LON), lonlat(ip4, LAT)); quad_quality(eta(ielem), rho(ielem), p1, p2, p3, p4); if (mpi::size() == 1) { ofs << std::setw(idt) << rho(ielem) << std::setw(idt) << eta(ielem) << "\n"; } } } } } eckit::PathName dual_stats_path("dual_stats.txt"); if (mpi::size() == 1) { ofs << "# STATISTICS dual_area \n"; ofs << std::setw(idt) << "# area"; ofs << "\n"; } if (nodes.has_field("dual_volumes")) { array::ArrayView dual_volumes = array::make_view(nodes.field("dual_volumes")); array::ArrayView dual_delta_sph = array::make_view( nodes.add(Field("dual_delta_sph", array::make_datatype(), array::make_shape(nodes.size(), 1)))); for (idx_t jnode = 0; jnode < nodes.size(); ++jnode) { const double lat = util::Constants::degreesToRadians() * lonlat(jnode, LAT); const double hx = util::Constants::degreesToRadians() * util::Earth::radius() * std::cos(lat); const double hy = util::Constants::degreesToRadians() * util::Earth::radius(); dual_delta_sph(jnode) = std::sqrt(dual_volumes(jnode) * hx * hy); } if (mpi::size() == 1) { for (idx_t jnode = 0; jnode < nodes.size(); ++jnode) { ofs << std::setw(idt) << dual_delta_sph(jnode) << "\n"; } } } if (mpi::size() == 1) { ofs.close(); } } // ------------------------------------------------------------------ // C wrapper interfaces to C++ routines void atlas__build_statistics(Mesh::Implementation* mesh) { ATLAS_ASSERT(mesh != nullptr, "Cannot access uninitialised atlas_Mesh"); Mesh m(mesh); build_statistics(m); } // ------------------------------------------------------------------ } // namespace actions } // namespace mesh } // namespace atlas atlas-0.46.0/src/atlas/mesh/actions/BuildHalo.h0000664000175000017500000000262615160212070021374 0ustar alastairalastair/* * (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" namespace atlas { class Mesh; namespace mesh { namespace actions { class BuildHalo { public: BuildHalo(Mesh& mesh); void operator()(int nb_elems); private: Mesh& mesh_; public: std::vector periodic_points_local_index_; std::vector> periodic_cells_local_index_; }; /// @brief Enlarge each partition of the mesh with a halo of elements /// @param [inout] mesh The mesh to enlarge /// @param [in] nb_elems Size of the halo /// @author Willem Deconinck /// @date June 2014 inline void build_halo(Mesh& mesh, int nb_elems) { BuildHalo f(mesh); f(nb_elems); } // ------------------------------------------------------------------ // C wrapper interfaces to C++ routines extern "C" { void atlas__build_halo(Mesh::Implementation* mesh, int nb_elems); } // ------------------------------------------------------------------ } // namespace actions } // namespace mesh } // namespace atlas atlas-0.46.0/src/atlas/mesh/actions/BuildTorusXYZField.h0000664000175000017500000000262115160212070023177 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include "atlas/domain/Domain.h" #include "atlas/domain/detail/RectangularDomain.h" #include "atlas/library/config.h" namespace atlas { class Field; } namespace atlas { class Mesh; namespace mesh { class Nodes; } } // namespace atlas namespace atlas { namespace mesh { namespace actions { //---------------------------------------------------------------------------------------------------------------------- /// Creates a XYZ field from the (lon,lat) field class BuildTorusXYZField { public: explicit BuildTorusXYZField(const std::string& name = "xyz"); Field& operator()(Mesh&, const atlas::Domain&, double r0, double r1, idx_t nx, idx_t ny) const; Field& operator()(mesh::Nodes&, const atlas::Domain&, double r0, double r1, idx_t nx, idx_t ny) const; private: std::string name_; }; //---------------------------------------------------------------------------------------------------------------------- } // namespace actions } // namespace mesh } // namespace atlas atlas-0.46.0/src/atlas/mesh/actions/Reorder.h0000664000175000017500000000733115160212070021131 0ustar alastairalastair/* * (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 : DOXYGEN_HIDE(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 atlas-0.46.0/src/atlas/mesh/actions/BuildCellCentres.h0000664000175000017500000000201115160212070022700 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include "atlas/util/Config.h" namespace atlas { class Mesh; class Field; namespace mesh { namespace actions { /// Generates the cell centres on each cell class BuildCellCentres { public: BuildCellCentres(const std::string& field_name = "centre", bool force_recompute = false); BuildCellCentres(eckit::Configuration&); /// @note Correct only for Linear Triangles and Quadrilaterals Field& operator()(Mesh&) const; private: std::string field_name_; bool force_recompute_; bool flatten_virtual_elements_; }; } // namespace actions } // namespace mesh } // namespace atlas atlas-0.46.0/src/atlas/mesh/actions/BuildStatistics.h0000664000175000017500000000150015160212070022631 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once namespace atlas { class Mesh; namespace mesh { namespace actions { void build_statistics(Mesh& mesh); // ------------------------------------------------------------------ // C wrapper interfaces to C++ routines extern "C" { void atlas__build_statistics(Mesh::Implementation* mesh); } // ------------------------------------------------------------------ } // namespace actions } // namespace mesh } // namespace atlas atlas-0.46.0/src/atlas/mesh/actions/BuildConvexHull3D.cc0000664000175000017500000001155215160212070023123 0ustar alastairalastair/* * (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 "eckit/log/BigNum.h" #include "atlas/util/QhullSphericalTriangulation.h" #include "atlas/util/CGALSphericalTriangulation.h" #include "atlas/library/config.h" #include "atlas/array/ArrayView.h" #include "atlas/array/MakeView.h" #include "atlas/field/Field.h" #include "atlas/grid/Grid.h" #include "atlas/interpolation/method/PointSet.h" #include "atlas/mesh/ElementType.h" #include "atlas/mesh/HybridElements.h" #include "atlas/mesh/Mesh.h" #include "atlas/mesh/Nodes.h" #include "atlas/mesh/actions/BuildConvexHull3D.h" #include "atlas/runtime/Log.h" #include "atlas/runtime/Trace.h" #include "atlas/util/CoordinateEnums.h" using atlas::interpolation::method::PointSet; namespace atlas { namespace mesh { namespace actions { //---------------------------------------------------------------------------------------------------------------------- BuildConvexHull3D::BuildConvexHull3D(const eckit::Parametrisation& config) { config.get("remove_duplicate_points", remove_duplicate_points_ = true); } //---------------------------------------------------------------------------------------------------------------------- void BuildConvexHull3D::operator()(Mesh& mesh) const { // don't tesselate meshes already with triags or quads if (mesh.cells().size()) { return; } std::string default_backend = (ATLAS_HAVE_CGAL ? "cgal" : "qhull"); std::string backend = eckit::Resource("$ATLAS_DELAUNAY_BACKEND",default_backend); ATLAS_TRACE("BuildConvexHull3D [" + backend + "]"); std::vector local_index; if (remove_duplicate_points_) { PointSet points(mesh); points.list_unique_points(local_index); } auto add_triangles = [&]( const auto& triangles ) { const idx_t nb_triags = triangles.size(); mesh.cells().add(mesh::ElementType::create("Triangle"), nb_triags); mesh::HybridElements::Connectivity& triag_nodes = mesh.cells().node_connectivity(); auto triag_gidx = array::make_view(mesh.cells().global_index()); auto triag_part = array::make_view(mesh.cells().partition()); Log::debug() << "Inserting triags (" << eckit::BigNum(nb_triags) << ")" << std::endl; for (idx_t tidx = 0; tidx idx{t[0],t[1],t[2]}; if( local_index.size() ) { idx[0] = local_index[idx[0]]; idx[1] = local_index[idx[1]]; idx[2] = local_index[idx[2]]; } triag_nodes.set(tidx, idx.data()); triag_gidx(tidx) = tidx + 1; triag_part(tidx) = 0; } }; if( local_index.size() == mesh.nodes().size() or local_index.empty() ) { local_index.clear(); auto lonlat = array::make_view(mesh.nodes().lonlat()); if (backend == "qhull") { ATLAS_TRACE("qhull"); auto triangles = util::QhullSphericalTriangulation{static_cast(lonlat.shape(0)),lonlat.data()}.triangles(); add_triangles(triangles); } else if (backend == "cgal") { ATLAS_TRACE("cgal"); auto triangles = util::CGALSphericalTriangulation{static_cast(lonlat.shape(0)),lonlat.data()}.triangles(); add_triangles(triangles); } else { ATLAS_THROW_EXCEPTION("backend " << backend << " not supported"); } } else { auto lonlat_view = array::make_view(mesh.nodes().lonlat()); std::vector lonlat(local_index.size()); size_t jnode = 0; for( auto& ip: local_index ) { lonlat[jnode] = {lonlat_view(ip,0),lonlat_view(ip,1)}; ++jnode; } if (backend == "qhull") { ATLAS_TRACE("qhull"); auto triangles = util::QhullSphericalTriangulation{lonlat}.triangles(); add_triangles(triangles); } else if (backend == "cgal") { ATLAS_TRACE("cgal"); auto triangles = util::CGALSphericalTriangulation{lonlat}.triangles(); add_triangles(triangles); } else { ATLAS_THROW_EXCEPTION("backend " << backend << " not supported"); } } } //---------------------------------------------------------------------------------------------------------------------- } // namespace actions } // namespace mesh } // namespace atlas atlas-0.46.0/src/atlas/mesh/actions/BuildXYZField.h0000664000175000017500000000233515160212070022144 0ustar alastairalastair/* * (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 namespace atlas { class Field; class Mesh; } // namespace atlas namespace atlas { namespace mesh { class Nodes; } } // namespace atlas namespace atlas { namespace mesh { namespace actions { //---------------------------------------------------------------------------------------------------------------------- /// Creates a XYZ field from the (lon,lat) field class BuildXYZField { public: explicit BuildXYZField(const std::string& name = "xyz", bool force_recompute = false); Field& operator()(Mesh&) const; Field& operator()(mesh::Nodes&) const; private: std::string name_; bool force_recompute_; }; //---------------------------------------------------------------------------------------------------------------------- } // namespace actions } // namespace mesh } // namespace atlas atlas-0.46.0/src/atlas/mesh/actions/ReorderHilbert.cc0000664000175000017500000003456615160212070022613 0ustar alastairalastair/* * (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{0}; 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 atlas-0.46.0/src/atlas/option/0000775000175000017500000000000015160212070016266 5ustar alastairalastairatlas-0.46.0/src/atlas/option/TransOptions.h0000664000175000017500000000563315160212070021111 0ustar alastairalastair/* * (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/util/Config.h" namespace eckit { class PathName; } // ---------------------------------------------------------------------------- namespace atlas { namespace option { // ---------------------------------------------------------------------------- enum class FFT { OFF = 0, FFT992 = 1, FFTW = 2, pocketfft = 3, }; // ---------------------------------------------------------------------------- class scalar_derivatives : public util::Config { public: scalar_derivatives(bool); }; // ---------------------------------------------------------------------------- class wind_EW_derivatives : public util::Config { public: wind_EW_derivatives(bool); }; // ---------------------------------------------------------------------------- class vorticity_divergence_fields : public util::Config { public: vorticity_divergence_fields(bool); }; // ---------------------------------------------------------------------------- class flt : public util::Config { public: flt(bool); }; // ---------------------------------------------------------------------------- class fft : public util::Config { public: fft(FFT); fft(const std::string&); }; class no_fft : public fft { public: no_fft(): fft(FFT::OFF) {} }; // ---------------------------------------------------------------------------- class split_y : public util::Config { public: split_y(bool); }; // ---------------------------------------------------------------------------- class write_legendre : public util::Config { public: write_legendre(const eckit::PathName&); }; // ---------------------------------------------------------------------------- class read_legendre : public util::Config { public: read_legendre(const eckit::PathName&); }; // ---------------------------------------------------------------------------- class write_fft : public util::Config { public: write_fft(const eckit::PathName&); }; // ---------------------------------------------------------------------------- class read_fft : public util::Config { public: read_fft(const eckit::PathName&); }; // ---------------------------------------------------------------------------- class nproma : public util::Config { public: nproma(int); }; // ---------------------------------------------------------------------------- class warning : public util::Config { public: warning(int = 1); }; // ---------------------------------------------------------------------------- } // namespace option } // namespace atlas atlas-0.46.0/src/atlas/option/TransOptions.cc0000664000175000017500000000361215160212070021242 0ustar alastairalastair/* * (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 "eckit/filesystem/PathName.h" #include "atlas/option/TransOptions.h" // ---------------------------------------------------------------------------- namespace atlas { namespace option { vorticity_divergence_fields::vorticity_divergence_fields(bool v) { set("vorticity_divergence_fields", v); } wind_EW_derivatives::wind_EW_derivatives(bool v) { set("wind_EW_derivatives", v); } scalar_derivatives::scalar_derivatives(bool v) { set("scalar_derivatives", v); } flt::flt(bool flt) { set("flt", flt); } fft::fft(FFT fft) { static const std::map FFT_to_string = { {FFT::OFF, "OFF"}, {FFT::FFT992, "FFT992"}, {FFT::FFTW, "FFTW"}, {FFT::pocketfft, "pocketfft"}}; set("fft", FFT_to_string.at(fft)); } fft::fft(const std::string& fft) { set("fft", fft); } split_y::split_y(bool split_y) { set("split_y", split_y); } write_legendre::write_legendre(const eckit::PathName& filepath) { set("write_legendre", filepath); } read_legendre::read_legendre(const eckit::PathName& filepath) { set("read_legendre", filepath); } write_fft::write_fft(const eckit::PathName& filepath) { set("write_fft", filepath); } read_fft::read_fft(const eckit::PathName& filepath) { set("read_fft", filepath); } nproma::nproma(int nproma) { set("nproma", nproma); } warning::warning(int warning) { set("warning", warning); } // ---------------------------------------------------------------------------- } // namespace option } // namespace atlas atlas-0.46.0/src/atlas/option/Options.h0000664000175000017500000000633615160212070020102 0ustar alastairalastair/* * (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/util/DataType.h" #include "atlas/util/Config.h" // ---------------------------------------------------------------------------- namespace atlas { namespace option { // ---------------------------------------------------------------------------- class type : public util::Config { public: type(const std::string&); }; // ---------------------------------------------------------------------------- class global : public util::Config { public: global(size_t owner = 0); }; // ---------------------------------------------------------------------------- class levels : public util::Config { public: levels(size_t); }; // ---------------------------------------------------------------------------- class variables : public util::Config { public: variables(size_t); }; // ---------------------------------------------------------------------------- class vector : public util::Config { public: vector(size_t = 2); }; // ---------------------------------------------------------------------------- class vector_component : public util::Config { public: vector_component(const std::string& vector_field_name, size_t index); }; // ---------------------------------------------------------------------------- class name : public util::Config { public: name(const std::string&); }; // ---------------------------------------------------------------------------- template class datatypeT : public util::Config { public: datatypeT(); }; // ---------------------------------------------------------------------------- class datatype : public util::Config { public: datatype(DataType::kind_t); datatype(const std::string&); datatype(DataType); }; // ---------------------------------------------------------------------------- class shape : public util::Config { public: template shape(const std::initializer_list& list) { set("shape", list); } }; class alignment : public util::Config { public: alignment(int); }; // ---------------------------------------------------------------------------- class halo : public util::Config { public: halo(size_t size); }; // ---------------------------------------------------------------------------- class radius : public util::Config { public: radius(double); radius(const std::string& = "Earth"); }; // --------------------------------- class pole_edges : public util::Config { public: pole_edges(bool = true); }; class on_device : public util::Config { public: on_device(); }; // ---------------------------------------------------------------------------- // Definitions // ---------------------------------------------------------------------------- template datatypeT::datatypeT() { set("datatype", DataType::kind()); } } // namespace option } // namespace atlas atlas-0.46.0/src/atlas/option/Options.cc0000664000175000017500000000416715160212070020240 0ustar alastairalastair/* * (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 "Options.h" #include "atlas/runtime/Exception.h" #include "atlas/util/Earth.h" // ---------------------------------------------------------------------------- namespace atlas { namespace option { type::type(const std::string& _type) { set("type", _type); } halo::halo(size_t size) { set("halo", size); } datatype::datatype(DataType::kind_t kind) { set("datatype", kind); } datatype::datatype(const std::string& str) { set("datatype", DataType::str_to_kind(str)); } datatype::datatype(DataType dtype) { set("datatype", dtype.kind()); } name::name(const std::string& _name) { set("name", _name); } global::global(size_t _owner) { set("global", true); set("owner", _owner); } levels::levels(size_t _levels) { set("levels", _levels); } variables::variables(size_t _variables) { set("variables", _variables); } vector::vector(size_t _components) { set("variables", _components); set("type", "vector"); } vector_component::vector_component(const std::string& vector_field_name, size_t index) { const auto conf = util::Config("vector_field_name", vector_field_name) | util::Config("index", index); set("vector_component", conf); } radius::radius(double _radius) { set("radius", _radius); } radius::radius(const std::string& key) { if (key == "Earth") { set("radius", util::Earth::radius()); } else { ATLAS_NOTIMPLEMENTED; } } pole_edges::pole_edges(bool _pole_edges) { set("pole_edges", _pole_edges); } alignment::alignment(int value) { set("alignment", value); } on_device::on_device() { set("execution_space", "device"); } // ---------------------------------------------------------------------------- } // namespace option } // namespace atlas atlas-0.46.0/src/atlas/library.h0000664000175000017500000000072715160212070016601 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ /// @author Willem Deconinck #pragma once #include "atlas/library/Library.h" atlas-0.46.0/src/atlas/io/0000775000175000017500000000000015160212070015365 5ustar alastairalastairatlas-0.46.0/src/atlas/io/VectorAdaptor.h0000664000175000017500000000321215160212070020311 0ustar alastairalastair/* * (C) Copyright 2020 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include "atlas/util/vector.h" #include "atlas-io.h" namespace atlas { //--------------------------------------------------------------------------------------------------------------------- template void encode_data(const atlas::vector& v, atlas::io::Data& out) { out.assign(v.data(), v.size() * sizeof(T)); } //--------------------------------------------------------------------------------------------------------------------- template size_t encode_metadata(const atlas::vector& v, atlas::io::Metadata& metadata) { using atlas::io::ArrayMetadata; using DataType = ArrayMetadata::DataType; return encode_metadata(ArrayMetadata{DataType::create(), {v.size()}}, metadata); } //--------------------------------------------------------------------------------------------------------------------- template void decode(const atlas::io::Metadata& m, const atlas::io::Data& encoded, atlas::vector& out) { atlas::io::ArrayMetadata array(m); const T* data = static_cast(encoded.data()); out.assign(data, data + array.size()); } //--------------------------------------------------------------------------------------------------------------------- } // end namespace atlas atlas-0.46.0/src/atlas/io/atlas-io.h0000664000175000017500000000077515160212070017260 0ustar alastairalastair/* * (C) Copyright 2020 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include "atlas_io/atlas-io.h" #include "atlas/io/ArrayAdaptor.h" #include "atlas/io/VectorAdaptor.h" atlas-0.46.0/src/atlas/io/ArrayAdaptor.cc0000664000175000017500000000366415160212070020276 0ustar alastairalastair/* * (C) Copyright 2020 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "ArrayAdaptor.h" #include // memcpy #include #include "atlas/array/Array.h" namespace atlas { namespace array { //--------------------------------------------------------------------------------------------------------------------- void interprete(const atlas::array::Array& a, atlas::io::ArrayReference& out) { out = io::ArrayReference(a.data(), atlas::io::DataType(a.datatype().str()), a.shape()); } //--------------------------------------------------------------------------------------------------------------------- void decode(const atlas::io::Metadata& metadata, const atlas::io::Data& data, atlas::array::Array& out) { atlas::io::ArrayMetadata array(metadata); if (array.datatype().str() != out.datatype().str()) { std::stringstream err; err << "Could not decode " << metadata.json() << " into Array with datatype " << out.datatype().str() << "." << "Incompatible datatype!"; throw atlas::io::Exception(err.str(), Here()); } if (array.rank() != out.rank()) { std::stringstream err; err << "Could not decode " << metadata.json() << " into Array with rank " << out.rank() << "." << "Incompatible rank!"; throw atlas::io::Exception(err.str(), Here()); } out.resize(array.shape()); ATLAS_IO_ASSERT(out.contiguous()); ::memcpy(out.data(), data, data.size()); } //--------------------------------------------------------------------------------------------------------------------- } // namespace array } // namespace atlas atlas-0.46.0/src/atlas/io/ArrayAdaptor.h0000664000175000017500000000177415160212070020140 0ustar alastairalastair/* * (C) Copyright 2020 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include "atlas-io.h" namespace atlas { namespace array { class Array; //--------------------------------------------------------------------------------------------------------------------- void interprete(const Array&, atlas::io::ArrayReference&); //--------------------------------------------------------------------------------------------------------------------- void decode(const atlas::io::Metadata&, const atlas::io::Data&, Array&); //--------------------------------------------------------------------------------------------------------------------- } // namespace array } // namespace atlas atlas-0.46.0/src/atlas/interpolation.h0000664000175000017500000000070515160212070020020 0ustar alastairalastair/* * (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/interpolation/Interpolation.h" atlas-0.46.0/src/atlas/atlas.h0000664000175000017500000000010715160212070016231 0ustar alastairalastair/// @namespace atlas /// @brief Contains all atlas classes and methods atlas-0.46.0/src/atlas/linalg/0000775000175000017500000000000015160212070016224 5ustar alastairalastairatlas-0.46.0/src/atlas/linalg/Indexing.h0000664000175000017500000000104715160212070020144 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once namespace atlas { namespace linalg { enum struct Indexing { layout_left, layout_right, }; } // namespace linalg } // namespace atlas atlas-0.46.0/src/atlas/linalg/View.h0000664000175000017500000000444115160212070017312 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include "eckit/linalg/Matrix.h" #include "eckit/linalg/Vector.h" #include "atlas/array/LocalView.h" #include "atlas/linalg/Introspection.h" namespace atlas { namespace linalg { template using View = array::LocalView; namespace view { template struct Convert { using value_type = typename std::conditional::value, const introspection::value_type, introspection::value_type>::type; using type = array::LocalView()>; static type apply(View& view) { return type(view.data(), view.shape()); } }; template struct Convert { using value_type = typename std::conditional::value, const eckit::linalg::Scalar, eckit::linalg::Scalar>::type; using type = array::LocalView; static type apply(View& v) { return type(v.data(), array::make_shape(v.size())); } }; template struct Convert { using value_type = typename std::conditional::value, const eckit::linalg::Scalar, eckit::linalg::Scalar>::type; using type = array::LocalView; static type apply(View& m) { return type(m.data(), array::make_shape(m.cols(), m.rows())); } }; template struct ConvertView { using type = typename Convert>::type; static type apply(View& v) { return Convert>::apply(v); } }; } // namespace view template using view_type = typename view::ConvertView::type; template view_type make_view(T& view) { return view::ConvertView::apply(view); } } // namespace linalg } // namespace atlas atlas-0.46.0/src/atlas/linalg/sparse/0000775000175000017500000000000015160212070017521 5ustar alastairalastairatlas-0.46.0/src/atlas/linalg/sparse/MakeSparseMatrixStorageEckit.h0000664000175000017500000001334015160212070025420 0ustar alastairalastair/* * (C) Copyright 2024- 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 #include #include "eckit/linalg/SparseMatrix.h" #include "atlas/array.h" #include "atlas/linalg/sparse/SparseMatrixStorage.h" namespace atlas { namespace linalg { //---------------------------------------------------------------------------------------------------------------------- template void host_copy_eckit (const InputT* input_data, array::Array& output) { auto size = output.size(); OutputT* output_data = output.host_data(); std::copy( input_data, input_data + size, output_data ); } inline SparseMatrixStorage make_sparse_matrix_storage(eckit::linalg::SparseMatrix&& m) { using Index = eckit::linalg::Index; using Value = eckit::linalg::Scalar; std::size_t rows = m.rows(); std::size_t cols = m.cols(); std::size_t nnz = m.nonZeros(); array::Array* outer; array::Array* inner; array::Array* value; { array::label label{"sparse_matrix.outer"}; outer = array::Array::wrap(const_cast(m.outer()), array::make_shape(rows+1)); } { array::label label{"sparse_matrix.inner"}; inner = array::Array::wrap(const_cast(m.inner()), array::make_shape(nnz)); } { array::label label{"sparse_matrix.value"}; value = array::Array::wrap(const_cast(m.data()), array::make_shape(nnz)); } // We now move the eckit::linalg::SparseMatrix into a generic storage so // the wrapped array data does not go out of scope auto storage = std::make_any(std::move(m)); return SparseMatrixStorage::make( rows, cols, nnz, std::unique_ptr(value), std::unique_ptr(inner), std::unique_ptr(outer), std::move(storage) ); } template< typename value_type, typename index_type = eckit::linalg::Index> SparseMatrixStorage make_sparse_matrix_storage(eckit::linalg::SparseMatrix&& m) { if (std::is_same_v && std::is_same_v) { return make_sparse_matrix_storage(std::move(m)); } std::size_t rows = m.rows(); std::size_t cols = m.cols(); std::size_t nnz = m.nonZeros(); array::Array* outer; array::Array* inner; array::Array* value; { array::label label{"sparse_matrix.outer"}; outer = array::Array::create(rows+1); } { array::label label{"sparse_matrix.inner"}; inner = array::Array::create(nnz); } { array::label label{"sparse_matrix.value"}; value = array::Array::create(nnz); } host_copy_eckit(m.outer(), *outer); host_copy_eckit(m.inner(), *inner); host_copy_eckit(m.data(), *value); return SparseMatrixStorage::make( rows, cols, nnz, std::unique_ptr(value), std::unique_ptr(inner), std::unique_ptr(outer), std::any() ); } inline SparseMatrixStorage make_sparse_matrix_storage(const eckit::linalg::SparseMatrix& m) { using Index = eckit::linalg::Index; using Value = eckit::linalg::Scalar; std::size_t rows = m.rows(); std::size_t cols = m.cols(); std::size_t nnz = m.nonZeros(); array::Array* outer; array::Array* inner; array::Array* value; { array::label label{"sparse_matrix.outer"}; outer = array::Array::create(rows+1); } { array::label label{"sparse_matrix.inner"}; inner = array::Array::create(nnz); } { array::label label{"sparse_matrix.value"}; value = array::Array::create(nnz); } host_copy_eckit(m.outer(), *outer); host_copy_eckit(m.inner(), *inner); host_copy_eckit(m.data(), *value); return SparseMatrixStorage::make( rows, cols, nnz, std::unique_ptr(value), std::unique_ptr(inner), std::unique_ptr(outer), std::any() ); } template< typename value_type, typename index_type = eckit::linalg::Index> SparseMatrixStorage make_sparse_matrix_storage(const eckit::linalg::SparseMatrix& m) { std::size_t rows = m.rows(); std::size_t cols = m.cols(); std::size_t nnz = m.nonZeros(); array::Array* outer; array::Array* inner; array::Array* value; { array::label label{"sparse_matrix.outer"}; outer = array::Array::create(rows+1); } { array::label label{"sparse_matrix.inner"}; inner = array::Array::create(nnz); } { array::label label{"sparse_matrix.value"}; value = array::Array::create(nnz); } host_copy_eckit(m.outer(), *outer); host_copy_eckit(m.inner(), *inner); host_copy_eckit(m.data(), *value); return SparseMatrixStorage::make( rows, cols, nnz, std::unique_ptr(value), std::unique_ptr(inner), std::unique_ptr(outer), std::any() ); } //---------------------------------------------------------------------------------------------------------------------- } // namespace linalg } // namespace atlasatlas-0.46.0/src/atlas/linalg/sparse/SparseMatrixMultiply_HicSparse.cc0000664000175000017500000002642315160212070026162 0ustar alastairalastair/* * (C) Copyright 2024 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/linalg/sparse/SparseMatrixMultiply_HicSparse.h" #include "atlas/parallel/omp/omp.h" #include "atlas/runtime/Exception.h" #include "hic/hic.h" #include "hic/hic_library_types.h" #include "hic/hicsparse.h" namespace { class HicSparseHandleRAIIWrapper { public: HicSparseHandleRAIIWrapper() { hicsparseCreate(&handle_); }; ~HicSparseHandleRAIIWrapper() { hicsparseDestroy(handle_); } hicsparseHandle_t value() { return handle_; } private: hicsparseHandle_t handle_; }; hicsparseHandle_t getDefaultHicSparseHandle() { static auto handle = HicSparseHandleRAIIWrapper(); return handle.value(); } template constexpr hicsparseIndexType_t getHicsparseIndexType() { using base_type = std::remove_const_t; if constexpr (std::is_same_v) { return HICSPARSE_INDEX_32I; } else { static_assert(std::is_same_v, "Unsupported index type"); return HICSPARSE_INDEX_64I; } } template constexpr auto getHicsparseValueType() { using base_type = std::remove_const_t; if constexpr (std::is_same_v) { return HIC_R_32F; } else { static_assert(std::is_same_v, "Unsupported value type");\ return HIC_R_64F; } } template hicsparseOrder_t getHicsparseOrder(const atlas::linalg::View& v) { constexpr int row_idx = (IndexLayout == atlas::linalg::Indexing::layout_left) ? 0 : 1; constexpr int col_idx = (IndexLayout == atlas::linalg::Indexing::layout_left) ? 1 : 0; if (v.stride(row_idx) == 1) { return HICSPARSE_ORDER_COL; } else if (v.stride(col_idx) == 1) { return HICSPARSE_ORDER_ROW; } else { atlas::throw_Exception("Unsupported dense matrix memory order", Here()); return HICSPARSE_ORDER_COL; } } template int64_t getLeadingDimension(const atlas::linalg::View& v) { if (v.stride(0) == 1) { return v.stride(1); } else if (v.stride(1) == 1) { return v.stride(0); } else { atlas::throw_Exception("Unsupported dense matrix memory order", Here()); return 0; } } } namespace atlas { namespace linalg { namespace sparse { template void hsSpMV(const SparseMatrixView& W, const View& src, Value beta, View& tgt) { // Assume that W, src, and tgt are all device views using Index [[maybe_unused]] = typename SparseMatrixView::index_type; ATLAS_ASSERT(src.shape(0) >= W.cols()); ATLAS_ASSERT(tgt.shape(0) >= W.rows()); [[maybe_unused]] auto handle = getDefaultHicSparseHandle(); // Create a sparse matrix descriptor [[maybe_unused]] hicsparseConstSpMatDescr_t matA; HICSPARSE_CALL(hicsparseCreateConstCsr( &matA, W.rows(), W.cols(), W.nnz(), W.outer(), // row_offsets W.inner(), // column_indices W.value(), // values getHicsparseIndexType(), getHicsparseIndexType(), HICSPARSE_INDEX_BASE_ZERO, getHicsparseValueType())); // Create dense matrix descriptors [[maybe_unused]] hicsparseConstDnVecDescr_t vecX; HICSPARSE_CALL(hicsparseCreateConstDnVec( &vecX, static_cast(W.cols()), src.data(), getHicsparseValueType())); [[maybe_unused]] hicsparseDnVecDescr_t vecY; HICSPARSE_CALL(hicsparseCreateDnVec( &vecY, W.rows(), tgt.data(), getHicsparseValueType())); [[maybe_unused]] const Value alpha = 1; // Determine buffer size [[maybe_unused]] size_t bufferSize = 0; HICSPARSE_CALL(hicsparseSpMV_bufferSize( handle, HICSPARSE_OPERATION_NON_TRANSPOSE, &alpha, matA, vecX, &beta, vecY, getHicsparseValueType(), HICSPARSE_SPMV_ALG_DEFAULT, &bufferSize)); // Allocate buffer [[maybe_unused]] char* buffer; HIC_CALL(hicMalloc(&buffer, bufferSize)); // Perform SpMV HICSPARSE_CALL(hicsparseSpMV( handle, HICSPARSE_OPERATION_NON_TRANSPOSE, &alpha, matA, vecX, &beta, vecY, getHicsparseValueType(), HICSPARSE_SPMV_ALG_DEFAULT, buffer)); HIC_CALL(hicFree(buffer)); HICSPARSE_CALL(hicsparseDestroyDnVec(vecX)); HICSPARSE_CALL(hicsparseDestroyDnVec(vecY)); HICSPARSE_CALL(hicsparseDestroySpMat(matA)); HIC_CALL(hicDeviceSynchronize()); } template void hsSpMM(const SparseMatrixView& W, const View& src, Value beta, View& tgt) { // Assume that W, src, and tgt are all device views using Index [[maybe_unused]] = typename SparseMatrixView::index_type; constexpr int row_idx = (IndexLayout == Indexing::layout_left) ? 0 : 1; constexpr int col_idx = (IndexLayout == Indexing::layout_left) ? 1 : 0; ATLAS_ASSERT(src.shape(row_idx) >= W.cols()); ATLAS_ASSERT(tgt.shape(row_idx) >= W.rows()); ATLAS_ASSERT(src.shape(col_idx) == tgt.shape(col_idx)); [[maybe_unused]] auto handle = getDefaultHicSparseHandle(); // Create a sparse matrix descriptor [[maybe_unused]] hicsparseConstSpMatDescr_t matA; HICSPARSE_CALL(hicsparseCreateConstCsr( &matA, W.rows(), W.cols(), W.nnz(), W.outer(), // row_offsets W.inner(), // column_indices W.value(), // values getHicsparseIndexType(), getHicsparseIndexType(), HICSPARSE_INDEX_BASE_ZERO, getHicsparseValueType())); // Create dense matrix descriptors [[maybe_unused]] hicsparseConstDnMatDescr_t matB; HICSPARSE_CALL(hicsparseCreateConstDnMat( &matB, W.cols(), src.shape(col_idx), getLeadingDimension(src), src.data(), getHicsparseValueType(), getHicsparseOrder(src))); [[maybe_unused]] hicsparseDnMatDescr_t matC; HICSPARSE_CALL(hicsparseCreateDnMat( &matC, W.rows(), tgt.shape(col_idx), getLeadingDimension(tgt), tgt.data(), getHicsparseValueType(), getHicsparseOrder(tgt))); [[maybe_unused]] const Value alpha = 1; // Determine buffer size [[maybe_unused]] size_t bufferSize = 0; HICSPARSE_CALL(hicsparseSpMM_bufferSize( handle, HICSPARSE_OPERATION_NON_TRANSPOSE, HICSPARSE_OPERATION_NON_TRANSPOSE, &alpha, matA, matB, &beta, matC, getHicsparseValueType(), HICSPARSE_SPMM_ALG_DEFAULT, &bufferSize)); // Allocate buffer [[maybe_unused]] char* buffer; HIC_CALL(hicMalloc(&buffer, bufferSize)); // Perform SpMM HICSPARSE_CALL(hicsparseSpMM( handle, HICSPARSE_OPERATION_NON_TRANSPOSE, HICSPARSE_OPERATION_NON_TRANSPOSE, &alpha, matA, matB, &beta, matC, getHicsparseValueType(), HICSPARSE_SPMM_ALG_DEFAULT, buffer)); HIC_CALL(hicFree(buffer)); HICSPARSE_CALL(hicsparseDestroyDnMat(matC)); HICSPARSE_CALL(hicsparseDestroyDnMat(matB)); HICSPARSE_CALL(hicsparseDestroySpMat(matA)); HIC_CALL(hicDeviceSynchronize()); } template void SparseMatrixMultiply::multiply( const SparseMatrixView& W, const View& src, View& tgt, const Configuration&) { ScalarValue beta = 0; hsSpMV(W, src, beta, tgt); } template void SparseMatrixMultiply::multiply_add( const SparseMatrixView& W, const View& src, View& tgt, const Configuration&) { ScalarValue beta = 1; hsSpMV(W, src, beta, tgt); } template void SparseMatrixMultiply::multiply( const SparseMatrixView& W, const View& src, View& tgt, const Configuration&) { ScalarValue beta = 0; hsSpMM(W, src, beta, tgt); } template void SparseMatrixMultiply::multiply_add( const SparseMatrixView& W, const View& src, View& tgt, const Configuration&) { ScalarValue beta = 1; hsSpMM(W, src, beta, tgt); } template void SparseMatrixMultiply::multiply( const SparseMatrixView& W, const View& src, View& tgt, const Configuration&) { ScalarValue beta = 0; hsSpMV(W, src, beta, tgt); } template void SparseMatrixMultiply::multiply_add( const SparseMatrixView& W, const View& src, View& tgt, const Configuration&) { ScalarValue beta = 1; hsSpMV(W, src, beta, tgt); } template void SparseMatrixMultiply::multiply( const SparseMatrixView& W, const View& src, View& tgt, const Configuration&) { ScalarValue beta = 0; hsSpMM(W, src, beta, tgt); } template void SparseMatrixMultiply::multiply_add( const SparseMatrixView& W, const View& src, View& tgt, const Configuration&) { ScalarValue beta = 1; hsSpMM(W, src, beta, tgt); } #define EXPLICIT_TEMPLATE_INSTANTIATION(TYPE) \ template struct SparseMatrixMultiply; \ template struct SparseMatrixMultiply; \ template struct SparseMatrixMultiply; \ template struct SparseMatrixMultiply; EXPLICIT_TEMPLATE_INSTANTIATION(double); EXPLICIT_TEMPLATE_INSTANTIATION(float); } // namespace sparse } // namespace linalg } // namespace atlasatlas-0.46.0/src/atlas/linalg/sparse/SparseMatrixMultiply_OpenMP.cc0000664000175000017500000003065715160212070025443 0ustar alastairalastair/* * (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/linalg/sparse/SparseMatrixMultiply_OpenMP.h" #include "atlas/parallel/omp/omp.h" #include "atlas/runtime/Exception.h" namespace atlas { namespace linalg { namespace sparse { template void spmv_layout_left(const SparseMatrixView& W, const View& src, View& tgt) { using Value = TargetValue; const auto outer = W.outer(); const auto index = W.inner(); const auto weight = W.value(); const idx_t rows = static_cast(W.rows()); ATLAS_ASSERT(src.shape(0) >= W.cols()); ATLAS_ASSERT(tgt.shape(0) >= W.rows()); atlas_omp_parallel_for(idx_t r = 0; r < rows; ++r) { if constexpr (SetZero) { tgt[r] = 0.; } for (idx_t c = outer[r]; c < outer[r + 1]; ++c) { idx_t n = index[c]; Value w = static_cast(weight[c]); tgt[r] += w * src[n]; } } } template void SparseMatrixMultiply::multiply( const SparseMatrixView& W, const View& src, View& tgt, const Configuration&) { spmv_layout_left(W, src, tgt); } template void SparseMatrixMultiply::multiply_add( const SparseMatrixView& W, const View& src, View& tgt, const Configuration&) { spmv_layout_left(W, src, tgt); } template void spmm_layout_left(const SparseMatrixView& W, const View& src, View& tgt) { using Value = TargetValue; const auto outer = W.outer(); const auto index = W.inner(); const auto weight = W.value(); const idx_t rows = static_cast(W.rows()); const idx_t Nk = src.shape(1); ATLAS_ASSERT(src.shape(0) >= W.cols()); ATLAS_ASSERT(tgt.shape(0) >= W.rows()); atlas_omp_parallel_for(idx_t r = 0; r < rows; ++r) { if constexpr (SetZero) { for (idx_t k = 0; k < Nk; ++k) { tgt(r, k) = 0.; } } for (idx_t c = outer[r]; c < outer[r + 1]; ++c) { idx_t n = index[c]; Value w = static_cast(weight[c]); for (idx_t k = 0; k < Nk; ++k) { tgt(r, k) += w * src(n, k); } } } } template void SparseMatrixMultiply::multiply( const SparseMatrixView& W, const View& src, View& tgt, const Configuration&) { spmm_layout_left(W, src, tgt); } template void SparseMatrixMultiply::multiply_add( const SparseMatrixView& W, const View& src, View& tgt, const Configuration&) { spmm_layout_left(W, src, tgt); } template void spmt_layout_left(const SparseMatrixView& W, const View& src, View& tgt) { if (src.contiguous() && tgt.contiguous()) { // We can take a more optimized route by reducing rank auto src_v = View(src.data(), array::make_shape(src.shape(0), src.stride(0))); auto tgt_v = View(tgt.data(), array::make_shape(tgt.shape(0), tgt.stride(0))); spmm_layout_left(W, src_v, tgt_v); return; } using Value = TargetValue; const auto outer = W.outer(); const auto index = W.inner(); const auto weight = W.value(); const idx_t rows = static_cast(W.rows()); const idx_t Nk = src.shape(1); const idx_t Nl = src.shape(2); atlas_omp_parallel_for(idx_t r = 0; r < rows; ++r) { if constexpr (SetZero) { for (idx_t k = 0; k < Nk; ++k) { for (idx_t l = 0; l < Nl; ++l) { tgt(r, k, l) = 0.; } } } for (idx_t c = outer[r]; c < outer[r + 1]; ++c) { idx_t n = index[c]; const Value w = static_cast(weight[c]); for (idx_t k = 0; k < Nk; ++k) { for (idx_t l = 0; l < Nl; ++l) { tgt(r, k, l) += w * src(n, k, l); } } } } } template void SparseMatrixMultiply::multiply( const SparseMatrixView& W, const View& src, View& tgt, const Configuration& config) { spmt_layout_left(W, src, tgt); } template void SparseMatrixMultiply::multiply_add( const SparseMatrixView& W, const View& src, View& tgt, const Configuration& config) { spmt_layout_left(W, src, tgt); } template void SparseMatrixMultiply::multiply( const SparseMatrixView& W, const View& src, View& tgt, const Configuration& config) { return SparseMatrixMultiply::multiply(W, src, tgt, config); } template void SparseMatrixMultiply::multiply_add( const SparseMatrixView& W, const View& src, View& tgt, const Configuration& config) { return SparseMatrixMultiply::multiply_add(W, src, tgt, config); } template void spmm_layout_right(const SparseMatrixView& W, const View& src, View& tgt) { using Value = TargetValue; const auto outer = W.outer(); const auto index = W.inner(); const auto weight = W.value(); const idx_t rows = static_cast(W.rows()); const idx_t Nk = src.shape(0); ATLAS_ASSERT(src.shape(1) >= W.cols()); ATLAS_ASSERT(tgt.shape(1) >= W.rows()); atlas_omp_parallel_for(idx_t r = 0; r < rows; ++r) { if constexpr (SetZero) { for (idx_t k = 0; k < Nk; ++k) { tgt(k, r) = 0.; } } for (idx_t c = outer[r]; c < outer[r + 1]; ++c) { idx_t n = index[c]; Value w = static_cast(weight[c]); for (idx_t k = 0; k < Nk; ++k) { tgt(k, r) += w * src(k, n); } } } } template void SparseMatrixMultiply::multiply( const SparseMatrixView& W, const View& src, View& tgt, const Configuration&) { spmm_layout_right(W, src, tgt); } template void SparseMatrixMultiply::multiply_add( const SparseMatrixView& W, const View& src, View& tgt, const Configuration&) { spmm_layout_right(W, src, tgt); } template void spmt_layout_right(const SparseMatrixView& W, const View& src, View& tgt) { if (src.contiguous() && tgt.contiguous()) { // We can take a more optimized route by reducing rank auto src_v = View(src.data(), array::make_shape(src.shape(0), src.stride(0))); auto tgt_v = View(tgt.data(), array::make_shape(tgt.shape(0), tgt.stride(0))); spmm_layout_right(W, src_v, tgt_v); return; } using Value = TargetValue; const auto outer = W.outer(); const auto index = W.inner(); const auto weight = W.value(); const idx_t rows = static_cast(W.rows()); const idx_t Nk = src.shape(1); const idx_t Nl = src.shape(0); atlas_omp_parallel_for(idx_t r = 0; r < rows; ++r) { if constexpr (SetZero){ for (idx_t k = 0; k < Nk; ++k) { for (idx_t l = 0; l < Nl; ++l) { tgt(l, k, r) = 0.; } } } for (idx_t c = outer[r]; c < outer[r + 1]; ++c) { idx_t n = index[c]; const Value w = static_cast(weight[c]); for (idx_t k = 0; k < Nk; ++k) { for (idx_t l = 0; l < Nl; ++l) { tgt(l, k, r) += w * src(l, k, n); } } } } } template void SparseMatrixMultiply::multiply( const SparseMatrixView& W, const View& src, View& tgt, const Configuration& config) { spmt_layout_right(W, src, tgt); } template void SparseMatrixMultiply::multiply_add( const SparseMatrixView& W, const View& src, View& tgt, const Configuration& config) { spmt_layout_right(W, src, tgt); } #define EXPLICIT_TEMPLATE_INSTANTIATION(TYPE) \ template struct SparseMatrixMultiply; \ template struct SparseMatrixMultiply; \ template struct SparseMatrixMultiply; \ template struct SparseMatrixMultiply; \ template struct SparseMatrixMultiply; \ template struct SparseMatrixMultiply; \ template struct SparseMatrixMultiply; \ template struct SparseMatrixMultiply; \ template struct SparseMatrixMultiply; \ template struct SparseMatrixMultiply; \ template struct SparseMatrixMultiply; \ template struct SparseMatrixMultiply; EXPLICIT_TEMPLATE_INSTANTIATION(double); EXPLICIT_TEMPLATE_INSTANTIATION(float); } // namespace sparse } // namespace linalg } // namespace atlas atlas-0.46.0/src/atlas/linalg/sparse/SparseMatrixTriplet.h0000664000175000017500000001464015160212070023665 0ustar alastairalastair/* * (C) Crown Copyright 2025 Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ #pragma once #include #include #include #include #include #include #include "atlas/linalg/sparse/SparseMatrixStorage.h" #include "atlas/linalg/sparse/SparseMatrixView.h" namespace atlas::linalg { /// @brief A triplet which represents a non-zero entry in a sparse matrix. template class Triplet { static_assert(std::is_integral_v, "Index must be an integral type"); public: Triplet() = default; Triplet(Index row, Index column, Value value): row_{row}, column_{column}, value_{value} {} Index row() const { return row_; } Index col() const { return column_; } Value value() const { return value_; } /// @brief Less-than operator for sorting triplets. bool operator<(const Triplet& other) const { return std::tie(row_, column_) < std::tie(other.row_, other.column_); } private: Index row_{}; Index column_{}; Value value_{}; }; namespace detail { // SFINAE types/variables in place of C++20 concepts. template struct is_triplet : std::false_type {}; template struct is_triplet> : std::true_type {}; template constexpr bool is_triplet_iterator = is_triplet::value_type>::value; template constexpr bool is_random_access_iterator = std::is_base_of_v::iterator_category, std::random_access_iterator_tag>; template constexpr bool is_mutable_iterator = !std::is_const_v::reference>>; template constexpr bool is_sortable_iterator = is_random_access_iterator && is_mutable_iterator; } // namespace detail /// @brief Construct a SparseMatrixStorage from a range of triplets. template std::enable_if_t, SparseMatrixStorage> make_sparse_matrix_storage_from_triplets( std::size_t n_rows, std::size_t n_cols, Iter triplets_begin, Iter triplets_end) { using TripletType = typename std::iterator_traits::value_type; using Index = decltype(std::declval().row()); using Value = decltype(std::declval().value()); const std::size_t n_non_zero = std::distance(triplets_begin, triplets_end); auto outer_array = std::unique_ptr(array::Array::create(n_rows + 1)); auto inner_array = std::unique_ptr(array::Array::create(n_non_zero)); auto values_array = std::unique_ptr(array::Array::create(n_non_zero)); auto outer_view = array::make_view(*outer_array); auto inner_view = array::make_view(*inner_array); auto values_view = array::make_view(*values_array); std::size_t index = 0; Iter triplet_iter = triplets_begin; TripletType previous_triplet = *triplet_iter; for (std::size_t row = 0; row < n_rows; ++row) { outer_view(row) = index; for (; triplet_iter != triplets_end && triplet_iter->row() == static_cast(row); ++index, ++triplet_iter) { ATLAS_ASSERT(!(triplet_iter->row() < previous_triplet.row()), "Triplets must be sorted per row."); ATLAS_ASSERT(static_cast(triplet_iter->col()) < n_cols, "Triplet column index out of bounds."); inner_view(index) = triplet_iter->col(); values_view(index) = triplet_iter->value(); previous_triplet = *triplet_iter; } } ATLAS_ASSERT(index == n_non_zero, "Triplet row index out of bounds."); outer_view(n_rows) = n_non_zero; return SparseMatrixStorage::make(n_rows, n_cols, n_non_zero, std::move(values_array), std::move(inner_array), std::move(outer_array), std::any()); } /// @brief Construct a SparseMatrixStorage from a random-access range of triplets, sorting if necessary. template std::enable_if_t && detail::is_sortable_iterator, SparseMatrixStorage> make_sparse_matrix_storage_from_triplets(std::size_t n_rows, std::size_t n_cols, std::size_t n_non_zero, Iter triplets_begin, bool is_sorted = false) { if (!is_sorted) { std::sort(triplets_begin, triplets_begin + n_non_zero); } return make_sparse_matrix_storage_from_triplets(n_rows, n_cols, triplets_begin, triplets_begin + n_non_zero); } /// @brief Construct a SparseMatrixStorage from a vector of triplets, sorting if necessary. template SparseMatrixStorage make_sparse_matrix_storage_from_triplets(std::size_t n_rows, std::size_t n_cols, std::vector>& triplets, bool is_sorted = false) { return make_sparse_matrix_storage_from_triplets(n_rows, n_cols, triplets.size(), triplets.begin(), is_sorted); } /// @brief For-each iteration over all non-zero triplets in row. template std::enable_if_t> sparse_matrix_for_each_row( std::size_t row, const SparseMatrixView& matrix, Functor&& functor) { const Index* outer = matrix.outer(); const Index* inner = matrix.inner(); const Value* values = matrix.value(); for (Index index = outer[row]; index < outer[row + 1]; ++index) { const Index column = inner[index]; const Value value = values[index]; functor(static_cast(row), column, value); } } /// @brief For-each iteration over all non-zero triplets in matrix. template std::enable_if_t> sparse_matrix_for_each( const SparseMatrixView& matrix, Functor&& functor) { for (std::size_t row = 0; row < matrix.rows(); ++row) { sparse_matrix_for_each_row(row, matrix, functor); } } } // namespace atlas::linalg atlas-0.46.0/src/atlas/linalg/sparse/SparseMatrixView.h0000664000175000017500000000405115160212070023147 0ustar alastairalastair/* * (C) Copyright 2024- ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include "eckit/linalg/types.h" namespace atlas { namespace linalg { //---------------------------------------------------------------------------------------------------------------------- template class SparseMatrixView { public: using value_type = Value; using index_type = Index; using size_type = std::size_t; ~SparseMatrixView() = default; SparseMatrixView() = default; SparseMatrixView( size_type rows, size_type cols, size_type nnz, value_type const* value, index_type const* inner, index_type const* outer) : rows_(rows), cols_(cols), nnz_(nnz), outer_(outer), inner_(inner), value_(value) { } SparseMatrixView(const SparseMatrixView& other) = default; size_type rows() const { return rows_; } size_type cols() const { return cols_; } size_type nnz() const { return nnz_; } size_type outer_size() const { return rows() + 1; } size_type inner_size() const { return nnz(); } size_type value_size() const { return nnz(); } index_type const* outer() const { return outer_; } index_type const* inner() const { return inner_; } value_type const* value() const { return value_; } bool empty() const { return nnz() == 0; } private: size_type rows_{0}; size_type cols_{0}; size_type nnz_{0}; index_type const* outer_{nullptr}; index_type const* inner_{nullptr}; value_type const* value_{nullptr}; }; //---------------------------------------------------------------------------------------------------------------------- } // namespace linalg } // namespace atlas atlas-0.46.0/src/atlas/linalg/sparse/SparseMatrixToTriplets.h0000664000175000017500000001277415160212070024361 0ustar alastairalastair/* * (C) Copyright 2025- 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/linalg/sparse/SparseMatrixStorage.h" #include "atlas/linalg/sparse/SparseMatrixView.h" #include "atlas/util/DataType.h" #include "atlas/runtime/Trace.h" namespace atlas::linalg { template void sparse_matrix_to_rows_columns_values(const linalg::SparseMatrixView& mat, Index rows[], Index columns[], Value values[]) { const auto outer = mat.outer(); const auto index = mat.inner(); const auto vals = mat.value(); std::size_t idx = 0; for(std::size_t r = 0; r < mat.rows(); ++r) { for (auto c = outer[r]; c < outer[r + 1]; ++c) { auto col = index[c]; rows[idx] = r; columns[idx] = col; values[idx] = vals[c]; ++idx; } } } template void sparse_matrix_to_rows_columns_values(const linalg::SparseMatrixView& mat, std::vector& rows, std::vector& columns, std::vector& values) { rows.resize(mat.nnz()); columns.resize(mat.nnz()); values.resize(mat.nnz()); sparse_matrix_to_rows_columns_values(mat, rows.data(), columns.data(), values.data()); } template void sparse_matrix_to_rows_columns_values(const SparseMatrixStorage& mat, std::vector& rows, std::vector& columns, std::vector& values) { if (mat.inner().datatype().kind() != DataType::kind() || mat.outer().datatype().kind() != DataType::kind() ) { ATLAS_NOTIMPLEMENTED; } switch (mat.value().datatype().kind() ) { case DataType::kind() : { sparse_matrix_to_rows_columns_values( atlas::linalg::make_host_view(mat), rows, columns, values ); return; } case DataType::kind() : { sparse_matrix_to_rows_columns_values( atlas::linalg::make_host_view(mat), rows, columns, values ); return; } default: ATLAS_NOTIMPLEMENTED; } } template SparseMatrixStorage make_sparse_matrix_storage_from_rows_columns_values(std::size_t nr, std::size_t nc, std::size_t nnz, const Index rows[], const Index cols[], const Value vals[], const IndexBase index_base = 0, bool is_sorted = true) { std::unique_ptr array_value(array::Array::create(nnz)); std::unique_ptr array_inner(array::Array::create(nnz)); std::unique_ptr array_outer(array::Array::create(nr+1)); auto* value = array_value->host_data(); auto* inner = array_inner->host_data(); auto* outer = array_outer->host_data(); std::fill(outer, outer + nr + 1, 0); Index base = index_base; if (not is_sorted) { std::vector sorted_index(nnz); std::iota(sorted_index.begin(), sorted_index.end(), 0); std::sort(sorted_index.begin(), sorted_index.end(), [&](auto i, auto j) { return rows[i] != rows[j] ? rows[i] < rows[j] : cols[i] != cols[j] ? cols[i] < cols[j] : i < j; }); for (size_t n = 0; n < nnz; ++n) { auto r = rows[sorted_index[n]] - base; auto c = cols[sorted_index[n]] - base; outer[r + 1]++; inner[n] = c; } for (size_t n = 0; n < nnz; ++n) { value[n] = vals[sorted_index[n]]; } } else { for (size_t n = 0; n < nnz; ++n) { auto r = rows[n] - base; auto c = cols[n] - base; outer[r + 1]++; inner[n] = c; } for (size_t n = 0; n < nnz; ++n) { value[n] = vals[n]; } } for (size_t r = 0; r < nr; ++r) { outer[r + 1] += outer[r]; } ATLAS_ASSERT(outer[0] == 0); ATLAS_ASSERT(outer[nr] == nnz); return SparseMatrixStorage::make(nr, nc, nnz, std::move(array_value), std::move(array_inner), std::move(array_outer), std::any()); } template SparseMatrixStorage make_sparse_matrix_storage_from_rows_columns_values(std::size_t nr, std::size_t nc, const std::vector& rows, const std::vector& cols, const std::vector& vals, const IndexBase index_base = 0, bool is_sorted = true) { ATLAS_TRACE("make_sparse_matrix_storage_from_rows_columns_values partition"); std::size_t nnz = vals.size(); ATLAS_ASSERT(rows.size() == nnz); ATLAS_ASSERT(cols.size() == nnz); return make_sparse_matrix_storage_from_rows_columns_values(nr, nc, nnz, rows.data(), cols.data(), vals.data(), index_base, is_sorted); } } // namespace atlas::linalg atlas-0.46.0/src/atlas/linalg/sparse/SparseMatrixMultiply.h0000664000175000017500000001227015160212070024056 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include "eckit/config/Configuration.h" #include "atlas/linalg/Indexing.h" #include "atlas/linalg/View.h" #include "atlas/linalg/sparse/Backend.h" #include "atlas/linalg/sparse/SparseMatrixView.h" #include "atlas/runtime/Exception.h" #include "atlas/util/Config.h" namespace atlas { namespace linalg { using Configuration = eckit::Configuration; template void sparse_matrix_multiply(const Matrix& matrix, const SourceView& src, TargetView& tgt); template void sparse_matrix_multiply(const Matrix& matrix, const SourceView& src, TargetView&& tgt); template void sparse_matrix_multiply(const Matrix& matrix, const SourceView& src, TargetView& tgt, const Configuration& config); template void sparse_matrix_multiply(const Matrix& matrix, const SourceView& src, TargetView& tgt, Indexing); template void sparse_matrix_multiply(const Matrix& matrix, const SourceView& src, TargetView& tgt, Indexing, const Configuration& config); template void sparse_matrix_multiply_add(const Matrix& matrix, const SourceView& src, TargetView& tgt); template void sparse_matrix_multiply_add(const Matrix& matrix, const SourceView& src, TargetView& tgt, const Configuration& config); template void sparse_matrix_multiply_add(const Matrix& matrix, const SourceView& src, TargetView& tgt, Indexing); template void sparse_matrix_multiply_add(const Matrix& matrix, const SourceView& src, TargetView& tgt, Indexing, const Configuration& config); class SparseMatrixMultiply { public: SparseMatrixMultiply() = default; SparseMatrixMultiply(const std::string& backend): backend_{backend} {} SparseMatrixMultiply(const sparse::Backend& backend): backend_(backend) {} template void operator()(const Matrix& matrix, const SourceView& src, TargetView& tgt) const { multiply(matrix, src, tgt); } template void operator()(const Matrix& matrix, const SourceView& src, TargetView& tgt, Indexing indexing) const { multiply(matrix, src, tgt, indexing); } template void multiply(const Matrix& matrix, const SourceView& src, TargetView& tgt) const { sparse_matrix_multiply(matrix, src, tgt, backend()); } template void multiply(const Matrix& matrix, const SourceView& src, TargetView& tgt, Indexing indexing) const { sparse_matrix_multiply(matrix, src, tgt, indexing, backend()); } template void multiply_add(const Matrix& matrix, const SourceView& src, TargetView& tgt) const { sparse_matrix_multiply_add(matrix, src, tgt, backend()); } template void multiply_add(const Matrix& matrix, const SourceView& src, TargetView& tgt, Indexing indexing) const { sparse_matrix_multiply_add(matrix, src, tgt, indexing, backend()); } const sparse::Backend& backend() const { return backend_; } private: sparse::Backend backend_; }; namespace sparse { // Template class which needs (full or partial) specialization for concrete template parameters template struct SparseMatrixMultiply { static void multiply(const SparseMatrixView&, const View&, View&, const Configuration&) { throw_NotImplemented("SparseMatrixMultiply needs a template specialization with the implementation", Here()); } static void multiply_add(const SparseMatrixView&, const View&, View&, const Configuration&) { throw_NotImplemented("SparseMatrixMultiply needs a template specialization with the implementation", Here()); } }; } // namespace sparse } // namespace linalg } // namespace atlas #include "SparseMatrixMultiply.tcc" #include "SparseMatrixMultiply_EckitLinalg.h" #include "SparseMatrixMultiply_OpenMP.h" #include "SparseMatrixMultiply_HicSparse.h" atlas-0.46.0/src/atlas/linalg/sparse/SparseMatrixMultiply_OpenMP.h0000664000175000017500000000733615160212070025303 0ustar alastairalastair/* * (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/linalg/sparse/SparseMatrixMultiply.h" namespace atlas { namespace linalg { namespace sparse { template struct SparseMatrixMultiply { static void multiply(const SparseMatrixView& W, const View& src, View& tgt, const Configuration&); static void multiply_add(const SparseMatrixView& W, const View& src, View& tgt, const Configuration&); }; template struct SparseMatrixMultiply { static void multiply(const SparseMatrixView& W, const View& src, View& tgt, const Configuration&); static void multiply_add(const SparseMatrixView& W, const View& src, View& tgt, const Configuration&); }; template struct SparseMatrixMultiply { static void multiply(const SparseMatrixView& W, const View& src, View& tgt, const Configuration&); static void multiply_add(const SparseMatrixView& W, const View& src, View& tgt, const Configuration&); }; template struct SparseMatrixMultiply { static void multiply(const SparseMatrixView& W, const View& src, View& tgt, const Configuration&); static void multiply_add(const SparseMatrixView& W, const View& src, View& tgt, const Configuration&); }; template struct SparseMatrixMultiply { static void multiply(const SparseMatrixView& W, const View& src, View& tgt, const Configuration&); static void multiply_add(const SparseMatrixView& W, const View& src, View& tgt, const Configuration&); }; template struct SparseMatrixMultiply { static void multiply(const SparseMatrixView& W, const View& src, View& tgt, const Configuration&); static void multiply_add(const SparseMatrixView& W, const View& src, View& tgt, const Configuration&); }; } // namespace sparse } // namespace linalg } // namespace atlas atlas-0.46.0/src/atlas/linalg/sparse/Backend.cc0000664000175000017500000000640215160212070021361 0ustar alastairalastair/* * (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 "Backend.h" #include #include "atlas/library/config.h" #if ATLAS_ECKIT_HAVE_ECKIT_585 #include "eckit/linalg/LinearAlgebraSparse.h" #else #include "eckit/linalg/LinearAlgebra.h" #endif #include "eckit/utils/Tokenizer.h" #include "atlas/library.h" #include "atlas/runtime/Exception.h" #include "atlas/util/Config.h" namespace atlas { namespace linalg { namespace sparse { namespace { struct backends { std::map map_; std::string current_backend_; static backends& instance() { static backends x; return x; } void set(const std::string& current_backend) { current_backend_ = current_backend; } sparse::Backend& get(const std::string& type) { if (map_.find(type) == map_.end()) { std::vector tokens; eckit::Tokenizer{'.'}(type, tokens); ATLAS_ASSERT(tokens.size() <= 2); if (tokens.size() == 1) { map_.emplace(type, util::Config("type", type)); } else { util::Config b; b.set("type", tokens[0]); b.set("backend", tokens[1]); map_.emplace(type, b); } } return map_[type]; } sparse::Backend& get() { return get(current_backend_); } private: backends() { auto configured = atlas::Library::instance().linalgSparseBackend(); current_backend_ = configured.empty() ? backend::openmp::type() : configured; } }; } // namespace void current_backend(const std::string& backend) { backends::instance().set(backend); } sparse::Backend& current_backend() { return backends::instance().get(); } sparse::Backend& default_backend(const std::string& backend) { return backends::instance().get(backend); } Backend::Backend(const std::string& type): util::Config() { if (not type.empty()) { set(default_backend(type)); } else { set(current_backend()); } } Backend::Backend(const eckit::Configuration& other): util::Config(other) { ATLAS_ASSERT(has("type")); } bool Backend::available() const { std::string t = type(); if (t == backend::openmp::type()) { return true; } if (t == backend::hicsparse::type()) { #if ATLAS_HAVE_GPU return true; #else return false; #endif } if (t == backend::eckit_linalg::type()) { if (has("backend")) { #if ATLAS_ECKIT_HAVE_ECKIT_585 return eckit::linalg::LinearAlgebraSparse::hasBackend(getString("backend")); #else return eckit::linalg::LinearAlgebra::hasBackend(getString("backend")); #endif } return true; } #if ATLAS_ECKIT_HAVE_ECKIT_585 return eckit::linalg::LinearAlgebraSparse::hasBackend(t); #else return eckit::linalg::LinearAlgebra::hasBackend(t); #endif } } // namespace sparse } // namespace linalg } // namespace atlas atlas-0.46.0/src/atlas/linalg/sparse/SparseMatrixStorage.h0000664000175000017500000002552115160212070023646 0ustar alastairalastair/* * (C) Copyright 2024- 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 #include #include "eckit/linalg/SparseMatrix.h" #include "atlas/array.h" #include "atlas/runtime/Exception.h" #include "atlas/linalg/sparse/SparseMatrixView.h" namespace atlas { namespace linalg { //---------------------------------------------------------------------------------------------------------------------- /// @brief SparseMatrixStorage /// Storage class based on atlas::array::Array with GPU offload capability /// This class contains only the data and no operators, iterators, or special /// sparse matrix construction from triplets etc. /// /// The construction is handled via SparseMatrixConvertor class. /// Examples: /// /// To construct it, taking ownership of a constructed Eigen::SparseMatrix /// /// SparseMatrixStorage s = make_sparse_matrix_storage(std::move(eigen_matrix)); /// /// To construct it, taking a copy of a constructed Eigen::SparseMatrix /// /// SparseMatrixStorage s = make_sparse_matrix_storage(eigen_matrix); /// /// To construct it, taking ownership of a constructed eckit::linalg::SparseMatrix, avoiding copies if data types match /// /// SparseMatrixStorage s = make_sparse_matrix_storage(std::move(eckit_matrix)); /// /// /// To construct it, taking a copy of a constructed eckit::linalg::SparseMatrix (no std::move) /// /// SparseMatrixStorage s = make_sparse_matrix_storage(eckit_matrix); /// /// /// It is also possible to initialise empty and move into it at later stage: /// /// SparseMatrixStorage s; /// s = make_sparse_matrix_storage(eckit_matrix); /// /// /// To construct it, taking a single precision copy of a constructed eckit::linalg::SparseMatrix /// /// s = make_sparse_matrix_storage(eckit_matrix); /// class SparseMatrixStorage { public: // Virtual destructor virtual ~SparseMatrixStorage() = default; /// Default constructor SparseMatrixStorage() = default; /// Move constructor, takes ownership! SparseMatrixStorage(SparseMatrixStorage&& other); /// Copy constructor, makes copy! SparseMatrixStorage(const SparseMatrixStorage& other); /// Copy from a SparseMatrixView template SparseMatrixStorage(const SparseMatrixView& host_view); /// Move assign from other SparseMatrixStorage, takes ownership! SparseMatrixStorage& operator=(SparseMatrixStorage&& other); /// Copy assign from other SparseMatrixStorage, takes ownership! SparseMatrixStorage& operator=(const SparseMatrixStorage& other); // Remove all storage, make empty void clear(); /// Empty if rows and cols are zero. bool empty() const { return rows_ == 0 && cols_ == 0; } /// Footprint in bytes in host memory space std::size_t footprint() const { return value_->footprint() + outer_->footprint() + inner_->footprint(); } const atlas::array::Array& value() const { return *value_; } const atlas::array::Array& outer() const { return *outer_; } const atlas::array::Array& inner() const { return *inner_; } std::size_t rows() const { return rows_;} std::size_t cols() const { return cols_;} std::size_t nnz() const { return nnz_;} void updateDevice() const; void updateHost() const; bool hostNeedsUpdate() const; bool deviceNeedsUpdate() const; void setHostNeedsUpdate(bool) const; void setDeviceNeedsUpdate(bool) const; bool deviceAllocated() const; void allocateDevice() const; void deallocateDevice() const; void syncHost() const; void syncDevice() const; static SparseMatrixStorage make( std::size_t rows, std::size_t cols, std::size_t nnz, std::unique_ptr&& value, std::unique_ptr&& inner, std::unique_ptr&& outer, std::any&& storage) { SparseMatrixStorage S; S.rows_ = rows; S.cols_ = cols; S.nnz_ = nnz; S.outer_ = std::move(outer); S.inner_ = std::move(inner); S.value_ = std::move(value); S.storage_ = std::move(storage); return S; } void swap(SparseMatrixStorage& other) { std::swap(other.rows_, rows_); std::swap(other.cols_, cols_); std::swap(other.nnz_, nnz_); std::swap(other.value_, value_); std::swap(other.inner_, inner_); std::swap(other.outer_, outer_); std::swap(other.storage_, storage_); } bool contains(DataType value_type, DataType index_type) { if (empty()) { return false; } return value_->datatype() == value_type && outer_->datatype() == index_type; } template bool contains() { return contains(make_datatype(), make_datatype()); } protected: std::size_t nnz_{0}; std::size_t rows_{0}; std::size_t cols_{0}; std::unique_ptr outer_; std::unique_ptr inner_; std::unique_ptr value_; std::any storage_; // This storage is usually empty. // It is used to allow to move alternative sparse matrix formats into it, // and then wrap this data using the atlas Arrays private: template void host_copy(const ValueT* input_data, atlas::array::Array& output) { auto size = output.size(); ValueT* output_data = output.host_data(); std::copy( input_data, input_data + size, output_data ); } }; //---------------------------------------------------------------------------------------------------------------------- template SparseMatrixStorage::SparseMatrixStorage(const SparseMatrixView& host_view) { nnz_ = host_view.nnz(); rows_ = host_view.rows(); cols_ = host_view.cols(); outer_.reset(atlas::array::Array::create(host_view.outer_size())); inner_.reset(atlas::array::Array::create(host_view.inner_size())); value_.reset(atlas::array::Array::create(host_view.value_size())); host_copy(host_view.outer(),*outer_); host_copy(host_view.inner(),*inner_); host_copy(host_view.value(),*value_); } //---------------------------------------------------------------------------------------------------------------------- namespace detail { void host_copy(const array::Array& input, array::Array& output); } template SparseMatrixStorage make_sparse_matrix_storage(const SparseMatrixStorage& other) { auto rows = other.rows(); auto cols = other.cols(); auto nnz = other.nnz(); std::unique_ptr value(array::Array::create(nnz)); std::unique_ptr inner(array::Array::create(nnz)); std::unique_ptr outer(array::Array::create(rows+1)); detail::host_copy(other.value(), *value); detail::host_copy(other.inner(), *inner); detail::host_copy(other.outer(), *outer); return SparseMatrixStorage::make(rows,cols,nnz,std::move(value), std::move(inner), std::move(outer), std::any()); } template SparseMatrixStorage make_sparse_matrix_storage(SparseMatrixStorage&& other) { SparseMatrixStorage S; if (other.contains()) { S = std::move(other); } else { auto rows = other.rows(); auto cols = other.cols(); auto nnz = other.nnz(); std::unique_ptr value(array::Array::create(nnz)); std::unique_ptr inner(array::Array::create(nnz)); std::unique_ptr outer(array::Array::create(rows+1)); detail::host_copy(other.value(), *value); detail::host_copy(other.inner(), *inner); detail::host_copy(other.outer(), *outer); S = SparseMatrixStorage::make(rows,cols,nnz,std::move(value), std::move(inner), std::move(outer), std::any()); } return S; } //---------------------------------------------------------------------------------------------------------------------- template inline SparseMatrixView make_host_view(const SparseMatrixStorage& m) { if(m.rows() == 0 && m.cols() == 0) { return SparseMatrixView(); } if( m.value().datatype().kind() != DataType::kind() || m.outer().datatype().kind() != DataType::kind() ) { ATLAS_THROW_EXCEPTION("Cannot make_host_view<" + DataType::str() + "," << DataType::str() + ">(const SparseMatrixStorage&) from SparseMatrixStorage containing values of type <" + m.value().datatype().str() + "> and indices of type <" + m.outer().datatype().str() +">" ); } return SparseMatrixView { m.rows(), m.cols(), m.nnz(), m.value().host_data(), m.inner().host_data(), m.outer().host_data() }; } //---------------------------------------------------------------------------------------------------------------------- template inline SparseMatrixView make_device_view(const SparseMatrixStorage& m) { if(m.rows() == 0 && m.cols() == 0) { return SparseMatrixView(); } if( m.value().datatype().kind() != DataType::kind() || m.outer().datatype().kind() != DataType::kind() ) { ATLAS_THROW_EXCEPTION("Cannot make_device_view<" + DataType::str() + "," << DataType::str() + ">(const SparseMatrixStorage&) from SparseMatrixStorage containing values of type <" + m.value().datatype().str() + "> and indices of type <" + m.outer().datatype().str() +">" ); } if( ! m.deviceAllocated() ) { ATLAS_THROW_EXCEPTION("Cannot make_device_view(const SparseMatrixStorage&) as the device data is not allocated"); } return SparseMatrixView{ m.rows(), m.cols(), m.nnz(), m.value().device_data(), m.inner().device_data(), m.outer().device_data() }; } //---------------------------------------------------------------------------------------------------------------------- } // namespace linalg } // namespace atlas atlas-0.46.0/src/atlas/linalg/sparse/SparseMatrixMultiply.tcc0000664000175000017500000002726415160212070024411 0ustar alastairalastair/* * (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 "SparseMatrixMultiply.h" #include "eckit/linalg/SparseMatrix.h" #include "atlas/linalg/Indexing.h" #include "atlas/linalg/Introspection.h" #include "atlas/linalg/View.h" #include "atlas/linalg/sparse/Backend.h" #include "atlas/linalg/sparse/SparseMatrixView.h" #include "atlas/runtime/Exception.h" #if ATLAS_ECKIT_HAVE_ECKIT_585 #include "eckit/linalg/LinearAlgebraSparse.h" #else #include "eckit/linalg/LinearAlgebra.h" #endif namespace atlas { namespace linalg { namespace sparse { namespace { inline SparseMatrixView make_view_from_eckit_sparse_matrix(const eckit::linalg::SparseMatrix& m) { return SparseMatrixView{ m.rows(), m.cols(), m.nonZeros(), m.data(), m.inner(), m.outer() }; } template struct SparseMatrixMultiplyHelper { template static void multiply( const Matrix& W, const SourceView& src, TargetView& tgt, const eckit::Configuration& config ) { using MatrixValue = typename std::remove_const::type; using SourceValue = const typename std::remove_const::type; using TargetValue = typename std::remove_const::type; constexpr int src_rank = introspection::rank(); constexpr int tgt_rank = introspection::rank(); static_assert( src_rank == tgt_rank, "src and tgt need same rank" ); SparseMatrixMultiply::multiply( W, src, tgt, config ); } template static void multiply_add( const Matrix& W, const SourceView& src, TargetView& tgt, const eckit::Configuration& config ) { using MatrixValue = typename std::remove_const::type; using SourceValue = const typename std::remove_const::type; using TargetValue = typename std::remove_const::type; constexpr int src_rank = introspection::rank(); constexpr int tgt_rank = introspection::rank(); static_assert( src_rank == tgt_rank, "src and tgt need same rank" ); SparseMatrixMultiply::multiply_add( W, src, tgt, config ); } template static void multiply( const eckit::linalg::SparseMatrix& W, const SourceView& src, TargetView& tgt, const eckit::Configuration& config ) { using MatrixValue = double; // using MatrixIndex = int; using SourceValue = const typename std::remove_const::type; using TargetValue = typename std::remove_const::type; constexpr int src_rank = introspection::rank(); constexpr int tgt_rank = introspection::rank(); static_assert( src_rank == tgt_rank, "src and tgt need same rank" ); SparseMatrixMultiply::multiply( make_view_from_eckit_sparse_matrix(W), src, tgt, config ); } template static void multiply_add( const eckit::linalg::SparseMatrix& W, const SourceView& src, TargetView& tgt, const eckit::Configuration& config ) { using MatrixValue = double; // using MatrixIndex = int; using SourceValue = const typename std::remove_const::type; using TargetValue = typename std::remove_const::type; constexpr int src_rank = introspection::rank(); constexpr int tgt_rank = introspection::rank(); static_assert( src_rank == tgt_rank, "src and tgt need same rank" ); SparseMatrixMultiply::multiply_add( make_view_from_eckit_sparse_matrix(W), src, tgt, config ); } }; template void dispatch_sparse_matrix_multiply( const Matrix& matrix, const SourceView& src, TargetView& tgt, Indexing indexing, const eckit::Configuration& config ) { auto src_v = make_view( src ); auto tgt_v = make_view( tgt ); if ( introspection::layout_right( src ) || introspection::layout_right( tgt ) ) { ATLAS_ASSERT( introspection::layout_right( src ) && introspection::layout_right( tgt ) ); // Override layout with known layout given by introspection SparseMatrixMultiplyHelper::multiply( matrix, src_v, tgt_v, config ); } else { if( indexing == Indexing::layout_left ) { SparseMatrixMultiplyHelper::multiply( matrix, src_v, tgt_v, config ); } else if( indexing == Indexing::layout_right ) { SparseMatrixMultiplyHelper::multiply( matrix, src_v, tgt_v, config ); } else { throw_NotImplemented( "indexing not implemented", Here() ); } } } template void dispatch_sparse_matrix_multiply_add( const Matrix& matrix, const SourceView& src, TargetView& tgt, Indexing indexing, const eckit::Configuration& config ) { auto src_v = make_view( src ); auto tgt_v = make_view( tgt ); if ( introspection::layout_right( src ) || introspection::layout_right( tgt ) ) { ATLAS_ASSERT( introspection::layout_right( src ) && introspection::layout_right( tgt ) ); // Override layout with known layout given by introspection SparseMatrixMultiplyHelper::multiply_add( matrix, src_v, tgt_v, config ); } else { if( indexing == Indexing::layout_left ) { SparseMatrixMultiplyHelper::multiply_add( matrix, src_v, tgt_v, config ); } else if( indexing == Indexing::layout_right ) { SparseMatrixMultiplyHelper::multiply_add( matrix, src_v, tgt_v, config ); } else { throw_NotImplemented( "indexing not implemented", Here() ); } } } } } template void sparse_matrix_multiply( const Matrix& matrix, const SourceView& src, TargetView& tgt, Indexing indexing, const eckit::Configuration& config ) { std::string type = config.getString( "type", sparse::current_backend() ); if ( type == sparse::backend::openmp::type() ) { sparse::dispatch_sparse_matrix_multiply( matrix, src, tgt, indexing, config ); } else if ( type == sparse::backend::eckit_linalg::type() ) { sparse::dispatch_sparse_matrix_multiply( matrix, src, tgt, indexing, config ); } #if ATLAS_ECKIT_HAVE_ECKIT_585 else if( eckit::linalg::LinearAlgebraSparse::hasBackend(type) ) { #else else if( eckit::linalg::LinearAlgebra::hasBackend(type) ) { #endif sparse::dispatch_sparse_matrix_multiply( matrix, src, tgt, indexing, util::Config("backend",type) ); } else if ( type == sparse::backend::hicsparse::type() ) { sparse::dispatch_sparse_matrix_multiply( matrix, src, tgt, indexing, config ); } else { throw_NotImplemented( "sparse_matrix_multiply cannot be performed with unsupported backend [" + type + "]", Here() ); } } template void sparse_matrix_multiply( const Matrix& matrix, const SourceView& src, TargetView& tgt, const eckit::Configuration& config ) { sparse_matrix_multiply( matrix, src, tgt, Indexing::layout_left, config ); } template void sparse_matrix_multiply( const Matrix& matrix, const SourceView& src, TargetView& tgt, Indexing indexing ) { sparse_matrix_multiply( matrix, src, tgt, indexing, sparse::Backend() ); } template void sparse_matrix_multiply( const Matrix& matrix, const SourceView& src, TargetView& tgt ) { sparse_matrix_multiply( matrix, src, tgt, Indexing::layout_left ); } template void sparse_matrix_multiply(const Matrix& matrix, const SourceView& src, TargetView&& tgt) { auto& tgt_lvalue = tgt; sparse_matrix_multiply( matrix, src, tgt_lvalue, Indexing::layout_left ); } template void sparse_matrix_multiply_add( const Matrix& matrix, const SourceView& src, TargetView& tgt, Indexing indexing, const eckit::Configuration& config ) { std::string type = config.getString( "type", sparse::current_backend() ); if ( type == sparse::backend::openmp::type() ) { sparse::dispatch_sparse_matrix_multiply_add( matrix, src, tgt, indexing, config ); } else if ( type == sparse::backend::eckit_linalg::type() ) { sparse::dispatch_sparse_matrix_multiply_add( matrix, src, tgt, indexing, config ); } #if ATLAS_ECKIT_HAVE_ECKIT_585 else if( eckit::linalg::LinearAlgebraSparse::hasBackend(type) ) { #else else if( eckit::linalg::LinearAlgebra::hasBackend(type) ) { #endif sparse::dispatch_sparse_matrix_multiply_add( matrix, src, tgt, indexing, util::Config("backend",type) ); } else if ( type == sparse::backend::hicsparse::type() ) { sparse::dispatch_sparse_matrix_multiply_add( matrix, src, tgt, indexing, config ); } else { throw_NotImplemented( "sparse_matrix_multiply_add cannot be performed with unsupported backend [" + type + "]", Here() ); } } template void sparse_matrix_multiply_add( const Matrix& matrix, const SourceView& src, TargetView& tgt, const eckit::Configuration& config ) { sparse_matrix_multiply_add( matrix, src, tgt, Indexing::layout_left, config ); } template void sparse_matrix_multiply_add( const Matrix& matrix, const SourceView& src, TargetView& tgt, Indexing indexing ) { sparse_matrix_multiply_add( matrix, src, tgt, indexing, sparse::Backend() ); } template void sparse_matrix_multiply_add( const Matrix& matrix, const SourceView& src, TargetView& tgt ) { sparse_matrix_multiply_add( matrix, src, tgt, Indexing::layout_left ); } } // namespace linalg } // namespace atlas #undef ATLAS_ENABLE_IF atlas-0.46.0/src/atlas/linalg/sparse/MakeEckitSparseMatrix.h0000664000175000017500000000736115160212070024101 0ustar alastairalastair/* * (C) Copyright 2024- ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include "eckit/linalg/SparseMatrix.h" #include "atlas/linalg/sparse/SparseMatrixView.h" #include "atlas/linalg/sparse/SparseMatrixStorage.h" namespace atlas { namespace linalg { //---------------------------------------------------------------------------------------------------------------------- class EckitSparseMatrixNonOwningAllocator : public eckit::linalg::SparseMatrix::Allocator { public: EckitSparseMatrixNonOwningAllocator(const SparseMatrixView& view) : view_{view} {} eckit::linalg::SparseMatrix::Layout allocate(eckit::linalg::SparseMatrix::Shape& shape) override { eckit::linalg::SparseMatrix::Layout p; using Index = eckit::linalg::Index; using OuterIndex = std::remove_pointer_t; using InnerIndex = std::remove_pointer_t; static_assert(sizeof(OuterIndex) == sizeof(Index)); static_assert(sizeof(InnerIndex) == sizeof(Index)); p.data_ = const_cast(view_.value()); p.outer_ = reinterpret_cast(const_cast(view_.outer())); p.inner_ = reinterpret_cast(const_cast(view_.inner())); shape.size_ = view_.nnz(); shape.rows_ = view_.rows(); shape.cols_ = view_.cols(); return p; } void deallocate(eckit::linalg::SparseMatrix::Layout p, eckit::linalg::SparseMatrix::Shape) override { /* do nothing */ } bool inSharedMemory() const override { return false; } void print(std::ostream& out) const override { out << "atlas::linalg::EckitSparseMatrixNonOwningAllocator[rows="< view_; }; //---------------------------------------------------------------------------------------------------------------------- // Create new eckit sparsematrix from a SparseMatrixView. Warning, this matrix does not own the data! inline eckit::linalg::SparseMatrix make_non_owning_eckit_sparse_matrix(const SparseMatrixView& view) { eckit::linalg::SparseMatrix m(new EckitSparseMatrixNonOwningAllocator(view)); return m; } // Create new eckit sparsematrix from a SparseMatrixStorage. Warning, this matrix does not own the data! inline eckit::linalg::SparseMatrix make_non_owning_eckit_sparse_matrix(const SparseMatrixStorage& m) { return make_non_owning_eckit_sparse_matrix( make_host_view(m) ); } inline eckit::linalg::SparseMatrix make_eckit_sparse_matrix(const SparseMatrixView& view) { eckit::linalg::SparseMatrix m(new EckitSparseMatrixNonOwningAllocator(view)); return eckit::linalg::SparseMatrix{m}; // makes copy } // Create new eckit sparsematrix from a SparseMatrixStorage. Warning, this creates a copy! inline eckit::linalg::SparseMatrix make_eckit_sparse_matrix(const SparseMatrixStorage& m) { return make_eckit_sparse_matrix( make_host_view(m) ); } //---------------------------------------------------------------------------------------------------------------------- } // namespace linalg } // namespace atlas atlas-0.46.0/src/atlas/linalg/sparse/SparseMatrixMultiply_EckitLinalg.h0000664000175000017500000000365215160212070026330 0ustar alastairalastair/* * (C) Copyright 2024- 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/linalg/sparse/SparseMatrixMultiply.h" namespace atlas { namespace linalg { namespace sparse { template <> struct SparseMatrixMultiply { static void multiply(const SparseMatrixView&, const View& src, View& tgt, const Configuration&); static void multiply_add(const SparseMatrixView&, const View& src, View& tgt, const Configuration&); }; template <> struct SparseMatrixMultiply { static void multiply(const SparseMatrixView&, const View& src, View& tgt, const Configuration&); static void multiply_add(const SparseMatrixView&, const View& src, View& tgt, const Configuration&); }; template <> struct SparseMatrixMultiply { static void multiply(const SparseMatrixView&, const View& src, View& tgt, const Configuration&); static void multiply_add(const SparseMatrixView&, const View& src, View& tgt, const Configuration&); }; } // namespace sparse } // namespace linalg } // namespace atlas atlas-0.46.0/src/atlas/linalg/sparse/MakeSparseMatrixStorageEigen.h0000664000175000017500000001473215160212070025416 0ustar alastairalastair/* * (C) Copyright 2024- 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 #include #include "atlas/library/defines.h" #if ATLAS_HAVE_EIGEN ATLAS_SUPPRESS_WARNINGS_PUSH ATLAS_SUPPRESS_WARNINGS_INTEGER_SIGN_CHANGE ATLAS_SUPPRESS_WARNINGS_CODE_IS_UNREACHABLE ATLAS_SUPPRESS_WARNINGS_UNUSED_BUT_SET_VARIABLE #include ATLAS_SUPPRESS_WARNINGS_POP #endif #include "atlas/array.h" #include "atlas/linalg/sparse/SparseMatrixStorage.h" namespace atlas { namespace linalg { //---------------------------------------------------------------------------------------------------------------------- #if ATLAS_HAVE_EIGEN template void host_copy_eigen (const InputT* input_data, atlas::array::Array& output) { auto size = output.size(); OutputT* output_data = output.host_data(); std::copy( input_data, input_data + size, output_data ); } template SparseMatrixStorage make_sparse_matrix_storage(Eigen::SparseMatrix&& m) { std::size_t rows = m.rows(); std::size_t cols = m.cols(); std::size_t nnz = m.nonZeros(); array::Array* outer; array::Array* inner; array::Array* value; { array::label label{"sparse_matrix.outer"}; outer = atlas::array::Array::wrap(const_cast(m.outerIndexPtr()), atlas::array::make_shape(rows+1)); } { array::label label{"sparse_matrix.inner"}; inner = atlas::array::Array::wrap(const_cast(m.innerIndexPtr()), atlas::array::make_shape(nnz)); } { array::label label{"sparse_matrix.value"}; value = atlas::array::Array::wrap(const_cast(m.valuePtr()), atlas::array::make_shape(nnz)); } using EigenMatrix = Eigen::SparseMatrix; auto m_ptr = std::make_shared(); m_ptr->swap(m); auto storage = std::make_any>(std::move(m_ptr)); return SparseMatrixStorage::make( rows, cols, nnz, std::unique_ptr(value), std::unique_ptr(inner), std::unique_ptr(outer), std::move(storage) ); } template< typename value_type, typename index_type = eckit::linalg::Index, typename Value, typename Index, typename = std::enable_if_t < !std::is_same_v>> SparseMatrixStorage make_sparse_matrix_storage(Eigen::SparseMatrix&& m) { if (std::is_same_v && std::is_same_v) { return make_sparse_matrix_storage(std::move(m)); } std::size_t rows = m.rows(); std::size_t cols = m.cols(); std::size_t nnz = m.nonZeros(); array::Array* outer; array::Array* inner; array::Array* value; { array::label label{"sparse_matrix.outer"}; outer = atlas::array::Array::create(rows+1); } { array::label label{"sparse_matrix.inner"}; inner = atlas::array::Array::create(nnz); } { array::label label{"sparse_matrix.value"}; value = atlas::array::Array::create(nnz); } host_copy_eigen(m.outerIndexPtr(), *outer); host_copy_eigen(m.innerIndexPtr(), *inner); host_copy_eigen(m.valuePtr(), *value); return SparseMatrixStorage::make( rows, cols, nnz, std::unique_ptr(value), std::unique_ptr(inner), std::unique_ptr(outer), std::any() ); } template SparseMatrixStorage make_sparse_matrix_storage(const Eigen::SparseMatrix& m) { std::size_t rows = m.rows(); std::size_t cols = m.cols(); std::size_t nnz = m.nonZeros(); array::Array* outer; array::Array* inner; array::Array* value; { array::label label{"sparse_matrix.outer"}; outer = atlas::array::Array::create(rows+1); } { array::label label{"sparse_matrix.inner"}; inner = atlas::array::Array::create(nnz); } { array::label label{"sparse_matrix.value"}; value = atlas::array::Array::create(nnz); } host_copy_eigen(m.outerIndexPtr(), *outer); host_copy_eigen(m.innerIndexPtr(), *inner); host_copy_eigen(m.valuePtr(), *value); return SparseMatrixStorage::make( rows, cols, nnz, std::unique_ptr(value), std::unique_ptr(inner), std::unique_ptr(outer), std::any() ); } template< typename value_type, typename index_type = eckit::linalg::Index, typename Value, typename Index, typename = std::enable_if_t < !std::is_same_v>> SparseMatrixStorage make_sparse_matrix_storage(const Eigen::SparseMatrix& m) { std::size_t rows = m.rows(); std::size_t cols = m.cols(); std::size_t nnz = m.nonZeros(); array::Array* outer; array::Array* inner; array::Array* value; { array::label label{"sparse_matrix.outer"}; outer = atlas::array::Array::create(rows+1); } { array::label label{"sparse_matrix.inner"}; inner = atlas::array::Array::create(nnz); } { array::label label{"sparse_matrix.value"}; value = atlas::array::Array::create(nnz); } host_copy_eigen(m.outerIndexPtr(), *outer); host_copy_eigen(m.innerIndexPtr(), *inner); host_copy_eigen(m.valuePtr(), *value); return SparseMatrixStorage::make( rows, cols, nnz, std::unique_ptr(value), std::unique_ptr(inner), std::unique_ptr(outer), std::any() ); } #endif //---------------------------------------------------------------------------------------------------------------------- } // namespace linalg } // namespace atlasatlas-0.46.0/src/atlas/linalg/sparse/SparseMatrixMultiply_EckitLinalg.cc0000664000175000017500000001242215160212070026461 0ustar alastairalastair/* * (C) Copyright 2024- 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 "SparseMatrixMultiply_EckitLinalg.h" #include "atlas/array.h" #include "atlas/library/config.h" #if ATLAS_ECKIT_HAVE_ECKIT_585 #include "eckit/linalg/LinearAlgebraSparse.h" #else #include "eckit/linalg/LinearAlgebra.h" #endif #include "eckit/linalg/Matrix.h" #include "eckit/linalg/Vector.h" #include "atlas/runtime/Exception.h" #include "atlas/linalg/sparse/MakeEckitSparseMatrix.h" namespace atlas { namespace linalg { namespace sparse { namespace { #if ATLAS_ECKIT_HAVE_ECKIT_585 const eckit::linalg::LinearAlgebraSparse& eckit_linalg_backend(const Configuration& config) { std::string backend = "default"; config.get("backend", backend); if (backend == "default") { return eckit::linalg::LinearAlgebraSparse::backend(); } ATLAS_ASSERT(eckit::linalg::LinearAlgebraSparse::hasBackend(backend)); return eckit::linalg::LinearAlgebraSparse::getBackend(backend); } #else const eckit::linalg::LinearAlgebra& eckit_linalg_backend(const Configuration& config) { std::string backend = "default"; config.get("backend", backend); if (backend == "default") { return eckit::linalg::LinearAlgebra::backend(); } ATLAS_ASSERT(eckit::linalg::LinearAlgebra::hasBackend(backend)); return eckit::linalg::LinearAlgebra::getBackend(backend); } #endif template auto linalg_make_view(atlas::array::ArrayT& array) { auto v_array = array::make_view(array); return atlas::linalg::make_view(v_array); } } // namespace void SparseMatrixMultiply::multiply( const SparseMatrixView& W, const View& src, View& tgt, const Configuration& config) { ATLAS_ASSERT(src.contiguous()); ATLAS_ASSERT(tgt.contiguous()); eckit::linalg::Vector v_src(src.data(), src.size()); eckit::linalg::Vector v_tgt(tgt.data(), tgt.size()); eckit::linalg::SparseMatrix m_W = make_non_owning_eckit_sparse_matrix(W); eckit_linalg_backend(config).spmv(m_W, v_src, v_tgt); } void SparseMatrixMultiply::multiply( const SparseMatrixView& W, const View& src, View& tgt, const Configuration& config) { ATLAS_ASSERT(src.contiguous()); ATLAS_ASSERT(tgt.contiguous()); ATLAS_ASSERT(src.shape(1) >= W.cols()); ATLAS_ASSERT(tgt.shape(1) >= W.rows()); eckit::linalg::Matrix m_src(src.data(), src.shape(1), src.shape(0)); eckit::linalg::Matrix m_tgt(tgt.data(), tgt.shape(1), tgt.shape(0)); eckit::linalg::SparseMatrix m_W = make_non_owning_eckit_sparse_matrix(W); eckit_linalg_backend(config).spmm(m_W, m_src, m_tgt); } void SparseMatrixMultiply::multiply( const SparseMatrixView& W, const View& src, View& tgt, const Configuration& config) { SparseMatrixMultiply::multiply(W, src, tgt, config); } void SparseMatrixMultiply::multiply_add( const SparseMatrixView& W, const View& src, View& tgt, const Configuration& config) { array::ArrayT tmp(src.shape(0)); auto v_tmp = linalg_make_view(tmp); v_tmp.assign(0.); SparseMatrixMultiply::multiply(W, src, v_tmp, config); for (idx_t t = 0; t < tmp.shape(0); ++t) { tgt(t) += v_tmp(t); } } void SparseMatrixMultiply::multiply_add( const SparseMatrixView& W, const View& src, View& tgt, const Configuration& config) { array::ArrayT tmp(src.shape(0), src.shape(1)); auto v_tmp = linalg_make_view(tmp); v_tmp.assign(0.); SparseMatrixMultiply::multiply(W, src, v_tmp, config); for (idx_t t = 0; t < tmp.shape(0); ++t) { for (idx_t k = 0; k < tmp.shape(1); ++k) { tgt(t, k) += v_tmp(t, k); } } } void SparseMatrixMultiply::multiply_add( const SparseMatrixView& W, const View& src, View& tgt, const Configuration& config) { SparseMatrixMultiply::multiply_add(W, src, tgt, config); } } // namespace sparse } // namespace linalg } // namespace atlas atlas-0.46.0/src/atlas/linalg/sparse/SparseMatrixMultiply_HicSparse.h0000664000175000017500000000511715160212070026021 0ustar alastairalastair/* * (C) Copyright 2024 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/linalg/sparse/SparseMatrixMultiply.h" namespace atlas { namespace linalg { namespace sparse { template struct SparseMatrixMultiply { static void multiply(const SparseMatrixView& W, const View& src, View& tgt, const Configuration&); static void multiply_add(const SparseMatrixView& W, const View& src, View& tgt, const Configuration&); }; template struct SparseMatrixMultiply { static void multiply(const SparseMatrixView& W, const View& src, View& tgt, const Configuration&); static void multiply_add(const SparseMatrixView& W, const View& src, View& tgt, const Configuration&); }; template struct SparseMatrixMultiply { static void multiply(const SparseMatrixView& W, const View& src, View& tgt, const Configuration&); static void multiply_add(const SparseMatrixView& W, const View& src, View& tgt, const Configuration&); }; template struct SparseMatrixMultiply { static void multiply(const SparseMatrixView& W, const View& src, View& tgt, const Configuration&); static void multiply_add(const SparseMatrixView& W, const View& src, View& tgt, const Configuration&); }; } // namespace sparse } // namespace linalg } // namespace atlasatlas-0.46.0/src/atlas/linalg/sparse/SparseMatrixStorage.cc0000664000175000017500000001546115160212070024006 0ustar alastairalastair/* * (C) Copyright 2024- 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 "SparseMatrixStorage.h" namespace atlas::linalg { SparseMatrixStorage::SparseMatrixStorage(SparseMatrixStorage&& other) { nnz_ = other.nnz_; rows_ = other.rows_; cols_ = other.cols_; outer_ = std::move(other.outer_); inner_ = std::move(other.inner_); value_ = std::move(other.value_); storage_ = std::move(other.storage_); // Invalidate other, just in case it is being used (IT SHOULDNT) other.nnz_ = 0; other.rows_ = 0; other.cols_ = 0; } SparseMatrixStorage::SparseMatrixStorage(const SparseMatrixStorage& other) { nnz_ = other.nnz_; rows_ = other.rows_; cols_ = other.cols_; { array::label label{"sparse_matrix.outer"}; outer_.reset(atlas::array::Array::create(other.outer_->datatype(), atlas::array::make_shape(other.outer_->size()))); } { array::label label{"sparse_matrix.inner"}; inner_.reset(atlas::array::Array::create(other.inner_->datatype(), atlas::array::make_shape(other.inner_->size()))); } { array::label label{"sparse_matrix.value"}; value_.reset(atlas::array::Array::create(other.value_->datatype(), atlas::array::make_shape(other.value_->size()))); } outer_->copy(*other.outer_); inner_->copy(*other.inner_); value_->copy(*other.value_); } SparseMatrixStorage& SparseMatrixStorage::operator=(SparseMatrixStorage&& other) { nnz_ = other.nnz_; rows_ = other.rows_; cols_ = other.cols_; outer_ = std::move(other.outer_); inner_ = std::move(other.inner_); value_ = std::move(other.value_); storage_ = std::move(other.storage_); // Invalidate other, just in case it is being used (IT SHOULDNT) other.nnz_ = 0; other.rows_ = 0; other.cols_ = 0; return *this; } SparseMatrixStorage& SparseMatrixStorage::operator=(const SparseMatrixStorage& other) { nnz_ = other.nnz_; rows_ = other.rows_; cols_ = other.cols_; { array::label label{"sparse_matrix.outer"}; outer_.reset(atlas::array::Array::create(other.outer_->datatype(), atlas::array::make_shape(other.outer_->size()))); } { array::label label{"sparse_matrix.inner"}; inner_.reset(atlas::array::Array::create(other.inner_->datatype(), atlas::array::make_shape(other.inner_->size()))); } { array::label label{"sparse_matrix.value"}; value_.reset(atlas::array::Array::create(other.value_->datatype(), atlas::array::make_shape(other.value_->size()))); } outer_->copy(*other.outer_); inner_->copy(*other.inner_); value_->copy(*other.value_); return *this; } void SparseMatrixStorage::clear() { nnz_ = 0; rows_ = 0; cols_ = 0; storage_.reset(); outer_.reset(); inner_.reset(); value_.reset(); } void SparseMatrixStorage::updateDevice() const { outer_->updateDevice(); inner_->updateDevice(); value_->updateDevice(); } void SparseMatrixStorage::updateHost() const { outer_->updateHost(); inner_->updateHost(); value_->updateHost(); } void SparseMatrixStorage::syncDevice() const { outer_->syncDevice(); inner_->syncDevice(); value_->syncDevice(); } void SparseMatrixStorage::syncHost() const { outer_->syncHost(); inner_->syncHost(); value_->syncHost(); } bool SparseMatrixStorage::hostNeedsUpdate() const { return outer_->hostNeedsUpdate() || inner_->hostNeedsUpdate() || value_->hostNeedsUpdate(); } bool SparseMatrixStorage::deviceNeedsUpdate() const { return outer_->deviceNeedsUpdate() || inner_->deviceNeedsUpdate() || value_->deviceNeedsUpdate(); } void SparseMatrixStorage::setHostNeedsUpdate(bool v) const { outer_->setHostNeedsUpdate(v); inner_->setHostNeedsUpdate(v); value_->setHostNeedsUpdate(v); } void SparseMatrixStorage::setDeviceNeedsUpdate(bool v) const { outer_->setDeviceNeedsUpdate(v); inner_->setDeviceNeedsUpdate(v); value_->setDeviceNeedsUpdate(v); } bool SparseMatrixStorage::deviceAllocated() const { return outer_->deviceAllocated() && inner_->deviceAllocated() && value_->deviceAllocated(); } void SparseMatrixStorage::allocateDevice() const { outer_->allocateDevice(); inner_->allocateDevice(); value_->allocateDevice(); } void SparseMatrixStorage::deallocateDevice() const { outer_->deallocateDevice(); inner_->deallocateDevice(); value_->deallocateDevice(); } namespace detail { template void host_copy(const InputT* input_data, array::Array& output) { auto size = output.size(); OutputT* output_data = output.host_data(); std::copy( input_data, input_data + size, output_data ); } template void host_copy(const array::Array& input, array::Array& output) { host_copy( input.host_data(), output ); } template void host_copy(const array::Array& input, array::Array& output) { switch(input.datatype().kind()) { case DataType::kind(): return host_copy( input, output ); case DataType::kind(): return host_copy( input, output ); case DataType::kind(): return host_copy( input, output ); case DataType::kind(): return host_copy( input, output ); case DataType::kind(): return host_copy( input, output ); case DataType::kind(): return host_copy( input, output ); default: ATLAS_NOTIMPLEMENTED; } } void host_copy(const array::Array& input, array::Array& output) { switch(output.datatype().kind()) { case DataType::kind(): return host_copy( input, output ); case DataType::kind(): return host_copy( input, output ); case DataType::kind(): return host_copy( input, output ); case DataType::kind(): return host_copy( input, output ); case DataType::kind(): return host_copy( input, output ); case DataType::kind(): return host_copy( input, output ); default: ATLAS_NOTIMPLEMENTED; } } } }atlas-0.46.0/src/atlas/linalg/sparse/Backend.h0000664000175000017500000000271115160212070021222 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include "atlas/util/Config.h" namespace atlas { namespace linalg { namespace sparse { struct Backend; void current_backend(const std::string& backend); sparse::Backend& current_backend(); sparse::Backend& default_backend(const std::string& backend); struct Backend : util::Config { Backend(): util::Config() { set(current_backend()); } Backend(const std::string& type); Backend(const eckit::Configuration& other); std::string type() const { return getString("type"); } operator std::string() const { return type(); } bool available() const; }; namespace backend { struct openmp : Backend { static std::string type() { return "openmp"; } openmp(): Backend(type()) {} }; struct eckit_linalg : Backend { static std::string type() { return "eckit_linalg"; } eckit_linalg(): Backend(type()) {} }; struct hicsparse : Backend { static std::string type() { return "hicsparse"; } hicsparse(): Backend(type()) {} }; } // namespace backend } // namespace sparse } // namespace linalg } // namespace atlas atlas-0.46.0/src/atlas/linalg/dense.h0000664000175000017500000000104415160212070017472 0ustar alastairalastair/* * (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 "dense/Backend.h" #include "dense/MatrixMultiply.h" namespace atlas { namespace linalg {} // namespace linalg } // namespace atlas atlas-0.46.0/src/atlas/linalg/fft.h0000664000175000017500000000074415160212070017161 0ustar alastairalastair/* * (C) Copyright 2025- 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 "fft/FFT.h" namespace atlas::linalg { } // namespace atlas::linalg atlas-0.46.0/src/atlas/linalg/fft/0000775000175000017500000000000015160212070017003 5ustar alastairalastairatlas-0.46.0/src/atlas/linalg/fft/FFTW.cc0000664000175000017500000001312215160212070020057 0ustar alastairalastair/* * (C) Copyright 2025- 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 "FFT.h" #include #include #include #include "eckit/log/Bytes.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Log.h" #include "atlas/runtime/Trace.h" #include "atlas/library/defines.h" #if ATLAS_HAVE_FFTW #include "fftw3.h" #endif namespace atlas::linalg { #define THROW_ATLAS_FFTW_NOT_SUPPORTED() throw_Exception("Atlas was not compiled with FFTW support", Here()) struct FFTW_Plan { #if ATLAS_HAVE_FFTW ~FFTW_Plan() { destroy(); } void create_inverse_c2r(size_t size_out, std::complex* in, double* out) { ATLAS_TRACE("FFTW::create_plan_inverse_c2r"); plan = fftw_plan_dft_c2r_1d(size_out, reinterpret_cast(in), out, fftw_plan_flags); constructed_ = true; } void create_inverse_c2r_many(size_t howmany, size_t size_in, size_t size_out, size_t dist_in, size_t dist_out, std::complex* in, double* out) { ATLAS_TRACE("FFTW::create_plan_inverse_c2r"); constexpr int rank = 1; /* 1d transforms */ const int n[] = {static_cast(size_out)}; /* each transform of length size_out */ constexpr int istride = 1; constexpr int ostride = 1; const int idist = dist_in; const int odist = dist_out; const int* inembed = nullptr; const int* onembed = nullptr; plan = fftw_plan_many_dft_c2r(rank, n, howmany, reinterpret_cast(in), inembed, istride, idist, out, onembed, ostride, odist, fftw_plan_flags); constructed_ = true; } void inverse_c2r(std::complex* in, double* out) { ATLAS_TRACE("FFTW::inverse_c2r"); ATLAS_ASSERT(constructed_); fftw_execute_dft_c2r(plan, reinterpret_cast(in), out); } void destroy() { if (constructed_) { fftw_destroy_plan(plan); } constructed_ = false; } fftw_plan plan; bool constructed_{false}; int fftw_plan_flags{FFTW_ESTIMATE}; #else void inverse_c2r(std::complex* in, double* out) { THROW_ATLAS_FFTW_NOT_SUPPORTED(); } void create_inverse_c2r(size_t size_out, std::complex* in, double* out) { THROW_ATLAS_FFTW_NOT_SUPPORTED(); } void create_inverse_c2r_many(size_t howmany, size_t size_in, size_t size_out, size_t dist_in, size_t dist_out, std::complex* in, double* out) { THROW_ATLAS_FFTW_NOT_SUPPORTED(); } #endif }; struct FFTW::FFTW_Plans { mutable std::map> fftw_plans_; mutable std::mutex fftw_plans_mutex; // protects fftw_plans_ FFTW_Plan& get_plan(size_t size_out, std::complex* in, double* out) const { std::lock_guard lock(fftw_plans_mutex); bool inverse = true; bool inplace = (static_cast(in) == static_cast(out)); bool aligned = ((reinterpret_cast(in) & 15) | (reinterpret_cast(out) & 15)) == 0; int n1 = size_out; std::int64_t key = ((n1 << 3) | (inverse << 2) | (inplace << 1) | aligned) << 1; auto it = fftw_plans_.find(key); if (it == fftw_plans_.end()) { it = fftw_plans_.emplace(key, new FFTW_Plan()).first; it->second->create_inverse_c2r(size_out, in, out); } return *(it->second); } FFTW_Plan& get_plan_many(size_t howmany, size_t size_in, size_t size_out, size_t dist_in, size_t dist_out, std::complex* in, double* out) const { std::lock_guard lock(fftw_plans_mutex); bool inverse = true; bool inplace = (static_cast(in) == static_cast(out)); bool aligned = ((reinterpret_cast(in) & 15) | (reinterpret_cast(out) & 15)) == 0; int n0 = howmany; int n1 = size_out; std::int64_t key = (((((std::int64_t)n0) << 30) | (n1 << 3) | (inverse << 2) | (inplace << 1) | aligned) << 1) + 1; auto it = fftw_plans_.find(key); if (it == fftw_plans_.end()) { it = fftw_plans_.emplace(key, new FFTW_Plan()).first; it->second->create_inverse_c2r_many(howmany, size_in, size_out, dist_in, dist_out, in, out); } return *(it->second); } }; FFTW::FFTW() { plans_ = new FFTW_Plans(); } FFTW::~FFTW() { delete plans_; } void FFTW::do_plan_inverse_c2r(size_t size_out, std::complex* in, double* out) const { plans_->get_plan(size_out, in, out); } void FFTW::do_plan_inverse_c2r_many(size_t howmany, size_t size_in, size_t size_out, size_t dist_in, size_t dist_out, std::complex* in, double* out) const { plans_->get_plan_many(howmany, size_in, size_out, dist_in, dist_out, in, out); } void FFTW::do_inverse_c2r(size_t size_out, std::complex* in, double* out) const { plans_->get_plan(size_out, in, out).inverse_c2r(in, out); } void FFTW::do_inverse_c2r_many(size_t howmany, size_t size_in, size_t size_out, size_t dist_in, size_t dist_out, std::complex* in, double* out) const { plans_->get_plan_many(howmany, size_in, size_out, dist_in, dist_out, in, out).inverse_c2r(in, out); } // -------------------------------------------------------------------------------------------------------------------- } // namespace atlas::linalg atlas-0.46.0/src/atlas/linalg/fft/FFT.h0000664000175000017500000001223515160212070017576 0ustar alastairalastair/* * (C) Copyright 2025- 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 #include #include #include "atlas/mdspan.h" namespace atlas::linalg { class FFT { public: virtual ~FFT() {} virtual const std::string& type() const = 0; void plan_inverse_c2r_many(size_t howmany, size_t size_in, size_t size_out, size_t dist_in, size_t dist_out, std::complex* in, double* out) const { do_plan_inverse_c2r_many(howmany, size_in, size_out, dist_in, dist_out, in, out); } void plan_inverse_c2r_many( mdspan,dims<2>,layout_stride> in, mdspan,layout_stride> out) { auto howmany = in.extent(0); auto size_in = in.extent(1); auto size_out = out.extent(1); auto dist_in = in.stride(0); auto dist_out = out.stride(0); do_plan_inverse_c2r_many(howmany, size_in, size_out, dist_in, dist_out, in.data_handle(), out.data_handle()); } void plan_inverse_c2r_many(size_t howmany, size_t size_out, std::complex* in, double* out) const { auto size_in = (size_out / 2 ) + 1; auto dist_in = size_in; auto dist_out = size_out; do_plan_inverse_c2r_many(howmany, size_in, size_out, dist_in, dist_out, in, out); } void inverse_c2r_many(size_t howmany, size_t size_in, size_t size_out, size_t dist_in, size_t dist_out, std::complex* in, double* out) const { do_inverse_c2r_many(howmany, size_in, size_out, dist_in, dist_out, in, out); } void inverse_c2r_many( mdspan,dims<2>,layout_stride> in, mdspan,layout_stride> out) { auto howmany = in.extent(0); auto size_in = in.extent(1); auto size_out = out.extent(1); auto dist_in = in.stride(0); auto dist_out = out.stride(0); do_inverse_c2r_many(howmany, size_in, size_out, dist_in, dist_out, in.data_handle(), out.data_handle()); } void inverse_c2r_many(size_t howmany, size_t size_out, std::complex* in, double* out) const { auto size_in = (size_out / 2 ) + 1; auto dist_in = size_in; auto dist_out = size_out; inverse_c2r_many(howmany, size_in, size_out, dist_in, dist_out, in, out); } void plan_inverse_c2r(size_t size_out, std::complex* in, double* out) { do_plan_inverse_c2r(size_out, in, out); } void inverse_c2r(size_t size_out, std::complex* in, double* out) { do_inverse_c2r(size_out, in, out); } private: virtual void do_plan_inverse_c2r_many(size_t howmany, size_t size_in, size_t size_out, size_t dist_in, size_t dist_out, std::complex* in, double* out) const = 0; virtual void do_inverse_c2r_many(size_t howmany, size_t size_in, size_t size_out, size_t dist_in, size_t dist_out, std::complex* in, double* out) const = 0; virtual void do_plan_inverse_c2r(size_t size_out, std::complex* in, double* out) const = 0; virtual void do_inverse_c2r(size_t size_out, std::complex* in, double* out) const = 0; }; class FFTW : public FFT { public: static const std::string& static_type() { static std::string _static_type{"FFTW"}; return _static_type; } const std::string& type() const override { return static_type(); } FFTW(); virtual ~FFTW(); private: void do_plan_inverse_c2r_many(size_t howmany, size_t size_in, size_t size_out, size_t dist_in, size_t dist_out, std::complex* in, double* out) const override; void do_inverse_c2r_many(size_t howmany, size_t size_in, size_t size_out, size_t dist_in, size_t dist_out, std::complex* in, double* out) const override; void do_plan_inverse_c2r(size_t size_out, std::complex* in, double* out) const override; void do_inverse_c2r(size_t size_out, std::complex* in, double* out) const override; private: struct FFTW_Plans; mutable FFTW_Plans* plans_; }; class pocketfft : public FFT { public: static const std::string& static_type() { static std::string _static_type{"pocketfft"}; return _static_type; } const std::string& type() const override { return static_type(); } virtual ~pocketfft() {} private: void do_plan_inverse_c2r_many(size_t howmany, size_t size_in, size_t size_out, size_t dist_in, size_t dist_out, std::complex* in, double* out) const override; void do_inverse_c2r_many(size_t howmany, size_t size_in, size_t size_out, size_t dist_in, size_t dist_out, std::complex* in, double* out) const override; void do_plan_inverse_c2r(size_t size_out, std::complex* in, double* out) const override; void do_inverse_c2r(size_t size_out, std::complex* in, double* out) const override; }; } // namespace atlas::linalg atlas-0.46.0/src/atlas/linalg/fft/pocketfft.cc0000664000175000017500000000712315160212070021302 0ustar alastairalastair/* * (C) Copyright 2025- 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 "FFT.h" #include #include "atlas/library/defines.h" #if ATLAS_HAVE_POCKETFFT #include "atlas/parallel/omp/omp.h" #include "atlas/runtime/Trace.h" ATLAS_SUPPRESS_WARNINGS_PUSH ATLAS_SUPPRESS_WARNINGS_BOTH_INLINE_NOINLINE #define POCKETFFT_CACHE_SIZE 4000 // POCKETFFT_CACHE_SIZE should keep O8000 grid fft's in cache #include "pocketfft_hdronly.h" ATLAS_SUPPRESS_WARNINGS_PUSH namespace atlas::linalg { void pocketfft::do_plan_inverse_c2r(size_t /*size_out*/, std::complex* /*in*/, double* /*out*/) const {} void pocketfft::do_plan_inverse_c2r_many(size_t /*howmany*/, size_t /*size_in*/, size_t /*size_out*/, size_t /*dist_in*/, size_t /*dist_out*/, std::complex* /*in*/, double* /*out*/) const {} void pocketfft::do_inverse_c2r(const size_t size_out, std::complex* in, double* out) const { ATLAS_TRACE("pocketfft::inverse_c2r"); static const ::pocketfft::stride_t stride_in{sizeof(std::complex)}; // in bytes static const ::pocketfft::stride_t stride_out{sizeof(double)}; // in bytes constexpr size_t axis = 0; constexpr double fct = 1.0; constexpr size_t nthreads = 1; constexpr bool direction = ::pocketfft::BACKWARD; ::pocketfft::shape_t shape_out{size_out}; ::pocketfft::c2r(shape_out, stride_in, stride_out, axis, direction, in, out, fct, nthreads); } void pocketfft::do_inverse_c2r_many(size_t howmany, size_t size_in, size_t size_out, size_t dist_in, size_t dist_out, std::complex* in, double* out) const { ATLAS_TRACE("pocketfft::inverse_c2r"); static const ::pocketfft::stride_t stride_in{sizeof(std::complex)}; // in bytes static const ::pocketfft::stride_t stride_out{sizeof(double)}; // in bytes const size_t idist = dist_in; const size_t odist = dist_out; constexpr size_t axis = 0; constexpr double fct = 1.0; constexpr size_t nthreads = 1; constexpr bool direction = ::pocketfft::BACKWARD; const ::pocketfft::shape_t shape_out{size_out}; atlas_omp_parallel_for(size_t j=0; j* /*in*/, double* /*out*/) const {} void pocketfft::do_plan_inverse_c2r_many(size_t /*howmany*/, size_t /*size_in*/, size_t /*size_out*/, size_t /*dist_in*/, size_t /*dist_out*/, std::complex* /*in*/, double* /*out*/) const {} void pocketfft::do_inverse_c2r(const size_t /*size_out*/, std::complex* /*in*/, double* /*out*/) const { THROW_ATLAS_POCKETFFT_NOT_SUPPORTED(); } void pocketfft::do_inverse_c2r_many(size_t howmany, size_t size_in, size_t size_out, size_t dist_in, size_t dist_out, std::complex* in, double* out) const { THROW_ATLAS_POCKETFFT_NOT_SUPPORTED(); } } // namespace atlas::linalg #endif atlas-0.46.0/src/atlas/linalg/dense/0000775000175000017500000000000015160212070017322 5ustar alastairalastairatlas-0.46.0/src/atlas/linalg/dense/Backend.cc0000664000175000017500000001005215160212070021156 0ustar alastairalastair/* * (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 "Backend.h" #include #include "atlas/library/config.h" #if ATLAS_ECKIT_HAVE_ECKIT_585 #include "eckit/linalg/LinearAlgebraDense.h" #else #include "eckit/linalg/LinearAlgebra.h" #endif #include "eckit/utils/Tokenizer.h" #include "atlas/library.h" #include "atlas/runtime/Exception.h" #include "atlas/util/Config.h" #include "atlas/runtime/Log.h" namespace atlas { namespace linalg { namespace dense { namespace { util::Config to_config(const std::string& type) { util::Config b; std::vector tokens; eckit::Tokenizer{'.'}(type, tokens); ATLAS_ASSERT(tokens.size() <= 2); ATLAS_ASSERT(tokens.size() > 0); if (tokens.size() == 1) { b.set("type", type); } else { util::Config b; b.set("type", tokens[0]); b.set("backend", tokens[1]); } return b; } struct backends { std::map map_; std::string current_backend_; static backends& instance() { static backends x; return x; } void set(const std::string& current_backend) { current_backend_ = current_backend; } dense::Backend& get(const std::string& type) { ATLAS_ASSERT(!type.empty()); if (map_.find(type) == map_.end()) { map_.emplace(type, to_config(type)); } return map_[type]; } dense::Backend& current() { return get(current_backend_); } private: backends() { auto configured = atlas::Library::instance().linalgDenseBackend(); if (configured.empty()) { // Proposal for default backend would be "eckit_linalg": // current_backend_ = configured.empty() ? backend::eckit_linalg::type() : configured; // // However to be identical in behaviour for TransLocal with atlas 0.26.0 and earlier before // any other codes can be adapted in short notice: #if ATLAS_ECKIT_HAVE_ECKIT_585 if (eckit::linalg::LinearAlgebraDense::hasBackend("mkl")) { #else if (eckit::linalg::LinearAlgebra::hasBackend("mkl")) { #endif current_backend_ = "mkl"; } else { current_backend_ = backend::eckit_linalg::type(); } map_.emplace("default", util::Config("type", current_backend_)); } else { current_backend_ = configured; map_.emplace("default", to_config(configured)); } } }; } // namespace void current_backend(const std::string& backend) { backends::instance().set(backend); } dense::Backend& current_backend() { return backends::instance().current(); } dense::Backend& default_backend(const std::string& backend) { return backends::instance().get(backend); } Backend::Backend(const std::string type): util::Config() { if (not type.empty()) { set(default_backend(type)); } else { set(current_backend()); } } Backend::Backend(const eckit::Configuration& other): util::Config(other) { ATLAS_ASSERT(has("type")); } Backend::operator std::string() const { return type(); } bool Backend::available() const { std::string t = type(); if (t == backend::eckit_linalg::type()) { if (has("backend")) { #if ATLAS_ECKIT_HAVE_ECKIT_585 return eckit::linalg::LinearAlgebraDense::hasBackend(getString("backend")); #else return eckit::linalg::LinearAlgebra::hasBackend(getString("backend")); #endif } return true; } #if ATLAS_ECKIT_HAVE_ECKIT_585 return eckit::linalg::LinearAlgebraDense::hasBackend(t); #else return eckit::linalg::LinearAlgebra::hasBackend(t); #endif } } // namespace dense } // namespace linalg } // namespace atlas atlas-0.46.0/src/atlas/linalg/dense/MatrixMultiply_EckitLinalg.h0000664000175000017500000000214215160212070024744 0ustar alastairalastair/* * (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. */ /* * (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/linalg/dense/MatrixMultiply.h" namespace atlas { namespace linalg { namespace dense { template <> struct MatrixMultiply { static void apply(const Matrix& A, const Matrix& B, Matrix& C, const Configuration&); }; } // namespace dense } // namespace linalg } // namespace atlas atlas-0.46.0/src/atlas/linalg/dense/MatrixMultiply_EckitLinalg.cc0000664000175000017500000000441315160212070025105 0ustar alastairalastair/* * (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. */ /* * (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 "MatrixMultiply_EckitLinalg.h" #include "atlas/library/config.h" #if ATLAS_ECKIT_HAVE_ECKIT_585 #include "eckit/linalg/LinearAlgebraDense.h" #else #include "eckit/linalg/LinearAlgebra.h" #endif #include "eckit/linalg/Matrix.h" #include "eckit/linalg/Vector.h" #include "atlas/runtime/Exception.h" namespace atlas { namespace linalg { namespace dense { namespace { #if ATLAS_ECKIT_HAVE_ECKIT_585 const eckit::linalg::LinearAlgebraDense& eckit_linalg_backend(const Configuration& config) { std::string backend = "default"; config.get("backend", backend); if (backend == "default") { return eckit::linalg::LinearAlgebraDense::backend(); } ATLAS_ASSERT(eckit::linalg::LinearAlgebraDense::hasBackend(backend)); return eckit::linalg::LinearAlgebraDense::getBackend(backend); } #else const eckit::linalg::LinearAlgebra& eckit_linalg_backend(const Configuration& config) { std::string backend = "default"; config.get("backend", backend); if (backend == "default") { return eckit::linalg::LinearAlgebra::backend(); } ATLAS_ASSERT(eckit::linalg::LinearAlgebra::hasBackend(backend)); return eckit::linalg::LinearAlgebra::getBackend(backend); } #endif } // namespace void MatrixMultiply::apply(const Matrix& A, const Matrix& B, Matrix& C, const Configuration& config) { eckit_linalg_backend(config).gemm(A, B, C); } } // namespace dense } // namespace linalg } // namespace atlas atlas-0.46.0/src/atlas/linalg/dense/MatrixMultiply.tcc0000664000175000017500000000446315160212070023030 0ustar alastairalastair/* * (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 "MatrixMultiply.h" #include "atlas/linalg/Indexing.h" #include "atlas/linalg/Introspection.h" #include "atlas/linalg/View.h" #include "atlas/linalg/dense/Backend.h" #include "atlas/runtime/Exception.h" #if ATLAS_ECKIT_HAVE_ECKIT_585 #include "eckit/linalg/LinearAlgebraDense.h" #else #include "eckit/linalg/LinearAlgebra.h" #endif namespace atlas { namespace linalg { namespace dense { namespace { template struct MatrixMultiplyHelper { template static void apply( const Mat& A, const Mat& B, Mat& C, const eckit::Configuration& config ) { MatrixMultiply::apply( A,B,C, config ); } }; } } template void matrix_multiply( const Mat& A, const Mat& B, Mat& C, const eckit::Configuration& config ) { std::string type = config.getString( "type", dense::current_backend() ); if ( type == dense::backend::eckit_linalg::type() ) { dense::MatrixMultiplyHelper::apply( A, B, C, config ); } else { if( type == "openmp" ) { type = "generic"; dense::MatrixMultiplyHelper::apply( A, B, C, util::Config("backend",type) ); } #if ATLAS_ECKIT_HAVE_ECKIT_585 else if( eckit::linalg::LinearAlgebraDense::hasBackend(type) ) { #else else if( eckit::linalg::LinearAlgebra::hasBackend(type) ) { #endif dense::MatrixMultiplyHelper::apply( A, B, C, util::Config("backend",type) ); } else { throw_NotImplemented( "matrix_multiply cannot be performed with unsupported backend [" + type + "]", Here() ); } } } template void matrix_multiply( const Matrix& A, const Matrix& B, Matrix& C ) { matrix_multiply( A, B, C, dense::Backend() ); } } // namespace linalg } // namespace atlas atlas-0.46.0/src/atlas/linalg/dense/MatrixMultiply.h0000664000175000017500000000364515160212070022507 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include "eckit/config/Configuration.h" #include "eckit/linalg/Matrix.h" #include "atlas/linalg/Indexing.h" #include "atlas/linalg/View.h" #include "atlas/linalg/dense/Backend.h" #include "atlas/runtime/Exception.h" #include "atlas/util/Config.h" namespace atlas { namespace linalg { using Matrix = eckit::linalg::Matrix; using Configuration = eckit::Configuration; // C = A . B template void matrix_multiply(const Matrix& A, const Matrix& B, Matrix& C); template void matrix_multiply(const Matrix& A, const Matrix& B, Matrix& C, const eckit::Configuration&); class MatrixMultiply { public: MatrixMultiply() = default; MatrixMultiply(const std::string& backend): backend_{backend} {} MatrixMultiply(const dense::Backend& backend): backend_(backend) {} void operator()(const Matrix& A, const Matrix& B, Matrix& C) const { matrix_multiply(A, B, C, backend()); } const dense::Backend& backend() const { return backend_; } private: dense::Backend backend_; }; namespace dense { // Template class which needs (full or partial) specialization for concrete template parameters template struct MatrixMultiply { static void apply(const Matrix& A, const Matrix& B, Matrix& C, const Configuration&) { throw_NotImplemented("MatrixMultiply needs a template specialization with the implementation", Here()); } }; } // namespace dense } // namespace linalg } // namespace atlas #include "MatrixMultiply.tcc" #include "MatrixMultiply_EckitLinalg.h" atlas-0.46.0/src/atlas/linalg/dense/Backend.h0000664000175000017500000000230515160212070021022 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include "atlas/util/Config.h" namespace atlas { namespace linalg { namespace dense { struct Backend; void current_backend(const std::string& backend); dense::Backend& current_backend(); dense::Backend& default_backend(const std::string& backend); struct Backend : util::Config { Backend(): util::Config() { set(current_backend()); } Backend(const std::string type); Backend(const eckit::Configuration& other); std::string type() const { return getString("type"); } operator std::string() const; bool available() const; }; namespace backend { struct eckit_linalg : Backend { static std::string type() { return "eckit_linalg"; } eckit_linalg(): Backend(type()) {} }; } // namespace backend } // namespace dense } // namespace linalg } // namespace atlas atlas-0.46.0/src/atlas/linalg/Introspection.h0000664000175000017500000001512015160212070021234 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include "eckit/linalg/Matrix.h" #include "eckit/linalg/Vector.h" #include "eckit/linalg/types.h" #include "atlas/library/config.h" namespace atlas { namespace linalg { namespace introspection { namespace detail { // void_t is a C++17 feature template struct make_void { typedef void type; }; template using void_t = typename make_void::type; } // namespace detail template struct has_contiguous : std::false_type {}; template struct has_contiguous> : std::true_type {}; template struct has_rank : std::false_type {}; template struct has_rank> : std::true_type {}; template struct has_RANK : std::false_type {}; template struct has_RANK> : std::true_type {}; template struct has_shape : std::false_type {}; template struct has_shape().shape(0))>> : std::true_type {}; namespace detail { template struct BaseType { template static constexpr int is_derived_from() { return std::is_base_of::value; } using type = typename std::conditional< is_derived_from(), eckit::linalg::Matrix, typename std::conditional(), eckit::linalg::Vector, typename std::remove_const::type /*default*/ >::type>::type; }; } // namespace detail template using base_type = typename detail::BaseType::type; namespace detail { template struct ValueType { using type = typename T::value_type; }; template struct ValueType { using type = eckit::linalg::Scalar; }; template struct ValueType { using type = eckit::linalg::Scalar; }; template struct Introspect { template ::value, T>::type* = nullptr> static bool contiguous(const T& view) { return view.contiguous(); } template ::value, T>::type* = nullptr> static bool contiguous(const T&) { return true; } template ::value, T>::type* = nullptr> static constexpr int rank() { static_assert(has_RANK::value, "workaround for Cray 8.7: we need to use T::RANK instead of T::rank()"); return T::RANK; } template ::value, T>::type* = nullptr> static constexpr int rank() { return 1; } static constexpr int rank() { return rank(); } template ::value, T>::type* = nullptr> static idx_t outer_dimension(const T& view) { return view.shape(0); } template ::value && (rank() == 1), T>::type* = nullptr> static idx_t outer_dimension(const T& view) { return view.size(); } template ::value, T>::type* = nullptr> static idx_t inner_dimension(const T& view) { return view.shape(rank() - 1); } template ::value && (rank() == 1), T>::type* = nullptr> static idx_t inner_dimension(const T& view) { return view.size(); } template ::value, T>::type* = nullptr> static idx_t shape(const T& view) { //static_assert( Dim == 0, "Dim must be 0 if shape() is not there" ); return view.size(); } template ::value, T>::type* = nullptr> static idx_t shape(const T& view) { return view.shape(Dim); } static constexpr bool layout_left() { return true; } using value_type = typename ValueType>::type; }; template struct Introspect { using value_type = eckit::linalg::Scalar; static constexpr int rank() { return 2; } static bool contiguous(const eckit::linalg::Matrix&) { return true; } static idx_t inner_dimension(const eckit::linalg::Matrix& m) { return static_cast(m.cols()); } static idx_t outer_dimension(const eckit::linalg::Matrix& m) { return static_cast(m.rows()); } template static idx_t shape(const eckit::linalg::Matrix& m) { static_assert(Dim <= 1, "Dim must be 0 or 1"); return (Dim == 0) ? outer_dimension(m) : inner_dimension(m); } static constexpr bool layout_left() { return false; } }; template using Introspection = Introspect>; } // namespace detail template using value_type = typename detail::Introspection::value_type; template using base_type = typename detail::BaseType::type; template static bool contiguous(const T& view) { return detail::Introspection::contiguous(view); } template static constexpr int rank() { return detail::Introspection::rank(); } template static idx_t outer_dimension(const T& view) { return detail::Introspection::outer_dimension(view); } template static idx_t inner_dimension(const T& view) { return detail::Introspection::inner_dimension(view); } template static idx_t shape(const T& view) { return detail::Introspection::template shape(view); } template static constexpr bool layout_left(const T&) { return detail::Introspection::layout_left(); } template static constexpr bool layout_right(const T&) { return not detail::Introspection::layout_left(); } } // namespace introspection } // namespace linalg } // namespace atlas atlas-0.46.0/src/atlas/linalg/sparse.h0000664000175000017500000000132415160212070017672 0ustar alastairalastair/* * (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 "sparse/Backend.h" #include "sparse/MakeEckitSparseMatrix.h" #include "sparse/MakeSparseMatrixStorageEckit.h" #include "sparse/SparseMatrixMultiply.h" #include "sparse/SparseMatrixStorage.h" #include "sparse/SparseMatrixView.h" namespace atlas { namespace linalg {} // namespace linalg } // namespace atlas atlas-0.46.0/src/atlas/meshgenerator.h0000664000175000017500000000074315160212070017776 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ /// @author Willem Deconinck #pragma once #include "atlas/meshgenerator/MeshGenerator.h" atlas-0.46.0/src/atlas/grid/0000775000175000017500000000000015160212070015703 5ustar alastairalastairatlas-0.46.0/src/atlas/grid/CubedSphereGrid.cc0000664000175000017500000000214115160212070021207 0ustar alastairalastair/* * (C) Copyright 2020 UCAR * * 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. */ #include #include "atlas/grid/CubedSphereGrid.h" #include "atlas/grid/Grid.h" #include "atlas/grid/detail/grid/CubedSphere.h" #include "atlas/projection/Projection.h" namespace atlas { inline const CubedSphereGrid::grid_t* cubedsphere_grid(const Grid::Implementation* grid) { return dynamic_cast(grid); } CubedSphereGrid::CubedSphereGrid(): Grid(), grid_(nullptr) {} CubedSphereGrid::CubedSphereGrid(const Grid& grid): Grid(grid), grid_(cubedsphere_grid(get())) {} CubedSphereGrid::CubedSphereGrid(const Grid::Implementation* grid): Grid(grid), grid_(cubedsphere_grid(get())) {} CubedSphereGrid::CubedSphereGrid(const std::string& grid): Grid(grid), grid_(cubedsphere_grid(get())) {} CubedSphereGrid::CubedSphereGrid(const int& N, const Projection& projection): Grid(new CubedSphereGrid::grid_t(N, projection, "C")), grid_(cubedsphere_grid(get())) {} } // namespace atlas atlas-0.46.0/src/atlas/grid/UnstructuredGrid.cc0000664000175000017500000000404615160212070021533 0ustar alastairalastair/* * (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/grid/UnstructuredGrid.h" #include #include #include #include "eckit/config/Parametrisation.h" #include "atlas/domain/Domain.h" #include "atlas/grid/Spacing.h" #include "atlas/projection/Projection.h" #include "atlas/util/Config.h" namespace atlas { inline const UnstructuredGrid::grid_t* unstructured_grid(const Grid::Implementation* grid) { return dynamic_cast(grid); } UnstructuredGrid::UnstructuredGrid(): Grid() {} UnstructuredGrid::UnstructuredGrid(const Grid& grid): Grid(grid), grid_(unstructured_grid(get())) {} UnstructuredGrid::UnstructuredGrid(const Grid::Implementation* grid): Grid(grid), grid_(unstructured_grid(get())) {} UnstructuredGrid::UnstructuredGrid(const Config& grid): Grid(grid), grid_(unstructured_grid(get())) {} UnstructuredGrid::UnstructuredGrid(std::vector* xy): Grid(new UnstructuredGrid::grid_t(xy)), grid_(unstructured_grid(get())) {} UnstructuredGrid::UnstructuredGrid(std::vector&& xy): Grid(new UnstructuredGrid::grid_t(std::forward>(xy))), grid_(unstructured_grid(get())) {} UnstructuredGrid::UnstructuredGrid(const std::vector& xy): Grid(new UnstructuredGrid::grid_t(std::forward>(xy))), grid_(unstructured_grid(get())) {} UnstructuredGrid::UnstructuredGrid(std::initializer_list xy): Grid(new UnstructuredGrid::grid_t(xy)), grid_(unstructured_grid(get())) {} UnstructuredGrid::UnstructuredGrid(const Grid& grid, const Grid::Domain& domain): Grid(new UnstructuredGrid::grid_t(*grid.get(), domain)), grid_(unstructured_grid(get())) {} } // namespace atlas atlas-0.46.0/src/atlas/grid/Vertical.cc0000664000175000017500000000352715160212070017772 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "eckit/types/Types.h" #include "atlas/array/ArrayView.h" #include "atlas/field/Field.h" #include "atlas/grid/Vertical.h" namespace atlas { //--------------------------------------------------------------------------------------------------------------------- namespace { std::vector linspace(double start, double end, idx_t N, bool endpoint) { std::vector x_; if (N > 0) { volatile double _N = N; // volatile keyword prevents agressive optimization by Cray compiler x_.resize(N); double step; if (endpoint && N > 1) { step = (end - start) / (_N - 1); } else if (N > 0) { step = (end - start) / _N; } else { step = 0.; } for (idx_t i = 0; i < N; ++i) { x_[i] = start + i * step; } } return x_; } idx_t get_levels(const util::Config& config) { return config.getInt("levels", 0); } } // namespace //--------------------------------------------------------------------------------------------------------------------- Vertical::Vertical(const util::Config& config): Vertical(get_levels(config), linspace(0., 1., get_levels(config), true), config) {} std::ostream& operator<<(std::ostream& os, const Vertical& v) { os << v.z_; return os; } //--------------------------------------------------------------------------------------------------------------------- } // namespace atlas atlas-0.46.0/src/atlas/grid/Iterator.h0000664000175000017500000001337315160212070017654 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include #include "atlas/grid/detail/grid/Grid.h" #include "atlas/util/Point.h" //--------------------------------------------------------------------------------------------------------------------- namespace atlas { namespace grid { //--------------------------------------------------------------------------------------------------------------------- class IteratorXY { using implementation_t = detail::grid::Grid::IteratorXY; public: using difference_type = implementation_t::difference_type; using iterator_category = implementation_t::iterator_category; using value_type = implementation_t::value_type; using pointer = implementation_t::pointer; using reference = implementation_t::reference; public: IteratorXY(std::unique_ptr iterator): iterator_(std::move(iterator)) {} IteratorXY(const IteratorXY& iterator): iterator_(iterator.iterator_->clone()) {} bool next(value_type& xy) { return iterator_->next(xy); } reference operator*() const { return iterator_->operator*(); } const IteratorXY& operator++() { iterator_->operator++(); return *this; } const IteratorXY& operator+=(difference_type distance) { iterator_->operator+=(distance); return *this; } friend IteratorXY operator+(const IteratorXY& a, difference_type distance) { IteratorXY result(a); result += distance; return result; } friend difference_type operator-(const IteratorXY& last, const IteratorXY& first) { return first.iterator_->distance(*last.iterator_); } bool operator==(const IteratorXY& other) const { return iterator_->operator==(*other.iterator_); } bool operator!=(const IteratorXY& other) const { return iterator_->operator!=(*other.iterator_); } private: IteratorXY::difference_type distance(const IteratorXY& other) const { return iterator_->distance(*other.iterator_); } private: std::unique_ptr iterator_; }; //--------------------------------------------------------------------------------------------------------------------- class IteratorLonLat { using implementation_t = detail::grid::Grid::IteratorLonLat; public: using difference_type = implementation_t::difference_type; using iterator_category = implementation_t::iterator_category; using value_type = implementation_t::value_type; using pointer = implementation_t::pointer; using reference = implementation_t::reference; public: IteratorLonLat(std::unique_ptr iterator): iterator_(std::move(iterator)) {} IteratorLonLat(const IteratorLonLat& iterator): iterator_(iterator.iterator_->clone()) {} bool next(value_type& xy) { return iterator_->next(xy); } reference operator*() const { return iterator_->operator*(); } const IteratorLonLat& operator++() { iterator_->operator++(); return *this; } const IteratorLonLat& operator+=(difference_type distance) { iterator_->operator+=(distance); return *this; } friend IteratorLonLat operator+(const IteratorLonLat& a, difference_type distance) { IteratorLonLat result(a); result += distance; return result; } friend difference_type operator-(const IteratorLonLat& last, const IteratorLonLat& first) { return first.iterator_->distance(*last.iterator_); } bool operator==(const IteratorLonLat& other) const { return iterator_->operator==(*other.iterator_); } bool operator!=(const IteratorLonLat& other) const { return iterator_->operator!=(*other.iterator_); } private: difference_type distance(const IteratorLonLat& other) const { return iterator_->distance(*other.iterator_); } private: std::unique_ptr iterator_; }; //--------------------------------------------------------------------------------------------------------------------- class IterateXY { public: using iterator = grid::IteratorXY; using const_iterator = iterator; using Grid = detail::grid::Grid; public: IterateXY(const Grid& grid): grid_(grid) {} iterator begin() const; iterator end() const; void access(size_t i, PointXY&); PointXY front() { return *begin(); } PointXY back() { return *(begin() + (grid_.size() - 1)); } private: const Grid& grid_; }; class IterateLonLat { public: using iterator = IteratorLonLat; using const_iterator = iterator; using Grid = detail::grid::Grid; public: IterateLonLat(const Grid& grid): grid_(grid) {} iterator begin() const; iterator end() const; PointLonLat front() { return *begin(); } PointLonLat back() { return *(begin() + (grid_.size() - 1)); } private: const Grid& grid_; }; } // namespace grid } // namespace atlas #ifndef DOXYGEN_SHOULD_SKIP_THIS namespace std { inline atlas::grid::IteratorXY::difference_type distance(const atlas::grid::IteratorXY& first, const atlas::grid::IteratorXY& last) { return last - first; } inline atlas::grid::IteratorLonLat::difference_type distance(const atlas::grid::IteratorLonLat& first, const atlas::grid::IteratorLonLat& last) { return last - first; } } // namespace std #endif atlas-0.46.0/src/atlas/grid/StencilComputer.h0000664000175000017500000001440015160212070021173 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include #include "atlas/grid/Vertical.h" #include "atlas/library/config.h" #include "atlas/runtime/Exception.h" namespace atlas { class StructuredGrid; } // namespace atlas namespace atlas { namespace grid { class ComputeLower { std::vector z_; std::vector nvaux_; idx_t nlev_; idx_t nlevaux_; double rlevaux_; public: ComputeLower() = default; ComputeLower(const Vertical& z); idx_t operator()(double z) const { idx_t idx = static_cast(std::floor(z * rlevaux_)); #ifndef NDEBUG ATLAS_ASSERT(idx < static_cast(nvaux_.size()) && idx >= 0); #endif idx = nvaux_[idx]; if (idx < nlev_ - 1 && z > z_[idx + 1]) { ++idx; } return idx; } }; //----------------------------------------------------------------------------- class ComputeNorth { std::vector y_; double dy_; idx_t halo_; idx_t ny_; static constexpr double tol() { return 0.5e-6; } public: ComputeNorth() = default; ComputeNorth(const StructuredGrid& grid, idx_t halo); idx_t operator()(double y) const { idx_t j = static_cast(std::floor((y_[halo_ + 0] - y) / dy_)); j = std::max(halo_, std::min(j, halo_ + ny_ - 1)); while (y_[halo_ + j] > y) { ++j; } do { --j; } while (y_[halo_ + j] < y); return j; } }; //----------------------------------------------------------------------------- class ComputeWest { std::vector dx; std::vector xref; idx_t halo_; // halo in north-south direction idx_t ny_; static constexpr double tol() { return 0.5e-6; } public: ComputeWest() = default; ComputeWest(const StructuredGrid& grid, idx_t halo = 0); idx_t operator()(const double& x, idx_t j) const { idx_t jj = halo_ + j; idx_t i = static_cast(std::floor((x - xref[jj]) / dx[jj])); return i; } }; //----------------------------------------------------------------------------- /// @class ComputeHorizontalStencil /// @brief Compute stencil in horizontal direction (i,j) /// /// @details /// Given a stencil width, the stencil for a given P{x,y} is: /// @code /// i[0] i[1] i[2] i[3] /// x x x x j + 0 /// x x x x j + 1 /// P /// x x x x j + 2 /// x x x x j + 3 /// @endcode /// /// In case the x-component of P is aligned with any /// stencil, gridpoint, the stencil will assume the grid point /// is on the point P's left side: /// @code /// i[0] i[1] i[2] i[3] /// x x x x j + 0 /// x x x x j + 1 /// P /// x x x x j + 2 /// x x x x j + 3 /// @endcode class ComputeHorizontalStencil { idx_t halo_; ComputeNorth compute_north_; ComputeWest compute_west_; idx_t stencil_width_; idx_t stencil_begin_; public: ComputeHorizontalStencil() = default; ComputeHorizontalStencil(const StructuredGrid& grid, idx_t stencil_width); template void operator()(const double& x, const double& y, stencil_t& stencil) const { stencil.j_begin_ = compute_north_(y) - stencil_begin_; for (idx_t jj = 0; jj < stencil_width_; ++jj) { stencil.i_begin_[jj] = compute_west_(x, stencil.j_begin_ + jj) - stencil_begin_; } } }; //----------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------------------------- /// @class ComputeVerticalStencil /// @brief Compute lower vertical level index for given coordinate /// zcoord: /// @verbatim /// 0----1----2----3--...--(n-1)----(n)----(n+1) /// --->|<---|<---|<--...-|<-------------------- /// @endverbatim /// If coordinate falls on vertical level (+- epsilon), that level is returned /// If coordinate falls in range [0,1) or [n,n+1], /// the index is snapped to 1 and (n-1) respectively. This allows reliably that /// the returned index can be used for stencil operations. /// /// IFS full levels don't have a level at the boundaries (0.,1.) /// It is the "half" levels that contain (0.,1.). For reasons of boundary conditions /// however, the full levels also have 0. prepended and 1. appended. /// /// Example IFS full levels for regular distribution dz ( level 0 and n+1 are added for boundary conditions ) /// 0 : 0.0 /// jlev : jlev*dz - 0.5*dz /// nlev : nlev*dz - 0.5*dz /// nlev+1 : 1.0 class ComputeVerticalStencil { ComputeLower compute_lower_; idx_t stencil_width_; idx_t stencil_begin_; idx_t clip_begin_; idx_t clip_end_; double vertical_min_; double vertical_max_; public: ComputeVerticalStencil() = default; ComputeVerticalStencil(const Vertical& vertical, idx_t stencil_width); template void operator()(const double& z, stencil_t& stencil) const { idx_t k_begin = compute_lower_(z) - stencil_begin_; idx_t k_end = k_begin + stencil_width_; idx_t move = 0; if (k_begin < clip_begin_) { move = k_begin - clip_begin_; if (z < vertical_min_) { --k_begin; --move; } } else if (k_end > clip_end_) { move = k_end - clip_end_; } stencil.k_begin_ = k_begin - move; stencil.k_interval_ = stencil_begin_ + move; } }; //--------------------------------------------------------------------------------------------------------------------- } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/Partitioner.h0000664000175000017500000000723015160212070020356 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include "atlas/library/config.h" #include "atlas/util/ObjectHandle.h" namespace eckit { class Parametrisation; } namespace atlas { class Grid; class Mesh; class FunctionSpace; namespace grid { class Distribution; class DistributionImpl; namespace detail { namespace partitioner { class Partitioner; } // namespace partitioner } // namespace detail } // namespace grid } // namespace atlas namespace atlas { namespace mesh { namespace detail { class MeshImpl; } } // namespace mesh } // namespace atlas namespace atlas { namespace functionspace { class FunctionSpaceImpl; } // namespace functionspace } // namespace atlas namespace atlas { namespace grid { namespace detail { namespace grid { class Grid; } // namespace grid } // namespace detail } // namespace grid } // namespace atlas namespace atlas { namespace grid { // ------------------------------------------------------------------ class Partitioner : DOXYGEN_HIDE(public util::ObjectHandle) { public: using Config = eckit::Parametrisation; using Implementation = detail::partitioner::Partitioner; public: static bool exists(const std::string& type); public: using Handle::Handle; Partitioner() = default; explicit Partitioner(const std::string& type); Partitioner(const std::string& type, const idx_t nb_partitions); Partitioner(const Config&); Partitioner(const std::string& type, const Config&); void partition(const Grid& grid, int part[]) const; Distribution partition(const Grid& grid) const; idx_t nb_partitions() const; std::string type() const; std::string mpi_comm() const; }; // ------------------------------------------------------------------ class MatchingPartitioner : public Partitioner { public: using Config = eckit::Parametrisation; public: static bool exists(const std::string& type); public: MatchingPartitioner(); MatchingPartitioner(const Mesh&); MatchingPartitioner(const Mesh&, const Config&); MatchingPartitioner(const FunctionSpace&); MatchingPartitioner(const FunctionSpace&, const Config&); }; // ------------------------------------------------------------------ class MatchingMeshPartitioner : public MatchingPartitioner { public: using MatchingPartitioner::MatchingPartitioner; }; // ------------------------------------------------------------------ #ifndef DOXYGEN_SHOULD_SKIP_THIS using GridImpl = detail::grid::Grid; extern "C" { Partitioner::Implementation* atlas__grid__Partitioner__new(const Partitioner::Config* config); Partitioner::Implementation* atlas__grid__Partitioner__new_type(const char* type); Partitioner::Implementation* atlas__grid__MatchingMeshPartitioner__new(const mesh::detail::MeshImpl* mesh, const Partitioner::Config* config); Partitioner::Implementation* atlas__grid__MatchingFunctionSpacePartitioner__new( const functionspace::FunctionSpaceImpl* functionspace, const Partitioner::Config* config); void atlas__grid__Partitioner__delete(Partitioner::Implementation* This); DistributionImpl* atlas__grid__Partitioner__partition(const Partitioner::Implementation* This, const GridImpl* grid); } #endif } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/Grid.cc0000664000175000017500000000515215160212070017102 0ustar alastairalastair/* * (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/grid/Grid.h" #include #include #include #include "atlas/domain/Domain.h" #include "atlas/grid/Grid.h" #include "atlas/grid/Iterator.h" #include "atlas/grid/Spacing.h" #include "atlas/projection/Projection.h" #include "atlas/runtime/Exception.h" #include "atlas/util/Config.h" namespace atlas { Grid::IterateXY Grid::xy() const { return Grid::IterateXY(*get()); } Grid::IterateLonLat Grid::lonlat() const { return Grid::IterateLonLat(*get()); } Grid::Grid(const std::string& shortname, const Domain& domain): Handle([&] { Config config; if (domain) { config.set("domain", domain.spec()); } return Grid::Implementation::create(shortname, config); }()) {} Grid::Grid(const std::string& shortname, const Projection& projection, const Domain& domain): Handle([&] { Config config; if (projection) { config.set("projection", projection.spec()); } if (domain) { config.set("domain", domain.spec()); } return Grid::Implementation::create(shortname, config); }()) {} Grid::Grid(const Grid& grid, const Grid::Domain& domain): Handle([&] { ATLAS_ASSERT(grid); return Grid::Implementation::create(*grid.get(), domain); }()) {} Grid::Grid(const Config& p): Handle(Grid::Implementation::create(p)) {} idx_t Grid::size() const { return get()->size(); } size_t Grid::footprint() const { return get()->footprint(); } const Grid::Projection& Grid::projection() const { return get()->projection(); } const Grid::Domain& Grid::domain() const { return get()->domain(); } RectangularLonLatDomain Grid::lonlatBoundingBox() const { return get()->lonlatBoundingBox(); } std::string Grid::name() const { return get()->name(); } std::string Grid::type() const { return get()->type(); } std::string Grid::uid() const { return get()->uid(); } void Grid::hash(eckit::Hash& h) const { get()->hash(h); } Grid::Spec Grid::spec() const { return get()->spec(); } Grid::Config Grid::meshgenerator() const { return get()->meshgenerator(); } Grid::Config Grid::partitioner() const { return get()->partitioner(); } } // namespace atlas atlas-0.46.0/src/atlas/grid/CubedSphereGrid2.cc0000664000175000017500000000133515160212070021275 0ustar alastairalastair#include "atlas/grid/CubedSphereGrid2.h" #include "atlas/grid/detail/grid/CubedSphere2.h" namespace atlas { inline const CubedSphereGrid2::grid_t* cubedsphere_grid2(const Grid::Implementation* grid) { return dynamic_cast(grid); } CubedSphereGrid2::CubedSphereGrid2(idx_t resolution): Grid(new grid::detail::grid::CubedSphere2(resolution)), grid_(cubedsphere_grid2(get())) {} CubedSphereGrid2::CubedSphereGrid2(const Grid& grid): Grid(grid), grid_(cubedsphere_grid2(get())) {} CubedSphereGrid2::CubedSphereGrid2(idx_t resolution, Projection projection): Grid(new grid::detail::grid::CubedSphere2(resolution, projection)), grid_(cubedsphere_grid2(get())) {} } // namespace atlas atlas-0.46.0/src/atlas/grid/StructuredPartitionPolygon.h0000664000175000017500000000426115160212070023465 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include "atlas/domain/Domain.h" #include "atlas/functionspace/FunctionSpace.h" #include "atlas/library/config.h" #include "atlas/projection/Projection.h" #include "atlas/util/Config.h" #include "atlas/util/Point.h" #include "atlas/util/Polygon.h" namespace atlas { namespace grid { // ------------------------------------------------------------------- /** * @brief StructuredPartitionPolygon class that holds the boundary of a structured grid partition */ class StructuredPartitionPolygon : public util::PartitionPolygon { public: // methods //-- Constructors /// @brief Construct "size" polygon StructuredPartitionPolygon(const functionspace::FunctionSpaceImpl& fs, idx_t halo); //-- Accessors idx_t halo() const override { return halo_; } /// @brief Return the memory footprint of the Polygon size_t footprint() const override; void outputPythonScript(const eckit::PathName&, const eckit::Configuration& = util::NoConfig()) const override; PointsXY xy() const override; PointsLonLat lonlat() const override; const RectangleXY& inscribedDomain() const override { return inscribed_domain_; } void allGather(util::PartitionPolygons&) const override; private: // void print( std::ostream& ) const; // friend std::ostream& operator<<( std::ostream& s, const StructuredPartitionPolygon& p ) { // p.print( s ); // return s; // } //util::Polygon::edge_set_t compute_edges( std::vector&, std::vector& ); private: PointsXY points_; PointsXY inner_bounding_box_; RectangleXY inscribed_domain_; const functionspace::FunctionSpaceImpl& fs_; idx_t halo_; }; // ------------------------------------------------------------------- } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/Iterator.cc0000664000175000017500000000167615160212070020015 0ustar alastairalastair/* * (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/grid/Iterator.h" //--------------------------------------------------------------------------------------------------------------------- namespace atlas { namespace grid { IterateXY::iterator IterateXY::begin() const { return grid_.xy_begin(); } IterateXY::iterator IterateXY::end() const { return grid_.xy_end(); } IterateLonLat::iterator IterateLonLat::begin() const { return grid_.lonlat_begin(); } IterateLonLat::iterator IterateLonLat::end() const { return grid_.lonlat_end(); } } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/Grid.h0000664000175000017500000000661315160212070016747 0ustar alastairalastair/* * (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 #include "atlas/domain/Domain.h" #include "atlas/library/config.h" #include "atlas/projection/Projection.h" #include "atlas/util/ObjectHandle.h" namespace eckit { class Hash; } namespace atlas { class PointXY; class PointLonLat; namespace util { class Config; } namespace grid { class IterateXY; class IterateLonLat; namespace detail { namespace grid { class Grid; } } // namespace detail } // namespace grid } // namespace atlas namespace atlas { //--------------------------------------------------------------------------------------------------------------------- /// @class Grid /// @brief Most general grid container /// @details /// @code{.sh} /// Grid /// | /// +----------+----------+ /// | | /// StructuredGrid UnstructuredGrid /// | /// +--------------------+-----------------------+ /// | | | /// ReducedGrid GaussianGrid RegularGrid /// | | | | | /// +--------+--------+ +--------+--------+ +-----+ /// | | | /// ReducedGaussianGrid RegularGaussianGrid RegularLonLatGrid /// @endcode class Grid : DOXYGEN_HIDE(public util::ObjectHandle) { public: using Config = util::Config; using Spec = util::Config; using Domain = atlas::Domain; using Projection = atlas::Projection; using PointXY = atlas::PointXY; // must be sizeof(double)*2 using PointLonLat = atlas::PointLonLat; // must be sizeof(double)*2 using IterateXY = grid::IterateXY; using IterateLonLat = grid::IterateLonLat; public: IterateXY xy() const; IterateLonLat lonlat() const; using Handle::Handle; Grid() = default; Grid(const std::string& name, const Domain& = Domain()); Grid(const std::string& name, const Projection&, const Domain& = Domain()); Grid(const Grid&, const Domain&); Grid(const Config&); bool operator==(const Grid& other) const { return uid() == other.uid(); } bool operator!=(const Grid& other) const { return uid() != other.uid(); } idx_t size() const; const Projection& projection() const; const Domain& domain() const; RectangularLonLatDomain lonlatBoundingBox() const; std::string name() const; std::string type() const; std::string uid() const; /// Adds to the hash the information that makes this Grid unique void hash(eckit::Hash& h) const; size_t footprint() const; Spec spec() const; Config meshgenerator() const; Config partitioner() const; }; //--------------------------------------------------------------------------------------------------------------------- } // namespace atlas atlas-0.46.0/src/atlas/grid/SpecRegistry.cc0000664000175000017500000000154215160212070020637 0ustar alastairalastair/* * (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/grid/SpecRegistry.h" namespace atlas { namespace grid { //--------------------------------------------------------------------------------------------------------------------- util::Registry& SpecRegistry::registry() { static util::Registry registry; return registry; } //--------------------------------------------------------------------------------------------------------------------- } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/SpecRegistry.h0000664000175000017500000000223615160212070020502 0ustar alastairalastair/* * (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/util/Config.h" #include "atlas/util/Registry.h" namespace atlas { namespace grid { using Spec = util::Config; class SpecRegistry { public: static void add(const std::string& id, Spec&& spec) { registry().add(id, std::move(spec)); } static bool has(const std::string& id) { return registry().has(id); } static Spec get(const std::string& id) { return registry().get(id); } static void list(std::ostream& out) { return registry().list(out); } static std::vector keys() { return registry().keys(); } private: static util::Registry& registry(); }; //--------------------------------------------------------------------------------------------------------------------- } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/UnstructuredGrid.h0000664000175000017500000000500315160212070021367 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include "atlas/grid/Grid.h" #include "atlas/grid/detail/grid/Unstructured.h" namespace atlas { //--------------------------------------------------------------------------------------------------------------------- // Further grid interpretation classes defined in this file class UnstructuredGrid; /* Grid | +----------+----------+ | | StructuredGrid UnstructuredGrid */ //--------------------------------------------------------------------------------------------------------------------- class UnstructuredGrid : public Grid { public: using grid_t = grid::detail::grid::Unstructured; public: UnstructuredGrid(); UnstructuredGrid(const Grid&); UnstructuredGrid(const Config&); UnstructuredGrid(const Grid::Implementation*); UnstructuredGrid(std::vector*); // takes ownership UnstructuredGrid(std::initializer_list); UnstructuredGrid(const Grid&, const Domain&); // Create a new unstructured grid! operator bool() const { return valid(); } UnstructuredGrid(std::vector&&); // move constructor UnstructuredGrid(const std::vector&); // creates copy bool valid() const { return grid_; } using Grid::lonlat; using Grid::xy; // @brief Copy coordinates x,y of point `n` into xy[2] void xy(idx_t n, double xy[]) const { grid_->xy(n, xy); } // @brief Copy coordinates lon,lat of point `n` into lonlat[2] void lonlat(idx_t n, double lonlat[]) const { grid_->lonlat(n, lonlat); } // @brief Create PointXY at grid index `n` PointXY xy(idx_t n) const { return grid_->xy(n); } // @brief Create PointLonLat at grid index `n` PointLonLat lonlat(idx_t n) const { return grid_->lonlat(n); } private: const grid_t* grid_; }; //--------------------------------------------------------------------------------------------------------------------- } // namespace atlas atlas-0.46.0/src/atlas/grid/Distribution.cc0000664000175000017500000000375615160212070020704 0ustar alastairalastair/* * (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 "Distribution.h" #include "eckit/utils/MD5.h" #include "atlas/grid/Grid.h" #include "atlas/grid/Partitioner.h" #include "atlas/grid/detail/distribution/DistributionArray.h" #include "atlas/grid/detail/distribution/SerialDistribution.h" namespace atlas { namespace grid { using namespace detail::distribution; Distribution::Distribution(const Grid& grid): Handle(new SerialDistribution{grid}) {} Distribution::Distribution(const Grid& grid, const Config& config): Handle(Partitioner(config).partition(grid).get()) {} Distribution::Distribution(const Grid& grid, const Partitioner& partitioner): Handle(partitioner.partition(grid)) {} Distribution::Distribution(int nb_partitions, idx_t npts, int part[], int part0): Handle(new DistributionArray(nb_partitions, npts, part, part0)) {} Distribution::Distribution(int nb_partitions, partition_t&& part): Handle(new DistributionArray(nb_partitions, std::move(part))) {} Distribution::~Distribution() = default; const std::vector& Distribution::nb_pts() const { return get()->nb_pts(); } idx_t Distribution::max_pts() const { return get()->max_pts(); } idx_t Distribution::min_pts() const { return get()->min_pts(); } const std::string& Distribution::type() const { return get()->type(); } std::ostream& operator<<(std::ostream& os, const Distribution& distribution) { distribution.get()->print(os); return os; } void Distribution::hash(eckit::Hash& hash) const { get()->hash(hash); } std::string Distribution::hash() const { eckit::MD5 h; hash(h); return h.digest(); } } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/Distribution.h0000664000175000017500000000502215160212070020532 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include "atlas/grid/detail/distribution/DistributionImpl.h" #include "atlas/library/config.h" #include "atlas/util/Config.h" #include "atlas/util/ObjectHandle.h" #include "atlas/util/vector.h" namespace atlas { class Grid; namespace grid { class Partitioner; } // namespace grid } // namespace atlas namespace atlas { namespace grid { class Distribution : DOXYGEN_HIDE(public util::ObjectHandle) { friend class Partitioner; public: using Config = DistributionImpl::Config; using partition_t = atlas::vector; using Handle::Handle; Distribution() = default; /// @brief Create a serial distribution Distribution(const Grid&); /// @brief Create a distribution specified by a configuration Distribution(const Grid&, const Config&); /// @brief Create a distribution using a given partitioner Distribution(const Grid&, const Partitioner&); /// @brief Create a distribution by given array, and make internal copy Distribution(int nb_partitions, idx_t npts, int partition[], int part0 = 0); /// @brief Create a distribution by given array, and take ownership (move) Distribution(int nb_partitions, partition_t&& partition); ~Distribution(); ATLAS_ALWAYS_INLINE int partition(gidx_t index) const { return get()->partition(index); } template void partition(gidx_t begin, gidx_t end, PartitionContainer& partitions) const { ATLAS_ASSERT(end - begin <= partitions.size()); return get()->partition(begin, end, partitions.data()); } size_t footprint() const { return get()->footprint(); } ATLAS_ALWAYS_INLINE idx_t nb_partitions() const { return get()->nb_partitions(); } ATLAS_ALWAYS_INLINE gidx_t size() const { return get()->size(); } const std::vector& nb_pts() const; idx_t max_pts() const; idx_t min_pts() const; const std::string& type() const; friend std::ostream& operator<<(std::ostream& os, const Distribution& distribution); std::string hash() const; void hash(eckit::Hash&) const; }; } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/Tiles.cc0000664000175000017500000000512515160212070017275 0ustar alastairalastair/* * (C) Crown Copyright 2021 Met Office. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "atlas/grid/Tiles.h" #include "atlas/grid/detail/tiles/FV3Tiles.h" #include "atlas/grid/detail/tiles/LFRicTiles.h" #include "atlas/grid/detail/tiles/Tiles.h" #include "atlas/projection/Jacobian.h" namespace atlas { namespace grid { CubedSphereTiles::CubedSphereTiles(const eckit::Parametrisation& p): Handle(atlas::grid::detail::CubedSphereTiles::create(p)) {} CubedSphereTiles::CubedSphereTiles(const std::string& s): Handle(atlas::grid::detail::CubedSphereTiles::create(s)) {} std::string CubedSphereTiles::type() const { return get()->type(); } std::array, 2> CubedSphereTiles::xy2abOffsets() const { return get()->xy2abOffsets(); } std::array, 2> CubedSphereTiles::ab2xyOffsets() const { return get()->ab2xyOffsets(); } void CubedSphereTiles::rotate(idx_t t, double xyz[]) const { return get()->rotate(t, xyz); } void CubedSphereTiles::unrotate(idx_t t, double xyz[]) const { return get()->unrotate(t, xyz); } idx_t CubedSphereTiles::indexFromXY(const double xy[]) const { return get()->indexFromXY(xy); } idx_t CubedSphereTiles::indexFromXY(const PointXY& xy) const { return get()->indexFromXY(xy); } idx_t CubedSphereTiles::indexFromLonLat(const double lonlat[]) const { return get()->indexFromLonLat(lonlat); } idx_t CubedSphereTiles::indexFromLonLat(const PointLonLat& lonlat) const { return get()->indexFromLonLat(lonlat.data()); } idx_t CubedSphereTiles::size() const { return get()->size(); } void CubedSphereTiles::enforceXYdomain(double xy[]) const { return get()->enforceXYdomain(xy); } atlas::PointXY CubedSphereTiles::tileCubePeriodicity(const atlas::PointXY& xyExtended, const atlas::idx_t tile) const { return get()->tileCubePeriodicity(xyExtended, tile); } void CubedSphereTiles::print(std::ostream& os) const { get()->print(os); } std::ostream& operator<<(std::ostream& os, const CubedSphereTiles& t) { t.print(os); return os; } const PointXY& CubedSphereTiles::tileCentre(size_t t) const { return get()->tileCentre(t); } const projection::Jacobian& CubedSphereTiles::tileJacobian(size_t t) const { return get()->tileJacobian(t); } } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/Spacing.h0000664000175000017500000000530015160212070017436 0ustar alastairalastair/* * (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 #include "atlas/library/config.h" #include "atlas/util/ObjectHandle.h" //--------------------------------------------------------------------------------------------------------------------- #ifndef DOXYGEN_SHOULD_SKIP_THIS // Forward declarations namespace eckit { class Parametrisation; } namespace atlas { namespace util { class Config; } namespace grid { namespace spacing { class Spacing; } } // namespace grid } // namespace atlas #endif //--------------------------------------------------------------------------------------------------------------------- namespace atlas { namespace grid { //--------------------------------------------------------------------------------------------------------------------- class Spacing : DOXYGEN_HIDE(public util::ObjectHandle) { public: using const_iterator = std::vector::const_iterator; using Interval = std::array; using Spec = atlas::util::Config; public: using Handle::Handle; Spacing() = default; Spacing(const eckit::Parametrisation&); size_t size() const; double operator[](size_t i) const; const_iterator begin() const; const_iterator end() const; double front() const; double back() const; Interval interval() const; double min() const; double max() const; std::string type() const; Spec spec() const; }; //--------------------------------------------------------------------------------------------------------------------- class LinearSpacing : public Spacing { public: using Interval = std::array; public: using Spacing::Spacing; LinearSpacing() = default; LinearSpacing(double start, double stop, long N, bool endpoint = true); LinearSpacing(const Interval&, long N, bool endpoint = true); double step() const; }; //--------------------------------------------------------------------------------------------------------------------- class GaussianSpacing : public Spacing { public: using Spacing::Spacing; GaussianSpacing() = default; GaussianSpacing(long N); }; //--------------------------------------------------------------------------------------------------------------------- } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/0000775000175000017500000000000015160212070017145 5ustar alastairalastairatlas-0.46.0/src/atlas/grid/detail/tiles/0000775000175000017500000000000015160212070020265 5ustar alastairalastairatlas-0.46.0/src/atlas/grid/detail/tiles/LFRicTiles.h0000664000175000017500000000433315160212070022401 0ustar alastairalastair/* * (C) Crown Copyright 2021, Met Office. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include #include "atlas/grid/detail/tiles/Tiles.h" #include "atlas/util/Point.h" namespace atlas { namespace grid { namespace detail { class LFRicCubedSphereTiles : public CubedSphereTiles { public: // constructor LFRicCubedSphereTiles(const eckit::Parametrisation&); static std::string static_type() { return "cubedsphere_lfric"; } virtual std::string type() const override { return static_type(); } virtual std::array, 2> xy2abOffsets() const override; virtual std::array, 2> ab2xyOffsets() const override; virtual void rotate(idx_t t, double xyz[]) const override; virtual void unrotate(idx_t t, double xyz[]) const override; virtual idx_t indexFromXY(const double xy[]) const override; virtual idx_t indexFromLonLat(const double lonlat[]) const override; virtual void enforceXYdomain(double xy[]) const override; virtual PointXY tileCubePeriodicity(const PointXY& xyExtended, const idx_t tile) const override; virtual void print(std::ostream&) const override; virtual const PointXY& tileCentre(size_t t) const override; virtual const Jacobian& tileJacobian(size_t t) const override; private: static PointXY botLeftTile(size_t t); static PointXY botRightTile(size_t t); static PointXY topLeftTile(size_t t); static PointXY topRightTile(size_t t); bool withinCross(const idx_t t, const PointXY& withinRange) const; void enforceWrapAround(const idx_t t, PointXY& withinRange) const; // Centre of each tile in xy-space. static const std::array tileCentres_; // Jacobian of xy with respect to tile curvilinear coordinates. static const std::array tileJacobians_; }; } // namespace detail } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/tiles/FV3Tiles.h0000664000175000017500000000332015160212070022033 0ustar alastairalastair/* * (C) Crown Copyright 2021 Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include "atlas/grid/detail/tiles/Tiles.h" namespace atlas { namespace grid { namespace detail { class FV3CubedSphereTiles : public CubedSphereTiles { public: // constructor FV3CubedSphereTiles(const eckit::Parametrisation&); static std::string static_type() { return "cubedsphere_fv3"; } virtual std::string type() const override { return static_type(); } virtual std::array, 2> xy2abOffsets() const override; virtual std::array, 2> ab2xyOffsets() const override; virtual void rotate(idx_t t, double xyz[]) const override; virtual void unrotate(idx_t, double xyz[]) const override; virtual idx_t indexFromXY(const double xy[]) const override; virtual idx_t indexFromLonLat(const double lonlat[]) const override; virtual void enforceXYdomain(double xy[]) const override; virtual atlas::PointXY tileCubePeriodicity(const atlas::PointXY& xyExtended, const atlas::idx_t tile) const override; virtual void print(std::ostream&) const override; virtual const PointXY& tileCentre(size_t t) const override; virtual const Jacobian& tileJacobian(size_t t) const override; private: }; } // namespace detail } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/tiles/FV3Tiles.cc0000664000175000017500000003342315160212070022200 0ustar alastairalastair/* * (C) Crown Copyright 2021 Met Office. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include #include #include #include #include #include #include "eckit/utils/Hash.h" #include "atlas/grid/detail/tiles/TilesFactory.h" #include "atlas/grid/detail/tiles/FV3Tiles.h" #include "atlas/grid/detail/tiles/Tiles.h" #include "atlas/projection/detail/ProjectionUtilities.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Log.h" #include "atlas/runtime/Trace.h" #include "atlas/util/Constants.h" #include "atlas/util/CoordinateEnums.h" #include "atlas/util/Point.h" namespace { static constexpr bool debug = false; // constexpr so compiler can optimize `if ( debug ) { ... }` out static constexpr double rad2deg = atlas::util::Constants::radiansToDegrees(); using atlas::projection::detail::ProjectionUtilities; static bool is_tiny(const double& x) { constexpr double epsilon = 1.e-12; return (std::abs(x) < epsilon); } static bool is_same(const double& x, const double& y, const double& tol = 1.0) { constexpr double epsilon = 1.e-12; return (std::abs(x - y) < epsilon * tol); } static bool is_less(const double& lhs, const double& rhs) { constexpr double epsilon = 1.e-12; return lhs < rhs - epsilon; } static bool is_geq(const double& lhs, const double& rhs) { constexpr double epsilon = 1.e-12; return lhs >= rhs - epsilon; } void sphericalToCartesian(const double lonlat[], double xyz[]) { auto crd_sys = ProjectionUtilities::CoordinateSystem::LEFT_HAND; constexpr double radius = 1.; ProjectionUtilities::sphericalToCartesian(lonlat, xyz, crd_sys, radius); } } // namespace namespace atlas { namespace grid { namespace detail { // constructor FV3CubedSphereTiles::FV3CubedSphereTiles(const eckit::Parametrisation&) {} std::array, 2> FV3CubedSphereTiles::xy2abOffsets() const { return {{{0., 1., 1., 2., 3., 3.}, {1., 1., 2., 1., 1., 0.}}}; } std::array, 2> FV3CubedSphereTiles::ab2xyOffsets() const { return {{{0., 90., 90., 180., 270., 270.}, {-45., -45., 45., -45., -45., -135.}}}; } static void tile0Rotate(double xyz[]) { // Face 0, no rotation. } static void tile1Rotate(double xyz[]) { // Face 1: rotate -90.0 degrees about z axis constexpr double angle = -M_PI / 2.0; ProjectionUtilities::rotate3dZ(angle, xyz); } static void tile2Rotate(double xyz[]) { // Face 2: rotate -90.0 degrees about z axis // rotate 90.0 degrees about x axis constexpr double angle_z = -M_PI / 2.0; ProjectionUtilities::rotate3dZ(angle_z, xyz); constexpr double angle_x = M_PI / 2.0; ProjectionUtilities::rotate3dX(angle_x, xyz); } static void tile3Rotate(double xyz[]) { // Face 3: rotate -180.0 degrees about z axis constexpr double angle = -M_PI; ProjectionUtilities::rotate3dZ(angle, xyz); } static void tile4Rotate(double xyz[]) { // Face 4: rotate 90.0 degrees about z axis constexpr double angle = M_PI / 2.0; ProjectionUtilities::rotate3dZ(angle, xyz); } static void tile5Rotate(double xyz[]) { // Face 5: rotate 90.0 degrees about y axis // rotate 90.0 degrees about z axis constexpr double angle_y = M_PI / 2.0; ProjectionUtilities::rotate3dY(angle_y, xyz); constexpr double angle_z = M_PI / 2.0; ProjectionUtilities::rotate3dZ(angle_z, xyz); } void FV3CubedSphereTiles::rotate(idx_t t, double xyz[]) const { switch (t) { case 0: { tile0Rotate(xyz); break; } case 1: { tile1Rotate(xyz); break; } case 2: { tile2Rotate(xyz); break; } case 3: { tile3Rotate(xyz); break; } case 4: { tile4Rotate(xyz); break; } case 5: { tile5Rotate(xyz); break; } default: { throw_OutOfRange("t", t, 6); } } } static void tile0RotateInverse(double xyz[]) { // Face 0, no rotation. } static void tile1RotateInverse(double xyz[]) { // Face 1: rotate 90.0 degrees about z axis constexpr double angle = M_PI / 2.0; ProjectionUtilities::rotate3dZ(angle, xyz); } static void tile2RotateInverse(double xyz[]) { // Face 2: rotate 90.0 degrees about x axis // rotate -90.0 degrees about z axis constexpr double angle_x = -M_PI / 2.0; ProjectionUtilities::rotate3dX(angle_x, xyz); constexpr double angle_z = M_PI / 2.0; ProjectionUtilities::rotate3dZ(angle_z, xyz); } static void tile3RotateInverse(double xyz[]) { // Face 3: rotate 180.0 degrees about z axis constexpr double angle = M_PI; ProjectionUtilities::rotate3dZ(angle, xyz); } static void tile4RotateInverse(double xyz[]) { // Face 4: rotate -90.0 degrees about y axis constexpr double angle = -M_PI / 2.0; ProjectionUtilities::rotate3dZ(angle, xyz); } static void tile5RotateInverse(double xyz[]) { // Face 5: rotate -90.0 degrees about y axis // rotate -90.0 degrees about z axis constexpr double angle_z = -M_PI / 2.; ProjectionUtilities::rotate3dZ(angle_z, xyz); constexpr double angle_y = -M_PI / 2.; ProjectionUtilities::rotate3dY(angle_y, xyz); } void FV3CubedSphereTiles::unrotate(idx_t t, double xyz[]) const { switch (t) { case 0: { tile0RotateInverse(xyz); break; } case 1: { tile1RotateInverse(xyz); break; } case 2: { tile2RotateInverse(xyz); break; } case 3: { tile3RotateInverse(xyz); break; } case 4: { tile4RotateInverse(xyz); break; } case 5: { tile5RotateInverse(xyz); break; } default: { throw_OutOfRange("t", t, 6); } } } idx_t FV3CubedSphereTiles::indexFromXY(const double xy[]) const { // Assume one face-edge is of length 90 degrees. // // y ^ // | // 135 ---------- // | | ^ | // | | | // | |=< 2 <| // | | v | // | | = | // 45 0----------2----------3----------4---------- // | | ^ | ^ | = | = | // | | | | ^ | ^ | // | |=< 0 <|=< 1 <|=< 3 <|=< 4 <| // | | v | v | | | // | | = | = | v | v | // -45 0 ---------1----------1----------5---------- // | | = | // | | ^ | // | |=< 5 <| // | | | // | | v | // -135 ----------(5 for end iterator) // ----0---------90--------180--------270--------360---> x idx_t t{-1}; if ((xy[XX] >= 0.) && (xy[YY] >= -45.) && (xy[XX] < 90.) && (xy[YY] < 45.)) { t = 0; } else if ((xy[XX] >= 90.) && (xy[YY] >= -45.) && (xy[XX] < 180.) && (xy[YY] < 45.)) { t = 1; } else if ((xy[XX] >= 90.) && (xy[YY] >= 45.) && (xy[XX] < 180.) && (xy[YY] < 135.)) { t = 2; } else if ((xy[XX] >= 180.) && (xy[YY] > -45.) && (xy[XX] < 270.) && (xy[YY] <= 45.)) { t = 3; } else if ((xy[XX] >= 270.) && (xy[YY] > -45.) && (xy[XX] < 360.) && (xy[YY] <= 45.)) { t = 4; } else if ((xy[XX] >= 270.) && (xy[YY] > -135.) && (xy[XX] < 360.) && (xy[YY] <= -45.)) { t = 5; } // extra points if (is_same(xy[XX], 0.) && is_same(xy[YY], 45.)) { t = 0; } if (is_same(xy[XX], 180.) && is_same(xy[YY], -45.)) { t = 1; } // for end iterator !!!! if (is_same(xy[XX], 360.) && is_same(xy[YY], -135.)) { t = 5; } ATLAS_ASSERT(t >= 0); return t; } // Calculates the FV3 panel // Input (crd) is Longitude and Latitude in Radians // Output is tile number idx_t FV3CubedSphereTiles::indexFromLonLat(const double crd[]) const { idx_t t(-1); // tile index double xyz[3]; sphericalToCartesian(crd, xyz); static const double cornerLat = std::asin(1. / std::sqrt(3.0)) * rad2deg; // the magnitude of the latitude at the corners of the cube (not including the sign) // in radians. const double& lon = crd[LON]; const double& lat = crd[LAT]; double zPlusAbsX = xyz[ZZ] + std::abs(xyz[XX]); double zPlusAbsY = xyz[ZZ] + std::abs(xyz[YY]); double zMinusAbsX = xyz[ZZ] - std::abs(xyz[XX]); double zMinusAbsY = xyz[ZZ] - std::abs(xyz[YY]); // Note that this method can lead to roundoff errors that can // cause the tile selection to fail. // To this end we enforce that tiny values close (in roundoff terms) // to a boundary should end up exactly on the boundary. if (is_tiny(zPlusAbsX)) { zPlusAbsX = 0.; } if (is_tiny(zPlusAbsY)) { zPlusAbsY = 0.; } if (is_tiny(zMinusAbsX)) { zMinusAbsX = 0.; } if (is_tiny(zMinusAbsY)) { zMinusAbsY = 0.; } if (is_geq(lon, 315.) || is_less(lon, 45.)) { if ((zPlusAbsX <= 0.) && (zPlusAbsY <= 0.)) { t = 2; } else if ((zMinusAbsX > 0.) && (zMinusAbsY > 0.)) { t = 5; } else { t = 0; } // extra point corner point (considering two different longitudes depending on // whether we are on longitude points [0,360] or [-45.,45.] if (is_same(lon, -45.) && is_same(lat, cornerLat)) { t = 0; } if (is_same(lon, 315.) && is_same(lat, cornerLat)) { t = 0; } } if (is_geq(lon, 45.) && is_less(lon, 135.)) { // interior if ((zPlusAbsX <= 0.) && (zPlusAbsY <= 0.)) { t = 2; } else if ((zMinusAbsX > 0.) && (zMinusAbsY > 0.)) { t = 5; } else { t = 1; } } if (is_geq(lon, 135.) && is_less(lon, 225.)) { // interior if ((zPlusAbsX < 0.) && (zPlusAbsY < 0.)) { t = 2; } else if ((zMinusAbsX >= 0.) && (zMinusAbsY >= 0.)) { t = 5; } else { t = 3; } // extra point corner point if (is_same(lon, 135.) && is_same(lat, -cornerLat)) { t = 1; } } if (is_geq(lon, 225.) && is_less(lon, 315.)) { // interior if ((zPlusAbsX < 0.) && (zPlusAbsY < 0.)) { t = 2; } else if ((zMinusAbsX >= 0.) && (zMinusAbsY >= 0.)) { t = 5; } else { t = 4; } } if (debug) { Log::info() << "tileFromLonLat:: lonlat abs xyz t = " << std::setprecision(std::numeric_limits::digits10 + 1) << zPlusAbsX << " " << zPlusAbsY << " " << zMinusAbsX << " " << zMinusAbsY << " " << crd[LON] << " " << crd[LAT] << " " << xyz[XX] << " " << xyz[YY] << " " << xyz[ZZ] << " " << t << std::endl; } return t; } void FV3CubedSphereTiles::enforceXYdomain(double xy[]) const { // the conversion from lonlat to xy can involve some small errors and small errors // can affect whether xy that is created within a valid space // This has been tested with N = 512 with equiangular and equidistant projections. const double tol{70.0}; constexpr double epsilon = std::numeric_limits::epsilon(); if (debug) { Log::info() << "enforceXYDomain before " << xy[XX] << " " << xy[YY] << std::endl; } xy[XX] = std::max(xy[XX], 0.0); xy[XX] = std::min(xy[XX], 360.0 - epsilon); xy[YY] = std::max(xy[YY], -135.0 + epsilon); xy[YY] = std::min(xy[YY], 135.0 - epsilon); if (is_same(xy[XX], 90.0, tol)) { xy[XX] = 90.0; } if (is_same(xy[XX], 180.0, tol)) { xy[XX] = 180.0; } if (is_same(xy[XX], 270.0, tol)) { xy[XX] = 270.0; } if (is_same(xy[YY], -45.0, tol) && (xy[XX] <= 180.0)) { xy[YY] = -45.0; } if (is_same(xy[YY], 45.0, tol) && (xy[XX] >= 180.0)) { xy[YY] = 45.0; } if (is_same(xy[YY], 45.0, tol) && is_same(xy[XX], 0.0, tol)) { xy[XX] = 0.0; xy[YY] = 45.0; } if (debug) { Log::info() << "enforceXYDomain after " << xy[XX] << " " << xy[YY] << std::endl; } } atlas::PointXY FV3CubedSphereTiles::tileCubePeriodicity(const atlas::PointXY& xyExtended, const atlas::idx_t tile) const { throw_NotImplemented("tileCubePeriodicity not implemented for FV3Tiles", Here()); } void FV3CubedSphereTiles::print(std::ostream& os) const { os << "FV3CubedSphereTiles" << "]"; } const PointXY& FV3CubedSphereTiles::tileCentre(size_t t) const { throw_NotImplemented("tileCentre not implemented for FV3Tiles", Here()); } const FV3CubedSphereTiles::Jacobian& FV3CubedSphereTiles::tileJacobian(size_t t) const { throw_NotImplemented("tileJacobian not implemented for FV3Tiles", Here()); ; } namespace { static CubedSphereTilesBuilder register_builder(FV3CubedSphereTiles::static_type()); } } // namespace detail } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/tiles/LFRicTiles.cc0000664000175000017500000006766315160212070022556 0ustar alastairalastair/* * (C) Crown Copyright 2021 Met Office. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include #include #include #include "atlas/grid/detail/tiles/LFRicTiles.h" #include "atlas/grid/detail/tiles/Tiles.h" #include "atlas/grid/detail/tiles/TilesFactory.h" #include "atlas/projection/detail/ProjectionUtilities.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Log.h" #include "atlas/util/CoordinateEnums.h" namespace atlas { namespace grid { namespace detail { namespace { static constexpr bool debug = false; // constexpr so compiler can optimize `if ( debug ) { ... }` out static constexpr double epsilon = 1.e-12; using projection::detail::ProjectionUtilities; static bool is_tiny(const double& x) { return (std::abs(x) < epsilon); } static bool is_same(const double& x, const double& y, const double& tol = 1.0) { return (std::abs(x - y) < epsilon * tol); } static bool is_less(const double& lhs, const double& rhs) { return lhs < rhs - epsilon; } static bool is_geq(const double& lhs, const double& rhs) { return lhs >= rhs - epsilon; } static void sphericalToCartesian(const double lonlat[], double xyz[]) { auto crd_sys = ProjectionUtilities::CoordinateSystem::LEFT_HAND; constexpr double radius = 1.; ProjectionUtilities::sphericalToCartesian(lonlat, xyz, crd_sys, radius); } static PointXY rotatePlus90AboutPt(const PointXY& xy, const PointXY& origin) { return PointXY{-xy.y() + origin.x() + origin.y(), xy.x() - origin.x() + origin.y()}; } static PointXY rotateMinus90AboutPt(const PointXY& xy, const PointXY& origin) { return PointXY{xy.y() + origin.x() - origin.y(), -xy.x() + origin.x() + origin.y()}; } static PointXY rotatePlus180AboutPt(const PointXY& xy, const PointXY& origin) { return PointXY{2.0 * origin.x() - xy.x(), 2.0 * origin.y() - xy.y()}; } } // anonymous namespace // constructor LFRicCubedSphereTiles::LFRicCubedSphereTiles(const eckit::Parametrisation&) {} std::array, 2> LFRicCubedSphereTiles::xy2abOffsets() const { return {{{0., 1., 2., 3., 0., 0.}, {1., 1., 1., 1., 2., 0.}}}; } std::array, 2> LFRicCubedSphereTiles::ab2xyOffsets() const { return {{{0., 90., 180., 270., 0., 0.}, {-45., -45., -45., -45., 45., -135.}}}; } void tile0Rotate(double xyz[]) { // Face 0, no rotation. } void tile1Rotate(double xyz[]) { double xyz_in[3]; std::copy(xyz, xyz + 3, xyz_in); xyz[XX] = -xyz_in[YY]; xyz[YY] = xyz_in[XX]; } void tile2Rotate(double xyz[]) { double xyz_in[3]; std::copy(xyz, xyz + 3, xyz_in); xyz[XX] = -xyz_in[XX]; xyz[YY] = -xyz_in[YY]; } void tile3Rotate(double xyz[]) { double xyz_in[3]; std::copy(xyz, xyz + 3, xyz_in); xyz[XX] = xyz_in[YY]; xyz[YY] = -xyz_in[XX]; } void tile4Rotate(double xyz[]) { double xyz_in[3]; std::copy(xyz, xyz + 3, xyz_in); xyz[XX] = xyz_in[ZZ]; xyz[ZZ] = -xyz_in[XX]; } void tile5Rotate(double xyz[]) { double xyz_in[3]; std::copy(xyz, xyz + 3, xyz_in); xyz[XX] = -xyz_in[ZZ]; xyz[ZZ] = xyz_in[XX]; } void tile0RotateInverse(double xyz[]) { // Face 0, no rotation. } void tile1RotateInverse(double xyz[]) { double xyz_in[3]; std::copy(xyz, xyz + 3, xyz_in); xyz[XX] = xyz_in[YY]; xyz[YY] = -xyz_in[XX]; } void tile2RotateInverse(double xyz[]) { double xyz_in[3]; std::copy(xyz, xyz + 3, xyz_in); xyz[XX] = -xyz_in[XX]; xyz[YY] = -xyz_in[YY]; } void tile3RotateInverse(double xyz[]) { double xyz_in[3]; std::copy(xyz, xyz + 3, xyz_in); xyz[XX] = -xyz_in[YY]; xyz[YY] = xyz_in[XX]; } void tile4RotateInverse(double xyz[]) { double xyz_in[3]; std::copy(xyz, xyz + 3, xyz_in); xyz[XX] = -xyz_in[ZZ]; xyz[ZZ] = xyz_in[XX]; } void tile5RotateInverse(double xyz[]) { double xyz_in[3]; std::copy(xyz, xyz + 3, xyz_in); xyz[XX] = xyz_in[ZZ]; xyz[ZZ] = -xyz_in[XX]; } void LFRicCubedSphereTiles::rotate(idx_t t, double xyz[]) const { switch (t) { case 0: { tile0Rotate(xyz); break; } case 1: { tile1Rotate(xyz); break; } case 2: { tile2Rotate(xyz); break; } case 3: { tile3Rotate(xyz); break; } case 4: { tile4Rotate(xyz); break; } case 5: { tile5Rotate(xyz); break; } default: { Log::info() << "ERROR: t out of range" << std::endl; throw_OutOfRange("t", t, 6); } } } void LFRicCubedSphereTiles::unrotate(idx_t t, double xyz[]) const { switch (t) { case 0: { tile0RotateInverse(xyz); break; } case 1: { tile1RotateInverse(xyz); break; } case 2: { tile2RotateInverse(xyz); break; } case 3: { tile3RotateInverse(xyz); break; } case 4: { tile4RotateInverse(xyz); break; } case 5: { tile5RotateInverse(xyz); break; } default: { throw_OutOfRange("t", t, 6); } } } idx_t LFRicCubedSphereTiles::indexFromXY(const double xy[]) const { // Assume one face-edge is of length 90 degrees. // // y ^ // | // 135 ---------- // | | <= | // | | | // | |=< 4 <=| // | | v | // | | <= | // 45 4----------4----------4----------4---------- // | | ^ | ^ | ^ | ^ | // | | | | | | // | |=< 0 <|=< 1 <|=< 2 <|=< 3 <| // | | v | v | v | v | // | | = | = | = | = | // -45 0 ---------1----------2----------3---------- // | | ^ | // | | | // | | < 5 <| // | | | // | | v | // -135 ----------(5 for end iterator) // ----0---------90--------180--------270--------360---> x idx_t t{-1}; // tile index if ((xy[XX] >= 0.) && (xy[YY] >= -45.) && (xy[XX] < 90.) && (xy[YY] < 45.)) { t = 0; } else if ((xy[XX] >= 90.) && (xy[YY] >= -45.) && (xy[XX] < 180.) && (xy[YY] < 45.)) { t = 1; } else if ((xy[XX] >= 180.) && (xy[YY] >= -45.) && (xy[XX] < 270.) && (xy[YY] < 45.)) { t = 2; } else if ((xy[XX] >= 270.) && (xy[YY] >= -45.) && (xy[XX] < 360.) && (xy[YY] < 45.)) { t = 3; } else if ((xy[XX] >= 0.) && (xy[YY] >= 45.) && (xy[XX] <= 90.) && (xy[YY] <= 135.)) { t = 4; } else if ((xy[XX] > 0.) && (xy[YY] > -135.) && (xy[XX] < 90.) && (xy[YY] < -45.)) { t = 5; } return t; } idx_t LFRicCubedSphereTiles::indexFromLonLat(const double crd[]) const { idx_t t{-1}; // tile index double xyz[3]; sphericalToCartesian(crd, xyz); const double& lon = crd[LON]; double zPlusAbsX = xyz[ZZ] + std::abs(xyz[XX]); double zPlusAbsY = xyz[ZZ] + std::abs(xyz[YY]); double zMinusAbsX = xyz[ZZ] - std::abs(xyz[XX]); double zMinusAbsY = xyz[ZZ] - std::abs(xyz[YY]); if (is_tiny(zPlusAbsX)) { zPlusAbsX = 0.; } if (is_tiny(zPlusAbsY)) { zPlusAbsY = 0.; } if (is_tiny(zMinusAbsX)) { zMinusAbsX = 0.; } if (is_tiny(zMinusAbsY)) { zMinusAbsY = 0.; } if ((zPlusAbsX <= 0.) && (zPlusAbsY <= 0.)) { t = 4; } else if ((zMinusAbsX > 0.) && (zMinusAbsY > 0.)) { t = 5; } else if (is_geq(lon, 315.) || is_less(lon, 45.)) { t = 0; } else if (is_geq(lon, 45.) && is_less(lon, 135.)) { t = 1; } else if (is_geq(lon, 135.) && is_less(lon, 225.)) { t = 2; } else if (is_geq(lon, 225.) && is_less(lon, 315.)) { t = 3; } return t; } void LFRicCubedSphereTiles::enforceXYdomain(double xy[]) const { // the conversion from lonlat to xy can involve some small errors and small errors // can affect whether xy that is created within a valid space // This has been tested with N = 512 with equiangular and equidistant projections. const double tol{70.0}; constexpr double epsilon = std::numeric_limits::epsilon(); if (debug) { Log::info() << "enforceXYDomain before " << xy[XX] << " " << xy[YY] << std::endl; } if (is_same(xy[YY], 45.0, tol)) { xy[YY] = 45.0; } if (is_same(xy[YY], -45.0, tol)) { xy[YY] = -45.0; } if (xy[YY] < -45.0) { xy[XX] = std::min(xy[XX], 90.0 - epsilon); xy[XX] = std::max(xy[XX], 0.0 + epsilon); } else if (xy[YY] >= -45.0) { xy[XX] = std::max(xy[XX], 0.0); } if (xy[YY] >= 45.0) { xy[XX] = std::min(xy[XX], 90.0); } xy[XX] = std::max(xy[XX], 0.0); xy[XX] = std::min(xy[XX], 360.0 - epsilon); xy[YY] = std::max(xy[YY], -135.0 + epsilon); xy[YY] = std::min(xy[YY], 135.0); if (debug) { Log::info() << "enforceXYDomain after " << xy[XX] << " " << xy[YY] << std::endl; } } // input is the xy value as PointXY that is a continous "cross " extension in terms of xy space from the tile in question // the output is an xy value that lives on the standard "|---" shape PointXY LFRicCubedSphereTiles::tileCubePeriodicity(const PointXY& xyExtended, const idx_t t) const { // xy space is a function of tiles--Tile 0) // xy space for Tile 1 // // // y ^ // y ^ // // | // | // 225 ---------- // 225 ---------- // | |To 2 | // | |To 3 as | // | |rotate | // | |below inst| // | |-180 | // | |and rotate| // | |about | // | |+90 about | // | |(90,90) | // | |(0,45) + |replace in [0,360) x range // 135 4----------4 // 135 4----------4 // | | <= | // | |To 4 | // | | | // | |rotate | // | |=< 4 <=| // | |+90 about | // | | v | // | |(90,45) | // | |* <= | // | | | // 45 4----------4----------4----------4---------- // 45 4----------4----------4----------4---------- // | | ^ | ^ | ^ | ^ | // | | ^ | ^ | ^ | ^ | // | | | | | | // | | | | | | // | |=< 0 <|=< 1 <|=< 2 <|=< 3 <| // | |=< 0 <|=< 1 <|=< 2 <|=< 3 <| // | | v | v | v | v | // | | v | v | v | v | // | |* = |* = |* = | * = | // | |* = |* = |* = | * = | // -45 0 ---------1----------2----------3---------- // -45 0 ---------1----------2----------3---------- // | | ^ | // | |To 5 | // | | | // | |rotate | // | |< 5 <| // | |-90 about | // | | | // | |(90,-45) | // | |* v | // | | | // -135 ---------- ---------- ---------- ---------- // -135 ---------- ---------- ---------- ---------- // ----0---------90--------180--------270--------360---> x ----0---------90--------180--------270--------360---> x // xy space for Tile 2 // xy space for Tile 3 // // // y ^ // y ^ // | // | // 225 ---------- // 225 ---------- // | |To 0 | // | |To 1 | // | |rotate-180| // | |rotate-180| // | |about | // | |about | // | |(135.,90) | // | |(225,90) | // | | | // | | | // 135 4----------4 // 135 4----------4 // | |To 4 dble | // | |To 4 | // | |+90 | // | |-90 | // | |rotations | // | |rotation | // | |about(180,45) // | |about(360,45) // | |(90,45) | // | |and | replace in x range // 45 4----------4----------4----------4---------- // 45 4----------4----------4----------4---------- // | | ^ | ^ | ^ | ^ | // | | ^ | ^ | ^ | ^ | // | | | | | | // | | | | | | // | |=< 0 <|=< 1 <|=< 2 <|=< 3 <| // | |=< 0 <|=< 1 <|=< 2 <|=< 3 <| // | | v | v | v | v | // | | v | v | v | v | // | |* = |* = |* = | * = | // | |* = |* = |* = | * = | // -45 0 ---------1----------2----------3---------- // -45 0 ---------1----------2----------3---------- // | |To 5 dble | // | |To 5 | // | |-90 | // | |90 | // | |rotations | // | |rotation | // | |about (180,-45)| // | |about (360,-45)| // | |(90,-45) | // | |and |replace in x range // -135 ---------- ---------- ---------- ---------- // -135 ---------- ---------- ---------- ---------- // ----0---------90--------180--------270--------360---> x// ----0---------90--------180--------270--------360---> x // xy space for Tile 4's // xy space Tile 5's // // // y ^ // y ^ // | // | // 225 ---------- // 225 ---------- // | |To 2 | // | |To 2 | // | |rotate | // | |rotate | // | |-180 | // | |-180 | // | |about | // | |about | // | |(135.,90) | // | |(135.,90) | // 135 4----------4----------4----------4----------| // 135 4----------4 // | | <= |To 1 |To 5 |To 3 | // | | <= | // | | |rotate |rotate 180|rotate+90 | // | | | // | |=< 4 <=|-90 about |about |about | // | |=< 4 <=| // | | v |(90,45) | | | // | | v | // | |* <= | |(135, 0) |(360, 45) | // | |* <= | // 45 4----------4----------4----------4---------- // 45 4----------4 // | | ^ | // | | ^ | // | | | // | | | // | |=< 0 <| // | |= 0 >| // | | v | // | | | // | |* = | // | |* = | // -45 0 ---------1 // -45 0 ---------1----------2----------3----------| // | | ^ | // | | ^ |To 1 |To 4 |To 3 as | // | | | // | | |rotate |rotate |left instr| // | |< 5 <| // | |< 5 <|+90 about |+180 |+ rotate | // | | | // | | |(90,-45) |(135,0) |+90 about | // | |* v | // | |* v | | |(0, 45) |+ replace in [0,360 range) // -135 ---------- ---------- ---------- ---------- // -135 ---------- ---------- ---------- ---------- // ----0---------90--------180--------270--------360---> x// ----0---------90--------180--------270--------360---> x // Step 1: wrap into range x = [ 0, 360], y = [135, 225] ensuring that we stay // on the cross associated with tile index. if (!withinCross(t, xyExtended)) { ATLAS_THROW_EXCEPTION("Point (" << xyExtended.x() << "," << xyExtended.y() << ")" << " is not in the cross extension of tile " << t); } PointXY withinRange = xyExtended; enforceWrapAround(t, withinRange); PointXY finalXY = withinRange; PointXY tempXY = withinRange; switch (t) { case 0: finalXY = (withinRange.y() > 135.0) ? rotatePlus180AboutPt(withinRange, PointXY{135.0, 90.0}) : withinRange; break; case 1: if ((withinRange.x() >= 90.0) && (withinRange.x() <= 180.0)) { if (withinRange.y() >= 45.0) { tempXY = rotatePlus90AboutPt(withinRange, PointXY{90.0, 45.0}); if (withinRange.y() > 135.0) { finalXY = rotatePlus90AboutPt(tempXY, PointXY{0.0, 45.0}); finalXY.x() += 360.0; } else { finalXY = tempXY; } } else if (withinRange.y() < -45.0) { finalXY = rotateMinus90AboutPt(withinRange, PointXY{90.0, -45.0}); } } break; case 2: if ((withinRange.x() >= 180.0) && (withinRange.x() <= 270.0)) { if (withinRange.y() > 135.0) { finalXY = rotatePlus180AboutPt(tempXY, PointXY{135.0, 90.0}); } else if (withinRange.y() >= 45.0) { tempXY = rotatePlus90AboutPt(withinRange, PointXY{180.0, 45.0}); finalXY = rotatePlus90AboutPt(tempXY, PointXY{90.0, 45.0}); } else if (withinRange.y() < -45.0) { tempXY = rotateMinus90AboutPt(withinRange, PointXY{180.0, -45.0}); finalXY = rotateMinus90AboutPt(tempXY, PointXY{90.0, -45.0}); } } break; case 3: if (((withinRange.x() >= 270.0) && (withinRange.x() <= 360.0)) || withinRange.x() == 0.0) { if (withinRange.y() > 135.0) { finalXY = rotatePlus180AboutPt(tempXY, PointXY{225.0, 90.0}); } else if (withinRange.y() >= 45.0) { finalXY = rotateMinus90AboutPt(tempXY, PointXY{360.0, 45.0}); finalXY.x() -= 360.0; } else if (withinRange.y() < -45.0) { finalXY = rotatePlus90AboutPt(tempXY, PointXY{360.0, -45.0}); finalXY.x() -= 360.0; } } break; case 4: if (withinRange.y() > 135.0) { finalXY = rotatePlus180AboutPt(tempXY, PointXY{135.0, 90.0}); } else if ((withinRange.y() >= 45.0) && (withinRange.y() <= 135.0)) { if ((withinRange.x() > 90.0) && (withinRange.x() <= 180.0)) { finalXY = rotateMinus90AboutPt(withinRange, PointXY{90.0, 45.0}); } if ((withinRange.x() > 180.0) && (withinRange.x() <= 270.0)) { finalXY = rotatePlus180AboutPt(withinRange, PointXY{135.0, 0.0}); } if ((withinRange.x() > 270.0) && (withinRange.x() <= 360.0)) { finalXY = rotatePlus90AboutPt(withinRange, PointXY{360.0, 45.0}); } } break; case 5: if (withinRange.y() > 135.0) { finalXY = rotatePlus180AboutPt(tempXY, PointXY{135.0, 90.0}); } else if ((withinRange.y() <= -45.0) && (withinRange.y() >= -135.0)) { if ((withinRange.x() > 90.0) && (withinRange.x() <= 180.0)) { finalXY = rotatePlus90AboutPt(withinRange, PointXY{90.0, -45.0}); } if ((withinRange.x() > 180.0) && (withinRange.x() <= 270.0)) { finalXY = rotatePlus180AboutPt(withinRange, PointXY{135.0, 0.0}); } if ((withinRange.x() > 270.0) && (withinRange.x() <= 360.0)) { finalXY = rotateMinus90AboutPt(withinRange, PointXY{360.0, -45.0}); } } break; } // Now we are on the standard tiles with their default rotate orientation. // The next step is to wrap the edges and corners to the correct tiles. if (finalXY.x() > 90. && finalXY.x() < 180. && finalXY.y() == 45.0) { finalXY.y() = 45.0 + (finalXY.x() - 90.0); finalXY.x() = 90.0; } if (finalXY.x() == 180. && finalXY.y() == 45.0) { finalXY.x() = 90.; finalXY.y() = 135.0; } if (finalXY.x() > 180. && finalXY.x() < 270. && finalXY.y() == 45.0) { finalXY.x() = 90.0 - (finalXY.x() - 90.0); finalXY.y() = 135.0; } if (finalXY.x() == 270. && finalXY.y() == 45.0) { finalXY.x() = 0.; finalXY.y() = 135.0; } if (finalXY.x() > 270. && finalXY.x() <= 360. && finalXY.y() == 45.0) { finalXY.y() = 135.0 - (finalXY.x() - 270.0); finalXY.x() = 0.0; } if (finalXY.x() >= 360.) { finalXY.x() -= 360.; } if (finalXY.x() == 0. && finalXY.y() < -45. && finalXY.y() > -135.0) { finalXY.x() = 360.0 + (finalXY.y() + 45.0); finalXY.y() = -45.; } if (finalXY.x() == 90. && finalXY.y() < -45. && finalXY.y() > -135.0) { finalXY.x() = 90.0 - (finalXY.y() + 45.0); finalXY.y() = -45.; } if (finalXY.y() <= -135. && finalXY.x() >= 0. && finalXY.x() <= 90.0) { finalXY.x() = 270.0 - finalXY.x(); finalXY.y() = -45.; } return finalXY; } void LFRicCubedSphereTiles::print(std::ostream& os) const { os << "CubedSphereTiles[" << "]"; } bool LFRicCubedSphereTiles::withinCross(const idx_t tiles, const PointXY& withinRange) const { std::size_t t = static_cast(tiles); return !((withinRange.x() < botLeftTile(t).x() && withinRange.y() < botLeftTile(t).y()) || (withinRange.x() > botRightTile(t).x() && withinRange.y() < botRightTile(t).y()) || (withinRange.x() > topRightTile(t).x() && withinRange.y() > topRightTile(t).y()) || (withinRange.x() < topLeftTile(t).x() && withinRange.y() > topLeftTile(t).y())); } void LFRicCubedSphereTiles::enforceWrapAround(const idx_t t, PointXY& withinRange) const { if (withinRange.x() < 0.0) { PointXY temp = withinRange; temp.x() += 360; if (withinCross(t, temp)) { withinRange = temp; } } if (withinRange.x() > 360.0) { PointXY temp = withinRange; temp.x() -= 360; if (withinCross(t, temp)) { withinRange = temp; } } if (withinRange.y() <= -135.0) { PointXY temp = withinRange; temp.y() += 360; if (withinCross(t, temp)) { withinRange = temp; } } if (withinRange.y() > 225.0) { PointXY temp = withinRange; temp.y() -= 360; if (withinCross(t, temp)) { withinRange = temp; } } return; } const PointXY& LFRicCubedSphereTiles::tileCentre(size_t t) const { return tileCentres_[t]; } const LFRicCubedSphereTiles::Jacobian& LFRicCubedSphereTiles::tileJacobian(size_t t) const { return tileJacobians_[t]; } PointXY LFRicCubedSphereTiles::botLeftTile(size_t t) { return tileCentres_[t] + PointXY{-45., -45.}; } PointXY LFRicCubedSphereTiles::botRightTile(size_t t) { return tileCentres_[t] + PointXY{45., -45.}; } PointXY LFRicCubedSphereTiles::topLeftTile(size_t t) { return tileCentres_[t] + PointXY{-45., 45.}; } PointXY LFRicCubedSphereTiles::topRightTile(size_t t) { return tileCentres_[t] + PointXY{45., 45.}; } // Centre of each tile in xy space. const std::array LFRicCubedSphereTiles::tileCentres_{ PointXY{45., 0.}, PointXY{135., 0.}, PointXY{225., 0.}, PointXY{315., 0.}, PointXY{45., 90.}, PointXY{45., -90.}}; // Jacobian of xy space with respect to curvilinear coordinates for each tile. const std::array LFRicCubedSphereTiles::tileJacobians_{ Jacobian{{1., 0.}, {0., 1.}}, Jacobian{{1., 0.}, {0., 1.}}, Jacobian{{0., -1.}, {1., 0.}}, Jacobian{{0., -1.}, {1., 0.}}, Jacobian{{1., 0.}, {0., 1.}}, Jacobian{{0., 1.}, {-1., 0.}}}; namespace { static CubedSphereTilesBuilder register_builder(LFRicCubedSphereTiles::static_type()); } } // namespace detail } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/tiles/Tiles.cc0000664000175000017500000000327415160212070021662 0ustar alastairalastair/* * (C) Crown Copyright 2021 Met Office. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include #include "eckit/utils/MD5.h" #include "atlas/grid/detail/tiles/Tiles.h" #include "atlas/grid/detail/tiles/TilesFactory.h" #include "atlas/runtime/Exception.h" #include "atlas/util/Config.h" namespace atlas { namespace grid { namespace detail { const CubedSphereTiles* CubedSphereTiles::create() { // default: LFRic version (for now) util::Config params; params.set("type", "cubedsphere_lfric"); return CubedSphereTiles::create(params); } const CubedSphereTiles* CubedSphereTiles::create(const std::string& s) { util::Config params; if (s == "") { return CubedSphereTiles::create(); } params.set("type", s); return CubedSphereTiles::create(params); } const CubedSphereTiles* CubedSphereTiles::create(const eckit::Parametrisation& p) { std::string CubedSphereTiles_type; if (p.has("type")) { p.get("type", CubedSphereTiles_type); return CubedSphereTilesFactory::build(CubedSphereTiles_type, p); } else { return create(); } } idx_t CubedSphereTiles::indexFromXY(const PointXY& xy) const { return indexFromXY(xy.data()); } idx_t CubedSphereTiles::indexFromLonLat(const PointLonLat& lonlat) const { return indexFromXY(lonlat.data()); } } // namespace detail } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/tiles/TilesFactory.cc0000664000175000017500000000334315160212070023207 0ustar alastairalastair/* * (C) Crown Copyright 2021, Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include #include "atlas/grid/detail/tiles/FV3Tiles.h" #include "atlas/grid/detail/tiles/LFRicTiles.h" #include "atlas/grid/detail/tiles/Tiles.h" #include "atlas/grid/detail/tiles/TilesFactory.h" #include "atlas/util/Factory.h" namespace atlas { namespace grid { namespace detail { //---------------------------------------------------------------------------------------------------------------------- namespace { void force_link() { static struct Link { Link() { CubedSphereTilesBuilder(); CubedSphereTilesBuilder(); } } link; } } // namespace //---------------------------------------------------------------------------------------------------------------------- const CubedSphereTiles* CubedSphereTilesFactory::build(const std::string& builder) { return build(builder, util::NoConfig()); } const CubedSphereTiles* CubedSphereTilesFactory::build(const std::string& builder, const eckit::Parametrisation& param) { force_link(); auto factory = get(builder); return factory->make(param); } //---------------------------------------------------------------------------------------------------------------------- } // namespace detail } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/tiles/TilesFactory.h0000664000175000017500000000326415160212070023053 0ustar alastairalastair/* * (C) Crown Copyright 2021 Met Office. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include "atlas/util/Config.h" #include "atlas/util/Factory.h" namespace atlas { namespace grid { namespace detail { class CubedSphereTiles; //---------------------------------------------------------------------------------------------------------------------- class CubedSphereTilesFactory : public util::Factory { public: static std::string className() { return "CubedSphereTilesFactory"; } static const CubedSphereTiles* build(const std::string&); static const CubedSphereTiles* build(const std::string&, const eckit::Parametrisation&); using Factory::Factory; private: virtual const CubedSphereTiles* make(const eckit::Parametrisation&) = 0; }; //---------------------------------------------------------------------------------------------------------------------- template class CubedSphereTilesBuilder : public CubedSphereTilesFactory { private: virtual const CubedSphereTiles* make(const eckit::Parametrisation& param) { return new T(param); } public: using CubedSphereTilesFactory::CubedSphereTilesFactory; }; //---------------------------------------------------------------------------------------------------------------------- } // namespace detail } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/tiles/Tiles.h0000664000175000017500000000431415160212070021520 0ustar alastairalastair/* * (C) Crown Copyright 2021 Met Office. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include #include "atlas/library/config.h" #include "atlas/projection/detail/ProjectionImpl.h" #include "atlas/util/Config.h" #include "atlas/util/Object.h" namespace eckit { class Parametrisation; } namespace atlas { class PointXY; } // namespace atlas namespace atlas { namespace grid { namespace detail { class CubedSphereTiles : public util::Object { public: using Spec = util::Config; using Jacobian = projection::Jacobian; public: static const CubedSphereTiles* create(); static const CubedSphereTiles* create(const eckit::Parametrisation&); static const CubedSphereTiles* create(const std::string&); virtual std::string type() const = 0; virtual std::array, 2> xy2abOffsets() const = 0; virtual std::array, 2> ab2xyOffsets() const = 0; virtual void rotate(idx_t t, double xyz[]) const = 0; virtual void unrotate(idx_t t, double xyz[]) const = 0; virtual idx_t indexFromXY(const double xy[]) const = 0; virtual idx_t indexFromLonLat(const double lonlat[]) const = 0; idx_t indexFromXY(const PointXY& xy) const; idx_t indexFromLonLat(const PointLonLat& lonlat) const; virtual void enforceXYdomain(double xy[]) const = 0; virtual const PointXY& tileCentre(size_t t) const = 0; virtual const Jacobian& tileJacobian(size_t t) const = 0; idx_t size() const { return 6; } virtual atlas::PointXY tileCubePeriodicity(const atlas::PointXY& xyExtended, const atlas::idx_t tile) const = 0; /// Output to stream virtual void print(std::ostream&) const = 0; friend std::ostream& operator<<(std::ostream& s, const CubedSphereTiles& cst) { cst.print(s); return s; } }; } // namespace detail } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/distribution/0000775000175000017500000000000015160212070021664 5ustar alastairalastairatlas-0.46.0/src/atlas/grid/detail/distribution/BandsDistribution.cc0000664000175000017500000000616215160212070025627 0ustar alastairalastair/* * (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 "BandsDistribution.h" #include #include #include #include "atlas/grid/Grid.h" #include "atlas/runtime/Exception.h" namespace atlas { namespace grid { namespace detail { namespace distribution { template BandsDistribution::BandsDistribution(const atlas::Grid& grid, atlas::idx_t nb_partitions, const std::string& type, size_t blocksize): DistributionFunctionT>(grid) { this->type_ = type; size_t gridsize = grid.size(); this->size_ = gridsize; this->nb_partitions_ = nb_partitions; nb_partitions_Int_ = nb_partitions; blocksize_ = blocksize; nb_blocks_ = gridsize / blocksize_; if (gridsize % blocksize_) { nb_blocks_++; } this->nb_pts_.reserve(nb_partitions_Int_); for (idx_t iproc = 0; iproc < nb_partitions; iproc++) { // Approximate values gidx_t imin = blocksize_ * (((iproc + 0) * nb_blocks_) / nb_partitions_Int_); gidx_t imax = blocksize_ * (((iproc + 1) * nb_blocks_) / nb_partitions_Int_); while (imin > 0) { if (function(imin - blocksize_) == iproc) { imin -= blocksize_; } else { break; } } while (function(imin) < iproc) { imin += blocksize_; } while (function(imax - 1) == iproc + 1) { imax -= blocksize_; } while (imax + blocksize_ <= gridsize) { if (function(imax) == iproc) { imax += blocksize_; } else { break; } } imax = std::min(imax, (gidx_t)gridsize); this->nb_pts_.push_back(imax - imin); } this->max_pts_ = *std::max_element(this->nb_pts_.begin(), this->nb_pts_.end()); this->min_pts_ = *std::min_element(this->nb_pts_.begin(), this->nb_pts_.end()); ATLAS_ASSERT(detectOverflow(gridsize, nb_partitions_Int_, blocksize_) == false); } template bool BandsDistribution::detectOverflow(size_t gridsize, size_t nb_partitions, size_t blocksize) { int64_t size = gridsize; int64_t iblock = (size - 1) / int64_t(blocksize); int64_t intermediate_product = iblock * int64_t(nb_partitions); ATLAS_ASSERT(intermediate_product > 0, "Even 64 bits is insufficient to prevent overflow."); if (intermediate_product > std::numeric_limits::max()) { return true; } return false; } template class BandsDistribution; template class BandsDistribution; } // namespace distribution } // namespace detail } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/distribution/DistributionArray.cc0000664000175000017500000001062215160212070025652 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "DistributionArray.h" #include #include #include #include #include "eckit/types/Types.h" #include "eckit/utils/Hash.h" #include "atlas/grid/Grid.h" #include "atlas/grid/Partitioner.h" #include "atlas/parallel/omp/omp.h" #include "atlas/util/Config.h" namespace atlas { namespace grid { namespace detail { namespace distribution { namespace { std::string distribution_type(int N, const Partitioner& p = Partitioner()) { if (N == 1) { return "serial"; } if (not p) { return "custom"; } return p.type(); } } // namespace DistributionArray::DistributionArray(const Grid& grid, const eckit::Parametrisation& config): DistributionArray(grid, Partitioner{config}) {} DistributionArray::DistributionArray(const Grid& grid, const Partitioner& partitioner) { part_.resize(grid.size()); partitioner.partition(grid, part_.data()); nb_partitions_ = partitioner.nb_partitions(); size_t size = part_.size(); nb_pts_.resize(nb_partitions_, 0); atlas_omp_parallel { std::vector nb_pts(nb_partitions_, 0); // thread-local atlas_omp_for(size_t j = 0; j < size; ++j) { ++nb_pts[part_[j]]; } atlas_omp_critical { for (int p = 0; p < nb_partitions_; ++p) { nb_pts_[p] += nb_pts[p]; } } } ATLAS_ASSERT( std::accumulate(nb_pts_.begin(),nb_pts_.end(),idx_t{0}) == part_.size() ); max_pts_ = *std::max_element(nb_pts_.begin(), nb_pts_.end()); min_pts_ = *std::min_element(nb_pts_.begin(), nb_pts_.end()); type_ = distribution_type(nb_partitions_, partitioner); } DistributionArray::DistributionArray(int nb_partitions, idx_t npts, int part[], int part0) { part_.assign(part, part + npts); if (nb_partitions == 0) { std::set partset(part_.begin(), part_.end()); nb_partitions_ = static_cast(partset.size()); } else { nb_partitions_ = nb_partitions; } nb_pts_.resize(nb_partitions_, 0); for (idx_t j = 0, size = static_cast(part_.size()); j < size; ++j) { part_[j] -= part0; ++nb_pts_[part_[j]]; } max_pts_ = *std::max_element(nb_pts_.begin(), nb_pts_.end()); min_pts_ = *std::min_element(nb_pts_.begin(), nb_pts_.end()); type_ = distribution_type(nb_partitions_); } DistributionArray::DistributionArray(int nb_partitions, partition_t&& part): nb_partitions_(nb_partitions), part_(std::move(part)), nb_pts_(nb_partitions_, 0) { size_t size = part_.size(); int num_threads = atlas_omp_get_max_threads(); std::vector > nb_pts_per_thread(num_threads, std::vector(nb_partitions_)); atlas_omp_parallel { int thread = atlas_omp_get_thread_num(); auto& nb_pts = nb_pts_per_thread[thread]; atlas_omp_for(size_t j = 0; j < size; ++j) { int p = part_[j]; ++nb_pts[p]; } } for (int thread = 0; thread < num_threads; ++thread) { for (int p = 0; p < nb_partitions_; ++p) { nb_pts_[p] += nb_pts_per_thread[thread][p]; } } max_pts_ = *std::max_element(nb_pts_.begin(), nb_pts_.end()); min_pts_ = *std::min_element(nb_pts_.begin(), nb_pts_.end()); type_ = distribution_type(nb_partitions_); } DistributionArray::~DistributionArray() = default; void DistributionArray::print(std::ostream& s) const { auto print_partition = [&](std::ostream& s) { eckit::output_list list_printer(s); for (size_t i = 0, size = part_.size(); i < size; i++) { list_printer.push_back(part_[i]); } }; s << "Distribution( " << "type: " << type_ << ", nb_points: " << size() << ", nb_partitions: " << nb_pts_.size() << ", parts : "; print_partition(s); } void DistributionArray::hash(eckit::Hash& hash) const { for (size_t i = 0; i < part_.size(); i++) { hash.add(part_[i]); } } } // namespace distribution } // namespace detail } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/distribution/BandsDistribution.h0000664000175000017500000000233515160212070025467 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include "atlas/grid/detail/distribution/DistributionFunction.h" namespace atlas { namespace grid { namespace detail { namespace distribution { template class BandsDistribution : public DistributionFunctionT> { private: Int blocksize_; Int nb_blocks_; Int nb_partitions_Int_; public: BandsDistribution(const Grid& grid, idx_t nb_partitions, const std::string& type, size_t blocksize = 1); ATLAS_ALWAYS_INLINE int function(gidx_t index) const { Int iblock = static_cast(index / blocksize_); return (iblock * nb_partitions_Int_) / nb_blocks_; } static bool detectOverflow(size_t gridsize, size_t nb_partitions, size_t blocksize); }; } // namespace distribution } // namespace detail } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/distribution/DistributionImpl.h0000664000175000017500000000473415160212070025346 0ustar alastairalastair/* * (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/Object.h" namespace eckit { class Parametrisation; } namespace atlas { class Grid; namespace grid { namespace detail { namespace partitioner { class Partitioner; } namespace grid { class Grid; } } // namespace detail } // namespace grid } // namespace atlas namespace atlas { namespace grid { class DistributionImpl : public util::Object { public: using Config = atlas::util::Config; virtual ~DistributionImpl() {} virtual int partition(const gidx_t gidx) const = 0; virtual bool functional() const = 0; virtual idx_t nb_partitions() const = 0; virtual gidx_t size() const = 0; virtual const std::vector& nb_pts() const = 0; virtual idx_t max_pts() const = 0; virtual idx_t min_pts() const = 0; virtual const std::string& type() const = 0; virtual void print(std::ostream&) const = 0; virtual size_t footprint() const = 0; virtual void hash(eckit::Hash&) const = 0; virtual void partition(gidx_t begin, gidx_t end, int partitions[]) const = 0; }; extern "C" { DistributionImpl* atlas__GridDistribution__new(idx_t size, int part[], int part0); DistributionImpl* atlas__GridDistribution__new__Grid_Config(const detail::grid::Grid* grid, const eckit::Parametrisation* config); DistributionImpl* atlas__GridDistribution__new__Grid_Partitioner(const detail::grid::Grid* grid, const detail::partitioner::Partitioner* partitioner); int atlas__GridDistribution__partition_int32(DistributionImpl* dist, int i); int atlas__GridDistribution__partition_int64(DistributionImpl* dist, long i); void atlas__GridDistribution__delete(DistributionImpl* This); void atlas__GridDistribution__nb_pts(DistributionImpl* This, idx_t nb_pts[]); idx_t atlas__atlas__GridDistribution__nb_partitions(DistributionImpl* This); } } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/distribution/DistributionFunction.cc0000664000175000017500000000243715160212070026366 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "DistributionFunction.h" #include #include #include "eckit/types/Types.h" #include "eckit/utils/Hash.h" #include "atlas/grid/Grid.h" namespace atlas { namespace grid { namespace detail { namespace distribution { void DistributionFunction::print(std::ostream& s) const { auto print_partition = [&](std::ostream& s) { eckit::output_list list_printer(s); for (gidx_t i = 0; i < size_; i++) { list_printer.push_back(partition(i)); } }; s << "Distribution( " << "type: " << type_ << ", nb_points: " << size_ << ", nb_partitions: " << nb_pts_.size() << ", parts : "; print_partition(s); } void DistributionFunction::hash(eckit::Hash& hash) const { for (gidx_t i = 0; i < size_; i++) { hash.add(partition(i)); } } } // namespace distribution } // namespace detail } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/distribution/SerialDistribution.h0000664000175000017500000000164615160212070025663 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include "atlas/grid/detail/distribution/DistributionFunction.h" namespace atlas { namespace grid { namespace detail { namespace distribution { class SerialDistribution : public DistributionFunctionT { public: SerialDistribution(const Grid& grid); SerialDistribution(const Grid& grid, int part); ATLAS_ALWAYS_INLINE int function(gidx_t gidx) const { return part_; } private: int part_{0}; }; } // namespace distribution } // namespace detail } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/distribution/DistributionFunction.h0000664000175000017500000000435315160212070026227 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include #include "atlas/grid/detail/distribution/DistributionImpl.h" namespace atlas { namespace grid { namespace detail { namespace distribution { class DistributionFunction : public DistributionImpl { public: DistributionFunction(const Grid&): DistributionImpl() {} bool functional() const override { return true; } size_t footprint() const override { return nb_pts_.size() * sizeof(nb_pts_[0]); } const std::string& type() const override { return nb_partitions_ == 1 ? serial : type_; } idx_t nb_partitions() const override { return nb_partitions_; } const std::vector& nb_pts() const override { return nb_pts_; } idx_t max_pts() const override { return max_pts_; } idx_t min_pts() const override { return min_pts_; } void print(std::ostream&) const override; gidx_t size() const override { return size_; } void hash(eckit::Hash&) const override; protected: gidx_t size_; idx_t nb_partitions_; std::vector nb_pts_; idx_t max_pts_; idx_t min_pts_; std::string type_{"functional"}; std::string serial{"serial"}; }; template class DistributionFunctionT : public DistributionFunction { public: DistributionFunctionT(const Grid& grid): DistributionFunction(grid) {} ATLAS_ALWAYS_INLINE int partition(gidx_t index) const override { return static_cast(this)->function(index); } ATLAS_ALWAYS_INLINE void partition(gidx_t begin, gidx_t end, int partitions[]) const override { const Derived& derived = *static_cast(this); size_t i = 0; for (gidx_t n = begin; n < end; ++n, ++i) { partitions[i] = derived.function(n); } } }; } // namespace distribution } // namespace detail } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/distribution/DistributionArray.h0000664000175000017500000000463515160212070025523 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include #include "atlas/grid/detail/distribution/DistributionImpl.h" #include "atlas/util/vector.h" namespace atlas { namespace grid { class Partitioner; namespace detail { namespace distribution { class DistributionArray : public DistributionImpl { public: using partition_t = atlas::vector; DistributionArray() = default; DistributionArray(const Grid&, const eckit::Parametrisation&); DistributionArray(const Grid&, const Partitioner&); DistributionArray(int nb_partitions, idx_t npts, int partition[], int part0 = 0); DistributionArray(int nb_partitions, partition_t&& partition); virtual ~DistributionArray(); int partition(const gidx_t gidx) const override { return part_[gidx]; } const partition_t& partition() const { return part_; } idx_t nb_partitions() const override { return nb_partitions_; } operator const partition_t&() const { return part_; } const int* data() const { return part_.data(); } const std::vector& nb_pts() const override { return nb_pts_; } idx_t max_pts() const override { return max_pts_; } idx_t min_pts() const override { return min_pts_; } const std::string& type() const override { return type_; } void print(std::ostream&) const override; size_t footprint() const override { return nb_pts_.size() * sizeof(nb_pts_[0]) + part_.size() * sizeof(part_[0]); } bool functional() const override { return false; } gidx_t size() const override { return part_.size(); } void hash(eckit::Hash&) const override; void partition(gidx_t begin, gidx_t end, int partitions[]) const override { size_t i = 0; for (gidx_t n = begin; n < end; ++n, ++i) { partitions[i] = part_[n]; } } protected: idx_t nb_partitions_ = 0; partition_t part_; std::vector nb_pts_; idx_t max_pts_; idx_t min_pts_; std::string type_; }; } // namespace distribution } // namespace detail } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/distribution/SerialDistribution.cc0000664000175000017500000000231715160212070026015 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "SerialDistribution.h" #include #include #include "atlas/grid/Grid.h" #include "atlas/parallel/mpi/mpi.h" namespace atlas { namespace grid { namespace detail { namespace distribution { SerialDistribution::SerialDistribution(const Grid& grid): SerialDistribution(grid, mpi::rank()) { } SerialDistribution::SerialDistribution(const Grid& grid, int part): DistributionFunctionT(grid) { type_ = "serial"; nb_partitions_ = 1; size_ = grid.size(); nb_pts_.resize(nb_partitions_, grid.size()); max_pts_ = *std::max_element(nb_pts_.begin(), nb_pts_.end()); min_pts_ = *std::min_element(nb_pts_.begin(), nb_pts_.end()); part_ = part; } } // namespace distribution } // namespace detail } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/distribution/DistributionImpl.cc0000664000175000017500000000510415160212070025474 0ustar alastairalastair/* * (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 "DistributionImpl.h" #include #include "atlas/grid/Distribution.h" #include "atlas/grid/Grid.h" #include "atlas/grid/Partitioner.h" #include "atlas/grid/detail/distribution/DistributionArray.h" #include "atlas/runtime/Exception.h" namespace atlas { namespace grid { DistributionImpl* atlas__GridDistribution__new(idx_t size, int part[], int part0) { return new detail::distribution::DistributionArray(0, size, part, part0); } void atlas__GridDistribution__delete(DistributionImpl* This) { delete This; } void atlas__GridDistribution__nb_pts(DistributionImpl* This, idx_t nb_pts[]) { const auto& nb_pts_ = This->nb_pts(); std::copy(nb_pts_.begin(), nb_pts_.end(), &nb_pts[0]); } idx_t atlas__atlas__GridDistribution__nb_partitions(DistributionImpl* This) { return This->nb_partitions(); } int atlas__GridDistribution__partition_int32(DistributionImpl* dist, int i) { return dist->partition(i); } int atlas__GridDistribution__partition_int64(DistributionImpl* dist, long i) { return dist->partition(i); } DistributionImpl* atlas__GridDistribution__new__Grid_Config(const detail::grid::Grid* grid, const eckit::Parametrisation* config) { ATLAS_ASSERT(grid != nullptr, "grid is an invalid pointer"); ATLAS_ASSERT(config != nullptr, "config is an invalid pointer"); DistributionImpl* distribution; { Distribution d{Grid{grid}, *config}; distribution = d.get(); distribution->attach(); } distribution->detach(); return distribution; } DistributionImpl* atlas__GridDistribution__new__Grid_Partitioner(const detail::grid::Grid* grid, const detail::partitioner::Partitioner* partitioner) { ATLAS_ASSERT(grid != nullptr, "grid is an invalid pointer"); ATLAS_ASSERT(partitioner != nullptr, "partitioner is an invalid pointer"); DistributionImpl* distribution; { Distribution d{Grid{grid}, Partitioner{partitioner}}; distribution = d.get(); distribution->attach(); } distribution->detach(); return distribution; } } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/pl/0000775000175000017500000000000015160212070017560 5ustar alastairalastairatlas-0.46.0/src/atlas/grid/detail/pl/classic_gaussian/0000775000175000017500000000000015160212070023073 5ustar alastairalastairatlas-0.46.0/src/atlas/grid/detail/pl/classic_gaussian/N16.cc0000664000175000017500000000134015160212070023744 0ustar alastairalastair/* * (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. */ // TL31 #include "N.h" namespace atlas { namespace grid { namespace detail { namespace pl { namespace classic_gaussian { DEFINE_POINTS_PER_LATITUDE(16, LIST(20, 27, 32, 40, 45, 48, 60, 60, 64, 64, 64, 64, 64, 64, 64, 64)) } // namespace classic_gaussian } // namespace pl } // namespace detail } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/pl/classic_gaussian/N8000.cc0000664000175000017500000016326715160212070024126 0ustar alastairalastair/* * (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. */ // TL15999 #include "N.h" namespace atlas { namespace grid { namespace detail { namespace pl { namespace classic_gaussian { DEFINE_POINTS_PER_LATITUDE( 8000, LIST(16, 24, 30, 36, 40, 45, 54, 60, 64, 72, 72, 75, 81, 90, 90, 96, 100, 108, 120, 120, 120, 125, 128, 135, 144, 144, 150, 160, 160, 160, 180, 180, 180, 180, 192, 192, 192, 200, 216, 216, 216, 216, 225, 225, 240, 240, 240, 243, 250, 250, 256, 270, 270, 270, 288, 288, 288, 288, 300, 300, 300, 320, 320, 320, 320, 324, 360, 360, 360, 360, 360, 360, 360, 360, 375, 375, 375, 375, 384, 384, 400, 400, 400, 405, 405, 432, 432, 432, 432, 432, 432, 450, 450, 450, 450, 480, 480, 480, 480, 480, 480, 480, 486, 500, 500, 500, 512, 512, 512, 540, 540, 540, 540, 540, 540, 540, 576, 576, 576, 576, 576, 576, 576, 576, 600, 600, 600, 600, 600, 625, 625, 625, 625, 625, 625, 640, 640, 640, 640, 648, 675, 675, 675, 675, 675, 675, 675, 720, 720, 720, 720, 720, 720, 720, 720, 720, 720, 729, 729, 750, 750, 750, 750, 750, 768, 768, 768, 768, 800, 800, 800, 800, 800, 800, 800, 800, 810, 810, 864, 864, 864, 864, 864, 864, 864, 864, 864, 864, 864, 864, 864, 900, 900, 900, 900, 900, 900, 900, 900, 960, 960, 960, 960, 960, 960, 960, 960, 960, 960, 960, 960, 960, 960, 972, 972, 972, 1000, 1000, 1000, 1000, 1000, 1000, 1024, 1024, 1024, 1024, 1024, 1024, 1080, 1080, 1080, 1080, 1080, 1080, 1080, 1080, 1080, 1080, 1080, 1080, 1080, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1152, 1152, 1152, 1152, 1152, 1152, 1200, 1200, 1200, 1200, 1200, 1200, 1200, 1200, 1200, 1200, 1200, 1215, 1215, 1215, 1215, 1250, 1250, 1250, 1250, 1250, 1250, 1250, 1250, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1296, 1296, 1296, 1350, 1350, 1350, 1350, 1350, 1350, 1350, 1350, 1350, 1350, 1350, 1350, 1350, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1458, 1458, 1458, 1458, 1500, 1500, 1500, 1500, 1500, 1500, 1500, 1500, 1500, 1500, 1536, 1536, 1536, 1536, 1536, 1536, 1536, 1536, 1536, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1620, 1620, 1620, 1620, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1944, 1944, 1944, 1944, 1944, 1944, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2025, 2025, 2025, 2025, 2025, 2025, 2048, 2048, 2048, 2048, 2048, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2187, 2187, 2187, 2187, 2187, 2187, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2430, 2430, 2430, 2430, 2430, 2430, 2430, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2592, 2592, 2592, 2592, 2592, 2592, 2592, 2592, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2916, 2916, 2916, 2916, 2916, 2916, 2916, 2916, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3072, 3072, 3072, 3072, 3072, 3072, 3072, 3072, 3072, 3072, 3072, 3072, 3072, 3072, 3072, 3072, 3072, 3125, 3125, 3125, 3125, 3125, 3125, 3125, 3125, 3125, 3125, 3125, 3125, 3125, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3240, 3240, 3240, 3240, 3240, 3240, 3240, 3240, 3240, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3456, 3456, 3456, 3456, 3456, 3456, 3456, 3456, 3456, 3456, 3456, 3456, 3456, 3456, 3456, 3456, 3456, 3456, 3456, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3645, 3645, 3645, 3645, 3645, 3645, 3645, 3645, 3645, 3645, 3645, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3888, 3888, 3888, 3888, 3888, 3888, 3888, 3888, 3888, 3888, 3888, 3888, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4050, 4050, 4050, 4050, 4050, 4050, 4050, 4050, 4050, 4050, 4050, 4050, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4374, 4374, 4374, 4374, 4374, 4374, 4374, 4374, 4374, 4374, 4374, 4374, 4374, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4860, 4860, 4860, 4860, 4860, 4860, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5184, 5184, 5184, 5184, 5184, 5184, 5184, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5832, 5832, 5832, 5832, 5832, 5832, 5832, 5832, 5832, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6075, 6075, 6075, 6075, 6075, 6075, 6075, 6075, 6075, 6144, 6144, 6144, 6144, 6144, 6144, 6144, 6144, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6480, 6480, 6480, 6480, 6480, 6480, 6480, 6480, 6480, 6480, 6480, 6561, 6561, 6561, 6561, 6561, 6561, 6561, 6561, 6561, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6912, 6912, 6912, 6912, 6912, 6912, 6912, 6912, 6912, 6912, 6912, 6912, 6912, 6912, 6912, 6912, 6912, 6912, 6912, 6912, 6912, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7290, 7290, 7290, 7290, 7290, 7290, 7290, 7290, 7290, 7290, 7290, 7290, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7776, 7776, 7776, 7776, 7776, 7776, 7776, 7776, 7776, 7776, 7776, 7776, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8100, 8100, 8100, 8100, 8100, 8100, 8100, 8100, 8100, 8100, 8100, 8100, 8100, 8100, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8748, 8748, 8748, 8748, 8748, 8748, 8748, 8748, 8748, 8748, 8748, 8748, 8748, 8748, 8748, 8748, 9000, 9000, 9000, 9000, 9000, 9000, 9000, 9000, 9000, 9000, 9000, 9000, 9000, 9000, 9000, 9000, 9000, 9000, 9000, 9000, 9000, 9000, 9000, 9000, 9000, 9000, 9000, 9000, 9000, 9000, 9000, 9000, 9000, 9000, 9000, 9216, 9216, 9216, 9216, 9216, 9216, 9216, 9216, 9216, 9216, 9216, 9216, 9216, 9216, 9216, 9216, 9216, 9216, 9216, 9216, 9216, 9216, 9216, 9216, 9216, 9216, 9216, 9216, 9216, 9216, 9216, 9375, 9375, 9375, 9375, 9375, 9375, 9375, 9375, 9375, 9375, 9375, 9375, 9375, 9375, 9375, 9375, 9375, 9375, 9375, 9375, 9375, 9375, 9375, 9375, 9600, 9600, 9600, 9600, 9600, 9600, 9600, 9600, 9600, 9600, 9600, 9600, 9600, 9600, 9600, 9600, 9600, 9600, 9600, 9600, 9600, 9600, 9600, 9600, 9600, 9600, 9600, 9600, 9600, 9600, 9600, 9600, 9720, 9720, 9720, 9720, 9720, 9720, 9720, 9720, 9720, 9720, 9720, 9720, 9720, 9720, 9720, 9720, 9720, 9720, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10125, 10125, 10125, 10125, 10125, 10125, 10125, 10125, 10125, 10125, 10125, 10125, 10125, 10125, 10125, 10125, 10125, 10125, 10125, 10240, 10240, 10240, 10240, 10240, 10240, 10240, 10240, 10240, 10240, 10240, 10240, 10240, 10240, 10240, 10240, 10240, 10240, 10368, 10368, 10368, 10368, 10368, 10368, 10368, 10368, 10368, 10368, 10368, 10368, 10368, 10368, 10368, 10368, 10368, 10368, 10368, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10935, 10935, 10935, 10935, 10935, 10935, 10935, 10935, 10935, 10935, 10935, 10935, 10935, 10935, 10935, 10935, 10935, 10935, 10935, 10935, 10935, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11664, 11664, 11664, 11664, 11664, 11664, 11664, 11664, 11664, 11664, 11664, 11664, 11664, 11664, 11664, 11664, 11664, 11664, 11664, 11664, 11664, 11664, 11664, 11664, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12150, 12150, 12150, 12150, 12150, 12150, 12150, 12150, 12150, 12150, 12150, 12150, 12150, 12150, 12150, 12150, 12150, 12150, 12150, 12150, 12150, 12150, 12150, 12150, 12150, 12288, 12288, 12288, 12288, 12288, 12288, 12288, 12288, 12288, 12288, 12288, 12288, 12288, 12288, 12288, 12288, 12288, 12288, 12288, 12288, 12288, 12500, 12500, 12500, 12500, 12500, 12500, 12500, 12500, 12500, 12500, 12500, 12500, 12500, 12500, 12500, 12500, 12500, 12500, 12500, 12500, 12500, 12500, 12500, 12500, 12500, 12500, 12500, 12500, 12500, 12500, 12500, 12500, 12500, 12500, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12960, 12960, 12960, 12960, 12960, 12960, 12960, 12960, 12960, 12960, 12960, 12960, 12960, 12960, 12960, 12960, 12960, 12960, 12960, 12960, 12960, 12960, 12960, 12960, 12960, 13122, 13122, 13122, 13122, 13122, 13122, 13122, 13122, 13122, 13122, 13122, 13122, 13122, 13122, 13122, 13122, 13122, 13122, 13122, 13122, 13122, 13122, 13122, 13122, 13122, 13122, 13122, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14580, 14580, 14580, 14580, 14580, 14580, 14580, 14580, 14580, 14580, 14580, 14580, 14580, 14580, 14580, 14580, 14580, 14580, 14580, 14580, 14580, 14580, 14580, 14580, 14580, 14580, 14580, 14580, 14580, 14580, 14580, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15625, 15625, 15625, 15625, 15625, 15625, 15625, 15625, 15625, 15625, 15625, 15625, 15625, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16200, 16200, 16200, 16200, 16200, 16200, 16200, 16200, 16200, 16200, 16200, 16200, 16200, 16200, 16200, 16200, 16200, 16200, 16200, 16200, 16200, 16200, 16200, 16200, 16200, 16200, 16200, 16200, 16200, 16200, 16200, 16200, 16200, 16200, 16200, 16200, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16384, 16875, 16875, 16875, 16875, 16875, 16875, 16875, 16875, 16875, 16875, 16875, 16875, 16875, 16875, 16875, 16875, 16875, 16875, 16875, 16875, 16875, 16875, 16875, 16875, 16875, 16875, 16875, 16875, 16875, 16875, 16875, 16875, 16875, 16875, 16875, 16875, 16875, 16875, 16875, 16875, 16875, 16875, 16875, 16875, 16875, 16875, 16875, 16875, 16875, 16875, 16875, 16875, 16875, 16875, 16875, 16875, 16875, 16875, 16875, 16875, 16875, 16875, 16875, 16875, 16875, 16875, 16875, 16875, 16875, 16875, 16875, 16875, 16875, 16875, 16875, 16875, 16875, 16875, 16875, 16875, 16875, 16875, 16875, 16875, 16875, 16875, 16875, 17280, 17280, 17280, 17280, 17280, 17280, 17280, 17280, 17280, 17280, 17280, 17280, 17280, 17280, 17280, 17280, 17280, 17280, 17280, 17280, 17280, 17280, 17280, 17280, 17280, 17280, 17280, 17280, 17280, 17280, 17280, 17280, 17280, 17280, 17280, 17280, 17280, 17280, 17280, 17280, 17280, 17280, 17280, 17280, 17280, 17280, 17280, 17280, 17280, 17280, 17280, 17280, 17280, 17280, 17280, 17280, 17280, 17280, 17280, 17280, 17280, 17280, 17280, 17280, 17280, 17280, 17280, 17280, 17280, 17280, 17280, 17280, 17496, 17496, 17496, 17496, 17496, 17496, 17496, 17496, 17496, 17496, 17496, 17496, 17496, 17496, 17496, 17496, 17496, 17496, 17496, 17496, 17496, 17496, 17496, 17496, 17496, 17496, 17496, 17496, 17496, 17496, 17496, 17496, 17496, 17496, 17496, 17496, 17496, 17496, 17496, 17496, 17496, 18000, 18000, 18000, 18000, 18000, 18000, 18000, 18000, 18000, 18000, 18000, 18000, 18000, 18000, 18000, 18000, 18000, 18000, 18000, 18000, 18000, 18000, 18000, 18000, 18000, 18000, 18000, 18000, 18000, 18000, 18000, 18000, 18000, 18000, 18000, 18000, 18000, 18000, 18000, 18000, 18000, 18000, 18000, 18000, 18000, 18000, 18000, 18000, 18000, 18000, 18000, 18000, 18000, 18000, 18000, 18000, 18000, 18000, 18000, 18000, 18000, 18000, 18000, 18000, 18000, 18000, 18000, 18000, 18000, 18000, 18000, 18000, 18000, 18000, 18000, 18000, 18000, 18000, 18000, 18000, 18000, 18000, 18000, 18000, 18000, 18000, 18000, 18000, 18000, 18000, 18000, 18000, 18225, 18225, 18225, 18225, 18225, 18225, 18225, 18225, 18225, 18225, 18225, 18225, 18225, 18225, 18225, 18225, 18225, 18225, 18225, 18225, 18225, 18225, 18225, 18225, 18225, 18225, 18225, 18225, 18225, 18225, 18225, 18225, 18225, 18225, 18225, 18225, 18225, 18225, 18225, 18225, 18225, 18225, 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18432, 18750, 18750, 18750, 18750, 18750, 18750, 18750, 18750, 18750, 18750, 18750, 18750, 18750, 18750, 18750, 18750, 18750, 18750, 18750, 18750, 18750, 18750, 18750, 18750, 18750, 18750, 18750, 18750, 18750, 18750, 18750, 18750, 18750, 18750, 18750, 18750, 18750, 18750, 18750, 18750, 18750, 18750, 18750, 18750, 18750, 18750, 18750, 18750, 18750, 18750, 18750, 18750, 18750, 18750, 18750, 18750, 18750, 18750, 18750, 18750, 18750, 19200, 19200, 19200, 19200, 19200, 19200, 19200, 19200, 19200, 19200, 19200, 19200, 19200, 19200, 19200, 19200, 19200, 19200, 19200, 19200, 19200, 19200, 19200, 19200, 19200, 19200, 19200, 19200, 19200, 19200, 19200, 19200, 19200, 19200, 19200, 19200, 19200, 19200, 19200, 19200, 19200, 19200, 19200, 19200, 19200, 19200, 19200, 19200, 19200, 19200, 19200, 19200, 19200, 19200, 19200, 19200, 19200, 19200, 19200, 19200, 19200, 19200, 19200, 19200, 19200, 19200, 19200, 19200, 19200, 19200, 19200, 19200, 19200, 19200, 19200, 19200, 19200, 19200, 19200, 19200, 19200, 19200, 19200, 19200, 19200, 19440, 19440, 19440, 19440, 19440, 19440, 19440, 19440, 19440, 19440, 19440, 19440, 19440, 19440, 19440, 19440, 19440, 19440, 19440, 19440, 19440, 19440, 19440, 19440, 19440, 19440, 19440, 19440, 19440, 19440, 19440, 19440, 19440, 19440, 19440, 19440, 19440, 19440, 19440, 19440, 19440, 19440, 19440, 19440, 19440, 19440, 19440, 19683, 19683, 19683, 19683, 19683, 19683, 19683, 19683, 19683, 19683, 19683, 19683, 19683, 19683, 19683, 19683, 19683, 19683, 19683, 19683, 19683, 19683, 19683, 19683, 19683, 19683, 19683, 19683, 19683, 19683, 19683, 19683, 19683, 19683, 19683, 19683, 19683, 19683, 19683, 19683, 19683, 19683, 19683, 19683, 19683, 19683, 19683, 19683, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20000, 20250, 20250, 20250, 20250, 20250, 20250, 20250, 20250, 20250, 20250, 20250, 20250, 20250, 20250, 20250, 20250, 20250, 20250, 20250, 20250, 20250, 20250, 20250, 20250, 20250, 20250, 20250, 20250, 20250, 20250, 20250, 20250, 20250, 20250, 20250, 20250, 20250, 20250, 20250, 20250, 20250, 20250, 20250, 20250, 20250, 20250, 20250, 20250, 20250, 20250, 20480, 20480, 20480, 20480, 20480, 20480, 20480, 20480, 20480, 20480, 20480, 20480, 20480, 20480, 20480, 20480, 20480, 20480, 20480, 20480, 20480, 20480, 20480, 20480, 20480, 20480, 20480, 20480, 20480, 20480, 20480, 20480, 20480, 20480, 20480, 20480, 20480, 20480, 20480, 20480, 20480, 20480, 20480, 20480, 20480, 20480, 20480, 20736, 20736, 20736, 20736, 20736, 20736, 20736, 20736, 20736, 20736, 20736, 20736, 20736, 20736, 20736, 20736, 20736, 20736, 20736, 20736, 20736, 20736, 20736, 20736, 20736, 20736, 20736, 20736, 20736, 20736, 20736, 20736, 20736, 20736, 20736, 20736, 20736, 20736, 20736, 20736, 20736, 20736, 20736, 20736, 20736, 20736, 20736, 20736, 20736, 20736, 20736, 20736, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21600, 21870, 21870, 21870, 21870, 21870, 21870, 21870, 21870, 21870, 21870, 21870, 21870, 21870, 21870, 21870, 21870, 21870, 21870, 21870, 21870, 21870, 21870, 21870, 21870, 21870, 21870, 21870, 21870, 21870, 21870, 21870, 21870, 21870, 21870, 21870, 21870, 21870, 21870, 21870, 21870, 21870, 21870, 21870, 21870, 21870, 21870, 21870, 21870, 21870, 21870, 21870, 21870, 21870, 21870, 21870, 21870, 21870, 21870, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 22500, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23040, 23328, 23328, 23328, 23328, 23328, 23328, 23328, 23328, 23328, 23328, 23328, 23328, 23328, 23328, 23328, 23328, 23328, 23328, 23328, 23328, 23328, 23328, 23328, 23328, 23328, 23328, 23328, 23328, 23328, 23328, 23328, 23328, 23328, 23328, 23328, 23328, 23328, 23328, 23328, 23328, 23328, 23328, 23328, 23328, 23328, 23328, 23328, 23328, 23328, 23328, 23328, 23328, 23328, 23328, 23328, 23328, 23328, 23328, 23328, 23328, 23328, 23328, 23328, 23328, 23328, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24000, 24300, 24300, 24300, 24300, 24300, 24300, 24300, 24300, 24300, 24300, 24300, 24300, 24300, 24300, 24300, 24300, 24300, 24300, 24300, 24300, 24300, 24300, 24300, 24300, 24300, 24300, 24300, 24300, 24300, 24300, 24300, 24300, 24300, 24300, 24300, 24300, 24300, 24300, 24300, 24300, 24300, 24300, 24300, 24300, 24300, 24300, 24300, 24300, 24300, 24300, 24300, 24300, 24300, 24300, 24300, 24300, 24300, 24300, 24300, 24300, 24300, 24300, 24300, 24300, 24300, 24300, 24300, 24300, 24300, 24300, 24300, 24300, 24300, 24576, 24576, 24576, 24576, 24576, 24576, 24576, 24576, 24576, 24576, 24576, 24576, 24576, 24576, 24576, 24576, 24576, 24576, 24576, 24576, 24576, 24576, 24576, 24576, 24576, 24576, 24576, 24576, 24576, 24576, 24576, 24576, 24576, 24576, 24576, 24576, 24576, 24576, 24576, 24576, 24576, 24576, 24576, 24576, 24576, 24576, 24576, 24576, 24576, 24576, 24576, 24576, 24576, 24576, 24576, 24576, 24576, 24576, 24576, 24576, 24576, 24576, 24576, 24576, 24576, 24576, 24576, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25000, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25600, 25920, 25920, 25920, 25920, 25920, 25920, 25920, 25920, 25920, 25920, 25920, 25920, 25920, 25920, 25920, 25920, 25920, 25920, 25920, 25920, 25920, 25920, 25920, 25920, 25920, 25920, 25920, 25920, 25920, 25920, 25920, 25920, 25920, 25920, 25920, 25920, 25920, 25920, 25920, 25920, 25920, 25920, 25920, 25920, 25920, 25920, 25920, 25920, 25920, 25920, 25920, 25920, 25920, 25920, 25920, 25920, 25920, 25920, 25920, 25920, 25920, 25920, 25920, 25920, 25920, 25920, 25920, 25920, 25920, 25920, 25920, 25920, 25920, 25920, 25920, 25920, 25920, 25920, 25920, 25920, 25920, 25920, 25920, 25920, 26244, 26244, 26244, 26244, 26244, 26244, 26244, 26244, 26244, 26244, 26244, 26244, 26244, 26244, 26244, 26244, 26244, 26244, 26244, 26244, 26244, 26244, 26244, 26244, 26244, 26244, 26244, 26244, 26244, 26244, 26244, 26244, 26244, 26244, 26244, 26244, 26244, 26244, 26244, 26244, 26244, 26244, 26244, 26244, 26244, 26244, 26244, 26244, 26244, 26244, 26244, 26244, 26244, 26244, 26244, 26244, 26244, 26244, 26244, 26244, 26244, 26244, 26244, 26244, 26244, 26244, 26244, 26244, 26244, 26244, 26244, 26244, 26244, 26244, 26244, 26244, 26244, 26244, 26244, 26244, 26244, 26244, 26244, 26244, 26244, 26244, 26244, 26244, 26244, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27000, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 27648, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28125, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 28800, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 29160, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30000, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30375, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 30720, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31104, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 31250, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000, 32000)) } // namespace classic_gaussian } // namespace pl } // namespace detail } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/pl/classic_gaussian/N.h0000664000175000017500000000717415160212070023452 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ /// @author Willem Deconinck /// @date Mar 2016 #pragma once #include #include #include "atlas/util/Factory.h" namespace atlas { namespace grid { namespace detail { namespace pl { namespace classic_gaussian { class PointsPerLatitude { public: /// @pre nlats has enough allocated memory to store the latitudes /// @param size of lats vector void assign(long nlon[], const size_t size) const; /// @pre nlats has enough allocated memory to store the latitudes /// @param size of lats vector void assign(int nlon[], const size_t size) const; /// @post resizes the vector to the number of latitutes void assign(std::vector& nlon) const; /// @post resizes the vector to the number of latitutes void assign(std::vector& nlon) const; size_t N() const { return nlon_.size(); } protected: std::vector nlon_; }; class PointsPerLatitudeFactory : public util::Factory { public: static std::string className() { return "PointsPerLatitudeFactory"; } static const PointsPerLatitude* build(const std::string& builder) { return get(builder)->make(); } using Factory::Factory; private: virtual const PointsPerLatitude* make() const = 0; }; template class PointsPerLatitudeBuilder : public PointsPerLatitudeFactory { public: using PointsPerLatitudeFactory::PointsPerLatitudeFactory; private: virtual const PointsPerLatitude* make() const override { return new T(); } }; #define DECLARE_POINTS_PER_LATITUDE(NUMBER) \ class N##NUMBER : public PointsPerLatitude { \ public: \ N##NUMBER(); \ }; #define LIST(...) __VA_ARGS__ #define DEFINE_POINTS_PER_LATITUDE(NUMBER, NLON) \ N##NUMBER::N##NUMBER() { \ size_t N = NUMBER; \ long nlon[] = {NLON}; \ nlon_.assign(nlon, nlon + N); \ } \ namespace { \ static PointsPerLatitudeBuilder builder_N##NUMBER(#NUMBER); \ } DECLARE_POINTS_PER_LATITUDE(16); DECLARE_POINTS_PER_LATITUDE(24); DECLARE_POINTS_PER_LATITUDE(32); DECLARE_POINTS_PER_LATITUDE(48); DECLARE_POINTS_PER_LATITUDE(64); DECLARE_POINTS_PER_LATITUDE(80); DECLARE_POINTS_PER_LATITUDE(96); DECLARE_POINTS_PER_LATITUDE(128); DECLARE_POINTS_PER_LATITUDE(160); DECLARE_POINTS_PER_LATITUDE(200); DECLARE_POINTS_PER_LATITUDE(256); DECLARE_POINTS_PER_LATITUDE(320); DECLARE_POINTS_PER_LATITUDE(400); DECLARE_POINTS_PER_LATITUDE(512); DECLARE_POINTS_PER_LATITUDE(576); DECLARE_POINTS_PER_LATITUDE(640); DECLARE_POINTS_PER_LATITUDE(800); DECLARE_POINTS_PER_LATITUDE(1024); DECLARE_POINTS_PER_LATITUDE(1280); DECLARE_POINTS_PER_LATITUDE(1600); DECLARE_POINTS_PER_LATITUDE(2000); DECLARE_POINTS_PER_LATITUDE(4000); DECLARE_POINTS_PER_LATITUDE(8000); #undef DECLARE_POINTS_PER_LATITUDE } // namespace classic_gaussian } // namespace pl } // namespace detail } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/pl/classic_gaussian/N32.cc0000664000175000017500000000152615160212070023750 0ustar alastairalastair/* * (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. */ // TL63 #include "N.h" namespace atlas { namespace grid { namespace detail { namespace pl { namespace classic_gaussian { DEFINE_POINTS_PER_LATITUDE(32, LIST(20, 27, 36, 40, 45, 50, 60, 64, 72, 75, 80, 90, 90, 96, 100, 108, 108, 120, 120, 120, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128, 128)) } // namespace classic_gaussian } // namespace pl } // namespace detail } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/pl/classic_gaussian/N48.cc0000664000175000017500000000171315160212070023755 0ustar alastairalastair/* * (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. */ // TL95 #include "N.h" namespace atlas { namespace grid { namespace detail { namespace pl { namespace classic_gaussian { DEFINE_POINTS_PER_LATITUDE(48, LIST(20, 25, 36, 40, 45, 50, 60, 60, 72, 75, 80, 90, 96, 100, 108, 120, 120, 120, 128, 135, 144, 144, 160, 160, 160, 160, 160, 180, 180, 180, 180, 180, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192, 192)) } // namespace classic_gaussian } // namespace pl } // namespace detail } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/pl/classic_gaussian/N200.cc0000664000175000017500000000331515160212070024023 0ustar alastairalastair/* * (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. */ // TL399 #include "N.h" namespace atlas { namespace grid { namespace detail { namespace pl { namespace classic_gaussian { DEFINE_POINTS_PER_LATITUDE( 200, LIST(18, 25, 36, 40, 45, 50, 60, 64, 72, 72, 75, 81, 90, 96, 100, 108, 120, 125, 128, 135, 144, 150, 160, 160, 180, 180, 180, 192, 192, 200, 216, 216, 225, 225, 240, 240, 243, 250, 256, 270, 270, 288, 288, 288, 300, 300, 320, 320, 320, 320, 360, 360, 360, 360, 360, 360, 375, 375, 375, 384, 400, 400, 400, 400, 432, 432, 432, 432, 432, 450, 450, 450, 480, 480, 480, 480, 480, 480, 486, 500, 500, 500, 512, 512, 512, 540, 540, 540, 540, 540, 576, 576, 576, 576, 576, 576, 576, 576, 600, 600, 600, 600, 600, 640, 640, 640, 640, 640, 640, 640, 640, 640, 640, 648, 648, 675, 675, 675, 675, 675, 675, 675, 720, 720, 720, 720, 720, 720, 720, 720, 720, 720, 720, 720, 720, 720, 729, 729, 729, 750, 750, 750, 750, 750, 750, 750, 750, 768, 768, 768, 768, 768, 768, 768, 768, 800, 800, 800, 800, 800, 800, 800, 800, 800, 800, 800, 800, 800, 800, 800, 800, 800, 800, 800, 800, 800, 800, 800, 800, 800, 800, 800, 800, 800, 800, 800, 800, 800, 800, 800, 800, 800, 800, 800, 800, 800, 800, 800, 800, 800)) } // namespace classic_gaussian } // namespace pl } // namespace detail } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/pl/classic_gaussian/N.cc0000664000175000017500000000347115160212070023604 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ /// @author Willem Deconinck /// @date Mar 2016 #include "N.h" #include "atlas/runtime/Exception.h" namespace atlas { namespace grid { namespace detail { namespace pl { namespace classic_gaussian { void PointsPerLatitude::assign(long nlon[], const size_t size) const { ATLAS_ASSERT(size >= nlon_.size()); for (size_t jlat = 0; jlat < nlon_.size(); ++jlat) { nlon[jlat] = nlon_[jlat]; } } void PointsPerLatitude::assign(int nlon[], const size_t size) const { ATLAS_ASSERT(size >= nlon_.size()); for (size_t jlat = 0; jlat < nlon_.size(); ++jlat) { nlon[jlat] = nlon_[jlat]; } } void PointsPerLatitude::assign(std::vector& nlon) const { nlon = nlon_; } void PointsPerLatitude::assign(std::vector& nlon) const { nlon.assign(nlon_.begin(), nlon_.end()); } template void load() { PointsPerLatitudeBuilder(); } void regist() { load(); load(); load(); load(); load(); load(); load(); load(); load(); load(); load(); load(); load(); load(); load(); load(); load(); load(); load(); load(); load(); load(); load(); } } // namespace classic_gaussian } // namespace pl } // namespace detail } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/pl/classic_gaussian/N512.cc0000664000175000017500000000735115160212070024035 0ustar alastairalastair/* * (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. */ // TL1023 #include "N.h" namespace atlas { namespace grid { namespace detail { namespace pl { namespace classic_gaussian { DEFINE_POINTS_PER_LATITUDE( 512, LIST(18, 25, 32, 40, 45, 50, 60, 60, 72, 72, 75, 81, 90, 96, 96, 100, 108, 120, 125, 128, 135, 144, 150, 160, 160, 180, 180, 180, 192, 192, 200, 216, 216, 225, 225, 240, 240, 243, 250, 256, 270, 270, 288, 288, 288, 300, 320, 320, 320, 320, 360, 360, 360, 360, 360, 360, 375, 375, 384, 384, 400, 400, 400, 432, 432, 432, 432, 450, 450, 450, 480, 480, 480, 480, 480, 486, 500, 500, 512, 512, 540, 540, 540, 540, 540, 576, 576, 576, 576, 576, 576, 600, 600, 600, 640, 640, 640, 640, 640, 640, 640, 648, 675, 675, 675, 675, 675, 720, 720, 720, 720, 720, 720, 720, 729, 729, 750, 750, 750, 768, 768, 768, 800, 800, 800, 800, 800, 800, 810, 864, 864, 864, 864, 864, 864, 864, 864, 864, 864, 900, 900, 900, 900, 900, 900, 960, 960, 960, 960, 960, 960, 960, 960, 960, 960, 960, 972, 972, 1000, 1000, 1000, 1000, 1000, 1024, 1024, 1024, 1024, 1080, 1080, 1080, 1080, 1080, 1080, 1080, 1080, 1080, 1080, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1152, 1152, 1152, 1152, 1152, 1200, 1200, 1200, 1200, 1200, 1200, 1200, 1200, 1200, 1215, 1215, 1215, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1296, 1296, 1296, 1350, 1350, 1350, 1350, 1350, 1350, 1350, 1350, 1350, 1350, 1350, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1458, 1458, 1458, 1458, 1500, 1500, 1500, 1500, 1500, 1500, 1500, 1500, 1500, 1500, 1536, 1536, 1536, 1536, 1536, 1536, 1536, 1536, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1620, 1620, 1620, 1620, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2048)) } // namespace classic_gaussian } // namespace pl } // namespace detail } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/pl/classic_gaussian/N1024.cc0000664000175000017500000001576315160212070024122 0ustar alastairalastair/* * (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. */ // TL2047 #include "N.h" namespace atlas { namespace grid { namespace detail { namespace pl { namespace classic_gaussian { DEFINE_POINTS_PER_LATITUDE( 1024, LIST(18, 25, 32, 40, 45, 50, 60, 64, 72, 72, 75, 81, 90, 96, 96, 108, 108, 120, 120, 125, 125, 135, 144, 150, 160, 160, 180, 180, 180, 192, 192, 200, 216, 216, 225, 225, 240, 240, 243, 250, 256, 270, 270, 288, 288, 288, 300, 300, 320, 320, 320, 360, 360, 360, 360, 360, 360, 375, 375, 384, 384, 400, 400, 405, 432, 432, 432, 432, 450, 450, 450, 480, 480, 480, 480, 480, 486, 500, 500, 512, 512, 540, 540, 540, 540, 576, 576, 576, 576, 576, 576, 600, 600, 600, 600, 625, 625, 625, 625, 640, 640, 648, 675, 675, 675, 675, 675, 720, 720, 720, 720, 720, 720, 720, 729, 750, 750, 750, 750, 768, 768, 800, 800, 800, 800, 800, 800, 810, 864, 864, 864, 864, 864, 864, 864, 864, 864, 900, 900, 900, 900, 900, 900, 960, 960, 960, 960, 960, 960, 960, 960, 960, 972, 972, 1000, 1000, 1000, 1000, 1000, 1024, 1024, 1024, 1024, 1080, 1080, 1080, 1080, 1080, 1080, 1080, 1080, 1080, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1152, 1152, 1152, 1152, 1152, 1200, 1200, 1200, 1200, 1200, 1200, 1200, 1215, 1215, 1215, 1250, 1250, 1250, 1250, 1250, 1250, 1280, 1280, 1280, 1280, 1280, 1296, 1296, 1350, 1350, 1350, 1350, 1350, 1350, 1350, 1350, 1350, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1458, 1458, 1458, 1500, 1500, 1500, 1500, 1500, 1500, 1500, 1500, 1536, 1536, 1536, 1536, 1536, 1536, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1620, 1620, 1620, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1944, 1944, 1944, 1944, 1944, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2025, 2025, 2025, 2025, 2048, 2048, 2048, 2048, 2048, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2187, 2187, 2187, 2187, 2187, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2430, 2430, 2430, 2430, 2430, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2592, 2592, 2592, 2592, 2592, 2592, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2916, 2916, 2916, 2916, 2916, 2916, 2916, 2916, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3072, 3072, 3072, 3072, 3072, 3072, 3072, 3072, 3072, 3072, 3072, 3072, 3072, 3072, 3072, 3072, 3072, 3125, 3125, 3125, 3125, 3125, 3125, 3125, 3125, 3125, 3125, 3125, 3125, 3125, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3240, 3240, 3240, 3240, 3240, 3240, 3240, 3240, 3240, 3240, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3456, 3456, 3456, 3456, 3456, 3456, 3456, 3456, 3456, 3456, 3456, 3456, 3456, 3456, 3456, 3456, 3456, 3456, 3456, 3456, 3456, 3456, 3456, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3645, 3645, 3645, 3645, 3645, 3645, 3645, 3645, 3645, 3645, 3645, 3645, 3645, 3645, 3645, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3888, 3888, 3888, 3888, 3888, 3888, 3888, 3888, 3888, 3888, 3888, 3888, 3888, 3888, 3888, 3888, 3888, 3888, 3888, 3888, 3888, 3888, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4050, 4050, 4050, 4050, 4050, 4050, 4050, 4050, 4050, 4050, 4050, 4050, 4050, 4050, 4050, 4050, 4050, 4050, 4050, 4050, 4050, 4050, 4050, 4050, 4050, 4050, 4050, 4050, 4050, 4050, 4050, 4050, 4050, 4050, 4050, 4050, 4050, 4050, 4050, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096)) } // namespace classic_gaussian } // namespace pl } // namespace detail } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/pl/classic_gaussian/N24.cc0000664000175000017500000000144415160212070023750 0ustar alastairalastair/* * (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. */ // TL47 #include "N.h" namespace atlas { namespace grid { namespace detail { namespace pl { namespace classic_gaussian { DEFINE_POINTS_PER_LATITUDE(24, LIST(20, 25, 36, 40, 45, 48, 54, 60, 64, 72, 80, 80, 90, 90, 96, 96, 96, 96, 96, 96, 96, 96, 96, 96)) } // namespace classic_gaussian } // namespace pl } // namespace detail } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/pl/classic_gaussian/N160.cc0000664000175000017500000000340115160212070024024 0ustar alastairalastair/* * (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. */ // TL319 #include "N.h" namespace atlas { namespace grid { namespace detail { namespace pl { namespace classic_gaussian { DEFINE_POINTS_PER_LATITUDE(160, LIST(18, 25, 36, 40, 45, 50, 60, 64, 72, 72, 80, 90, 90, 96, 108, 120, 120, 125, 128, 135, 144, 150, 160, 160, 180, 180, 180, 192, 192, 200, 216, 216, 225, 225, 240, 240, 243, 250, 256, 270, 270, 288, 288, 288, 300, 300, 320, 320, 320, 320, 324, 360, 360, 360, 360, 360, 360, 375, 375, 375, 384, 384, 400, 400, 400, 405, 432, 432, 432, 432, 432, 450, 450, 450, 450, 480, 480, 480, 480, 480, 480, 480, 500, 500, 500, 500, 500, 512, 512, 540, 540, 540, 540, 540, 540, 540, 540, 576, 576, 576, 576, 576, 576, 576, 576, 576, 576, 600, 600, 600, 600, 600, 600, 600, 600, 600, 640, 640, 640, 640, 640, 640, 640, 640, 640, 640, 640, 640, 640, 640, 640, 640, 640, 640, 640, 640, 640, 640, 640, 640, 640, 640, 640, 640, 640, 640, 640, 640, 640, 640, 640, 640, 640, 640, 640, 640, 640, 640, 640, 640)) } // namespace classic_gaussian } // namespace pl } // namespace detail } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/pl/classic_gaussian/PointsPerLatitude.cc0000664000175000017500000000463115160212070027025 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ /// @author Willem Deconinck /// @date Mar 2016 #include "PointsPerLatitude.h" #include #include #include "atlas/grid/detail/pl/classic_gaussian/N.h" #include "atlas/runtime/Exception.h" namespace atlas { namespace grid { namespace detail { namespace pl { namespace classic_gaussian { //----------------------------------------------------------------------------- template void points_per_latitude_npole_equator_impl(const size_t N, Int nlon[]) { std::stringstream Nstream; Nstream << N; std::string Nstr = Nstream.str(); if (PointsPerLatitudeFactory::has(Nstr)) { std::unique_ptr pl(PointsPerLatitudeFactory::build(Nstr)); pl->assign(nlon, N); } else { throw_Exception("gaussian::classic::PointsPerLatitude not available for N" + Nstr, Here()); } } //----------------------------------------------------------------------------- template void points_per_latitude_npole_spole_impl(const size_t N, Int nlon[]) { points_per_latitude_npole_equator(N, nlon); size_t end = 2 * N - 1; for (size_t jlat = 0; jlat < N; ++jlat) { nlon[end--] = nlon[jlat]; } } //----------------------------------------------------------------------------- void points_per_latitude_npole_equator(const size_t N, long nlon[]) { points_per_latitude_npole_equator_impl(N, nlon); } void points_per_latitude_npole_equator(const size_t N, int nlon[]) { points_per_latitude_npole_equator_impl(N, nlon); } //----------------------------------------------------------------------------- void points_per_latitude_npole_spole(const size_t N, long nlon[]) { points_per_latitude_npole_spole_impl(N, nlon); } void points_per_latitude_npole_spole(const size_t N, int nlon[]) { points_per_latitude_npole_spole_impl(N, nlon); } //----------------------------------------------------------------------------- } // namespace classic_gaussian } // namespace pl } // namespace detail } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/pl/classic_gaussian/N640.cc0000664000175000017500000001105215160212070024030 0ustar alastairalastair/* * (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. */ // TL1279 #include "N.h" namespace atlas { namespace grid { namespace detail { namespace pl { namespace classic_gaussian { DEFINE_POINTS_PER_LATITUDE( 640, LIST(18, 25, 32, 40, 45, 50, 60, 60, 72, 72, 75, 81, 90, 90, 96, 100, 108, 120, 120, 125, 135, 144, 150, 160, 160, 180, 180, 180, 192, 192, 200, 216, 216, 216, 225, 240, 240, 243, 250, 256, 270, 270, 288, 288, 288, 300, 300, 320, 320, 320, 360, 360, 360, 360, 360, 360, 375, 375, 384, 384, 400, 400, 400, 432, 432, 432, 432, 450, 450, 450, 480, 480, 480, 480, 480, 486, 500, 500, 512, 512, 540, 540, 540, 540, 540, 576, 576, 576, 576, 576, 600, 600, 600, 600, 640, 640, 640, 640, 640, 640, 640, 648, 675, 675, 675, 675, 720, 720, 720, 720, 720, 720, 720, 720, 729, 750, 750, 750, 750, 768, 768, 768, 800, 800, 800, 800, 800, 810, 810, 864, 864, 864, 864, 864, 864, 864, 864, 900, 900, 900, 900, 900, 900, 960, 960, 960, 960, 960, 960, 960, 960, 960, 960, 960, 972, 972, 1000, 1000, 1000, 1000, 1000, 1024, 1024, 1024, 1024, 1080, 1080, 1080, 1080, 1080, 1080, 1080, 1080, 1080, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1152, 1152, 1152, 1152, 1152, 1200, 1200, 1200, 1200, 1200, 1200, 1200, 1200, 1215, 1215, 1215, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1296, 1296, 1350, 1350, 1350, 1350, 1350, 1350, 1350, 1350, 1350, 1350, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1458, 1458, 1458, 1458, 1500, 1500, 1500, 1500, 1500, 1500, 1500, 1500, 1536, 1536, 1536, 1536, 1536, 1536, 1536, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1620, 1620, 1620, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1944, 1944, 1944, 1944, 1944, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2025, 2025, 2025, 2025, 2025, 2025, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2187, 2187, 2187, 2187, 2187, 2187, 2187, 2187, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2430, 2430, 2430, 2430, 2430, 2430, 2430, 2430, 2430, 2430, 2430, 2430, 2430, 2430, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560)) } // namespace classic_gaussian } // namespace pl } // namespace detail } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/pl/classic_gaussian/N576.cc0000664000175000017500000001021615160212070024041 0ustar alastairalastair/* * (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. */ // TL1151 #include "N.h" namespace atlas { namespace grid { namespace detail { namespace pl { namespace classic_gaussian { DEFINE_POINTS_PER_LATITUDE( 576, LIST(18, 25, 32, 40, 45, 50, 60, 60, 72, 72, 75, 81, 90, 96, 96, 100, 108, 120, 120, 125, 135, 144, 150, 160, 160, 180, 180, 180, 192, 192, 200, 216, 216, 225, 225, 240, 240, 243, 250, 256, 270, 270, 288, 288, 288, 300, 300, 320, 320, 320, 360, 360, 360, 360, 360, 360, 375, 375, 384, 384, 400, 400, 400, 432, 432, 432, 432, 450, 450, 450, 480, 480, 480, 480, 480, 486, 500, 500, 512, 512, 540, 540, 540, 540, 540, 576, 576, 576, 576, 576, 600, 600, 600, 600, 640, 640, 640, 640, 640, 640, 640, 648, 675, 675, 675, 675, 675, 720, 720, 720, 720, 720, 720, 720, 729, 750, 750, 750, 750, 768, 768, 768, 800, 800, 800, 800, 800, 810, 810, 864, 864, 864, 864, 864, 864, 864, 864, 864, 900, 900, 900, 900, 900, 900, 960, 960, 960, 960, 960, 960, 960, 960, 960, 960, 972, 972, 972, 1000, 1000, 1000, 1000, 1000, 1024, 1024, 1024, 1024, 1080, 1080, 1080, 1080, 1080, 1080, 1080, 1080, 1080, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1152, 1152, 1152, 1152, 1152, 1200, 1200, 1200, 1200, 1200, 1200, 1200, 1200, 1200, 1215, 1215, 1215, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1296, 1296, 1296, 1350, 1350, 1350, 1350, 1350, 1350, 1350, 1350, 1350, 1350, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1458, 1458, 1458, 1458, 1500, 1500, 1500, 1500, 1500, 1500, 1500, 1500, 1536, 1536, 1536, 1536, 1536, 1536, 1536, 1536, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1620, 1620, 1620, 1620, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1944, 1944, 1944, 1944, 1944, 1944, 1944, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2025, 2048, 2048, 2048, 2048, 2048, 2048, 2048, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2187, 2187, 2187, 2187, 2187, 2187, 2187, 2187, 2187, 2187, 2187, 2187, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304)) } // namespace classic_gaussian } // namespace pl } // namespace detail } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/pl/classic_gaussian/N2000.cc0000664000175000017500000003227015160212070024105 0ustar alastairalastair/* * (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. */ // TL3999 #include "N.h" namespace atlas { namespace grid { namespace detail { namespace pl { namespace classic_gaussian { DEFINE_POINTS_PER_LATITUDE( 2000, LIST(18, 25, 32, 40, 45, 50, 60, 60, 72, 72, 75, 81, 90, 96, 96, 108, 108, 120, 120, 120, 125, 135, 135, 144, 144, 150, 160, 160, 180, 180, 180, 180, 192, 192, 192, 200, 216, 216, 216, 225, 240, 240, 243, 250, 256, 270, 270, 288, 300, 300, 320, 320, 320, 360, 360, 360, 360, 360, 360, 375, 375, 384, 400, 400, 400, 405, 432, 432, 432, 432, 450, 450, 450, 480, 480, 480, 480, 486, 500, 500, 500, 512, 512, 540, 540, 540, 540, 576, 576, 576, 576, 576, 600, 600, 600, 600, 625, 625, 625, 625, 640, 640, 640, 648, 675, 675, 675, 675, 720, 720, 720, 720, 720, 720, 720, 729, 750, 750, 750, 750, 768, 768, 768, 800, 800, 800, 800, 800, 810, 864, 864, 864, 864, 864, 864, 864, 864, 864, 900, 900, 900, 900, 900, 900, 960, 960, 960, 960, 960, 960, 960, 960, 960, 972, 972, 1000, 1000, 1000, 1000, 1000, 1024, 1024, 1024, 1080, 1080, 1080, 1080, 1080, 1080, 1080, 1080, 1080, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1152, 1152, 1152, 1152, 1152, 1200, 1200, 1200, 1200, 1200, 1200, 1200, 1215, 1215, 1215, 1250, 1250, 1250, 1250, 1250, 1280, 1280, 1280, 1280, 1280, 1296, 1296, 1296, 1350, 1350, 1350, 1350, 1350, 1350, 1350, 1350, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1458, 1458, 1500, 1500, 1500, 1500, 1500, 1500, 1500, 1536, 1536, 1536, 1536, 1536, 1536, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1620, 1620, 1620, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1944, 1944, 1944, 1944, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2025, 2025, 2025, 2025, 2048, 2048, 2048, 2048, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2187, 2187, 2187, 2187, 2187, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2430, 2430, 2430, 2430, 2430, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2592, 2592, 2592, 2592, 2592, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2916, 2916, 2916, 2916, 2916, 2916, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3072, 3072, 3072, 3072, 3072, 3072, 3072, 3072, 3072, 3072, 3072, 3072, 3125, 3125, 3125, 3125, 3125, 3125, 3125, 3125, 3125, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3240, 3240, 3240, 3240, 3240, 3240, 3240, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3456, 3456, 3456, 3456, 3456, 3456, 3456, 3456, 3456, 3456, 3456, 3456, 3456, 3456, 3456, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3645, 3645, 3645, 3645, 3645, 3645, 3645, 3645, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3888, 3888, 3888, 3888, 3888, 3888, 3888, 3888, 3888, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4050, 4050, 4050, 4050, 4050, 4050, 4050, 4050, 4050, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4374, 4374, 4374, 4374, 4374, 4374, 4374, 4374, 4374, 4374, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4860, 4860, 4860, 4860, 4860, 4860, 4860, 4860, 4860, 4860, 4860, 4860, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5184, 5184, 5184, 5184, 5184, 5184, 5184, 5184, 5184, 5184, 5184, 5184, 5184, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5832, 5832, 5832, 5832, 5832, 5832, 5832, 5832, 5832, 5832, 5832, 5832, 5832, 5832, 5832, 5832, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6075, 6075, 6075, 6075, 6075, 6075, 6075, 6075, 6075, 6075, 6075, 6075, 6075, 6075, 6075, 6075, 6075, 6075, 6075, 6144, 6144, 6144, 6144, 6144, 6144, 6144, 6144, 6144, 6144, 6144, 6144, 6144, 6144, 6144, 6144, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6480, 6480, 6480, 6480, 6480, 6480, 6480, 6480, 6480, 6480, 6480, 6480, 6480, 6480, 6480, 6480, 6480, 6480, 6480, 6480, 6480, 6561, 6561, 6561, 6561, 6561, 6561, 6561, 6561, 6561, 6561, 6561, 6561, 6561, 6561, 6561, 6561, 6561, 6561, 6561, 6561, 6561, 6561, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6912, 6912, 6912, 6912, 6912, 6912, 6912, 6912, 6912, 6912, 6912, 6912, 6912, 6912, 6912, 6912, 6912, 6912, 6912, 6912, 6912, 6912, 6912, 6912, 6912, 6912, 6912, 6912, 6912, 6912, 6912, 6912, 6912, 6912, 6912, 6912, 6912, 6912, 6912, 6912, 6912, 6912, 6912, 6912, 6912, 6912, 6912, 6912, 6912, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7290, 7290, 7290, 7290, 7290, 7290, 7290, 7290, 7290, 7290, 7290, 7290, 7290, 7290, 7290, 7290, 7290, 7290, 7290, 7290, 7290, 7290, 7290, 7290, 7290, 7290, 7290, 7290, 7290, 7290, 7290, 7290, 7290, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7776, 7776, 7776, 7776, 7776, 7776, 7776, 7776, 7776, 7776, 7776, 7776, 7776, 7776, 7776, 7776, 7776, 7776, 7776, 7776, 7776, 7776, 7776, 7776, 7776, 7776, 7776, 7776, 7776, 7776, 7776, 7776, 7776, 7776, 7776, 7776, 7776, 7776, 7776, 7776, 7776, 7776, 7776, 7776, 7776, 7776, 7776, 7776, 7776, 7776, 7776, 7776, 7776, 7776, 7776, 7776, 7776, 7776, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000)) } // namespace classic_gaussian } // namespace pl } // namespace detail } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/pl/classic_gaussian/PointsPerLatitude.h0000664000175000017500000000344015160212070026664 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ /// @author Willem Deconinck /// @date Mar 2016 #pragma once #include namespace atlas { namespace grid { namespace detail { namespace pl { namespace classic_gaussian { /** * @brief Compute points per latitude between North pole and equator * @param N [in] Number of latitudes between pole and equator (Gaussian N * number) * @param nlon [out] points per latitude */ void points_per_latitude_npole_equator(const size_t N, long nlon[]); /** * @brief Compute points per latitude between North pole and equator * @param N [in] Number of latitudes between pole and equator (Gaussian N * number) * @param nlon [out] points per latitude */ void points_per_latitude_npole_equator(const size_t N, int nlon[]); /** * @brief Compute points per latitude between North pole and South pole * @param N [in] Number of latitudes between pole and equator (Gaussian N * number) * @param nlon [out] points per latitude (size 2*N) */ void points_per_latitude_npole_spole(const size_t N, long nlon[]); /** * @brief Compute points per latitude between North pole and South pole * @param N [in] Number of latitudes between pole and equator (Gaussian N * number) * @param nlon [out] points per latitude (size 2*N) */ void points_per_latitude_npole_spole(const size_t N, int nlon[]); } // namespace classic_gaussian } // namespace pl } // namespace detail } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/pl/classic_gaussian/N400.cc0000664000175000017500000000601715160212070024027 0ustar alastairalastair/* * (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. */ // TL799 #include "N.h" namespace atlas { namespace grid { namespace detail { namespace pl { namespace classic_gaussian { DEFINE_POINTS_PER_LATITUDE( 400, LIST(18, 25, 32, 40, 45, 50, 60, 60, 72, 72, 75, 81, 90, 96, 100, 108, 120, 120, 125, 128, 144, 144, 150, 160, 160, 180, 180, 192, 192, 200, 200, 216, 216, 225, 240, 240, 240, 250, 250, 256, 270, 288, 288, 288, 300, 300, 320, 320, 320, 324, 360, 360, 360, 360, 360, 360, 375, 375, 384, 400, 400, 400, 405, 432, 432, 432, 432, 450, 450, 450, 480, 480, 480, 480, 480, 486, 500, 500, 512, 512, 540, 540, 540, 540, 540, 576, 576, 576, 576, 576, 576, 600, 600, 600, 600, 640, 640, 640, 640, 640, 640, 640, 648, 675, 675, 675, 675, 675, 720, 720, 720, 720, 720, 720, 720, 729, 729, 750, 750, 750, 750, 768, 768, 768, 800, 800, 800, 800, 800, 800, 810, 864, 864, 864, 864, 864, 864, 864, 864, 864, 864, 900, 900, 900, 900, 900, 900, 900, 960, 960, 960, 960, 960, 960, 960, 960, 960, 960, 960, 960, 972, 972, 1000, 1000, 1000, 1000, 1000, 1000, 1024, 1024, 1024, 1024, 1024, 1080, 1080, 1080, 1080, 1080, 1080, 1080, 1080, 1080, 1080, 1080, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1152, 1152, 1152, 1152, 1152, 1152, 1200, 1200, 1200, 1200, 1200, 1200, 1200, 1200, 1200, 1200, 1200, 1215, 1215, 1215, 1215, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1296, 1296, 1296, 1296, 1350, 1350, 1350, 1350, 1350, 1350, 1350, 1350, 1350, 1350, 1350, 1350, 1350, 1350, 1350, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1458, 1458, 1458, 1458, 1458, 1458, 1458, 1500, 1500, 1500, 1500, 1500, 1500, 1500, 1500, 1500, 1500, 1500, 1500, 1500, 1500, 1500, 1500, 1500, 1536, 1536, 1536, 1536, 1536, 1536, 1536, 1536, 1536, 1536, 1536, 1536, 1536, 1536, 1536, 1536, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600)) } // namespace classic_gaussian } // namespace pl } // namespace detail } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/pl/classic_gaussian/N256.cc0000664000175000017500000000417615160212070024044 0ustar alastairalastair/* * (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. */ // TL511 #include "N.h" namespace atlas { namespace grid { namespace detail { namespace pl { namespace classic_gaussian { DEFINE_POINTS_PER_LATITUDE( 256, LIST(18, 25, 32, 40, 45, 50, 60, 64, 72, 72, 75, 81, 90, 96, 100, 108, 120, 120, 125, 135, 144, 150, 160, 160, 180, 180, 180, 192, 192, 200, 216, 216, 216, 225, 240, 240, 243, 250, 256, 270, 270, 288, 288, 288, 300, 300, 320, 320, 320, 324, 360, 360, 360, 360, 360, 360, 375, 375, 384, 384, 400, 400, 400, 432, 432, 432, 432, 432, 450, 450, 450, 480, 480, 480, 480, 480, 486, 500, 500, 500, 512, 512, 540, 540, 540, 540, 540, 576, 576, 576, 576, 576, 576, 600, 600, 600, 600, 600, 640, 640, 640, 640, 640, 640, 640, 640, 648, 675, 675, 675, 675, 675, 675, 720, 720, 720, 720, 720, 720, 720, 720, 720, 729, 729, 750, 750, 750, 750, 750, 768, 768, 768, 768, 800, 800, 800, 800, 800, 800, 800, 800, 810, 810, 864, 864, 864, 864, 864, 864, 864, 864, 864, 864, 864, 864, 864, 864, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 900, 960, 960, 960, 960, 960, 960, 960, 960, 960, 960, 960, 960, 960, 960, 960, 960, 960, 960, 960, 960, 960, 960, 972, 972, 972, 972, 972, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024, 1024)) } // namespace classic_gaussian } // namespace pl } // namespace detail } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/pl/classic_gaussian/N80.cc0000664000175000017500000000226415160212070023753 0ustar alastairalastair/* * (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. */ // TL159 #include "N.h" namespace atlas { namespace grid { namespace detail { namespace pl { namespace classic_gaussian { DEFINE_POINTS_PER_LATITUDE(80, LIST(18, 25, 36, 40, 45, 54, 60, 64, 72, 72, 80, 90, 96, 100, 108, 120, 120, 128, 135, 144, 144, 150, 160, 160, 180, 180, 180, 192, 192, 200, 200, 216, 216, 216, 225, 225, 240, 240, 240, 256, 256, 256, 256, 288, 288, 288, 288, 288, 288, 288, 288, 288, 300, 300, 300, 300, 320, 320, 320, 320, 320, 320, 320, 320, 320, 320, 320, 320, 320, 320, 320, 320, 320, 320, 320, 320, 320, 320, 320, 320)) } // namespace classic_gaussian } // namespace pl } // namespace detail } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/pl/classic_gaussian/N1600.cc0000664000175000017500000002522315160212070024112 0ustar alastairalastair/* * (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. */ // TL3199 #include "N.h" namespace atlas { namespace grid { namespace detail { namespace pl { namespace classic_gaussian { DEFINE_POINTS_PER_LATITUDE( 1600, LIST(18, 25, 32, 40, 45, 50, 54, 60, 72, 72, 75, 80, 90, 90, 96, 100, 108, 120, 120, 120, 125, 128, 135, 144, 144, 150, 160, 160, 162, 180, 180, 180, 192, 192, 216, 216, 225, 240, 240, 243, 250, 256, 270, 270, 288, 288, 288, 300, 300, 320, 320, 320, 360, 360, 360, 360, 360, 360, 375, 375, 384, 384, 400, 400, 405, 432, 432, 432, 432, 450, 450, 450, 480, 480, 480, 480, 480, 486, 500, 500, 512, 512, 540, 540, 540, 540, 576, 576, 576, 576, 576, 576, 600, 600, 600, 600, 625, 625, 625, 625, 640, 640, 648, 675, 675, 675, 675, 720, 720, 720, 720, 720, 720, 720, 729, 729, 750, 750, 750, 768, 768, 768, 800, 800, 800, 800, 800, 810, 810, 864, 864, 864, 864, 864, 864, 864, 864, 900, 900, 900, 900, 900, 900, 960, 960, 960, 960, 960, 960, 960, 960, 960, 960, 972, 1000, 1000, 1000, 1000, 1000, 1024, 1024, 1024, 1024, 1080, 1080, 1080, 1080, 1080, 1080, 1080, 1080, 1080, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1152, 1152, 1152, 1152, 1200, 1200, 1200, 1200, 1200, 1200, 1200, 1200, 1215, 1215, 1250, 1250, 1250, 1250, 1250, 1250, 1280, 1280, 1280, 1280, 1280, 1296, 1296, 1350, 1350, 1350, 1350, 1350, 1350, 1350, 1350, 1350, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1458, 1458, 1458, 1500, 1500, 1500, 1500, 1500, 1500, 1536, 1536, 1536, 1536, 1536, 1536, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1620, 1620, 1620, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1944, 1944, 1944, 1944, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2025, 2025, 2025, 2025, 2048, 2048, 2048, 2048, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2187, 2187, 2187, 2187, 2187, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2430, 2430, 2430, 2430, 2430, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2592, 2592, 2592, 2592, 2592, 2592, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2916, 2916, 2916, 2916, 2916, 2916, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3072, 3072, 3072, 3072, 3072, 3072, 3072, 3072, 3072, 3072, 3072, 3072, 3072, 3125, 3125, 3125, 3125, 3125, 3125, 3125, 3125, 3125, 3125, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3240, 3240, 3240, 3240, 3240, 3240, 3240, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3456, 3456, 3456, 3456, 3456, 3456, 3456, 3456, 3456, 3456, 3456, 3456, 3456, 3456, 3456, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3645, 3645, 3645, 3645, 3645, 3645, 3645, 3645, 3645, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3888, 3888, 3888, 3888, 3888, 3888, 3888, 3888, 3888, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4050, 4050, 4050, 4050, 4050, 4050, 4050, 4050, 4050, 4050, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4374, 4374, 4374, 4374, 4374, 4374, 4374, 4374, 4374, 4374, 4374, 4374, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4860, 4860, 4860, 4860, 4860, 4860, 4860, 4860, 4860, 4860, 4860, 4860, 4860, 4860, 4860, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5184, 5184, 5184, 5184, 5184, 5184, 5184, 5184, 5184, 5184, 5184, 5184, 5184, 5184, 5184, 5184, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5832, 5832, 5832, 5832, 5832, 5832, 5832, 5832, 5832, 5832, 5832, 5832, 5832, 5832, 5832, 5832, 5832, 5832, 5832, 5832, 5832, 5832, 5832, 5832, 5832, 5832, 5832, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6075, 6075, 6075, 6075, 6075, 6075, 6075, 6075, 6075, 6075, 6075, 6075, 6075, 6075, 6075, 6075, 6075, 6075, 6075, 6075, 6075, 6075, 6075, 6075, 6075, 6075, 6075, 6075, 6075, 6075, 6075, 6075, 6075, 6075, 6075, 6144, 6144, 6144, 6144, 6144, 6144, 6144, 6144, 6144, 6144, 6144, 6144, 6144, 6144, 6144, 6144, 6144, 6144, 6144, 6144, 6144, 6144, 6144, 6144, 6144, 6144, 6144, 6144, 6144, 6144, 6144, 6144, 6144, 6144, 6144, 6144, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400)) } // namespace classic_gaussian } // namespace pl } // namespace detail } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/pl/classic_gaussian/N128.cc0000664000175000017500000000303015160212070024026 0ustar alastairalastair/* * (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. */ // TL255 #include "N.h" namespace atlas { namespace grid { namespace detail { namespace pl { namespace classic_gaussian { DEFINE_POINTS_PER_LATITUDE(128, LIST(18, 25, 36, 40, 45, 50, 60, 64, 72, 72, 80, 90, 90, 100, 108, 120, 120, 125, 128, 144, 144, 150, 160, 160, 180, 180, 180, 192, 192, 200, 216, 216, 216, 225, 240, 240, 240, 250, 250, 256, 270, 270, 288, 288, 288, 300, 300, 320, 320, 320, 320, 324, 360, 360, 360, 360, 360, 360, 360, 375, 375, 375, 375, 384, 384, 400, 400, 400, 400, 405, 432, 432, 432, 432, 432, 432, 432, 450, 450, 450, 450, 450, 480, 480, 480, 480, 480, 480, 480, 480, 480, 480, 486, 486, 486, 500, 500, 500, 500, 500, 500, 500, 512, 512, 512, 512, 512, 512, 512, 512, 512, 512, 512, 512, 512, 512, 512, 512, 512, 512, 512, 512, 512, 512, 512, 512, 512, 512)) } // namespace classic_gaussian } // namespace pl } // namespace detail } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/pl/classic_gaussian/N96.cc0000664000175000017500000000245015160212070023757 0ustar alastairalastair/* * (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. */ // TL191 #include "N.h" namespace atlas { namespace grid { namespace detail { namespace pl { namespace classic_gaussian { DEFINE_POINTS_PER_LATITUDE(96, LIST(18, 25, 36, 40, 45, 50, 60, 64, 72, 72, 80, 90, 96, 100, 108, 120, 120, 125, 135, 144, 144, 150, 160, 160, 180, 180, 180, 192, 192, 200, 200, 216, 216, 225, 225, 240, 240, 240, 250, 250, 256, 270, 270, 270, 288, 288, 288, 288, 300, 300, 300, 320, 320, 320, 320, 320, 324, 360, 360, 360, 360, 360, 360, 360, 360, 360, 360, 360, 375, 375, 375, 375, 375, 375, 375, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384, 384)) } // namespace classic_gaussian } // namespace pl } // namespace detail } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/pl/classic_gaussian/N320.cc0000664000175000017500000000477215160212070024036 0ustar alastairalastair/* * (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. */ // TL639 #include "N.h" namespace atlas { namespace grid { namespace detail { namespace pl { namespace classic_gaussian { DEFINE_POINTS_PER_LATITUDE( 320, LIST(18, 25, 36, 40, 45, 50, 60, 64, 72, 72, 75, 81, 90, 96, 100, 108, 120, 120, 125, 135, 144, 144, 150, 160, 180, 180, 180, 192, 192, 200, 216, 216, 216, 225, 240, 240, 240, 250, 256, 270, 270, 288, 288, 288, 300, 300, 320, 320, 320, 324, 360, 360, 360, 360, 360, 360, 375, 375, 384, 384, 400, 400, 405, 432, 432, 432, 432, 450, 450, 450, 480, 480, 480, 480, 480, 486, 500, 500, 500, 512, 512, 540, 540, 540, 540, 540, 576, 576, 576, 576, 576, 576, 600, 600, 600, 600, 640, 640, 640, 640, 640, 640, 640, 648, 648, 675, 675, 675, 675, 720, 720, 720, 720, 720, 720, 720, 720, 720, 729, 750, 750, 750, 750, 768, 768, 768, 768, 800, 800, 800, 800, 800, 800, 810, 810, 864, 864, 864, 864, 864, 864, 864, 864, 864, 864, 864, 900, 900, 900, 900, 900, 900, 900, 900, 960, 960, 960, 960, 960, 960, 960, 960, 960, 960, 960, 960, 960, 960, 972, 972, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1024, 1024, 1024, 1024, 1024, 1024, 1080, 1080, 1080, 1080, 1080, 1080, 1080, 1080, 1080, 1080, 1080, 1080, 1080, 1080, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1152, 1200, 1200, 1200, 1200, 1200, 1200, 1200, 1200, 1200, 1200, 1200, 1200, 1200, 1200, 1200, 1200, 1200, 1200, 1215, 1215, 1215, 1215, 1215, 1215, 1215, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1280)) } // namespace classic_gaussian } // namespace pl } // namespace detail } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/pl/classic_gaussian/N4000.cc0000664000175000017500000007030615160212070024111 0ustar alastairalastair/* * (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. */ // TL7999 #include "N.h" namespace atlas { namespace grid { namespace detail { namespace pl { namespace classic_gaussian { DEFINE_POINTS_PER_LATITUDE( 4000, LIST(18, 24, 32, 40, 45, 48, 54, 60, 64, 72, 75, 80, 90, 90, 96, 100, 108, 108, 120, 120, 125, 128, 135, 135, 144, 150, 150, 160, 160, 180, 180, 180, 180, 192, 192, 192, 200, 216, 216, 216, 216, 225, 225, 240, 240, 240, 243, 250, 256, 256, 270, 270, 270, 288, 288, 288, 288, 300, 300, 300, 320, 320, 320, 320, 324, 360, 360, 360, 360, 360, 360, 360, 360, 375, 375, 375, 375, 384, 384, 400, 400, 400, 405, 405, 432, 432, 432, 432, 432, 432, 450, 450, 450, 450, 480, 480, 480, 480, 480, 480, 486, 486, 500, 500, 500, 512, 512, 512, 540, 540, 540, 540, 540, 540, 576, 576, 576, 576, 576, 576, 576, 576, 600, 600, 600, 600, 600, 600, 625, 625, 625, 625, 625, 625, 640, 640, 640, 648, 648, 675, 675, 675, 675, 675, 675, 720, 720, 720, 720, 720, 720, 720, 720, 720, 720, 720, 729, 729, 750, 750, 750, 750, 750, 768, 768, 768, 768, 800, 800, 800, 800, 800, 800, 800, 810, 810, 864, 864, 864, 864, 864, 864, 864, 864, 864, 864, 864, 864, 864, 900, 900, 900, 900, 900, 900, 900, 900, 960, 960, 960, 960, 960, 960, 960, 960, 960, 960, 960, 960, 960, 960, 972, 972, 972, 1000, 1000, 1000, 1000, 1000, 1000, 1000, 1024, 1024, 1024, 1024, 1024, 1080, 1080, 1080, 1080, 1080, 1080, 1080, 1080, 1080, 1080, 1080, 1080, 1080, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1152, 1152, 1152, 1152, 1152, 1152, 1200, 1200, 1200, 1200, 1200, 1200, 1200, 1200, 1200, 1200, 1200, 1215, 1215, 1215, 1215, 1250, 1250, 1250, 1250, 1250, 1250, 1250, 1250, 1280, 1280, 1280, 1280, 1280, 1280, 1280, 1296, 1296, 1296, 1296, 1350, 1350, 1350, 1350, 1350, 1350, 1350, 1350, 1350, 1350, 1350, 1350, 1350, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1458, 1458, 1458, 1458, 1500, 1500, 1500, 1500, 1500, 1500, 1500, 1500, 1500, 1500, 1536, 1536, 1536, 1536, 1536, 1536, 1536, 1536, 1536, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1620, 1620, 1620, 1620, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1944, 1944, 1944, 1944, 1944, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2025, 2025, 2025, 2025, 2025, 2025, 2048, 2048, 2048, 2048, 2048, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2187, 2187, 2187, 2187, 2187, 2187, 2187, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2430, 2430, 2430, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2592, 2592, 2592, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2916, 2916, 2916, 2916, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3072, 3072, 3072, 3072, 3072, 3072, 3072, 3072, 3072, 3125, 3125, 3125, 3125, 3125, 3125, 3125, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3240, 3240, 3240, 3240, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3456, 3456, 3456, 3456, 3456, 3456, 3456, 3456, 3456, 3456, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3645, 3645, 3645, 3645, 3645, 3645, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3888, 3888, 3888, 3888, 3888, 3888, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4050, 4050, 4050, 4050, 4050, 4050, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4374, 4374, 4374, 4374, 4374, 4374, 4374, 4374, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4860, 4860, 4860, 4860, 4860, 4860, 4860, 4860, 4860, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5184, 5184, 5184, 5184, 5184, 5184, 5184, 5184, 5184, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5400, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5625, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5760, 5832, 5832, 5832, 5832, 5832, 5832, 5832, 5832, 5832, 5832, 5832, 5832, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6075, 6075, 6075, 6075, 6075, 6075, 6075, 6075, 6075, 6075, 6075, 6075, 6075, 6144, 6144, 6144, 6144, 6144, 6144, 6144, 6144, 6144, 6144, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6250, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6400, 6480, 6480, 6480, 6480, 6480, 6480, 6480, 6480, 6480, 6480, 6480, 6480, 6480, 6561, 6561, 6561, 6561, 6561, 6561, 6561, 6561, 6561, 6561, 6561, 6561, 6561, 6561, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6750, 6912, 6912, 6912, 6912, 6912, 6912, 6912, 6912, 6912, 6912, 6912, 6912, 6912, 6912, 6912, 6912, 6912, 6912, 6912, 6912, 6912, 6912, 6912, 6912, 6912, 6912, 6912, 6912, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7200, 7290, 7290, 7290, 7290, 7290, 7290, 7290, 7290, 7290, 7290, 7290, 7290, 7290, 7290, 7290, 7290, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7500, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7680, 7776, 7776, 7776, 7776, 7776, 7776, 7776, 7776, 7776, 7776, 7776, 7776, 7776, 7776, 7776, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8000, 8100, 8100, 8100, 8100, 8100, 8100, 8100, 8100, 8100, 8100, 8100, 8100, 8100, 8100, 8100, 8100, 8100, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8192, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8640, 8748, 8748, 8748, 8748, 8748, 8748, 8748, 8748, 8748, 8748, 8748, 8748, 8748, 8748, 8748, 8748, 8748, 8748, 8748, 8748, 9000, 9000, 9000, 9000, 9000, 9000, 9000, 9000, 9000, 9000, 9000, 9000, 9000, 9000, 9000, 9000, 9000, 9000, 9000, 9000, 9000, 9000, 9000, 9000, 9000, 9000, 9000, 9000, 9000, 9000, 9000, 9000, 9000, 9000, 9000, 9000, 9000, 9000, 9000, 9000, 9000, 9000, 9000, 9000, 9000, 9216, 9216, 9216, 9216, 9216, 9216, 9216, 9216, 9216, 9216, 9216, 9216, 9216, 9216, 9216, 9216, 9216, 9216, 9216, 9216, 9216, 9216, 9216, 9216, 9216, 9216, 9216, 9216, 9216, 9216, 9216, 9216, 9216, 9216, 9216, 9216, 9216, 9216, 9216, 9216, 9216, 9375, 9375, 9375, 9375, 9375, 9375, 9375, 9375, 9375, 9375, 9375, 9375, 9375, 9375, 9375, 9375, 9375, 9375, 9375, 9375, 9375, 9375, 9375, 9375, 9375, 9375, 9375, 9375, 9375, 9375, 9600, 9600, 9600, 9600, 9600, 9600, 9600, 9600, 9600, 9600, 9600, 9600, 9600, 9600, 9600, 9600, 9600, 9600, 9600, 9600, 9600, 9600, 9600, 9600, 9600, 9600, 9600, 9600, 9600, 9600, 9600, 9600, 9600, 9600, 9600, 9600, 9600, 9600, 9600, 9600, 9600, 9600, 9600, 9600, 9720, 9720, 9720, 9720, 9720, 9720, 9720, 9720, 9720, 9720, 9720, 9720, 9720, 9720, 9720, 9720, 9720, 9720, 9720, 9720, 9720, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10000, 10125, 10125, 10125, 10125, 10125, 10125, 10125, 10125, 10125, 10125, 10125, 10125, 10125, 10125, 10125, 10125, 10125, 10125, 10125, 10125, 10125, 10125, 10125, 10125, 10125, 10240, 10240, 10240, 10240, 10240, 10240, 10240, 10240, 10240, 10240, 10240, 10240, 10240, 10240, 10240, 10240, 10240, 10240, 10240, 10240, 10240, 10240, 10240, 10240, 10368, 10368, 10368, 10368, 10368, 10368, 10368, 10368, 10368, 10368, 10368, 10368, 10368, 10368, 10368, 10368, 10368, 10368, 10368, 10368, 10368, 10368, 10368, 10368, 10368, 10368, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10800, 10935, 10935, 10935, 10935, 10935, 10935, 10935, 10935, 10935, 10935, 10935, 10935, 10935, 10935, 10935, 10935, 10935, 10935, 10935, 10935, 10935, 10935, 10935, 10935, 10935, 10935, 10935, 10935, 10935, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11250, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11520, 11664, 11664, 11664, 11664, 11664, 11664, 11664, 11664, 11664, 11664, 11664, 11664, 11664, 11664, 11664, 11664, 11664, 11664, 11664, 11664, 11664, 11664, 11664, 11664, 11664, 11664, 11664, 11664, 11664, 11664, 11664, 11664, 11664, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12000, 12150, 12150, 12150, 12150, 12150, 12150, 12150, 12150, 12150, 12150, 12150, 12150, 12150, 12150, 12150, 12150, 12150, 12150, 12150, 12150, 12150, 12150, 12150, 12150, 12150, 12150, 12150, 12150, 12150, 12150, 12150, 12150, 12150, 12150, 12150, 12150, 12288, 12288, 12288, 12288, 12288, 12288, 12288, 12288, 12288, 12288, 12288, 12288, 12288, 12288, 12288, 12288, 12288, 12288, 12288, 12288, 12288, 12288, 12288, 12288, 12288, 12288, 12288, 12288, 12288, 12288, 12288, 12288, 12288, 12500, 12500, 12500, 12500, 12500, 12500, 12500, 12500, 12500, 12500, 12500, 12500, 12500, 12500, 12500, 12500, 12500, 12500, 12500, 12500, 12500, 12500, 12500, 12500, 12500, 12500, 12500, 12500, 12500, 12500, 12500, 12500, 12500, 12500, 12500, 12500, 12500, 12500, 12500, 12500, 12500, 12500, 12500, 12500, 12500, 12500, 12500, 12500, 12500, 12500, 12500, 12500, 12500, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12800, 12960, 12960, 12960, 12960, 12960, 12960, 12960, 12960, 12960, 12960, 12960, 12960, 12960, 12960, 12960, 12960, 12960, 12960, 12960, 12960, 12960, 12960, 12960, 12960, 12960, 12960, 12960, 12960, 12960, 12960, 12960, 12960, 12960, 12960, 12960, 12960, 12960, 12960, 12960, 12960, 12960, 12960, 13122, 13122, 13122, 13122, 13122, 13122, 13122, 13122, 13122, 13122, 13122, 13122, 13122, 13122, 13122, 13122, 13122, 13122, 13122, 13122, 13122, 13122, 13122, 13122, 13122, 13122, 13122, 13122, 13122, 13122, 13122, 13122, 13122, 13122, 13122, 13122, 13122, 13122, 13122, 13122, 13122, 13122, 13122, 13122, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13500, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 13824, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14400, 14580, 14580, 14580, 14580, 14580, 14580, 14580, 14580, 14580, 14580, 14580, 14580, 14580, 14580, 14580, 14580, 14580, 14580, 14580, 14580, 14580, 14580, 14580, 14580, 14580, 14580, 14580, 14580, 14580, 14580, 14580, 14580, 14580, 14580, 14580, 14580, 14580, 14580, 14580, 14580, 14580, 14580, 14580, 14580, 14580, 14580, 14580, 14580, 14580, 14580, 14580, 14580, 14580, 14580, 14580, 14580, 14580, 14580, 14580, 14580, 14580, 14580, 14580, 14580, 14580, 14580, 14580, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15000, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15360, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15552, 15625, 15625, 15625, 15625, 15625, 15625, 15625, 15625, 15625, 15625, 15625, 15625, 15625, 15625, 15625, 15625, 15625, 15625, 15625, 15625, 15625, 15625, 15625, 15625, 15625, 15625, 15625, 15625, 15625, 15625, 15625, 15625, 15625, 15625, 15625, 15625, 15625, 15625, 15625, 15625, 15625, 15625, 15625, 15625, 15625, 15625, 15625, 15625, 15625, 15625, 15625, 15625, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000, 16000)) } // namespace classic_gaussian } // namespace pl } // namespace detail } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/pl/classic_gaussian/N64.cc0000664000175000017500000000210015160212070023742 0ustar alastairalastair/* * (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. */ // TL127 #include "N.h" namespace atlas { namespace grid { namespace detail { namespace pl { namespace classic_gaussian { DEFINE_POINTS_PER_LATITUDE(64, LIST(20, 25, 36, 40, 45, 54, 60, 64, 72, 75, 80, 90, 96, 100, 108, 120, 120, 125, 135, 135, 144, 150, 160, 160, 180, 180, 180, 180, 192, 192, 200, 200, 216, 216, 216, 216, 225, 225, 225, 240, 240, 240, 240, 243, 250, 250, 250, 250, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256, 256)) } // namespace classic_gaussian } // namespace pl } // namespace detail } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/pl/classic_gaussian/N800.cc0000664000175000017500000001307415160212070024034 0ustar alastairalastair/* * (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. */ // TL1599 #include "N.h" namespace atlas { namespace grid { namespace detail { namespace pl { namespace classic_gaussian { DEFINE_POINTS_PER_LATITUDE( 800, LIST(18, 25, 32, 40, 45, 50, 60, 60, 72, 72, 75, 80, 90, 90, 96, 100, 108, 120, 120, 125, 128, 135, 144, 150, 160, 160, 180, 180, 192, 192, 200, 200, 216, 216, 225, 240, 240, 240, 250, 250, 256, 270, 288, 288, 288, 300, 300, 320, 320, 320, 324, 360, 360, 360, 360, 360, 375, 375, 375, 384, 400, 400, 400, 405, 432, 432, 432, 432, 450, 450, 450, 480, 480, 480, 480, 486, 500, 500, 500, 512, 512, 540, 540, 540, 540, 576, 576, 576, 576, 576, 576, 600, 600, 600, 625, 625, 625, 625, 625, 640, 640, 648, 675, 675, 675, 675, 720, 720, 720, 720, 720, 720, 720, 720, 729, 750, 750, 750, 768, 768, 768, 800, 800, 800, 800, 800, 800, 810, 864, 864, 864, 864, 864, 864, 864, 864, 864, 900, 900, 900, 900, 900, 900, 960, 960, 960, 960, 960, 960, 960, 960, 960, 960, 972, 972, 1000, 1000, 1000, 1000, 1000, 1024, 1024, 1024, 1024, 1080, 1080, 1080, 1080, 1080, 1080, 1080, 1080, 1080, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1152, 1152, 1152, 1152, 1152, 1200, 1200, 1200, 1200, 1200, 1200, 1200, 1200, 1215, 1215, 1250, 1250, 1250, 1250, 1250, 1250, 1280, 1280, 1280, 1280, 1280, 1280, 1296, 1296, 1350, 1350, 1350, 1350, 1350, 1350, 1350, 1350, 1350, 1350, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1458, 1458, 1458, 1500, 1500, 1500, 1500, 1500, 1500, 1500, 1500, 1536, 1536, 1536, 1536, 1536, 1536, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1620, 1620, 1620, 1620, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1944, 1944, 1944, 1944, 1944, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2025, 2025, 2025, 2025, 2025, 2048, 2048, 2048, 2048, 2048, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2187, 2187, 2187, 2187, 2187, 2187, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2430, 2430, 2430, 2430, 2430, 2430, 2430, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2592, 2592, 2592, 2592, 2592, 2592, 2592, 2592, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2916, 2916, 2916, 2916, 2916, 2916, 2916, 2916, 2916, 2916, 2916, 2916, 2916, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3072, 3072, 3072, 3072, 3072, 3072, 3072, 3072, 3072, 3072, 3072, 3072, 3072, 3072, 3072, 3072, 3072, 3072, 3072, 3072, 3072, 3072, 3072, 3072, 3072, 3072, 3072, 3072, 3072, 3072, 3072, 3072, 3072, 3072, 3072, 3125, 3125, 3125, 3125, 3125, 3125, 3125, 3125, 3125, 3125, 3125, 3125, 3125, 3125, 3125, 3125, 3125, 3125, 3125, 3125, 3125, 3125, 3125, 3125, 3125, 3125, 3125, 3125, 3125, 3125, 3125, 3125, 3125, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200)) } // namespace classic_gaussian } // namespace pl } // namespace detail } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/pl/classic_gaussian/N1280.cc0000664000175000017500000002116115160212070024113 0ustar alastairalastair/* * (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. */ // TL2559 #include "N.h" namespace atlas { namespace grid { namespace detail { namespace pl { namespace classic_gaussian { DEFINE_POINTS_PER_LATITUDE( 1280, LIST(18, 25, 32, 40, 45, 50, 60, 64, 72, 72, 75, 81, 90, 96, 96, 108, 108, 120, 120, 120, 125, 135, 135, 144, 144, 160, 160, 180, 180, 180, 192, 200, 200, 216, 216, 225, 240, 240, 240, 250, 250, 256, 270, 288, 288, 288, 300, 300, 320, 320, 320, 324, 360, 360, 360, 360, 360, 375, 375, 375, 384, 400, 400, 400, 432, 432, 432, 432, 432, 450, 450, 480, 480, 480, 480, 480, 486, 500, 500, 512, 512, 540, 540, 540, 540, 540, 576, 576, 576, 576, 576, 600, 600, 600, 600, 625, 625, 625, 625, 640, 640, 640, 648, 675, 675, 675, 675, 720, 720, 720, 720, 720, 720, 720, 729, 729, 750, 750, 750, 768, 768, 768, 800, 800, 800, 800, 800, 810, 864, 864, 864, 864, 864, 864, 864, 864, 864, 900, 900, 900, 900, 900, 900, 960, 960, 960, 960, 960, 960, 960, 960, 960, 972, 972, 1000, 1000, 1000, 1000, 1000, 1024, 1024, 1024, 1024, 1080, 1080, 1080, 1080, 1080, 1080, 1080, 1080, 1080, 1125, 1125, 1125, 1125, 1125, 1125, 1125, 1152, 1152, 1152, 1152, 1200, 1200, 1200, 1200, 1200, 1200, 1200, 1200, 1215, 1215, 1250, 1250, 1250, 1250, 1250, 1250, 1280, 1280, 1280, 1280, 1280, 1296, 1296, 1296, 1350, 1350, 1350, 1350, 1350, 1350, 1350, 1350, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1440, 1458, 1458, 1458, 1500, 1500, 1500, 1500, 1500, 1500, 1500, 1536, 1536, 1536, 1536, 1536, 1536, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1600, 1620, 1620, 1620, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1728, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1800, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1875, 1920, 1920, 1920, 1920, 1920, 1920, 1920, 1944, 1944, 1944, 1944, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2000, 2025, 2025, 2025, 2025, 2048, 2048, 2048, 2048, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2160, 2187, 2187, 2187, 2187, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2250, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2304, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2400, 2430, 2430, 2430, 2430, 2430, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2500, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2560, 2592, 2592, 2592, 2592, 2592, 2592, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2700, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2880, 2916, 2916, 2916, 2916, 2916, 2916, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3000, 3072, 3072, 3072, 3072, 3072, 3072, 3072, 3072, 3072, 3072, 3072, 3072, 3072, 3072, 3125, 3125, 3125, 3125, 3125, 3125, 3125, 3125, 3125, 3125, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3200, 3240, 3240, 3240, 3240, 3240, 3240, 3240, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3375, 3456, 3456, 3456, 3456, 3456, 3456, 3456, 3456, 3456, 3456, 3456, 3456, 3456, 3456, 3456, 3456, 3456, 3456, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3600, 3645, 3645, 3645, 3645, 3645, 3645, 3645, 3645, 3645, 3645, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3750, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3840, 3888, 3888, 3888, 3888, 3888, 3888, 3888, 3888, 3888, 3888, 3888, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4000, 4050, 4050, 4050, 4050, 4050, 4050, 4050, 4050, 4050, 4050, 4050, 4050, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4096, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4320, 4374, 4374, 4374, 4374, 4374, 4374, 4374, 4374, 4374, 4374, 4374, 4374, 4374, 4374, 4374, 4374, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4500, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4608, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4800, 4860, 4860, 4860, 4860, 4860, 4860, 4860, 4860, 4860, 4860, 4860, 4860, 4860, 4860, 4860, 4860, 4860, 4860, 4860, 4860, 4860, 4860, 4860, 4860, 4860, 4860, 4860, 4860, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5000, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120, 5120)) } // namespace classic_gaussian } // namespace pl } // namespace detail } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/vertical/0000775000175000017500000000000015160212070020756 5ustar alastairalastairatlas-0.46.0/src/atlas/grid/detail/vertical/VerticalInterface.cc0000664000175000017500000000311515160212070024657 0ustar alastairalastair/* * (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 "VerticalInterface.h" #include "atlas/field/Field.h" #include "atlas/field/detail/FieldImpl.h" #include "atlas/grid/Vertical.h" #include "atlas/runtime/Exception.h" namespace atlas { Vertical* atlas__Vertical__new(idx_t levels, const double z[]) { std::vector zvec(z, z + levels); return new Vertical(levels, zvec); } Vertical* atlas__Vertical__new_interval(idx_t levels, const double z[], const double interval[]) { std::vector zvec(z, z + levels); return new Vertical(levels, zvec, interval); } void atlas__Vertical__delete(Vertical* This) { delete This; } field::FieldImpl* atlas__Vertical__z(const Vertical* This) { ATLAS_ASSERT(This != nullptr); field::FieldImpl* field; { auto zfield = Field("z", array::make_datatype(), array::make_shape(This->size())); auto zview = array::make_view(zfield); for (idx_t k = 0; k < zview.size(); ++k) { zview(k) = (*This)[k]; } field = zfield.get(); field->attach(); } field->detach(); return field; } int atlas__Vertical__size(const Vertical* This) { return This->size(); } } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/vertical/VerticalInterface.h0000664000175000017500000000170215160212070024521 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include //#include "atlas/util/Object.h" #include "atlas/library/config.h" namespace atlas { class Vertical; namespace field { class FieldImpl; } } // namespace atlas namespace atlas { extern "C" { Vertical* atlas__Vertical__new(idx_t levels, const double z[]); Vertical* atlas__Vertical__new_interval(idx_t levels, const double z[], const double interval[]); void atlas__Vertical__delete(Vertical* This); field::FieldImpl* atlas__Vertical__z(const Vertical* This); int atlas__Vertical__size(const Vertical* This); } } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/partitioner/0000775000175000017500000000000015160212070021505 5ustar alastairalastairatlas-0.46.0/src/atlas/grid/detail/partitioner/MatchingMeshPartitionerSphericalPolygon.h0000664000175000017500000000355715160212070031663 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include "atlas/grid/detail/partitioner/MatchingMeshPartitioner.h" namespace atlas { namespace grid { namespace detail { namespace partitioner { class MatchingMeshPartitionerSphericalPolygon : public MatchingMeshPartitioner { public: static std::string static_type() { return "spherical-polygon"; } public: MatchingMeshPartitionerSphericalPolygon(const Mesh& mesh, const eckit::Parametrisation& config): MatchingMeshPartitioner(mesh, config) {} // Following throw ATLAS_NOT_IMPLEMENTED MatchingMeshPartitionerSphericalPolygon(): MatchingMeshPartitioner() {} MatchingMeshPartitionerSphericalPolygon(const idx_t nb_partitions): MatchingMeshPartitioner() {} MatchingMeshPartitionerSphericalPolygon(const idx_t nb_partitions, const eckit::Parametrisation& config): MatchingMeshPartitioner() {} MatchingMeshPartitionerSphericalPolygon(const eckit::Parametrisation&): MatchingMeshPartitioner() {} using MatchingMeshPartitioner::partition; /** * @brief Partition a grid, using the same partitions from a pre-partitioned * mesh. * The method constructs a partition edges polygon to test every target grid * node with. * @param[in] grid grid to be partitioned * @param[out] partitioning partitioning result */ void partition(const Grid& grid, int partitioning[]) const; virtual std::string type() const { return static_type(); } }; } // namespace partitioner } // namespace detail } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/partitioner/BandsPartitioner.cc0000664000175000017500000000521215160212070025264 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "BandsPartitioner.h" #include #include "atlas/grid/StructuredGrid.h" #include "atlas/grid/detail/distribution/BandsDistribution.h" #include "atlas/runtime/Exception.h" namespace atlas { namespace grid { namespace detail { namespace partitioner { size_t BandsPartitioner::blocksize(const Grid& grid) const { if (blocksize_ == BLOCKSIZE_NX) { if (auto regular = RegularGrid(grid)) { return regular.nx(); } return 1; } ATLAS_ASSERT(blocksize_ > 0); return size_t(blocksize_); } BandsPartitioner::BandsPartitioner( const eckit::Parametrisation& config): Partitioner(config), blocksize_{1} {} BandsPartitioner::BandsPartitioner(int N, int blocksize, const eckit::Parametrisation& config): Partitioner(N, config), blocksize_(blocksize) {} Distribution BandsPartitioner::partition(const Partitioner::Grid& grid) const { if (not distribution::BandsDistribution::detectOverflow(grid.size(), nb_partitions(), blocksize((grid)))) { return Distribution{ new distribution::BandsDistribution{grid, nb_partitions(), type(), blocksize(grid)}}; } else { return Distribution{ new distribution::BandsDistribution{grid, nb_partitions(), type(), blocksize(grid)}}; } } void BandsPartitioner::partition(const Partitioner::Grid& grid, int part[]) const { gidx_t gridsize = grid.size(); if (not distribution::BandsDistribution::detectOverflow(grid.size(), nb_partitions(), blocksize((grid)))) { distribution::BandsDistribution distribution{grid, nb_partitions(), type(), blocksize(grid)}; for (gidx_t n = 0; n < gridsize; ++n) { part[n] = distribution.function(n); } } else { distribution::BandsDistribution distribution{grid, nb_partitions(), type(), blocksize(grid)}; for (gidx_t n = 0; n < gridsize; ++n) { part[n] = distribution.function(n); } } } } // namespace partitioner } // namespace detail } // namespace grid } // namespace atlas namespace { atlas::grid::detail::partitioner::PartitionerBuilder __Bands( atlas::grid::detail::partitioner::BandsPartitioner::static_type()); } atlas-0.46.0/src/atlas/grid/detail/partitioner/CubedSpherePartitioner.h0000664000175000017500000001005515160212070026271 0ustar alastairalastair/* * (C) Crown Copyright Met Office 2021 * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include #include "atlas/grid/detail/partitioner/Partitioner.h" namespace atlas { namespace grid { namespace detail { namespace partitioner { class CubedSpherePartitioner : public Partitioner { public: CubedSpherePartitioner(); CubedSpherePartitioner(int N); // N is the number of parts (aka MPI tasks) CubedSpherePartitioner(int N, const eckit::Parametrisation&); CubedSpherePartitioner(const eckit::Parametrisation&); CubedSpherePartitioner(const int N, const std::vector& globalProcStartPE, const std::vector& globalProcEndPE, const std::vector& nprocx, const std::vector& nprocy); CubedSpherePartitioner(const int N, const bool regularGrid); // Cell struct that holds the x and y and t indices // This could be replaced using the tij iterator that is part of the grid class struct CellInt { int x, y, t; int n; }; struct CubedSphere { std::array nproc; std::array nprocx; // number of PEs in the x direction of xy space on each tile. std::array nprocy; // number of PEs in the y direction of xy space on each tile. std::array globalProcStartPE; // lowest global mpi rank on each tile; std::array globalProcEndPE; // final global mpi rank on each tile; // note that mpi ranks on each tile are vary contiguously from globalProcStartPE to // globalProcEndPE. // grid dimensions on each tile - for all cell-centered grids they will be same. std::array nx; std::array ny; // these are the offsets in the x and y directions // they are allocated in "void partition(CubedSphere& cb, int nb_nodes, CellInt nodes[], int part[] );" std::vector> xoffset; std::vector> yoffset; // the two variables below are for now the main options // in the future this will be extended std::array startingCornerOnTile; // for now bottom left corner (0) default. // Could be configurable to top left (1), top right(2) bottom right(3) std::array xFirst; // if 1 then x is leading index - if 0 y is leading index; CubedSphere() { nprocx = std::array{1, 1, 1, 1, 1, 1}; nprocy = std::array{1, 1, 1, 1, 1, 1}; startingCornerOnTile = std::array{0, 0, 0, 0, 0, 0}; xFirst = std::array{1, 1, 1, 1, 1, 1}; } }; CubedSphere cubedsphere(const Grid&) const; void partition(CubedSphere& cb, const int nb_nodes, const CellInt nodes[], int part[]) const; virtual std::string type() const { return "cubedsphere"; } private: using Partitioner::partition; virtual void partition(const Grid&, int part[]) const; void check() const; private: std::vector globalProcStartPE_{0, 0, 0, 0, 0, 0}; std::vector globalProcEndPE_{0, 0, 0, 0, 0, 0}; std::vector nprocx_{1, 1, 1, 1, 1, 1}; // number of ranks in x direction on each tile std::vector nprocy_{1, 1, 1, 1, 1, 1}; // number of ranks in x direction on each tile bool regular_ = true; // regular algorithm for partitioning. }; } // namespace partitioner } // namespace detail } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/partitioner/MatchingMeshPartitioner.h0000664000175000017500000000176515160212070026457 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include "atlas/grid/detail/partitioner/Partitioner.h" #include "atlas/mesh/Mesh.h" namespace atlas { namespace grid { namespace detail { namespace partitioner { class MatchingMeshPartitioner : public Partitioner { public: MatchingMeshPartitioner(); // MatchingMeshPartitioner(const idx_t nb_partitions); MatchingMeshPartitioner(const Mesh&, const eckit::Parametrisation&); virtual ~MatchingMeshPartitioner() override {} protected: const Mesh prePartitionedMesh_; }; } // namespace partitioner } // namespace detail } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/partitioner/MatchingFunctionSpacePartitionerLonLatPolygon.h0000664000175000017500000000333015160212070032774 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include "atlas/grid/detail/partitioner/MatchingFunctionSpacePartitioner.h" namespace atlas { namespace grid { namespace detail { namespace partitioner { class MatchingFunctionSpacePartitionerLonLatPolygon : public MatchingFunctionSpacePartitioner { public: static std::string static_type() { return "lonlat-polygon"; } public: MatchingFunctionSpacePartitionerLonLatPolygon(): MatchingFunctionSpacePartitioner() {} // MatchingFunctionSpacePartitionerLonLatPolygon(const size_t nb_partitions): // MatchingFunctionSpacePartitioner(nb_partitions) {} MatchingFunctionSpacePartitionerLonLatPolygon(const FunctionSpace& FunctionSpace, const eckit::Parametrisation& config): MatchingFunctionSpacePartitioner(FunctionSpace, config) {} using MatchingFunctionSpacePartitioner::partition; /** * @brief Partition a grid, using the same partitions from a pre-partitioned * FunctionSpace. * The method constructs a partition edges polygon to test every target grid * node with. * @param[in] grid grid to be partitioned * @param[out] partitioning partitioning result */ void partition(const Grid& grid, int partitioning[]) const; virtual std::string type() const { return static_type(); } }; } // namespace partitioner } // namespace detail } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/partitioner/TransPartitioner.h0000664000175000017500000000323615160212070025172 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include "atlas/grid/detail/partitioner/Partitioner.h" namespace atlas { namespace grid { namespace detail { namespace partitioner { /// @class TransPartitioner /// @brief Equal regions partitioning algorithm computed by the IFS trans /// library /// /// The user is advised to use the "EqualRegionsPartitioner" class instead. This /// implementation is only here to to guarantee the exact same distribution /// as IFS is using. The difference with "EqualRegionsPartitioner" is minimal. /// (a few points may be assigned to different partitions). class TransPartitioner : public Partitioner { public: /// @brief Constructor TransPartitioner(); TransPartitioner(const eckit::Parametrisation&); TransPartitioner(const idx_t nb_partitions, const eckit::Parametrisation& = util::NoConfig()); virtual ~TransPartitioner(); using Partitioner::partition; /// Warning: this function temporariliy allocates a new Trans, but without the /// computations /// of the spectral coefficients (LDGRIDONLY=TRUE) virtual void partition(const Grid&, int part[]) const; virtual std::string type() const { return "ectrans"; } private: bool split_y_{true}; }; } // namespace partitioner } // namespace detail } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/partitioner/EqualBandsPartitioner.cc0000664000175000017500000000165515160212070026263 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "EqualBandsPartitioner.h" #include "atlas/parallel/mpi/mpi.h" namespace atlas { namespace grid { namespace detail { namespace partitioner { EqualBandsPartitioner::EqualBandsPartitioner(): EqualBandsPartitioner(mpi::size()) {} } // namespace partitioner } // namespace detail } // namespace grid } // namespace atlas namespace { atlas::grid::detail::partitioner::PartitionerBuilder __EqualBands(atlas::grid::detail::partitioner::EqualBandsPartitioner::static_type()); } atlas-0.46.0/src/atlas/grid/detail/partitioner/MatchingMeshPartitionerBruteForce.cc0000664000175000017500000001566715160212070030604 0ustar alastairalastair/* * (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/grid/detail/partitioner/MatchingMeshPartitionerBruteForce.h" #include #include "eckit/log/ProgressTimer.h" #include "atlas/array/ArrayView.h" #include "atlas/field/Field.h" #include "atlas/grid.h" #include "atlas/mesh/Elements.h" #include "atlas/mesh/Mesh.h" #include "atlas/mesh/Nodes.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" namespace atlas { namespace grid { namespace detail { namespace partitioner { namespace { PartitionerBuilder __builder("brute-force"); double dot_sign(const double& Ax, const double& Ay, const double& Bx, const double& By, const double& Cx, const double& Cy) { return (Ax - Cx) * (By - Cy) - (Bx - Cx) * (Ay - Cy); } /// Point-in-triangle test /// @note "dot product" test, equivalent to half-plane check /// @see /// http://stackoverflow.com/questions/2049582/how-to-determine-if-a-point-is-in-a-2d-triangle bool point_in_triangle(const PointLonLat& P, const PointLonLat& A, const PointLonLat& B, const PointLonLat& C) { // Compute signs of dot products const bool b1 = dot_sign(P.lon(), P.lat(), A.lon(), A.lat(), B.lon(), B.lat()) < 0, b2 = dot_sign(P.lon(), P.lat(), B.lon(), B.lat(), C.lon(), C.lat()) < 0, b3 = dot_sign(P.lon(), P.lat(), C.lon(), C.lat(), A.lon(), A.lat()) < 0; // Check if point is in triangle // - check for b1 && b2 && b3 only works for triangles ordered // counter-clockwise // - check for b1==b2 && b2==b3 works for both, equivalent to "all points on // the same side" return (b1 == b2) && (b2 == b3); } /// Point-in-quadrilateral test /// @note limited to convex quadrilaterals, @see /// https://en.wikipedia.org/wiki/Convex_polygon bool point_in_quadrilateral(const PointLonLat& P, const PointLonLat& A, const PointLonLat& B, const PointLonLat& C, const PointLonLat& D) { const bool b1 = dot_sign(P.lon(), P.lat(), A.lon(), A.lat(), B.lon(), B.lat()) < 0, b2 = dot_sign(P.lon(), P.lat(), B.lon(), B.lat(), C.lon(), C.lat()) < 0, b3 = dot_sign(P.lon(), P.lat(), C.lon(), C.lat(), D.lon(), D.lat()) < 0, b4 = dot_sign(P.lon(), P.lat(), D.lon(), D.lat(), A.lon(), A.lat()) < 0; return (b1 == b2) && (b2 == b3) && (b3 == b4); } } // namespace void MatchingMeshPartitionerBruteForce::partition(const Grid& grid, int partitioning[]) const { ATLAS_TRACE("MatchingMeshPartitionerBruteForce::partition"); const auto& comm = mpi::comm(prePartitionedMesh_.mpi_comm()); const int mpi_rank = int(comm.rank()); // Point coordinates // - use a bounding box to quickly discard points, // - except when that is above/below bounding box but poles should be included // FIXME: THIS IS A HACK! the coordinates include North/South Pole // (first/last partitions only) ATLAS_ASSERT(grid.domain().global()); bool includesNorthPole = (mpi_rank == 0); bool includesSouthPole = (mpi_rank == (int(comm.size()) - 1)); ATLAS_ASSERT(prePartitionedMesh_.nodes().size()); auto lonlat_src = array::make_view(prePartitionedMesh_.nodes().lonlat()); std::vector coordinates; coordinates.reserve(lonlat_src.shape(0)); PointLonLat coordinatesMin = PointLonLat(lonlat_src(0, LON), lonlat_src(0, LAT)); PointLonLat coordinatesMax = coordinatesMin; for (idx_t i = 0; i < lonlat_src.shape(0); ++i) { PointLonLat A(lonlat_src(i, LON), lonlat_src(i, LAT)); coordinatesMin = PointLonLat::componentsMin(coordinatesMin, A); coordinatesMax = PointLonLat::componentsMax(coordinatesMax, A); coordinates.emplace_back(A); } { eckit::ProgressTimer timer("Partitioning target", grid.size(), "point", double(10), atlas::Log::trace()); auto grid_iter = grid.lonlat().begin(); for (idx_t i = 0; i < grid.size(); ++i, ++grid_iter) { partitioning[i] = -1; const PointLonLat& P = *grid_iter; if (coordinatesMin[LON] <= P[LON] && P[LON] <= coordinatesMax[LON] && coordinatesMin[LAT] <= P[LAT] && P[LAT] <= coordinatesMax[LAT]) { const mesh::Cells& elements_src = prePartitionedMesh_.cells(); const idx_t nb_types = elements_src.nb_types(); bool found = false; for (idx_t t = 0; t < nb_types && !found; ++t) { idx_t idx[4]; const mesh::Elements& elements = elements_src.elements(t); const mesh::BlockConnectivity& conn = elements.node_connectivity(); const idx_t nb_nodes = elements.nb_nodes(); ATLAS_ASSERT((nb_nodes == 3 && elements.name() == "Triangle") || (nb_nodes == 4 && elements.name() == "Quadrilateral")); for (idx_t j = 0; j < elements.size() && !found; ++j) { idx[0] = conn(j, 0); idx[1] = conn(j, 1); idx[2] = conn(j, 2); idx[3] = nb_nodes > 3 ? conn(j, 3) : 0; if ((elements.name() == "Triangle" && point_in_triangle(P, coordinates[idx[0]], coordinates[idx[1]], coordinates[idx[2]])) || (elements.name() == "Quadrilateral" && point_in_quadrilateral(P, coordinates[idx[0]], coordinates[idx[1]], coordinates[idx[2]], coordinates[idx[3]]))) { partitioning[i] = mpi_rank; found = true; } } } } else if ((includesNorthPole && P[LAT] > coordinatesMax[LAT]) || (includesSouthPole && P[LAT] < coordinatesMin[LAT])) { partitioning[i] = mpi_rank; } } } // Synchronize the partitioning and return a grid partitioner comm.allReduceInPlace(partitioning, grid.size(), eckit::mpi::Operation::MAX); const int min = *std::min_element(partitioning, partitioning + grid.size()); if (min < 0) { throw_Exception( "Could not find partition for target node (source " "mesh does not contain all target grid points)", Here()); } } } // namespace partitioner } // namespace detail } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/partitioner/Partitioner.h0000664000175000017500000000737615160212070024173 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include "atlas/library/config.h" #include "atlas/util/Config.h" #include "atlas/util/Object.h" namespace atlas { class Grid; class Mesh; class FunctionSpace; namespace grid { class Distribution; } // namespace grid } // namespace atlas namespace atlas { namespace grid { namespace detail { namespace partitioner { class Partitioner : public util::Object { public: using Grid = atlas::Grid; public: Partitioner(); Partitioner(const idx_t nb_partitions, const eckit::Parametrisation&); Partitioner(const eckit::Parametrisation&); virtual ~Partitioner(); virtual void partition(const Grid& grid, int part[]) const = 0; virtual Distribution partition(const Grid& grid) const; idx_t nb_partitions() const; virtual std::string type() const = 0; std::string mpi_comm() const; static int extract_nb_partitions(const eckit::Parametrisation&); static std::string extract_mpi_comm(const eckit::Parametrisation&); private: idx_t nb_partitions_; std::string mpi_comm_; }; // ------------------------------------------------------------------ class PartitionerFactory { public: using Grid = Partitioner::Grid; public: /*! * \brief build Partitioner with factory key, constructor arguments * \return Partitioner */ static Partitioner* build(const std::string&); static Partitioner* build(const std::string&, const idx_t nb_partitions); static Partitioner* build(const std::string&, const idx_t nb_partitions, const eckit::Parametrisation&); static Partitioner* build(const std::string&, const eckit::Parametrisation&); /*! * \brief list all registered partioner builders */ static void list(std::ostream&); static bool has(const std::string& name); private: std::string name_; virtual Partitioner* make() = 0; virtual Partitioner* make(const idx_t nb_partitions) = 0; virtual Partitioner* make(const idx_t nb_partitions, const eckit::Parametrisation&) = 0; virtual Partitioner* make(const eckit::Parametrisation&) = 0; protected: PartitionerFactory(const std::string&); virtual ~PartitionerFactory(); }; // ------------------------------------------------------------------ template class PartitionerBuilder : public PartitionerFactory { virtual Partitioner* make() { return new T(); } virtual Partitioner* make(const idx_t nb_partitions) { return new T(nb_partitions, util::NoConfig()); } virtual Partitioner* make(const idx_t nb_partitions, const eckit::Parametrisation& config) { return new T(nb_partitions, config); } virtual Partitioner* make(const eckit::Parametrisation& config) { return new T(config); } public: PartitionerBuilder(const std::string& name): PartitionerFactory(name) {} }; // ------------------------------------------------------------------ class MatchingPartitionerFactory { public: static Partitioner* build(const std::string& type, const Mesh& partitioned, const eckit::Parametrisation& config); static Partitioner* build(const std::string& type, const FunctionSpace& partitioned, const eckit::Parametrisation& config); }; // ------------------------------------------------------------------ } // namespace partitioner } // namespace detail } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/partitioner/SerialPartitioner.cc0000664000175000017500000000171015160212070025453 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "SerialPartitioner.h" #include "atlas/parallel/mpi/mpi.h" namespace { atlas::grid::detail::partitioner::PartitionerBuilder __Serial( atlas::grid::detail::partitioner::SerialPartitioner::static_type()); } namespace atlas { namespace grid { namespace detail { namespace partitioner { SerialPartitioner::SerialPartitioner(const eckit::Parametrisation& config): Partitioner(config) { part_ = mpi::comm(mpi_comm()).rank(); config.get("part", part_); config.get("partition", part_); } } } } } atlas-0.46.0/src/atlas/grid/detail/partitioner/MatchingMeshPartitionerCubedSphere.h0000664000175000017500000000237215160212070030564 0ustar alastairalastair/* * (C) Crown Copyright 2022 Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ #pragma once #include "atlas/grid/detail/partitioner/MatchingMeshPartitioner.h" namespace atlas { namespace grid { namespace detail { namespace partitioner { class MatchingMeshPartitionerCubedSphere : public MatchingMeshPartitioner { public: static std::string static_type() { return "cubedsphere"; } public: MatchingMeshPartitionerCubedSphere(): MatchingMeshPartitioner() {} // MatchingMeshPartitionerCubedSphere(const idx_t nb_partitions): MatchingMeshPartitioner(nb_partitions) {} // MatchingMeshPartitionerCubedSphere(const idx_t nb_partitions, const eckit::Parametrisation&): // MatchingMeshPartitioner(nb_partitions) {} MatchingMeshPartitionerCubedSphere(const Mesh& mesh, const eckit::Parametrisation& config): MatchingMeshPartitioner(mesh, config) {} using MatchingMeshPartitioner::partition; virtual void partition(const Grid& grid, int partitioning[]) const; virtual std::string type() const { return static_type(); } }; } // namespace partitioner } // namespace detail } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/partitioner/SerialPartitioner.h0000664000175000017500000000315315160212070025320 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include "atlas/grid/Distribution.h" #include "atlas/grid/Grid.h" #include "atlas/grid/detail/distribution/SerialDistribution.h" #include "atlas/grid/detail/partitioner/Partitioner.h" namespace atlas { namespace grid { namespace detail { namespace partitioner { class SerialPartitioner : public Partitioner { public: SerialPartitioner(): Partitioner(1, util::NoConfig()) { } SerialPartitioner(int /*N*/): SerialPartitioner() { } SerialPartitioner(int /*N*/, const eckit::Parametrisation& config): SerialPartitioner(config) { } SerialPartitioner(const eckit::Parametrisation& config); std::string type() const override { return static_type(); } static std::string static_type() { return "serial"; } Distribution partition(const Grid& grid) const override { return Distribution{new distribution::SerialDistribution{grid, part_}}; } void partition(const Grid& grid, int part[]) const override { gidx_t gridsize = grid.size(); for (gidx_t n = 0; n < gridsize; ++n) { part[n] = part_; } } private: int part_{0}; }; } // namespace partitioner } // namespace detail } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/partitioner/MatchingMeshPartitioner.cc0000664000175000017500000000227115160212070026606 0ustar alastairalastair/* * (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/grid/detail/partitioner/MatchingMeshPartitioner.h" #include #include "atlas/runtime/Exception.h" namespace atlas { namespace grid { namespace detail { namespace partitioner { MatchingMeshPartitioner::MatchingMeshPartitioner(): Partitioner() { ATLAS_THROW_EXCEPTION("Error: A MatchingMeshPartitioner needs to be initialised with a Mesh"); } // MatchingMeshPartitioner::MatchingMeshPartitioner(const idx_t nb_partitions): Partitioner(nb_partitions) { // ATLAS_NOTIMPLEMENTED; // } MatchingMeshPartitioner::MatchingMeshPartitioner(const Mesh& mesh, const eckit::Parametrisation&): Partitioner(mesh.nb_parts(),util::Config("mpi_comm",mesh.mpi_comm())), prePartitionedMesh_(mesh) {} } // namespace partitioner } // namespace detail } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/partitioner/CheckerboardPartitioner.h0000664000175000017500000000435615160212070026463 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include #include "atlas/grid/detail/partitioner/Partitioner.h" namespace atlas { namespace grid { namespace detail { namespace partitioner { class CheckerboardPartitioner : public Partitioner { public: CheckerboardPartitioner(); // CheckerboardPartitioner(int N); // N is the number of parts (aka MPI tasks) CheckerboardPartitioner(int N, const eckit::Parametrisation&); CheckerboardPartitioner(int N, int nbands); CheckerboardPartitioner(int N, int nbands, bool checkerboard); CheckerboardPartitioner(const eckit::Parametrisation&); // Node struct that holds the x and y indices (for global, it's longitude and // latitude in millidegrees (integers)) // This structure is used in sorting algorithms, and uses less memory than // if x and y were in double precision. struct NodeInt { int x, y; int n; }; virtual std::string type() const { return "checkerboard"; } std::array checkerboardDimensions(const Grid&); private: struct Checkerboard { idx_t nbands; // number of bands idx_t nx, ny; // grid dimensions }; Checkerboard checkerboard(const Grid&) const; // Doesn't matter if nodes[] is in degrees or radians, as a sorting // algorithm is used internally void partition(const Checkerboard& cb, int nb_nodes, NodeInt nodes[], int part[]) const; using Partitioner::partition; virtual void partition(const Grid&, int part[]) const; void check() const; private: idx_t nbands_ = 0; // number of bands from configuration bool regular_ = false; bool split_x_ = true; bool split_y_ = true; bool checkerboard_ = true; // exact (true) or approximate (false) checkerboard }; } // namespace partitioner } // namespace detail } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/partitioner/EqualRegionsPartitioner.cc0000664000175000017500000010654315160212070026644 0ustar alastairalastair/* * (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/grid/detail/partitioner/EqualRegionsPartitioner.h" #include #include #include #include #include #include #include "atlas/grid/Iterator.h" #include "atlas/grid/StructuredGrid.h" #include "atlas/parallel/mpi/Buffer.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/parallel/omp/sort.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Log.h" #include "atlas/runtime/Trace.h" #include "atlas/util/MicroDeg.h" #include "atlas/util/vector.h" using atlas::util::microdeg; namespace atlas { namespace grid { namespace detail { namespace partitioner { namespace { static bool valid_mpi_size(size_t size) { return size < size_t(std::numeric_limits::max()); } template struct range_t { Iterator b; Sentinel e; Iterator begin() const { return b; } Sentinel end() const { return e; } // unused: // bool empty() const { return begin() == end(); } }; template range_t range(Iterator b, Sentinel e) { return {b, e}; } template range_t subrange( const Container& c, std::initializer_list&& _range) { const Int rbegin = _range.begin()[0]; const Int rend = _range.begin()[1]; return range(c.begin() + rbegin, c.begin() + rend); } } // namespace double gamma(const double& x) { double p[14]; double w, y; int k, n; p[0] = 0.999999999999999990e+00; p[1] = -0.422784335098466784e+00; p[2] = -0.233093736421782878e+00; p[3] = 0.191091101387638410e+00; p[4] = -0.024552490005641278e+00; p[5] = -0.017645244547851414e+00; p[6] = 0.008023273027855346e+00; p[7] = -0.000804329819255744e+00; p[8] = -0.000360837876648255e+00; p[9] = 0.000145596568617526e+00; p[10] = -0.000017545539395205e+00; p[11] = -0.000002591225267689e+00; p[12] = 0.000001337767384067e+00; p[13] = -0.000000199542863674e+00; n = round(x - 2); w = x - (n + 2); y = ((((((((((((p[13] * w + p[12]) * w + p[11]) * w + p[10]) * w + p[9]) * w + p[8]) * w + p[7]) * w + p[6]) * w + p[5]) * w + p[4]) * w + p[3]) * w + p[2]) * w + p[1]) * w + p[0]; if (n > 0) { w = x - 1; for (k = 2; k <= n; ++k) { w = w * (x - k); } } else { w = 1; for (k = 0; k <= -n - 1; ++k) { y = y * (x + k); } } return w / y; } double area_of_cap(const double& s_cap) { // // AREA_OF_CAP Area of spherical cap // // AREA_OF_CAP(S_CAP) sets AREA to be the area of an S^2 spherical // cap of spherical radius S_CAP. // return 4.0 * M_PI * std::pow(std::sin(0.5 * s_cap), 2); } double area_of_collar(const double& a_top, const double& a_bot) { // AREA_OF_COLLAR Area of spherical collar // // AREA_OF_COLLAR(A_TOP, A_BOT) sets AREA to be the area of an S^2 spherical // collar specified by A_TOP, A_BOT, where A_TOP is top (smaller) spherical // radius, // A_BOT is bottom (larger) spherical radius. // return area_of_cap(a_bot) - area_of_cap(a_top); } double sradius_of_cap(const double& area) { // SRADIUS_OF_CAP(AREA) returns the spherical radius of // an S^2 spherical cap of area AREA. // return 2. * std::asin(0.5 * std::sqrt(area / M_PI)); } double area_of_ideal_region(int N) { // // AREA_OF_IDEAL_REGION(N) sets AREA to be the area of one of N equal // area regions on S^2, that is 1/N times AREA_OF_SPHERE. // double area_of_sphere = 2. * std::pow(M_PI, 1.5) / gamma(1.5); return area_of_sphere / static_cast(N); } double polar_colat(int N) { // // Given N, determine the colatitude of the North polar spherical cap. // double polar_c(0); if (N == 1) { polar_c = M_PI; } if (N == 2) { polar_c = 0.5 * M_PI; } if (N > 2) { polar_c = sradius_of_cap(area_of_ideal_region(N)); } return polar_c; } double ideal_collar_angle(int N) { // // IDEAL_COLLAR_ANGLE The ideal angle for spherical collars of an EQ partition // // IDEAL_COLLAR_ANGLE(N) sets ANGLE to the ideal angle for the // spherical collars of an EQ partition of the unit sphere S^2 into N regions. // return std::sqrt(area_of_ideal_region(N)); } void ideal_region_list(int N, const double& c_polar, int n_collars, double r_regions[]) { // // IDEAL_REGION_LIST The ideal real number of regions in each zone // // List the ideal real number of regions in each collar, plus the polar caps. // // Given N, c_polar and n_collars, determine r_regions, a list of the ideal // real // number of regions in each collar, plus the polar caps. // The number of elements is n_collars+2. // r_regions[1] is 1. // r_regions[n_collars+2] is 1. // The sum of r_regions is N. // // real(wp),intent(out) :: r_regions(n_collars+2) double ideal_region_area, ideal_collar_area; r_regions[0] = 1.; if (n_collars > 0) { // // Based on n_collars and c_polar, determine a_fitting, // the collar angle such that n_collars collars fit between the polar caps. // double a_fitting = (M_PI - 2. * c_polar) / static_cast(n_collars); ideal_region_area = area_of_ideal_region(N); for (int collar_n = 0; collar_n < n_collars; ++collar_n) { ideal_collar_area = area_of_collar(c_polar + collar_n * a_fitting, c_polar + (collar_n + 1) * a_fitting); r_regions[1 + collar_n] = ideal_collar_area / ideal_region_area; } } r_regions[2 + n_collars - 1] = 1.; } int num_collars(int N, const double& c_polar, const double& a_ideal) { // // NUM_COLLARS The number of collars between the polar caps // // Given N, an ideal angle, and c_polar, // determine n_collars, the number of collars between the polar caps. // bool enough = (N > 2) && (a_ideal > 0); if (enough) { return std::max(1, static_cast(round((M_PI - 2. * c_polar) / a_ideal))); } else { return 0; } } void round_to_naturals(int N, int ncollars, double r_regions[], int n_regions[]) { // ROUND_TO_NATURALS Round off a given list of numbers of regions // // Given N and r_regions, determine n_regions, // a list of the natural number of regions in each collar and the polar caps. // This list is as close as possible to r_regions, using rounding. // The number of elements is n_collars+2. // n_regions[1] is 1. // n_regions[n_collars+2] is 1. // The sum of n_regions is N. // double discrepancy = 0.; for (int zone_n = 0; zone_n < ncollars + 2; ++zone_n) { n_regions[zone_n] = round(r_regions[zone_n] + discrepancy); discrepancy += r_regions[zone_n] - n_regions[zone_n]; } } void cap_colats(int N, int n_collars, const double& c_polar, int n_regions[], double c_caps[]) { // CAP_COLATS Colatitudes of spherical caps enclosing cumulative sum of // regions // // Given dim, N, c_polar and n_regions, determine c_caps, // an increasing list of colatitudes of spherical caps which enclose the same // area // as that given by the cumulative sum of regions. // The number of elements is n_collars+2. // c_caps[1] is c_polar. // c_caps[n_collars+1] is Pi-c_polar. // c_caps[n_collars+2] is Pi. // // c_caps = cap_colats(dim,N,c_polar,n_regions); c_caps[0] = c_polar; double ideal_region_area = area_of_ideal_region(N); int subtotal_n_regions = 1; for (int collar_n = 0; collar_n < n_collars; ++collar_n) { subtotal_n_regions = subtotal_n_regions + n_regions[1 + collar_n]; c_caps[collar_n + 1] = sradius_of_cap(subtotal_n_regions * ideal_region_area); } c_caps[n_collars + 1] = M_PI; } // Disable optimisation for Cray (See ATLAS-327) #ifdef _CRAYC #pragma _CRI noopt #endif void eq_caps(int N, std::vector& n_regions, std::vector& s_cap) { // // eq_regions uses the zonal equal area sphere partitioning algorithm to // partition // the surface of a sphere into N regions of equal area and small diameter. // if (N == 1) { // // We have only one region, which must be the whole sphere. // n_regions.resize(1); s_cap.resize(1); n_regions[0] = 1; s_cap[0] = M_PI; // int n_regions_ns=1; } else { // // Given N, determine c_polar // the colatitude of the North polar spherical cap. // const double c_polar = polar_colat(N); // // Given N, determine the ideal angle for spherical collars. // Based on N, this ideal angle, and c_polar, // determine n_collars, the number of collars between the polar caps. // const int n_collars = num_collars(N, c_polar, ideal_collar_angle(N)); // int n_regions_ns=n_collars+2; // // Given N, c_polar and n_collars, determine r_regions, // a list of the ideal real number of regions in each collar, // plus the polar caps. // The number of elements is n_collars+2. // r_regions[0] is 1. // r_regions[2+n_collars-1] is 1. // The sum of r_regions is N. std::vector r_regions(n_collars + 2); ideal_region_list(N, c_polar, n_collars, r_regions.data()); // // Given N and r_regions, determine n_regions, a list of the natural number // of regions in each collar and the polar caps. // This list is as close as possible to r_regions. // The number of elements is n_collars+2. // n_regions[0] is 1. // n_regions[2+n_collars-1] is 1. // The sum of n_regions is N. // n_regions.resize(n_collars + 2); round_to_naturals(N, n_collars, r_regions.data(), n_regions.data()); // // Given dim, N, c_polar and n_regions, determine s_cap, // an increasing list of colatitudes of spherical caps which enclose the // same area // as that given by the cumulative sum of regions. // The number of elements is n_collars+2. // s_cap[0] is c_polar. // s_cap[n_collars] is Pi-c_polar. // s_cap[n_collars+1] is Pi. // s_cap.resize(n_collars + 2); cap_colats(N, n_collars, c_polar, n_regions.data(), s_cap.data()); } // int n_regions_ew=maxval(n_regions(:)); } // Reenable optimisation for Cray (See ATLAS-327) #ifdef _CRAYC #pragma _CRI opt #endif void eq_regions(int N, double xmin[], double xmax[], double ymin[], double ymax[]) { // EQ_REGIONS Recursive zonal equal area (EQ) partition of sphere // // Syntax // [regions,dim_1_rot] = eq_regions(dim,N,options); // // Description // REGIONS = EQ_REGIONS(dim,N) uses the recursive zonal equal area sphere // partitioning algorithm to partition S^dim (the unit sphere in dim+1 // dimensional space) into N regions of equal area and small diameter. // // The arguments dim and N must be positive integers. // // The result REGIONS is a (dim by 2 by N) array, representing the regions // of S^dim. Each element represents a pair of vertex points in spherical // polar // coordinates. // // Each region is defined as a product of intervals in spherical polar // coordinates. The pair of vertex points regions(:,1,n) and regions(:,2,n) // give // the lower and upper limits of each interval. // // REGIONS = EQ_REGIONS(dim,N,'offset','extra') uses experimental extra // offsets for S^2 and S^3 to try to minimize energy. If dim > 3, extra // offsets // are not used. // // REGIONS = EQ_REGIONS(dim,N,extra_offset) uses experimental extra offsets // if extra_offset is true or non-zero. // // [REGIONS,DIM_1_ROT] = EQ_REGIONS(dim,N) also returns DIM_1_ROT, a cell // array containing N rotation matrices, one per region, each of size dim by // dim. // These describe the R^dim rotation needed to place the region in its final // position. // // [REGIONS,DIM_1_ROT] = EQ_REGIONS(dim,N,'offset','extra') partitions S^dim // into N regions, using extra offsets, and also returning DIM_1_ROT, as // above. // if (N == 1) { // // We have only one region, which must be the whole sphere. // xmin[0] = 0.; ymin[0] = -0.5 * M_PI; xmax[0] = 2. * M_PI; ymax[0] = 0.5 * M_PI; return; } // // Start the partition of the sphere into N regions by partitioning // to caps defined in the current dimension. // std::vector n_regions; std::vector s_cap; eq_caps(N, n_regions, s_cap); // // s_cap is an increasing list of colatitudes of the caps. // // // We have a number of zones: two polar caps and a number of collars. // n_regions is the list of the number of regions in each zone. // int n_collars = n_regions.size() - 2; // // Start with the top cap. // xmin[0] = 0.; ymin[0] = 0.5 * M_PI - s_cap[0]; xmax[0] = 2. * M_PI; ymax[0] = 0.5 * M_PI; int region_n = 1; for (int collar_n = 0; collar_n < n_collars; ++collar_n) { for (int region_ew = 0; region_ew < n_regions[collar_n + 1]; ++region_ew) { xmin[region_n] = 2. * M_PI / (static_cast(n_regions[collar_n + 1])) * region_ew; ymin[region_n] = 0.5 * M_PI - s_cap[collar_n + 1]; xmax[region_n] = 2. * M_PI / (static_cast(n_regions[collar_n + 1])) * (region_ew + 1.); ymax[region_n] = 0.5 * M_PI - s_cap[collar_n]; ++region_n; } } // // End with the bottom cap. // xmin[N - 1] = 0.; ymin[N - 1] = -0.5 * M_PI; xmax[N - 1] = 2. * M_PI; ymax[N - 1] = 0.5 * M_PI - s_cap[s_cap.size() - 2]; } void EqualRegionsPartitioner::init() { N_ = nb_partitions(); std::vector s_cap; eq_caps(N_, sectors_, s_cap); bands_.resize(s_cap.size()); for (size_t n = 0; n < s_cap.size(); ++n) { bands_[n] = 0.5 * M_PI - s_cap[n]; } } EqualRegionsPartitioner::EqualRegionsPartitioner(): Partitioner() { init(); } EqualRegionsPartitioner::EqualRegionsPartitioner(int N): EqualRegionsPartitioner(N, util::NoConfig()) { } EqualRegionsPartitioner::EqualRegionsPartitioner(int N, const eckit::Parametrisation& config): Partitioner(N,config) { std::string crds; if (config.get("coordinates", crds)) { if (crds == "lonlat") { coordinates_ = Coordinates::LONLAT; } } init(); } EqualRegionsPartitioner::EqualRegionsPartitioner(const eckit::Parametrisation& config): Partitioner(config) { std::string crds; if (config.get("coordinates", crds)) { if (crds == "lonlat") { coordinates_ = Coordinates::LONLAT; } } init(); } int EqualRegionsPartitioner::partition(const double& x, const double& y) const { int b = band(y); int p = 0; for (int n = 0; n < b; ++n) { p += sectors_[n]; } return p + sector(b, x); } int EqualRegionsPartitioner::band(const double& y) const { return std::distance(bands_.begin(), std::lower_bound(bands_.begin(), bands_.end(), y, std::greater())); } int EqualRegionsPartitioner::sector(int band, const double& x) const { constexpr double two_pi = 2. * M_PI; constexpr double div_two_pi = 1. / (2. * M_PI + 1.e-8); double xreg = x; if (x < 0.) { xreg += two_pi; } else if (x > two_pi) { xreg -= two_pi; } return std::floor(xreg * sectors_[band] * div_two_pi); } void EqualRegionsPartitioner::where(int partition, int& band, int& sector) const { int p = 0; for (size_t b = 0; b < bands_.size(); ++b) { for (int s = 0; s < sectors_[b]; ++s) { if (partition == p) { band = b; sector = s; return; } ++p; } } } bool compare_NS_WE(const EqualRegionsPartitioner::NodeInt& node1, const EqualRegionsPartitioner::NodeInt& node2) { if (node1.y > node2.y) { return true; } if (node1.y == node2.y) { return (node1.x < node2.x); } return false; } bool compare_WE_NS(const EqualRegionsPartitioner::NodeInt& node1, const EqualRegionsPartitioner::NodeInt& node2) { if (node1.x < node2.x) { return true; } if (node1.x == node2.x) { return (node1.y > node2.y); } return false; } void EqualRegionsPartitioner::partition(int nb_nodes, NodeInt nodes[], int part[]) const { ATLAS_TRACE("EqualRegionsPartitioner::partition"); // std::clock_t init, final; // init=std::clock(); // std::cout << "partition start (" << nb_nodes << " points)" << std::endl; int nb_parts = N_; int p; int i; int begin; int end; int band; /* Sort nodes from north to south, and west to east. Now we can easily split the points in bands. Note, for RGG, this should not be necessary, as it is already by construction in this order, but then sorting is really fast */ // std::sort( nodes, nodes+nb_nodes, compare_NS_WE); /* For every band, now sort from west to east, and north to south. Inside every band we can now easily split nodes in sectors. */ int chunk_size = nb_nodes / nb_parts; int chunk_remainder = nb_nodes - chunk_size * nb_parts; int remainder = chunk_remainder; std::vector count; count.reserve(nb_parts); end = 0; for (band = 0; band < nb_bands(); ++band) { begin = end; for (p = 0; p < nb_regions(band); ++p) { count.push_back(chunk_size + (remainder-- > 0 ? 1 : 0)); end += count.back(); } std::sort(nodes + begin, nodes + end, compare_WE_NS); } /* Create list that tells in original node numbering which part the node belongs to */ end = 0; for (p = 0; p < nb_parts; ++p) { begin = end; end = begin + count[p]; for (i = begin; i < end; ++i) { part[nodes[i].n] = p; } } /* This piece of code is to reorder back along latitudes */ // begin = 0; // for( p=0; p(comm.rank()); int mpi_size = static_cast(comm.size()); atlas::vector nodes(grid.size()); int* nodes_buffer = reinterpret_cast(nodes.data()); long nb_workers = comm.size(); /* Sort nodes from north to south, and west to east. Now we can easily split the points in bands. Note, for StructuredGrid, this should not be necessary, as it is already by construction in this order, but then sorting is really fast */ if (StructuredGrid(grid) && (coordinates_ == Coordinates::XY)) { // The grid comes sorted from north to south and west to east by // construction // ATLAS_ASSERT to make sure. StructuredGrid structured_grid(grid); //ATLAS_ASSERT( structured_grid.y( 1 ) < structured_grid.y( 0 ) ); ATLAS_ASSERT(structured_grid.x(1, 0) > structured_grid.x(0, 0)); ATLAS_TRACE("Take shortcut"); if (atlas_omp_get_max_threads() > 1) { atlas_omp_parallel { const idx_t num_threads = atlas_omp_get_num_threads(); const idx_t thread_num = atlas_omp_get_thread_num(); const idx_t begin = static_cast(thread_num * size_t(structured_grid.size()) / num_threads); const idx_t end = static_cast((thread_num + 1) * size_t(structured_grid.size()) / num_threads); idx_t thread_j_begin = 0; std::vector thread_i_begin(structured_grid.ny()); std::vector thread_i_end(structured_grid.ny()); idx_t n = 0; for (idx_t j = 0; j < structured_grid.ny(); ++j) { if (n + structured_grid.nx(j) > begin) { thread_j_begin = j; thread_i_begin[j] = begin - n; break; } n += structured_grid.nx(j); } idx_t thread_j_end{thread_j_begin}; for (idx_t j = thread_j_begin; j < structured_grid.ny(); ++j) { idx_t i_end = end - n; if (j > thread_j_begin) { thread_i_begin[j] = 0; } if (i_end > structured_grid.nx(j)) { thread_i_end[j] = structured_grid.nx(j); n += structured_grid.nx(j); } else { thread_i_end[j] = i_end; thread_j_end = j + 1; break; } } int nn = begin; for (idx_t j = thread_j_begin; j < thread_j_end; ++j) { int y = microdeg(structured_grid.y(j)); for (idx_t i = thread_i_begin[j]; i < thread_i_end[j]; ++i, ++nn) { nodes[nn].x = microdeg(structured_grid.x(i, j)); nodes[nn].y = y; nodes[nn].n = nn; } } ATLAS_ASSERT(nn == end); } } else { int n(0); for (idx_t j = 0; j < structured_grid.ny(); ++j) { int y = microdeg(structured_grid.y(j)); for (idx_t i = 0; i < structured_grid.nx(j); ++i, ++n) { nodes[n].x = microdeg(structured_grid.x(i, j)); nodes[n].y = y; nodes[n].n = n; } } } } else { ATLAS_TRACE("sort all"); std::vector requests; for (int w = 0; w < nb_workers; ++w) { idx_t w_begin = w * grid.size() / N_; idx_t w_end = (w + 1) * grid.size() / N_; if (w == nb_workers - 1) { w_end = grid.size(); } idx_t w_size = w_end - w_begin; int work_rank = std::min(w, mpi_size - 1); if (mpi_rank == 0) { ATLAS_ASSERT(valid_mpi_size(w_size * 3)); requests.push_back(comm.iReceive(nodes_buffer + w_begin * 3, w_size * 3, /* source= */ work_rank, /* tag= */ 0)); } if (mpi_rank == work_rank) { atlas::vector w_nodes(w_size); int* w_nodes_buffer = reinterpret_cast(w_nodes.data()); if (coordinates_ == Coordinates::XY) { ATLAS_TRACE_SCOPE("create one bit - Coordinates::XY") { if (true) // optimized experimental when true (still need to // benchmark) { int i = w_begin; int j(0); for (const auto& point : subrange(grid.xy(), {w_begin, w_end})) { w_nodes[j].x = microdeg(point[0]); w_nodes[j].y = microdeg(point[1]); w_nodes[j].n = i++; ++j; } } else { idx_t i(0); idx_t j(0); for (const auto& point : grid.xy()) { if (i >= w_begin && i < w_end) { w_nodes[j].x = microdeg(point[0]); w_nodes[j].y = microdeg(point[1]); w_nodes[j].n = i; ++j; } ++i; } } } } else if (coordinates_ == Coordinates::LONLAT) { ATLAS_TRACE_SCOPE("create one bit - Coordinates::LONLAT") { if (true) // optimized experimental when true (still need to // benchmark) { int i = w_begin; int j(0); for (const auto& point : subrange(grid.lonlat(), {w_begin, w_end})) { w_nodes[j].x = microdeg(point[0]); w_nodes[j].y = microdeg(point[1]); w_nodes[j].n = i++; ++j; } } else { idx_t i(0); idx_t j(0); for (const auto& point : grid.xy()) { if (i >= w_begin && i < w_end) { w_nodes[j].x = microdeg(point[0]); w_nodes[j].y = microdeg(point[1]); w_nodes[j].n = i; ++j; } ++i; } } } } else { ATLAS_THROW_EXCEPTION("Should not be here"); } ATLAS_TRACE_SCOPE("sort one bit") { omp::sort(w_nodes.begin(), w_nodes.end(), compare_NS_WE); } ATLAS_TRACE_SCOPE("send to rank0") { ATLAS_ASSERT(valid_mpi_size(w_size * 3)); comm.send(w_nodes_buffer, w_size * 3, /* dest= */ 0, /* tag= */ 0); } } } ATLAS_TRACE_MPI(WAIT) { for (auto request : requests) { comm.wait(request); } } ATLAS_TRACE_SCOPE("merge sorted") { for (int w = 0; w < nb_workers; ++w) { int w_begin = w * grid.size() / N_; int w_end = (w + 1) * grid.size() / N_; if (w == nb_workers - 1) { w_end = grid.size(); } if (w != 0) { std::inplace_merge(nodes.begin(), nodes.begin() + w_begin, nodes.begin() + w_end, compare_NS_WE); } } } ATLAS_TRACE_MPI(BROADCAST) { ATLAS_ASSERT(valid_mpi_size(grid.size() * 3)); comm.broadcast(nodes_buffer, grid.size() * 3, /* root= */ 0); } } // sort all /* For every band, now sort from west to east, and north to south. Inside every band we can now easily split nodes in sectors. */ ATLAS_TRACE_SCOPE("sort bands") { ATLAS_TRACE_BARRIERS(false); // avoid deadlock std::vector requests; int nb_parts = N_; size_t nb_nodes = grid.size(); size_t chunk_size = nb_nodes / nb_parts; size_t chunk_remainder = nb_nodes - chunk_size * nb_parts; int remainder = chunk_remainder; std::vector count; count.reserve(nb_parts); std::vector displs; displs.reserve(nb_parts + 1); std::vector b_count; b_count.reserve(nb_bands()); std::vector b_displs; b_displs.reserve(nb_bands() + 1); { size_t end = 0; for (int band = 0; band < nb_bands(); ++band) { b_displs.emplace_back(end); size_t b_size(0); for (int p = 0; p < nb_regions(band); ++p) { size_t w_size = chunk_size + (remainder-- > 0 ? size_t(1) : size_t(0)); displs.emplace_back(end); count.emplace_back(w_size); end += w_size; b_size += w_size; } b_count.emplace_back(b_size); } displs.emplace_back(end); b_displs.emplace_back(end); } int w(0); for (int band = 0; band < nb_bands(); ++band) { int w0 = w; int w0_work_rank = std::min(w0, mpi_size - 1); for (int p = 0; p < nb_regions(band); ++p) { size_t w_begin = displs[w]; size_t w_size = count[w]; size_t w_end = w_begin + w_size; int work_rank = std::min(w, mpi_size - 1); if (mpi_rank == w0_work_rank) { if (mpi_rank != work_rank) { ATLAS_ASSERT(valid_mpi_size(w_size * 3)); requests.push_back(comm.iReceive(nodes_buffer + w_begin * 3, w_size * 3, /* source= */ work_rank, /* tag= */ 0)); } } if (work_rank == mpi_rank) { ATLAS_TRACE_SCOPE("sort bit of band on each MPI rank") { omp::sort(nodes.data() + w_begin, nodes.data() + w_end, compare_WE_NS); } if (mpi_rank != w0_work_rank) { ATLAS_TRACE_SCOPE("send bit of band to band leader " + std::to_string(w0_work_rank)) { ATLAS_ASSERT(valid_mpi_size(w_size * 3)); comm.send(nodes_buffer + w_begin * 3, w_size * 3, /* dest= */ w0_work_rank, /* tag= */ 0); } } } ++w; } } ATLAS_TRACE_MPI(WAIT) { for (auto request : requests) { comm.wait(request); } } requests.clear(); std::vector w0_band(nb_bands()); for (int band = 0, w = 0; band < nb_bands(); ++band) { w0_band[band] = w; w += nb_regions(band); } for (int band = 0; band < nb_bands(); ++band) { int w0 = w0_band[band]; int w0_work_rank = std::min(w0, mpi_size - 1); if (mpi_rank == w0_work_rank) { auto nodes_band_begin = nodes.begin() + b_displs[band]; auto nodes_band_end = nodes_band_begin + b_count[band]; auto blocks_size_begin = count.begin() + w0; auto blocks_size_end = blocks_size_begin + nb_regions(band); ATLAS_TRACE_SCOPE("band leader merging bits for band " + std::to_string(band)) { omp::merge_blocks(nodes_band_begin, nodes_band_end, blocks_size_begin, blocks_size_end, compare_WE_NS); } } } for (int band = 0; band < nb_bands(); ++band) { int w0 = w0_band[band]; size_t w0_begin = b_displs[band]; size_t w0_size = b_count[band]; int w0_work_rank = std::min(w0, mpi_size - 1); if (mpi_rank == 0) { if (mpi_rank != w0_work_rank) { ATLAS_ASSERT(valid_mpi_size(w0_size * 3)); requests.push_back(comm.iReceive(nodes_buffer + w0_begin * 3, w0_size * 3, /* source= */ w0_work_rank, /* tag= */ 0)); } } if (mpi_rank == w0_work_rank) { ATLAS_TRACE_SCOPE("send band to rank 0") { if (mpi_rank != 0) { ATLAS_ASSERT(valid_mpi_size(w0_size * 3)); comm.send(nodes_buffer + w0_begin * 3, w0_size * 3, /* dest= */ 0, /* tag= */ 0); } } } } if (mpi_rank == 0) { ATLAS_TRACE_SCOPE("rank 0 waiting for all bands to come in") { ATLAS_TRACE_MPI(WAIT) { for (auto request : requests) { comm.wait(request); } } } } /* Create list that tells in original node numbering which part the node belongs to */ for (int p = 0; p < nb_parts; ++p) { size_t begin = displs[p]; size_t end = begin + count[p]; atlas_omp_parallel_for(size_t i = begin; i < end; ++i) { part[nodes[i].n] = p; } } ATLAS_TRACE_MPI(BROADCAST) { ATLAS_ASSERT(valid_mpi_size(nb_nodes)); comm.broadcast(part, nb_nodes, 0); } } // sort bands } // else } } // namespace partitioner } // namespace detail } // namespace grid } // namespace atlas namespace { atlas::grid::detail::partitioner::PartitionerBuilder __EqualRegions("equal_regions"); } atlas-0.46.0/src/atlas/grid/detail/partitioner/MatchingMeshPartitionerSphericalPolygon.cc0000664000175000017500000000600615160212070032011 0ustar alastairalastair/* * (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/grid/detail/partitioner/MatchingMeshPartitionerSphericalPolygon.h" #include #include "eckit/log/ProgressTimer.h" #include "atlas/grid/Grid.h" #include "atlas/grid/Iterator.h" #include "atlas/mesh/Nodes.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/parallel/omp/fill.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Log.h" #include "atlas/util/CoordinateEnums.h" #include "atlas/util/SphericalPolygon.h" namespace atlas { namespace grid { namespace detail { namespace partitioner { namespace { PartitionerBuilder __builder("spherical-polygon"); } void MatchingMeshPartitionerSphericalPolygon::partition(const Grid& grid, int partitioning[]) const { const auto& comm = mpi::comm(prePartitionedMesh_.mpi_comm()); const int mpi_rank = int(comm.rank()); const int mpi_size = int(comm.size()); ATLAS_TRACE("MatchingMeshPartitionerSphericalPolygon::partition"); ATLAS_ASSERT(grid.domain().global()); if (mpi_size == 1) { Log::debug() << "MatchingMeshPartionerSphericalPolygon [mpi_size=0] --> trivial solution" << std::endl; omp::fill(partitioning, partitioning + grid.size(), 0); return; } // FIXME: THIS IS A HACK! the coordinates include North/South Pole (first/last // partitions only) bool includesNorthPole = (mpi_rank == 0); bool includesSouthPole = (mpi_rank == mpi_size - 1); if (prePartitionedMesh_.projection()) { ATLAS_NOTIMPLEMENTED; } const util::SphericalPolygon poly{prePartitionedMesh_.polygon(0)}; const double maxlat = poly.coordinatesMax()[LAT]; const double minlat = poly.coordinatesMin()[LAT]; auto at_the_pole = [&](const PointLonLat& P) { return (includesNorthPole && P[LAT] >= maxlat) || (includesSouthPole && P[LAT] < minlat); }; { eckit::ProgressTimer timer("Partitioning", grid.size(), "point", double(10), atlas::Log::trace()); size_t i = 0; for (const PointLonLat& P : grid.lonlat()) { ++timer; partitioning[i++] = at_the_pole(P) || poly.contains(P) ? mpi_rank : -1; } } // Synchronize partitioning, do a sanity check comm.allReduceInPlace(partitioning, grid.size(), eckit::mpi::Operation::MAX); const int min = *std::min_element(partitioning, partitioning + grid.size()); if (min < 0) { throw_Exception( "Could not find partition for target node (source " "mesh does not contain all target grid points)", Here()); } } } // namespace partitioner } // namespace detail } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/partitioner/RegularBandsPartitioner.h0000664000175000017500000000220615160212070026450 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include "atlas/grid/detail/partitioner/BandsPartitioner.h" namespace atlas { namespace grid { namespace detail { namespace partitioner { class RegularBandsPartitioner : public BandsPartitioner { public: RegularBandsPartitioner(); RegularBandsPartitioner(int N, const eckit::Parametrisation& config = util::NoConfig()): BandsPartitioner(N, BLOCKSIZE_NX, config) {} RegularBandsPartitioner(const eckit::Parametrisation& config): RegularBandsPartitioner(extract_nb_partitions(config), config) {} std::string type() const override { return static_type(); } static std::string static_type() { return "regular_bands"; } }; } // namespace partitioner } // namespace detail } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/partitioner/EqualBandsPartitioner.h0000664000175000017500000000215415160212070026120 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include "atlas/grid/detail/partitioner/BandsPartitioner.h" namespace atlas { namespace grid { namespace detail { namespace partitioner { class EqualBandsPartitioner : public BandsPartitioner { public: EqualBandsPartitioner(); EqualBandsPartitioner(const eckit::Parametrisation& config): BandsPartitioner(extract_nb_partitions(config), config) {} EqualBandsPartitioner(int N, const eckit::Parametrisation& config = util::NoConfig()): BandsPartitioner(N, 1, config) {} std::string type() const override { return static_type(); } static std::string static_type() { return "equal_bands"; } }; } // namespace partitioner } // namespace detail } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/partitioner/EqualRegionsPartitioner.h0000664000175000017500000001252115160212070026476 0ustar alastairalastair/* * (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. */ // Purpose. // -------- // eq_regions provides the code to perform a high level // partitioning of the surface of a sphere into regions of // equal area and small diameter. // // Background. // ----------- // This C++ implementation is ported from the MATLAB // "Recursive Zonal Equal Area (EQ) Sphere Partitioning Toolbox" of the // same name developed by Paul Leopardi at the University of New South // Wales. // This version has been coded specifically for the case of partitioning the // surface of a sphere or S^dim (where dim=2) as denoted in the original // code. // Only a subset of the original eq_regions package has been coded to // determin // the high level distribution of regions on a sphere, as the detailed // distribution of grid points to each region is left to implentatios. // // The following copyright notice for the eq_regions package is included // from // the original MatLab release. // // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // + Release 1.10 2005-06-26 + // + + // + Copyright (c) 2004, 2005, University of New South Wales + // + + // + Permission is hereby granted, free of charge, to any person obtaining + // + a copy of this software and associated documentation files (the + // + "Software"), to deal in the Software without restriction, including + // + without limitation the rights to use, copy, modify, merge, publish, + // + distribute, sublicense, and/or sell copies of the Software, and to + // + permit persons to whom the Software is furnished to do so, subject to + // + the following conditions: + // + + // + The above copyright notice and this permission notice shall be included // + // + in all copies or substantial portions of the Software. + // + + // + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + // + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + // + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. // + // + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY + // + CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, + // + TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE + // + SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + // + + // +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ // #pragma once #include #include "atlas/grid/detail/partitioner/Partitioner.h" namespace atlas { namespace grid { namespace detail { namespace partitioner { void eq_caps(int N, std::vector& n_regions, std::vector& s_cap); void eq_regions(int N, double xmin[], double xmax[], double ymin[], double ymax[]); class EqualAreaPartitioner; class EqualRegionsPartitioner : public Partitioner { public: EqualRegionsPartitioner(); EqualRegionsPartitioner(int N); EqualRegionsPartitioner(int N, const eckit::Parametrisation& config); EqualRegionsPartitioner(const eckit::Parametrisation& config); void where(int partition, int& band, int& sector) const; int nb_bands() const { return bands_.size(); } int nb_regions(int band) const { return sectors_[band]; } using Partitioner::partition; virtual void partition(const Grid&, int part[]) const; virtual std::string type() const { return "equal_regions"; } public: // Node struct that holds the longitude and latitude in millidegrees // (integers) // This structure is used in sorting algorithms, and uses less memory than // if x and y were in double precision. struct NodeInt { int x, y; int n; bool operator!=(const NodeInt& other) const { return n != other.n; } bool operator==(const NodeInt& other) const { return n == other.n; } void swap(NodeInt& other) { auto _swap = [](int& a, int& b) { int tmp = a; a = b; b = tmp; }; _swap(x, other.x); _swap(y, other.y); _swap(n, other.n); } friend void swap(NodeInt& a, NodeInt& b) { a.swap(b); } }; private: void init(); // Doesn't matter if nodes[] is in degrees or radians, as a sorting // algorithm is used internally void partition(int nb_nodes, NodeInt nodes[], int part[]) const; friend class EqualAreaPartitioner; // y in radians int band(const double& y) const; // x in radians int sector(int band, const double& x) const; // x and y in radians int partition(const double& x, const double& y) const; private: int N_; std::vector bands_; std::vector sectors_; enum class Coordinates { XY, LONLAT, }; Coordinates coordinates_ = Coordinates::XY; }; } // namespace partitioner } // namespace detail } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/partitioner/EqualAreaPartitioner.h0000664000175000017500000000224715160212070025744 0ustar alastairalastair/* * (C) Copyright 2023 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include "atlas/grid/detail/partitioner/EqualRegionsPartitioner.h" #include "atlas/grid/detail/partitioner/Partitioner.h" namespace atlas { namespace grid { namespace detail { namespace partitioner { class EqualAreaPartitioner : public Partitioner { public: EqualAreaPartitioner(); EqualAreaPartitioner(int N); EqualAreaPartitioner(int N, const eckit::Parametrisation& config); EqualAreaPartitioner(const eckit::Parametrisation& config); using Partitioner::partition; void partition(const Grid&, int part[]) const override; std::string type() const override { return "equal_area"; } private: EqualRegionsPartitioner partitioner_; }; } // namespace partitioner } // namespace detail } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/partitioner/CheckerboardPartitioner.cc0000664000175000017500000002054215160212070026614 0ustar alastairalastair/* * (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 "CheckerboardPartitioner.h" #include #include #include #include #include #include #include "atlas/grid/StructuredGrid.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Log.h" #include "atlas/util/MicroDeg.h" using atlas::util::microdeg; namespace atlas { namespace grid { namespace detail { namespace partitioner { CheckerboardPartitioner::CheckerboardPartitioner(): Partitioner() {} // CheckerboardPartitioner::CheckerboardPartitioner(int N): Partitioner(N) {} CheckerboardPartitioner::CheckerboardPartitioner(int N, const eckit::Parametrisation& config): Partitioner(N, config) { config.get("bands", nbands_); config.get("regular", regular_); if (regular_) { split_x_ = false; split_y_ = false; } config.get("split_x", split_x_); config.get("split_y", split_y_); } CheckerboardPartitioner::CheckerboardPartitioner(const eckit::Parametrisation& config) : Partitioner(config) { config.get("bands", nbands_); config.get("regular", regular_); if (regular_) { split_x_ = false; split_y_ = false; } config.get("split_x", split_x_); config.get("split_y", split_y_); } std::array CheckerboardPartitioner::checkerboardDimensions(const Grid& grid) { int nbands = checkerboard(grid).nbands; int nparts = nb_partitions(); return {nparts/nbands, nbands}; } // CheckerboardPartitioner::CheckerboardPartitioner(int N, int nbands): Partitioner(N), nbands_{nbands} {} // CheckerboardPartitioner::CheckerboardPartitioner(int N, int nbands, bool checkerboard): // Partitioner(N), nbands_{nbands} {} CheckerboardPartitioner::Checkerboard CheckerboardPartitioner::checkerboard(const Grid& grid) const { // grid dimensions const RegularGrid rg(grid); if (!rg) { throw_Exception("Checkerboard Partitioner only works for Regular grids.", Here()); } Checkerboard cb; cb.nx = rg.nx(); cb.ny = rg.ny(); idx_t nparts = nb_partitions(); if (nbands_ > 0) { cb.nbands = nbands_; } else { // default number of bands double zz = std::sqrt(double(nparts * cb.ny) / double(cb.nx)); // aim at +/-square regions cb.nbands = std::floor(zz + 0.5); if (cb.nbands < 1) { cb.nbands = 1; // at least one band } if (cb.nbands > nparts) { cb.nbands = nparts; // not more bands than procs } // true checkerboard means nbands must divide nparts if (checkerboard_) { while (nparts % cb.nbands != 0) { cb.nbands--; } } } if (checkerboard_ && nparts % cb.nbands != 0) { throw_Exception("number of bands doesn't divide number of partitions", Here()); } return cb; } bool compare_Y_X(const CheckerboardPartitioner::NodeInt& node1, const CheckerboardPartitioner::NodeInt& node2) { // comparison of two locations; X1 < X2 if it's to the south, then to the // east. if (node1.y < node2.y) { return true; } if (node1.y == node2.y) { return (node1.x < node2.x); } return false; } bool compare_X_Y(const CheckerboardPartitioner::NodeInt& node1, const CheckerboardPartitioner::NodeInt& node2) { // comparison of two locations; X1 < X2 if it's to the east, then to the // south. if (node1.x < node2.x) { return true; } if (node1.x == node2.x) { return (node1.y < node2.y); } return false; } void CheckerboardPartitioner::partition(const Checkerboard& cb, int nb_nodes, NodeInt nodes[], int part[]) const { size_t nparts = nb_partitions(); size_t nbands = cb.nbands; size_t nx = cb.nx; size_t ny = cb.ny; long remainder; /* Sort nodes from south to north (increasing y), and west to east (increasing x). Now we can easily split the points in bands. Note this may not be necessary, as it could be already by construction in this order, but then sorting is really fast */ /* Number of procs per band */ std::vector npartsb(nbands, 0); // number of procs per band remainder = nparts; for (size_t iband = 0; iband < nbands; iband++) { npartsb[iband] = nparts / nbands; remainder -= npartsb[iband]; } // distribute remaining procs over first bands for (size_t iband = 0; iband < remainder; iband++) { ++npartsb[iband]; } bool split_lons = split_x_; bool split_lats = split_y_; /* Number of gridpoints per band */ std::vector ngpb(nbands, 0); // split latitudes? if (split_lats) { remainder = nb_nodes; for (size_t iband = 0; iband < nbands; iband++) { ngpb[iband] = (nb_nodes * npartsb[iband]) / nparts; remainder -= ngpb[iband]; } // distribute remaining gridpoints over first bands for (size_t iband = 0; iband < remainder; iband++) { ++ngpb[iband]; } } else { remainder = ny; for (size_t iband = 0; iband < nbands; iband++) { ngpb[iband] = nx * ((ny * npartsb[iband]) / nparts); remainder -= ngpb[iband] / nx; } // distribute remaining rows over first bands for (size_t iband = 0; iband < remainder; iband++) { ngpb[iband] += nx; } } // sort nodes according to Y first, to determine bands std::sort(nodes, nodes + nb_nodes, compare_Y_X); // for each band, select gridpoints belonging to that band, and sort them // according to X first size_t offset = 0; int jpart = 0; for (size_t iband = 0; iband < nbands; iband++) { // sort according to X first std::sort(nodes + offset, nodes + offset + ngpb[iband], compare_X_Y); // number of gridpoints per task std::vector ngpp(npartsb[iband], 0); remainder = ngpb[iband]; int part_ny = ngpb[iband] / cb.nx; int part_nx = ngpb[iband] / npartsb[iband] / part_ny; for (size_t ipart = 0; ipart < npartsb[iband]; ipart++) { if (split_lons) { ngpp[ipart] = ngpb[iband] / npartsb[iband]; } else { ngpp[ipart] = part_nx * part_ny; } remainder -= ngpp[ipart]; } if (split_lons) { // distribute remaining gridpoints over first parts for (size_t ipart = 0; ipart < remainder; ipart++) { ++ngpp[ipart]; } } else { size_t ipart = 0; while (remainder > part_ny) { ngpp[ipart++] += part_ny; remainder -= part_ny; } ngpp[npartsb[iband] - 1] += remainder; } // set partition number for each part for (size_t ipart = 0; ipart < npartsb[iband]; ipart++) { for (size_t jj = offset; jj < offset + ngpp[ipart]; jj++) { part[nodes[jj].n] = jpart; } offset += ngpp[ipart]; ++jpart; } } } void CheckerboardPartitioner::partition(const Grid& grid, int part[]) const { if (nb_partitions() == 1) // trivial solution, so much faster { for (idx_t j = 0; j < grid.size(); ++j) { part[j] = 0; } } else { auto cb = checkerboard(grid); std::vector nodes(grid.size()); int n(0); for (idx_t iy = 0; iy < cb.ny; ++iy) { for (idx_t ix = 0; ix < cb.nx; ++ix) { nodes[n].x = static_cast(ix); nodes[n].y = static_cast(iy); nodes[n].n = static_cast(n); ++n; } } partition(cb, grid.size(), nodes.data(), part); } } } // namespace partitioner } // namespace detail } // namespace grid } // namespace atlas namespace { atlas::grid::detail::partitioner::PartitionerBuilder __CheckerBoard("checkerboard"); } atlas-0.46.0/src/atlas/grid/detail/partitioner/BandsPartitioner.h0000664000175000017500000000313415160212070025127 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include "atlas/grid/detail/partitioner/Partitioner.h" #include "atlas/grid/Distribution.h" #include "atlas/grid/Grid.h" namespace atlas { namespace grid { namespace detail { namespace partitioner { class BandsPartitioner : public Partitioner { private: int blocksize_; static size_t extract_blocksize(const eckit::Parametrisation& config) { size_t blocksize{1}; config.get("blocksize", blocksize); return blocksize; } size_t blocksize(const Grid& grid) const; protected: static constexpr int BLOCKSIZE_NX{-1}; public: BandsPartitioner(const eckit::Parametrisation& config = util::NoConfig()); BandsPartitioner(int N, const eckit::Parametrisation& config): BandsPartitioner(N, extract_blocksize(config), config) {} BandsPartitioner(int N, int blocksize, const eckit::Parametrisation& config = util::NoConfig()); std::string type() const override { return static_type(); } static std::string static_type() { return "bands"; } Distribution partition(const Grid& grid) const override; void partition(const Grid& grid, int part[]) const override; }; } // namespace partitioner } // namespace detail } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/partitioner/MatchingMeshPartitionerBruteForce.h0000664000175000017500000000272715160212070030437 0ustar alastairalastair/* * (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 "MatchingMeshPartitioner.h" namespace atlas { namespace grid { namespace detail { namespace partitioner { class MatchingMeshPartitionerBruteForce : public MatchingMeshPartitioner { public: static std::string static_type() { return "brute-force"; } public: MatchingMeshPartitionerBruteForce(const Mesh& mesh, const eckit::Parametrisation& config): MatchingMeshPartitioner(mesh, config) {} MatchingMeshPartitionerBruteForce(): MatchingMeshPartitioner() {} MatchingMeshPartitionerBruteForce(const eckit::Parametrisation&): MatchingMeshPartitioner() {} MatchingMeshPartitionerBruteForce(const idx_t nb_partitions): MatchingMeshPartitioner() {} MatchingMeshPartitionerBruteForce(const idx_t nb_partitions, const eckit::Parametrisation&): MatchingMeshPartitioner() {} using MatchingMeshPartitioner::partition; virtual void partition(const Grid& grid, int partitioning[]) const; virtual std::string type() const { return static_type(); } }; } // namespace partitioner } // namespace detail } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/partitioner/MatchingMeshPartitionerLonLatPolygon.h0000664000175000017500000000345115160212070031133 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include "atlas/grid/detail/partitioner/MatchingMeshPartitioner.h" namespace atlas { namespace grid { namespace detail { namespace partitioner { class MatchingMeshPartitionerLonLatPolygon : public MatchingMeshPartitioner { public: static std::string static_type() { return "lonlat-polygon"; } public: MatchingMeshPartitionerLonLatPolygon(const Mesh& mesh, const eckit::Parametrisation& config); MatchingMeshPartitionerLonLatPolygon(): MatchingMeshPartitioner() {} MatchingMeshPartitionerLonLatPolygon(const eckit::Parametrisation&): MatchingMeshPartitioner() {} MatchingMeshPartitionerLonLatPolygon(const size_t nb_partitions): MatchingMeshPartitioner() {} MatchingMeshPartitionerLonLatPolygon(const size_t nb_partitions, const eckit::Parametrisation& config): MatchingMeshPartitioner() {} using Partitioner::partition; /** * @brief Partition a grid, using the same partitions from a pre-partitioned * mesh. * The method constructs a partition edges polygon to test every target grid * node with. * @param[in] grid grid to be partitioned * @param[out] partitioning partitioning result */ void partition(const Grid& grid, int partitioning[]) const; virtual std::string type() const { return static_type(); } private: bool fallback_nearest_{false}; }; } // namespace partitioner } // namespace detail } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/partitioner/RegularBandsPartitioner.cc0000664000175000017500000000167315160212070026615 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "RegularBandsPartitioner.h" #include "atlas/parallel/mpi/mpi.h" namespace atlas { namespace grid { namespace detail { namespace partitioner { RegularBandsPartitioner::RegularBandsPartitioner(): RegularBandsPartitioner(mpi::size()) {} } // namespace partitioner } // namespace detail } // namespace grid } // namespace atlas namespace { atlas::grid::detail::partitioner::PartitionerBuilder __RegularBands(atlas::grid::detail::partitioner::RegularBandsPartitioner::static_type()); } atlas-0.46.0/src/atlas/grid/detail/partitioner/MatchingMeshPartitionerLonLatPolygon.cc0000664000175000017500000001600515160212070031270 0ustar alastairalastair/* * (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/grid/detail/partitioner/MatchingMeshPartitionerLonLatPolygon.h" #include #include #include #include "eckit/config/Resource.h" #include "eckit/log/ProgressTimer.h" #include "atlas/grid/Grid.h" #include "atlas/grid/Iterator.h" #include "atlas/mesh/Nodes.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/parallel/omp/fill.h" #include "atlas/parallel/omp/omp.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Log.h" #include "atlas/util/CoordinateEnums.h" #include "atlas/util/PolygonXY.h" #include "atlas/util/KDTree.h" namespace atlas { namespace grid { namespace detail { namespace partitioner { namespace { PartitionerBuilder __builder("lonlat-polygon"); } MatchingMeshPartitionerLonLatPolygon::MatchingMeshPartitionerLonLatPolygon(const Mesh& mesh, const eckit::Parametrisation& config): MatchingMeshPartitioner(mesh, config) { config.get("fallback_nearest", fallback_nearest_); } void MatchingMeshPartitionerLonLatPolygon::partition(const Grid& grid, int partitioning[]) const { const auto& comm = mpi::comm(prePartitionedMesh_.mpi_comm()); const int mpi_rank = int(comm.rank()); ATLAS_TRACE("MatchingMeshPartitionerLonLatPolygon::partition"); ATLAS_ASSERT(grid.domain().global()); Log::debug() << "MatchingMeshPartitionerLonLatPolygon::partition" << std::endl; const util::PolygonXY poly{prePartitionedMesh_.polygon(0)}; double west = poly.coordinatesMin().x(); double east = poly.coordinatesMax().x(); ATLAS_TRACE_MPI(ALLREDUCE) { comm.allReduceInPlace(west, eckit::mpi::Operation::MIN); comm.allReduceInPlace(east, eckit::mpi::Operation::MAX); } Projection projection = prePartitionedMesh_.projection(); omp::fill(partitioning, partitioning + grid.size(), -1); auto compute = [&](double west) { #if !defined(__NVCOMPILER) atlas_omp_parallel #elif (__NVCOMPILER_MAJOR__ >= 21) && (__NVCOMPILER_MINOR__ > 9 ) // Internal compiler error with nvhpc 21.9: // // NVC++-S-0000-Internal compiler error. BAD sptr in var_refsym 0 (MatchingMeshPartitionerLonLatPolygon.cc: 64) (=following line) // NVC++/x86-64 Linux 21.9-0: compilation completed with severe errors atlas_omp_parallel #endif { const idx_t num_threads = atlas_omp_get_num_threads(); const idx_t thread_num = atlas_omp_get_thread_num(); const idx_t begin = static_cast(thread_num * size_t(grid.size()) / num_threads); const idx_t end = static_cast((thread_num + 1) * size_t(grid.size()) / num_threads); size_t i = begin; auto it = grid.lonlat().begin() + i; for(; i < end; ++i, ++it) { PointLonLat P = *it; if (partitioning[i] < 0) { projection.lonlat2xy(P); P.normalise(west); partitioning[i] = poly.contains(P) ? mpi_rank : -1; } } } // Synchronize partitioning ATLAS_TRACE_MPI(ALLREDUCE) { comm.allReduceInPlace(partitioning, grid.size(), eckit::mpi::Operation::MAX); } std::vector thread_min(atlas_omp_get_max_threads(),std::numeric_limits::max()); #if !defined(__NVCOMPILER) atlas_omp_parallel #elif (__NVCOMPILER_MAJOR__ >= 21) && (__NVCOMPILER_MINOR__ > 9 ) // Internal compiler error with nvhpc 21.9: // // NVC++-S-0000-Internal compiler error. BAD sptr in var_refsym 0 (MatchingMeshPartitionerLonLatPolygon.cc: 64) (=following line) // NVC++/x86-64 Linux 21.9-0: compilation completed with severe errors atlas_omp_parallel #endif { const idx_t num_threads = atlas_omp_get_num_threads(); const idx_t thread_num = atlas_omp_get_thread_num(); const idx_t begin = static_cast(thread_num * size_t(grid.size()) / num_threads); const idx_t end = static_cast((thread_num + 1) * size_t(grid.size()) / num_threads); thread_min[thread_num] = *std::min_element(partitioning+begin,partitioning+end); } return *std::min_element(thread_min.begin(), thread_min.end()); }; int min = compute(east - 360.); constexpr double eps = 1.e-10; bool second_try = [&]() { if (min < 0 && east - west > 360. + eps) { min = compute(west - eps); return true; } return false; }(); if (min < 0) { size_t i = 0; size_t max_failures = grid.size(); std::vector failed_index; std::vector failed_lonlat; failed_index.reserve(max_failures); failed_lonlat.reserve(max_failures); for (PointLonLat P : grid.lonlat()) { if (partitioning[i] < 0) { failed_index.emplace_back(i); failed_lonlat.emplace_back(P); } ++i; } size_t nb_failures = failed_index.size(); if (fallback_nearest_) { util::IndexKDTree3D kdtree; kdtree.reserve(grid.size()); size_t j=0; for (auto& p: grid.lonlat()) { if (partitioning[j] >= 0) { kdtree.insert(p,partitioning[j]); } ++j; } kdtree.build(); for (size_t n = 0; n < nb_failures; ++n) { auto closest = kdtree.closestPoint(failed_lonlat[n]); partitioning[failed_index[n]] = closest.payload(); } } else { std::stringstream err; err.precision(20); err << "Could not find partition of " << nb_failures << " target grid points (source mesh does not contain all target grid points)\n" << "Tried first normalizing coordinates with west=" << east - 360. << "\n"; if (second_try) { err << "Tried second time normalizing coordinates with west=" << west - eps << "\n"; } err << "Failed target grid points with global index:\n"; for (size_t n = 0; n < nb_failures; ++n) { err << " - " << std::setw(10) << std::left << failed_index[n] + 1 << " {lon,lat} : " << failed_lonlat[n] << "\n"; } // prePartitionedMesh_.polygon(0).outputPythonScript("partitions.py"); throw_Exception(err.str(), Here()); } } } } // namespace partitioner } // namespace detail } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/partitioner/MatchingFunctionSpacePartitionerLonLatPolygon.cc0000664000175000017500000001132615160212070033136 0ustar alastairalastair/* * (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/grid/detail/partitioner/MatchingFunctionSpacePartitionerLonLatPolygon.h" #include #include "eckit/config/Resource.h" #include "eckit/log/ProgressTimer.h" #include "atlas/grid/Grid.h" #include "atlas/grid/Iterator.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Log.h" #include "atlas/util/CoordinateEnums.h" #include "atlas/util/PolygonXY.h" #include "atlas/parallel/omp/fill.h" #include "atlas/parallel/omp/omp.h" namespace atlas { namespace grid { namespace detail { namespace partitioner { namespace { // We did not yet implement self-registration in MatchedPartitionerFactory // PartitionerBuilder __builder( "lonlat-polygon" ); } void MatchingFunctionSpacePartitionerLonLatPolygon::partition(const Grid& grid, int part[]) const { ATLAS_TRACE("MatchingFunctionSpacePartitionerLonLatPolygon"); //atlas::vector part( grid.size() ); const auto& comm = mpi::comm(partitioned_.mpi_comm()); const int mpi_rank = int(comm.rank()); const int mpi_size = int(comm.size()); if (mpi_size == 1) { // shortcut omp::fill(part, part + grid.size(), 0); } else { const auto& poly = partitioned_.polygon(); const auto& proj = partitioned_.projection(); util::PolygonXY poly_xy{poly}; { ATLAS_TRACE("point-in-polygon check for entire grid (" + std::to_string(grid.size()) + " points)"); size_t num_threads = atlas_omp_get_max_threads(); size_t chunk_size = grid.size() / (1000 * num_threads); if (chunk_size == 0) { chunk_size = grid.size(); } size_t chunks = num_threads == 1 ? 1 : std::max(size_t(1), size_t(grid.size()) / chunk_size); atlas_omp_pragma(omp parallel for schedule(dynamic,1)) for( size_t chunk=0; chunk < chunks; ++chunk) { const size_t begin = chunk * size_t(grid.size()) / chunks; const size_t end = (chunk + 1) * size_t(grid.size()) / chunks; auto lonlat_it = grid.lonlat().begin() + chunk * grid.size() / chunks; PointXY xy; for (size_t n = begin; n < end; ++n) { xy = proj.xy(*lonlat_it); if (poly_xy.contains(xy)) { part[n] = mpi_rank; } else { part[n] = -1; } ++lonlat_it; } } } ATLAS_TRACE_MPI(ALLREDUCE) { comm.allReduceInPlace(part, grid.size(), eckit::mpi::max()); } } } #if 0 const auto& comm = mpi::comm(partitioned_.mpi_comm()); const int mpi_rank = int(comm.rank()); const int mpi_size = int(comm.size()); ATLAS_TRACE( "MatchingFunctionSpacePartitionerLonLatPolygon::partition" ); ATLAS_ASSERT( grid.domain().global() ); Log::debug() << "MatchingFunctionSpacePartitionerLonLatPolygon::partition" << std::endl; // FIXME: THIS IS A HACK! the coordinates include North/South Pole (first/last // partitions only) bool includesNorthPole = ( mpi_rank == 0 ); bool includesSouthPole = ( mpi_rank == mpi_size - 1 ); const util::PolygonXY poly{prePartitionedFunctionSpace_.polygon( 0 )}; { eckit::ProgressTimer timer( "Partitioning", grid.size(), "point", double( 10 ), atlas::Log::trace() ); size_t i = 0; for ( const PointLonLat& P : grid.lonlat() ) { ++timer; const bool atThePole = ( includesNorthPole && P[LAT] >= poly.coordinatesMax()[LAT] ) || ( includesSouthPole && P[LAT] < poly.coordinatesMin()[LAT] ); partitioning[i++] = atThePole || poly.contains( P ) ? mpi_rank : -1; } } // Synchronize partitioning, do a sanity check comm.allReduceInPlace( partitioning, grid.size(), eckit::mpi::Operation::MAX ); const int min = *std::min_element( partitioning, partitioning + grid.size() ); if ( min < 0 ) { throw_Exception( "Could not find partition for target node (source " "FunctionSpace does not contain all target grid points)", Here() ); } #endif } // namespace partitioner } // namespace detail } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/partitioner/TransPartitioner.cc0000664000175000017500000000750715160212070025335 0ustar alastairalastair/* * (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/array.h" #include "atlas/grid/Grid.h" #include "atlas/grid/detail/partitioner/EqualRegionsPartitioner.h" #include "atlas/grid/detail/partitioner/TransPartitioner.h" #include "atlas/option/TransOptions.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Trace.h" #include "atlas/trans/ifs/TransIFS.h" namespace atlas { namespace grid { namespace detail { namespace partitioner { TransPartitioner::TransPartitioner(): Partitioner() { } TransPartitioner::TransPartitioner(const eckit::Parametrisation& config): Partitioner(config) { } TransPartitioner::TransPartitioner(const idx_t N, const eckit::Parametrisation& config): Partitioner(N,config) { } TransPartitioner::~TransPartitioner() = default; void TransPartitioner::partition(const Grid& grid, int part[]) const { ATLAS_TRACE("TransPartitioner::partition"); StructuredGrid g(grid); if (not g) { throw_Exception("Grid is not a grid::Structured type. Cannot partition using IFS trans", Here()); } trans::TransIFS t(grid, option::split_y(split_y_)); if (nb_partitions() != idx_t(t.nproc())) { std::stringstream msg; msg << "Requested to partition grid with TransPartitioner in " << nb_partitions() << " partitions, but " "the internal Trans library could only be set-up for " << t.nproc() << " partitions " "(equal to number of MPI tasks in communicator)."; throw_Exception(msg.str(), Here()); } int nlonmax = g.nxmax(); array::LocalView nloen = t.nloen(); array::LocalView n_regions = t.n_regions(); array::LocalView nfrstlat = t.nfrstlat(); array::LocalView nlstlat = t.nlstlat(); array::LocalView nptrfrstlat = t.nptrfrstlat(); array::LocalView nsta = t.nsta(); array::LocalView nonl = t.nonl(); int i(0); int maxind(0); std::vector iglobal(t.ndgl() * nlonmax, -1); for (int jgl = 0; jgl < t.ndgl(); ++jgl) { for (int jl = 0; jl < nloen(jgl); ++jl) { ++i; iglobal[jgl * nlonmax + jl] = i; maxind = std::max(maxind, jgl * nlonmax + jl); } } int iproc(0); for (int ja = 0; ja < t.n_regions_NS(); ++ja) { for (int jb = 0; jb < n_regions(ja); ++jb) { for (int jgl = nfrstlat(ja) - 1; jgl < nlstlat(ja); ++jgl) { 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()) { throw_OutOfRange("part", ind, grid.size(), Here()); } part[ind] = iproc; } } ++iproc; } } } } // namespace partitioner } // namespace detail } // namespace grid } // namespace atlas namespace { atlas::grid::detail::partitioner::PartitionerBuilder __ecTrans( "ectrans"); atlas::grid::detail::partitioner::PartitionerBuilder __Trans( "trans"); atlas::grid::detail::partitioner::PartitionerBuilder __TransIFS( "ifs"); } // namespace atlas-0.46.0/src/atlas/grid/detail/partitioner/MatchingFunctionSpacePartitioner.cc0000664000175000017500000000254115160212070030453 0ustar alastairalastair/* * (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/grid/detail/partitioner/MatchingFunctionSpacePartitioner.h" #include #include "atlas/functionspace/FunctionSpace.h" #include "atlas/runtime/Exception.h" namespace atlas { namespace grid { namespace detail { namespace partitioner { MatchingFunctionSpacePartitioner::MatchingFunctionSpacePartitioner(): Partitioner() { ATLAS_THROW_EXCEPTION("Error: A MatchingFunctionPartitioner needs to be initialised with a FunctionSpace"); } // MatchingFunctionSpacePartitioner::MatchingFunctionSpacePartitioner(const idx_t nb_partitions): // Partitioner(nb_partitions) { // ATLAS_NOTIMPLEMENTED; // } MatchingFunctionSpacePartitioner::MatchingFunctionSpacePartitioner(const FunctionSpace& functionspace, const eckit::Parametrisation&): Partitioner(functionspace.nb_parts(),util::Config("mpi_comm",functionspace.mpi_comm())), partitioned_(functionspace) {} } // namespace partitioner } // namespace detail } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/partitioner/Partitioner.cc0000664000175000017500000002242615160212070024322 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ // file deepcode ignore CppMemoryLeak: static pointers for global registry are OK and will be cleaned up at end #include "atlas/grid/detail/partitioner/Partitioner.h" #include #include #include "eckit/thread/AutoLock.h" #include "eckit/thread/Mutex.h" #include "atlas/grid/Distribution.h" #include "atlas/grid/Partitioner.h" #include "atlas/grid/detail/distribution/DistributionArray.h" #include "atlas/grid/detail/partitioner/BandsPartitioner.h" #include "atlas/grid/detail/partitioner/CheckerboardPartitioner.h" #include "atlas/grid/detail/partitioner/CubedSpherePartitioner.h" #include "atlas/grid/detail/partitioner/EqualBandsPartitioner.h" #include "atlas/grid/detail/partitioner/EqualRegionsPartitioner.h" #include "atlas/grid/detail/partitioner/MatchingFunctionSpacePartitionerLonLatPolygon.h" #include "atlas/grid/detail/partitioner/MatchingMeshPartitioner.h" #include "atlas/grid/detail/partitioner/MatchingMeshPartitionerBruteForce.h" #include "atlas/grid/detail/partitioner/MatchingMeshPartitionerCubedSphere.h" #include "atlas/grid/detail/partitioner/MatchingMeshPartitionerLonLatPolygon.h" #include "atlas/grid/detail/partitioner/MatchingMeshPartitionerSphericalPolygon.h" #include "atlas/grid/detail/partitioner/RegularBandsPartitioner.h" #include "atlas/grid/detail/partitioner/SerialPartitioner.h" #include "atlas/library/config.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Log.h" #include "atlas/util/Config.h" #if ATLAS_HAVE_TRANS #include "atlas/grid/detail/partitioner/TransPartitioner.h" #endif namespace { static eckit::Mutex* local_mutex = nullptr; static std::map* m = nullptr; static pthread_once_t once = PTHREAD_ONCE_INIT; static void init() { local_mutex = new eckit::Mutex(); m = new std::map(); } } // namespace namespace atlas { namespace grid { namespace detail { namespace partitioner { int Partitioner::extract_nb_partitions(const eckit::Parametrisation& config) { int N; if (config.has("mpi_comm")) { std::string mpi_comm; config.get("mpi_comm",mpi_comm); N = mpi::comm(mpi_comm).size(); } else { N = mpi::size(); } config.get("partitions",N); config.get("nb_partitions",N); config.get("nb_parts",N); return N; } std::string Partitioner::extract_mpi_comm(const eckit::Parametrisation& config) { if (config.has("mpi_comm")) { std::string mpi_comm; config.get("mpi_comm",mpi_comm); return mpi_comm; } return mpi::comm().name(); } Partitioner::Partitioner(): Partitioner(util::NoConfig()) { } Partitioner::Partitioner(const idx_t nb_partitions, const eckit::Parametrisation& config): nb_partitions_(nb_partitions), mpi_comm_(extract_mpi_comm(config)) { } Partitioner::Partitioner(const eckit::Parametrisation& config) : nb_partitions_(extract_nb_partitions(config)), mpi_comm_(extract_mpi_comm(config)) { } Partitioner::~Partitioner() = default; idx_t Partitioner::nb_partitions() const { return nb_partitions_; } Distribution Partitioner::partition(const Grid& grid) const { return new distribution::DistributionArray{grid, atlas::grid::Partitioner(this)}; } std::string Partitioner::mpi_comm() const { return mpi_comm_; } namespace { template void load_builder() { PartitionerBuilder("tmp"); } struct force_link { force_link() { load_builder(); load_builder(); load_builder(); load_builder(); load_builder(); load_builder(); load_builder(); #if ATLAS_HAVE_TRANS load_builder(); #endif } }; } // namespace PartitionerFactory::PartitionerFactory(const std::string& name): name_(name) { pthread_once(&once, init); eckit::AutoLock lock(local_mutex); if (m->find(name) != m->end()) { throw_Exception("Partitioner with name [" + name + "] is already registered.", Here()); } (*m)[name] = this; } PartitionerFactory::~PartitionerFactory() { eckit::AutoLock lock(local_mutex); m->erase(name_); } void PartitionerFactory::list(std::ostream& out) { pthread_once(&once, init); eckit::AutoLock lock(local_mutex); static force_link static_linking; const char* sep = ""; for (std::map::const_iterator j = m->begin(); j != m->end(); ++j) { out << sep << (*j).first; sep = ", "; } } bool PartitionerFactory::has(const std::string& name) { pthread_once(&once, init); eckit::AutoLock lock(local_mutex); static force_link static_linking; return (m->find(name) != m->end()); } Partitioner* PartitionerFactory::build(const std::string& name) { pthread_once(&once, init); eckit::AutoLock lock(local_mutex); static force_link static_linking; std::map::const_iterator j = m->find(name); Log::debug() << "Looking for PartitionerFactory [" << name << "]" << std::endl; if (j == m->end()) { Log::error() << "No PartitionerFactory for [" << name << "]" << '\n'; Log::error() << "PartitionerFactories are:" << '\n'; for (j = m->begin(); j != m->end(); ++j) { Log::error() << " " << (*j).first << '\n'; } Log::error() << std::flush; throw_Exception(std::string("No PartitionerFactory called ") + name); } return (*j).second->make(); } Partitioner* PartitionerFactory::build(const std::string& name, const idx_t nb_partitions) { return build(name, nb_partitions, util::NoConfig()); } Partitioner* PartitionerFactory::build(const std::string& name, const idx_t nb_partitions, const eckit::Parametrisation& config) { pthread_once(&once, init); eckit::AutoLock lock(local_mutex); static force_link static_linking; std::map::const_iterator j = m->find(name); Log::debug() << "Looking for PartitionerFactory [" << name << "]" << '\n'; if (j == m->end()) { Log::error() << "No PartitionerFactory for [" << name << "]" << '\n'; Log::error() << "PartitionerFactories are:" << '\n'; for (j = m->begin(); j != m->end(); ++j) { Log::error() << " " << (*j).first << '\n'; } throw_Exception(std::string("No PartitionerFactory called ") + name); } return (*j).second->make(nb_partitions, config); } Partitioner* PartitionerFactory::build(const std::string& name, const eckit::Parametrisation& config) { pthread_once(&once, init); eckit::AutoLock lock(local_mutex); static force_link static_linking; std::map::const_iterator j = m->find(name); Log::debug() << "Looking for PartitionerFactory [" << name << "]" << '\n'; if (j == m->end()) { Log::error() << "No PartitionerFactory for [" << name << "]" << '\n'; Log::error() << "PartitionerFactories are:" << '\n'; for (j = m->begin(); j != m->end(); ++j) { Log::error() << " " << (*j).first << '\n'; } throw_Exception(std::string("No PartitionerFactory called ") + name); } return (*j).second->make(config); } Partitioner* MatchingPartitionerFactory::build(const std::string& type, const Mesh& partitioned, const eckit::Parametrisation& config) { if (type == MatchingMeshPartitionerSphericalPolygon::static_type()) { return new MatchingMeshPartitionerSphericalPolygon(partitioned, config); } else if (type == MatchingMeshPartitionerLonLatPolygon::static_type()) { return new MatchingMeshPartitionerLonLatPolygon(partitioned, config); } else if (type == MatchingMeshPartitionerBruteForce::static_type()) { return new MatchingMeshPartitionerBruteForce(partitioned, config); } else if (type == MatchingMeshPartitionerCubedSphere::static_type()) { return new MatchingMeshPartitionerCubedSphere(partitioned, config); } else { ATLAS_NOTIMPLEMENTED; } } Partitioner* MatchingPartitionerFactory::build(const std::string& type, const FunctionSpace& partitioned, const eckit::Parametrisation& config) { if (type == MatchingFunctionSpacePartitionerLonLatPolygon::static_type()) { return new MatchingFunctionSpacePartitionerLonLatPolygon(partitioned, config); } else { ATLAS_NOTIMPLEMENTED; } } } // namespace partitioner } // namespace detail } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/partitioner/MatchingMeshPartitionerCubedSphere.cc0000664000175000017500000000525115160212070030721 0ustar alastairalastair/* * (C) Crown Copyright 2022 Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ #include "atlas/grid/detail/partitioner/MatchingMeshPartitionerCubedSphere.h" #include #include #include #include "atlas/grid/CubedSphereGrid.h" #include "atlas/grid/Iterator.h" #include "atlas/interpolation/method/cubedsphere/CellFinder.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/parallel/omp/omp.h" #include "atlas/util/Point.h" namespace atlas { namespace grid { namespace detail { namespace partitioner { void MatchingMeshPartitionerCubedSphere::partition(const Grid& grid, int partitioning[]) const { const auto& comm = mpi::comm(prePartitionedMesh_.mpi_comm()); const int mpi_rank = int(comm.rank()); // Make cell finder from owned mesh cells. const auto finder = interpolation::method::cubedsphere::CellFinder(prePartitionedMesh_); // Numeric tolerance should scale with N. const auto N = CubedSphereGrid(prePartitionedMesh_.grid()).N(); const auto epsilon = 2. * std::numeric_limits::epsilon() * N; const auto edgeEpsilon = epsilon; const size_t listSize = 8; const auto setPartitioning = [&](const auto& lonLatIt) { atlas_omp_parallel_for(gidx_t i = 0; i < grid.size(); ++i) { const auto lonLat = *(lonLatIt + i); // This is probably more expensive than it needs to be, as it performs // a dry run of the cubedsphere interpolation method. partitioning[i] = finder.getCell(lonLat, listSize, edgeEpsilon, epsilon).isect ? mpi_rank : -1; } }; // CubedSphereIterator::operator+=() is not implemented properly. if (CubedSphereGrid(grid)) { auto lonLats = std::vector{}; lonLats.reserve(grid.size()); std::copy(grid.lonlat().begin(), grid.lonlat().end(), std::back_inserter(lonLats)); setPartitioning(lonLats.begin()); } else { setPartitioning(grid.lonlat().begin()); } // AllReduce to get full partitioning array. comm.allReduceInPlace(partitioning, grid.size(), eckit::mpi::Operation::MAX); const auto misses = std::count_if(partitioning, partitioning + grid.size(), [](int elem) { return elem < 0; }); if (misses > 0) { throw_Exception( "Could not find partition for target node (source " "mesh does not contain all target grid points)\n" + std::to_string(misses) + " misses.", Here()); } } } // namespace partitioner } // namespace detail } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/partitioner/CubedSpherePartitioner.cc0000664000175000017500000002353215160212070026433 0ustar alastairalastair/* * (C) Crown Copyright 2021 Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "CubedSpherePartitioner.h" #include #include #include #include #include #include #include "atlas/grid/CubedSphereGrid.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Log.h" namespace atlas { namespace grid { namespace detail { namespace partitioner { namespace { bool isNearInt(double value) { const double diff = value - std::floor(value); return (diff <= std::numeric_limits::epsilon() || diff >= (1.0 - std::numeric_limits::epsilon())); } bool isConfigSufficient(const eckit::Parametrisation& config) { return !(config.has("starting rank on tile")) || !(config.has("final rank on tile")) || !(config.has("nprocx")) || !(config.has("nprocy")); } std::vector> createOffset(const std::array& ngpt, const std::array nproc1D, const std::array maxDim1D, const std::array maxDim2D) { std::vector> offset; idx_t totalNproc1D = std::accumulate(std::begin(nproc1D), std::end(nproc1D), 0); // ngpbt - number of grid points per band and tile. std::vector ngpbt(static_cast(totalNproc1D), 0); idx_t j(0); for (std::size_t t = 0; t < 6; ++t) { idx_t jt = static_cast(j); std::size_t nproc = static_cast(nproc1D[t]); std::vector offsetPerTile(nproc + 1, 0); for (std::size_t proc1D = 0; proc1D < nproc; ++proc1D, ++j) { ngpbt[static_cast(j)] = proc1D < nproc - 1 ? ngpt[t] / nproc : ngpt[t] - static_cast(std::accumulate(ngpbt.begin() + jt, ngpbt.begin() + j, 0)); offsetPerTile[proc1D] = proc1D == 0 ? 0 : std::accumulate(ngpbt.begin() + jt, ngpbt.begin() + j, 0) / maxDim2D[t]; } offsetPerTile[nproc] = maxDim1D[t]; offset.push_back(offsetPerTile); } return offset; } } // namespace CubedSpherePartitioner::CubedSpherePartitioner(): Partitioner() {} CubedSpherePartitioner::CubedSpherePartitioner(int N): Partitioner(N, util::NoConfig()), regular_{true} {} CubedSpherePartitioner::CubedSpherePartitioner(const eckit::Parametrisation& config): Partitioner(config), regular_{isConfigSufficient(config)} { if (config.has("starting rank on tile") && config.has("final rank on tile") && config.has("nprocx") && config.has("nprocy")) { config.get("starting rank on tile", globalProcStartPE_); config.get("final rank on tile", globalProcEndPE_); config.get("nprocx", nprocx_); config.get("nprocy", nprocy_); } } CubedSpherePartitioner::CubedSpherePartitioner(int N, const eckit::Parametrisation& config): Partitioner(N, config), regular_{isConfigSufficient(config)} { if (config.has("starting rank on tile") && config.has("final rank on tile") && config.has("nprocx") && config.has("nprocy")) { config.get("starting rank on tile", globalProcStartPE_); config.get("final rank on tile", globalProcEndPE_); config.get("nprocx", nprocx_); config.get("nprocy", nprocy_); } } CubedSpherePartitioner::CubedSpherePartitioner(const int N, const std::vector& globalProcStartPE, const std::vector& globalProcEndPE, const std::vector& nprocx, const std::vector& nprocy): Partitioner(N, util::NoConfig()), globalProcStartPE_(globalProcStartPE.begin(), globalProcStartPE.end()), globalProcEndPE_(globalProcEndPE.begin(), globalProcEndPE.end()), nprocx_{nprocx.begin(), nprocx.end()}, nprocy_{nprocy.begin(), nprocy.end()}, regular_{false} {} CubedSpherePartitioner::CubedSphere CubedSpherePartitioner::cubedsphere(const Grid& grid) const { // grid dimensions const CubedSphereGrid cg(grid); if (!cg) { throw_Exception("CubedSphere Partitioner only works for cubed sphere grids.", Here()); } CubedSphere cb; for (std::size_t t = 0; t < 6; ++t) { cb.nx[t] = cg.N(); cb.ny[t] = cg.N(); } atlas::idx_t nparts = nb_partitions(); if (regular_) { // share PEs around tiles // minRanksPerTile idx_t ranksPerTile = nparts / 6; idx_t reminder = nparts - 6 * ranksPerTile; for (std::size_t t = 0; t < 6; ++t) { cb.nproc[t] = ranksPerTile; } // round-robin; std::size_t t{0}; while (reminder > 0) { if (t == 6) { t = 0; } cb.nproc[t] += 1; t += 1; reminder -= 1; } // now need to specify nprocx and nprocy. // nproc is 0 for the tile we use default nprocx and nprocy = 1 // if we can square-root nproc and get an integer // we use that for nprocx and nprocy // otherwise we split just in nprocx and keep nprocy =1; for (std::size_t t = 0; t < 6; ++t) { if (cb.nproc[t] > 0) { double sq = std::sqrt(static_cast(cb.nproc[t])); if (isNearInt(sq)) { cb.nprocx[t] = static_cast(std::round(sq)); cb.nprocy[t] = static_cast(std::round(sq)); } else { cb.nprocx[t] = 1; cb.nprocy[t] = cb.nproc[t]; } } } cb.globalProcStartPE[0] = 0; cb.globalProcEndPE[0] = cb.nproc[0] - 1; for (size_t t = 1; t < 6; ++t) { if (cb.nproc[t] == 0) { cb.globalProcStartPE[t] = cb.globalProcEndPE[t - 1]; cb.globalProcEndPE[t] = cb.globalProcEndPE[t - 1]; } else { cb.globalProcStartPE[t] = cb.globalProcEndPE[t - 1] + 1; cb.globalProcEndPE[t] = cb.globalProcStartPE[t] + cb.nproc[t] - 1; } } } else { for (size_t t = 0; t < 6; ++t) { cb.globalProcStartPE[t] = globalProcStartPE_[t]; cb.globalProcEndPE[t] = globalProcEndPE_[t]; cb.nprocx[t] = nprocx_[t]; cb.nprocy[t] = nprocy_[t]; } cb.nproc[0] = globalProcEndPE_[0] + 1; for (std::size_t t = 1; t < 6; ++t) { cb.nproc[t] = cb.globalProcStartPE[t] == cb.globalProcEndPE[t - 1] ? cb.nproc[t] = 0 : cb.globalProcEndPE[t] - cb.globalProcStartPE[t] + 1; } } return cb; } void CubedSpherePartitioner::partition(CubedSphere& cb, const int nb_cells, const CellInt cells[], int part[]) const { // ngpt number of grid points per tile (this is for cell centers where the number of cells are the same std::size_t tileCells = static_cast(nb_cells / 6); std::array ngpt{tileCells, tileCells, tileCells, tileCells, tileCells, tileCells}; // xoffset - the xoffset per band per tile (including cb.nx[t]) //std::vector> xoffset(createOffset(ngpt, cb.nprocx, cb.nx, cb.ny)); cb.xoffset = createOffset(ngpt, cb.nprocx, cb.nx, cb.ny); // yoffset - the yoffset per band per tile (including cb.ny[t]) cb.yoffset = createOffset(ngpt, cb.nprocy, cb.ny, cb.nx); int p; // partition index // loop over all data tile by tile for (int n = 0; n < nb_cells; ++n) { std::size_t t = static_cast(cells[n].t); p = cb.globalProcStartPE[t]; for (std::size_t yproc = 0; yproc < static_cast(cb.nprocy[t]); ++yproc) { for (std::size_t xproc = 0; xproc < static_cast(cb.nprocx[t]); ++xproc) { if ((cells[n].y >= cb.yoffset[t][yproc]) && (cells[n].y < cb.yoffset[t][yproc + 1]) && (cells[n].x >= cb.xoffset[t][xproc]) && (cells[n].x < cb.xoffset[t][xproc + 1])) { part[n] = p; } ++p; } } } ATLAS_ASSERT(part[nb_cells - 1] == nb_partitions() - 1, "number of partitions created not what is expected"); } void CubedSpherePartitioner::partition(const Grid& grid, int part[]) const { if (nb_partitions() == 1) // trivial solution, so much faster { for (idx_t j = 0; j < grid.size(); ++j) { part[j] = 0; } } else { auto cb = cubedsphere(grid); std::vector cells(static_cast(grid.size())); std::size_t n(0); for (std::size_t it = 0; it < 6; ++it) { for (idx_t iy = 0; iy < cb.ny[it]; ++iy) { for (idx_t ix = 0; ix < cb.nx[it]; ++ix) { cells[n].t = static_cast(it); cells[n].x = static_cast(ix); cells[n].y = static_cast(iy); cells[n].n = static_cast(n); ++n; } } } partition(cb, grid.size(), cells.data(), part); } } } // namespace partitioner } // namespace detail } // namespace grid } // namespace atlas namespace { atlas::grid::detail::partitioner::PartitionerBuilder __CubedSphere("cubedsphere"); } atlas-0.46.0/src/atlas/grid/detail/partitioner/MatchingFunctionSpacePartitioner.h0000664000175000017500000000207715160212070030321 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include "atlas/functionspace/FunctionSpace.h" #include "atlas/grid/detail/partitioner/Partitioner.h" namespace atlas { namespace grid { namespace detail { namespace partitioner { class MatchingFunctionSpacePartitioner : public Partitioner { public: MatchingFunctionSpacePartitioner(); // MatchingFunctionSpacePartitioner(const idx_t nb_partitions); MatchingFunctionSpacePartitioner(const FunctionSpace&, const eckit::Parametrisation&); virtual ~MatchingFunctionSpacePartitioner() override {} protected: const FunctionSpace partitioned_; }; } // namespace partitioner } // namespace detail } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/partitioner/EqualAreaPartitioner.cc0000664000175000017500000000453715160212070026106 0ustar alastairalastair/* * (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/grid/detail/partitioner/EqualAreaPartitioner.h" #include "atlas/grid.h" #include "atlas/grid/Iterator.h" #include "atlas/util/Constants.h" namespace atlas { namespace grid { namespace detail { namespace partitioner { EqualAreaPartitioner::EqualAreaPartitioner(): Partitioner(), partitioner_() { } EqualAreaPartitioner::EqualAreaPartitioner(int N): EqualAreaPartitioner(N, util::NoConfig()) { } EqualAreaPartitioner::EqualAreaPartitioner(int N, const eckit::Parametrisation& config): Partitioner(N,config), partitioner_(N,config) { } EqualAreaPartitioner::EqualAreaPartitioner(const eckit::Parametrisation& config): Partitioner(config), partitioner_(config) { } void EqualAreaPartitioner::partition(const Grid& grid, int part[]) const { if( partitioner_.coordinates_ == EqualRegionsPartitioner::Coordinates::XY && StructuredGrid(grid) ) { StructuredGrid g(grid); size_t n = 0; for (idx_t j=0; j __EqualArea("equal_area"); } atlas-0.46.0/src/atlas/grid/detail/spacing/0000775000175000017500000000000015160212070020571 5ustar alastairalastairatlas-0.46.0/src/atlas/grid/detail/spacing/CustomSpacing.h0000664000175000017500000000411115160212070023516 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include #include "atlas/grid/detail/spacing/Spacing.h" namespace atlas { namespace grid { namespace spacing { // clang-format off /// @brief Custom spacing in interval /// /// There are N points, by default in the open interval (90, -90). The interval is used to /// determine if a grid's domain contains poles. /// /// Using the constructor CustomSpacing( N, x[], {min,max} ) we can create /// /// CustomSpacing( 4, {75,25,-25,-75} ) --> { 75 , 25 , -25, -75 } /// CustomSpacing( 4, {75,25,-25,-75}, {-90,90} ) --> { 75 , 25 , -25, -75 } /// /// The optional argument {min,max} serves as purpose to indicate that the points /// lie in the open interval (min,max). If not specified, the default values are taken /// to be the North and South pole's latitudes. /// /// Configuration parameters can be passed as well with following keys: /// /// {"N":4, "values":[75,25,-25,75] } --> { 75 , 25 , -25 , -75 } /// {"N":4, "values":[75,25,-25,75], "interval":[-90,90] } --> { 75 , 25 , -25 , -75 } // clang-format on class CustomSpacing : public Spacing { private: using Interval = std::array; static constexpr double North() { return 90.; } static constexpr double South() { return -90.; } public: // constructor CustomSpacing(const eckit::Parametrisation& p); CustomSpacing(long N, const double x[], const Interval& = {North(), South()}); // class name static std::string static_type() { return "custom"; } virtual std::string type() const { return static_type(); } virtual Spec spec() const; }; } // namespace spacing } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/spacing/GaussianSpacing.h0000664000175000017500000000315715160212070024027 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include "atlas/grid/detail/spacing/Spacing.h" namespace atlas { namespace grid { namespace spacing { // clang-format off /// @brief Gaussian spacing in interval /// /// There are N Gaussian spaced points in the open interval (90, -90) /// /// Using the constructor GaussianSpacing( N ) we can create /// /// GaussianSpacing( 4 ) --> { 59.44... , 19.87... , -19.87... , -59.44... } /// /// Configuration parameters can be passed as well with following keys: /// /// {"N":4 } --> { 59.44... , 19.87... , -19.87... , -59.44... } /// /// To reverse the orientation of points to go from negative to positive /// instead, pass also the start and end keys: /// /// {"N":4, "start":-90, "end":90 } --> { -59.44... , -19.87... , 19.87... , 59.44... } // clang-format on class GaussianSpacing : public Spacing { public: // constructor GaussianSpacing(const eckit::Parametrisation& p); GaussianSpacing(long N); // class name static std::string static_type() { return "gaussian"; } virtual std::string type() const { return static_type(); } virtual Spec spec() const; }; } // namespace spacing } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/spacing/FocusSpacing.h0000664000175000017500000000167715160212070023341 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include "atlas/grid/detail/spacing/Spacing.h" namespace atlas { namespace grid { namespace spacing { class FocusSpacing : public Spacing { public: // constructor FocusSpacing(const eckit::Parametrisation& p); // class name static std::string static_type() { return "focus"; } virtual std::string type() const { return static_type(); } virtual Spec spec() const; private: double focus_factor_; double start_; double end_; }; } // namespace spacing } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/spacing/GaussianSpacing.cc0000664000175000017500000000430015160212070024154 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "eckit/config/Parametrisation.h" #include "atlas/grid/detail/spacing/GaussianSpacing.h" #include "atlas/grid/detail/spacing/SpacingFactory.h" #include "atlas/grid/detail/spacing/gaussian/Latitudes.h" #include "atlas/runtime/Exception.h" namespace atlas { namespace grid { namespace spacing { GaussianSpacing::GaussianSpacing(long N) { // perform checks ATLAS_ASSERT(N % 2 == 0); // initialize latitudes during setup, to avoid repeating it. x_.resize(N); gaussian::gaussian_latitudes_npole_spole(N / 2, x_.data()); min_ = -90.; max_ = 90.; } GaussianSpacing::GaussianSpacing(const eckit::Parametrisation& params) { // retrieve N from params long N; if (!params.get("N", N)) { throw_Exception("N missing in Params", Here()); } // perform checks ATLAS_ASSERT(N % 2 == 0); // initialize latitudes during setup, to avoid repeating it. x_.resize(N); gaussian::gaussian_latitudes_npole_spole(N / 2, x_.data()); // Not yet implemented: specify different bounds or direction (e.g from south // to north pole) double start = 90.; double end = -90.; params.get("start", start); params.get("end", end); std::vector interval; if (params.get("interval", interval)) { start = interval[0]; end = interval[1]; } if (start != 90. && end != -90.) { ATLAS_NOTIMPLEMENTED; } min_ = std::min(start, end); max_ = std::max(start, end); } GaussianSpacing::Spec GaussianSpacing::spec() const { Spec spacing_specs; spacing_specs.set("type", static_type()); spacing_specs.set("N", size()); return spacing_specs; } namespace { static SpacingBuilder __builder(GaussianSpacing::static_type()); } } // namespace spacing } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/spacing/LinearSpacing.h0000664000175000017500000000553315160212070023467 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include #include "atlas/grid/detail/spacing/Spacing.h" namespace atlas { namespace grid { namespace spacing { // clang-format off /// @brief Linear spacing in interval /// /// There are N equally spaced points in the closed interval [start, stop] or the /// half-open interval [start, stop) (depending on whether endpoint is True or False) /// /// Using the constructor LinearSpacing( start, end, N, endpoint ) we can create /// LinearSpacing( 2, 3, 5, true ) --> { 2.0 , 2.25 , 2.5 , 2.75 , 3.0 } /// LinearSpacing( 2, 3, 5, false ) --> { 2.0 , 2.2 , 2.4 , 2.6 , 2.8 } /// /// Configuration parameters can be passed as well with following keys: /// /// {"start":2 , "end":3, "N":5, "endpoint":true } --> { 2.0 , 2.25 , 2.5 , 2.75 , 3.0 } /// {"start":2 , "end":3, "N":5, "endpoint":false} --> { 2.0 , 2.2 , 2.4 , 2.6 , 2.8 } /// /// Instead of the "end" key, you can provide the "length" key, to achieve the same results: /// /// {"start":2 , "length":1, "N":5, "endpoint":true } --> { 2.0 , 2.25 , 2.5 , 2.75 , 3.0 } /// {"start":2 , "length":1, "N":5, "endpoint":false} --> { 2.0 , 2.2 , 2.4 , 2.6 , 2.8 } // clang-format on class LinearSpacing : public Spacing { public: using Interval = std::array; public: /// constructor LinearSpacing(const eckit::Parametrisation& p); LinearSpacing(double start, double end, long N, bool endpoint = true); LinearSpacing(const Interval&, long N, bool endpoint = true); // LinearSpacing( double centre, double step, long N, bool endpoint=true ); // class name static std::string static_type() { return "linear"; } virtual std::string type() const { return static_type(); } double step() const; bool endpoint() const; virtual Spec spec() const; public: struct Params { double start; double end; long N; double length; bool endpoint; double step; Params() = default; Params(const eckit::Parametrisation& p); Params(double start, double end, long N, bool endpoint); }; protected: // points are equally spaced between xmin and xmax // Depending on value of endpoint, the spacing will be different void setup(double start, double end, long N, bool endpoint); double start_; double end_; long N_; bool endpoint_; }; } // namespace spacing } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/spacing/LinearSpacing.cc0000664000175000017500000000741415160212070023625 0ustar alastairalastair/* * (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/grid/detail/spacing/LinearSpacing.h" #include #include #include "eckit/config/Parametrisation.h" #include "atlas/grid/detail/spacing/SpacingFactory.h" #include "atlas/runtime/Exception.h" namespace atlas { namespace grid { namespace spacing { LinearSpacing::Params::Params(const eckit::Parametrisation& params) { endpoint = true; params.get("endpoint", endpoint); std::vector interval; if (params.get("N", N)) { // Only one remaining combinations possible: if (params.get("start", start) && params.get("end", end)) { // OK } else if (params.get("interval", interval)) { start = interval[0]; end = interval[1]; } else if (params.get("start", start) && params.get("length", length)) { end = start + length; } else { throw_Exception("Invalid combination of parameters", Here()); } } else { throw_Exception("Invalid combination of parameters", Here()); } length = end - start; if (endpoint && N > 1) { step = length / double(N - 1); } else { step = length / double(N); } } LinearSpacing::Params::Params(double _start, double _end, long _N, bool _endpoint): start(_start), end(_end), N(_N), endpoint(_endpoint) { length = end - start; if (endpoint && N > 1) { step = length / double(N - 1); } else { step = length / double(N); } } LinearSpacing::LinearSpacing(const eckit::Parametrisation& params) { Params p(params); setup(p.start, p.end, p.N, p.endpoint); } LinearSpacing::LinearSpacing(double start, double end, long N, bool endpoint) { setup(start, end, N, endpoint); } LinearSpacing::LinearSpacing(const Interval& interval, long N, bool endpoint) { setup(interval[0], interval[1], N, endpoint); } void LinearSpacing::setup(double start, double end, long N, bool endpoint) { x_.resize(N); double step; volatile double _N = N; // volatile keyword prevents agressive optimization by Cray compiler // deepcode ignore FloatingPointEquals: Expect possible bit-identical start and end if (start == end) { step = 0.; } else if (endpoint && N > 1) { step = (end - start) / (_N - 1); } else { step = (end - start) / _N; } for (long i = 0; i < N; ++i) { x_[i] = start + i * step; } min_ = std::min(start, end); max_ = std::max(start, end); start_ = start; end_ = end; N_ = N; endpoint_ = endpoint; // For exact comparisons: if (N > 1) { x_.front() = start; } if (N > 2 && endpoint) { x_.back() = end; } } double LinearSpacing::step() const { if (size() > 1) { return x_[1] - x_[0]; } else { return 0.; } } bool LinearSpacing::endpoint() const { return std::abs(x_.back() - max_) < 1.e-12; } LinearSpacing::Spec LinearSpacing::spec() const { Spec spacing_specs; spacing_specs.set("type", static_type()); spacing_specs.set("start", start_); spacing_specs.set("end", end_); spacing_specs.set("N", N_); spacing_specs.set("endpoint", endpoint_); return spacing_specs; } namespace { static SpacingBuilder __builder(LinearSpacing::static_type()); } } // namespace spacing } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/spacing/FocusSpacing.cc0000664000175000017500000000464015160212070023470 0ustar alastairalastair/* * (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 "eckit/config/Parametrisation.h" #include "atlas/grid/detail/spacing/FocusSpacing.h" #include "atlas/grid/detail/spacing/SpacingFactory.h" #include "atlas/runtime/Exception.h" namespace atlas { namespace grid { namespace spacing { FocusSpacing::FocusSpacing(const eckit::Parametrisation& params) { double xmin; double xmax; long N; // retrieve xmin, xmax and N from params if (!params.get("start", xmin)) { throw_Exception("start missing in Params", Here()); } if (!params.get("end", xmax)) { throw_Exception("end missing in Params", Here()); } if (!params.get("N", N)) { throw_Exception("N missing in Params", Here()); } // additional parameters for focus spacing if (!params.get("focus_factor", focus_factor_)) { throw_Exception("focus_factor missing in Params", Here()); } x_.resize(N); if (N == 1) { x_[0] = 0.5 * (xmin + xmax); } else { const double midpoint = 0.5 * (xmin + xmax); const double d2 = 2. / double(N - 1); const double c1 = (xmax - xmin) * M_1_PI; const double c2 = 1. / focus_factor_; x_[0] = xmin; x_[N - 1] = xmax; for (long i = 1; i < N - 1; ++i) { const double x2 = -1. + i * d2; // x2 between -1 and 1; x_[i] = midpoint + c1 * std::atan(c2 * std::tan(0.5 * M_PI * x2)); } } min_ = std::min(xmin, xmax); max_ = std::max(xmin, xmax); start_ = xmin; end_ = xmax; } FocusSpacing::Spec FocusSpacing::spec() const { Spec spacing_specs; spacing_specs.set("type", static_type()); spacing_specs.set("N", size()); spacing_specs.set("start", start_); spacing_specs.set("end", end_); spacing_specs.set("focus_factor", focus_factor_); return spacing_specs; } namespace { static SpacingBuilder __builder(FocusSpacing::static_type()); } } // namespace spacing } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/spacing/gaussian/0000775000175000017500000000000015160212070022403 5ustar alastairalastairatlas-0.46.0/src/atlas/grid/detail/spacing/gaussian/N16.cc0000664000175000017500000000211115160212070023251 0ustar alastairalastair/* * (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. */ // TL31 #include "atlas/grid/detail/spacing/gaussian/N.h" namespace atlas { namespace grid { namespace spacing { namespace gaussian { DEFINE_GAUSSIAN_LATITUDES(16, LIST(85.7605871204438159, 80.2687790722500125, 74.7445403686357679, 69.2129761693708616, 63.6786355610968613, 58.1429540492032828, 52.6065260343452650, 47.0696420596876806, 41.5324612466560765, 35.9950784112715994, 30.4575539611520938, 24.9199286299486111, 19.3822313464343878, 13.8444837343848572, 8.3067028565188039, 2.7689030077360099)) } // namespace gaussian } // namespace spacing } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/spacing/gaussian/N8000.cc0000664000175000017500000044020715160212070023426 0ustar alastairalastair/* * (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. */ // TL15999 #include "atlas/grid/detail/spacing/gaussian/N.h" namespace atlas { namespace grid { namespace spacing { namespace gaussian { DEFINE_GAUSSIAN_LATITUDES( 8000, LIST(89.991388621915, 89.980233294064, 89.969012087973, 89.957775997170, 89.946534260250, 89.935289791164, 89.924043795176, 89.912796860149, 89.901549306701, 89.890301324497, 89.879053032872, 89.867804510659, 89.856555812013, 89.845306975370, 89.834058028757, 89.822808993098, 89.811559884324, 89.800310714781, 89.789061494187, 89.777812230288, 89.766562929342, 89.755313596457, 89.744064235840, 89.732814850994, 89.721565444855, 89.710316019900, 89.699066578241, 89.687817121682, 89.676567651779, 89.665318169874, 89.654068677140, 89.642819174600, 89.631569663150, 89.620320143583, 89.609070616601, 89.597821082824, 89.586571542809, 89.575321997048, 89.564072445989, 89.552822890030, 89.541573329533, 89.530323764823, 89.519074196195, 89.507824623919, 89.496575048240, 89.485325469380, 89.474075887543, 89.462826302916, 89.451576715671, 89.440327125966, 89.429077533946, 89.417827939746, 89.406578343489, 89.395328745290, 89.384079145256, 89.372829543485, 89.361579940070, 89.350330335095, 89.339080728640, 89.327831120779, 89.316581511583, 89.305331901115, 89.294082289437, 89.282832676606, 89.271583062674, 89.260333447692, 89.249083831708, 89.237834214766, 89.226584596907, 89.215334978171, 89.204085358595, 89.192835738215, 89.181586117063, 89.170336495171, 89.159086872568, 89.147837249283, 89.136587625343, 89.125338000772, 89.114088375595, 89.102838749834, 89.091589123511, 89.080339496647, 89.069089869262, 89.057840241373, 89.046590612999, 89.035340984158, 89.024091354864, 89.012841725134, 89.001592094982, 88.990342464422, 88.979092833468, 88.967843202133, 88.956593570428, 88.945343938367, 88.934094305960, 88.922844673218, 88.911595040151, 88.900345406769, 88.889095773083, 88.877846139100, 88.866596504830, 88.855346870282, 88.844097235463, 88.832847600381, 88.821597965045, 88.810348329460, 88.799098693634, 88.787849057574, 88.776599421287, 88.765349784777, 88.754100148052, 88.742850511118, 88.731600873979, 88.720351236641, 88.709101599110, 88.697851961390, 88.686602323487, 88.675352685404, 88.664103047147, 88.652853408719, 88.641603770126, 88.630354131371, 88.619104492457, 88.607854853390, 88.596605214173, 88.585355574808, 88.574105935301, 88.562856295653, 88.551606655869, 88.540357015951, 88.529107375903, 88.517857735727, 88.506608095426, 88.495358455004, 88.484108814463, 88.472859173805, 88.461609533033, 88.450359892150, 88.439110251158, 88.427860610059, 88.416610968856, 88.405361327551, 88.394111686145, 88.382862044642, 88.371612403043, 88.360362761349, 88.349113119564, 88.337863477688, 88.326613835725, 88.315364193674, 88.304114551539, 88.292864909320, 88.281615267020, 88.270365624640, 88.259115982182, 88.247866339647, 88.236616697036, 88.225367054351, 88.214117411594, 88.202867768765, 88.191618125867, 88.180368482900, 88.169118839865, 88.157869196765, 88.146619553599, 88.135369910370, 88.124120267079, 88.112870623726, 88.101620980312, 88.090371336839, 88.079121693308, 88.067872049720, 88.056622406075, 88.045372762376, 88.034123118621, 88.022873474814, 88.011623830954, 88.000374187042, 87.989124543080, 87.977874899067, 87.966625255006, 87.955375610897, 87.944125966739, 87.932876322536, 87.921626678286, 87.910377033991, 87.899127389652, 87.887877745269, 87.876628100842, 87.865378456374, 87.854128811864, 87.842879167313, 87.831629522721, 87.820379878089, 87.809130233419, 87.797880588709, 87.786630943962, 87.775381299177, 87.764131654356, 87.752882009498, 87.741632364604, 87.730382719676, 87.719133074712, 87.707883429714, 87.696633784683, 87.685384139618, 87.674134494521, 87.662884849392, 87.651635204230, 87.640385559038, 87.629135913814, 87.617886268560, 87.606636623276, 87.595386977963, 87.584137332620, 87.572887687248, 87.561638041848, 87.550388396420, 87.539138750965, 87.527889105482, 87.516639459973, 87.505389814437, 87.494140168875, 87.482890523287, 87.471640877673, 87.460391232035, 87.449141586371, 87.437891940684, 87.426642294972, 87.415392649236, 87.404143003477, 87.392893357695, 87.381643711889, 87.370394066062, 87.359144420212, 87.347894774339, 87.336645128445, 87.325395482530, 87.314145836593, 87.302896190636, 87.291646544658, 87.280396898659, 87.269147252640, 87.257897606602, 87.246647960543, 87.235398314465, 87.224148668368, 87.212899022252, 87.201649376117, 87.190399729964, 87.179150083793, 87.167900437603, 87.156650791396, 87.145401145170, 87.134151498928, 87.122901852668, 87.111652206391, 87.100402560097, 87.089152913787, 87.077903267461, 87.066653621117, 87.055403974758, 87.044154328383, 87.032904681993, 87.021655035586, 87.010405389165, 86.999155742728, 86.987906096276, 86.976656449810, 86.965406803328, 86.954157156832, 86.942907510322, 86.931657863798, 86.920408217259, 86.909158570707, 86.897908924141, 86.886659277561, 86.875409630968, 86.864159984362, 86.852910337742, 86.841660691110, 86.830411044464, 86.819161397806, 86.807911751135, 86.796662104452, 86.785412457757, 86.774162811049, 86.762913164329, 86.751663517597, 86.740413870853, 86.729164224098, 86.717914577331, 86.706664930552, 86.695415283763, 86.684165636961, 86.672915990149, 86.661666343326, 86.650416696492, 86.639167049647, 86.627917402791, 86.616667755925, 86.605418109048, 86.594168462161, 86.582918815264, 86.571669168357, 86.560419521439, 86.549169874512, 86.537920227574, 86.526670580627, 86.515420933670, 86.504171286704, 86.492921639728, 86.481671992742, 86.470422345748, 86.459172698744, 86.447923051731, 86.436673404709, 86.425423757678, 86.414174110638, 86.402924463589, 86.391674816532, 86.380425169466, 86.369175522391, 86.357925875308, 86.346676228216, 86.335426581117, 86.324176934009, 86.312927286892, 86.301677639768, 86.290427992636, 86.279178345495, 86.267928698347, 86.256679051191, 86.245429404028, 86.234179756856, 86.222930109677, 86.211680462491, 86.200430815297, 86.189181168095, 86.177931520887, 86.166681873671, 86.155432226448, 86.144182579217, 86.132932931980, 86.121683284736, 86.110433637484, 86.099183990226, 86.087934342961, 86.076684695689, 86.065435048411, 86.054185401125, 86.042935753833, 86.031686106535, 86.020436459230, 86.009186811919, 85.997937164601, 85.986687517277, 85.975437869947, 85.964188222610, 85.952938575268, 85.941688927919, 85.930439280564, 85.919189633203, 85.907939985836, 85.896690338463, 85.885440691085, 85.874191043700, 85.862941396310, 85.851691748914, 85.840442101512, 85.829192454105, 85.817942806692, 85.806693159274, 85.795443511850, 85.784193864420, 85.772944216986, 85.761694569545, 85.750444922100, 85.739195274649, 85.727945627193, 85.716695979732, 85.705446332265, 85.694196684794, 85.682947037317, 85.671697389835, 85.660447742349, 85.649198094857, 85.637948447361, 85.626698799859, 85.615449152353, 85.604199504842, 85.592949857326, 85.581700209805, 85.570450562280, 85.559200914750, 85.547951267215, 85.536701619676, 85.525451972132, 85.514202324584, 85.502952677031, 85.491703029474, 85.480453381912, 85.469203734346, 85.457954086776, 85.446704439201, 85.435454791622, 85.424205144038, 85.412955496451, 85.401705848859, 85.390456201263, 85.379206553663, 85.367956906059, 85.356707258450, 85.345457610838, 85.334207963222, 85.322958315601, 85.311708667977, 85.300459020349, 85.289209372717, 85.277959725081, 85.266710077441, 85.255460429797, 85.244210782149, 85.232961134498, 85.221711486843, 85.210461839184, 85.199212191522, 85.187962543856, 85.176712896186, 85.165463248512, 85.154213600835, 85.142963953155, 85.131714305471, 85.120464657783, 85.109215010092, 85.097965362398, 85.086715714700, 85.075466066998, 85.064216419293, 85.052966771585, 85.041717123874, 85.030467476159, 85.019217828441, 85.007968180719, 84.996718532994, 84.985468885266, 84.974219237535, 84.962969589801, 84.951719942063, 84.940470294323, 84.929220646579, 84.917970998832, 84.906721351082, 84.895471703329, 84.884222055572, 84.872972407813, 84.861722760051, 84.850473112286, 84.839223464518, 84.827973816747, 84.816724168973, 84.805474521196, 84.794224873416, 84.782975225633, 84.771725577847, 84.760475930059, 84.749226282268, 84.737976634474, 84.726726986677, 84.715477338877, 84.704227691075, 84.692978043270, 84.681728395462, 84.670478747652, 84.659229099838, 84.647979452023, 84.636729804204, 84.625480156383, 84.614230508559, 84.602980860733, 84.591731212904, 84.580481565073, 84.569231917239, 84.557982269402, 84.546732621563, 84.535482973721, 84.524233325877, 84.512983678031, 84.501734030182, 84.490484382331, 84.479234734477, 84.467985086620, 84.456735438762, 84.445485790901, 84.434236143037, 84.422986495172, 84.411736847303, 84.400487199433, 84.389237551560, 84.377987903685, 84.366738255808, 84.355488607928, 84.344238960047, 84.332989312163, 84.321739664276, 84.310490016388, 84.299240368497, 84.287990720604, 84.276741072709, 84.265491424812, 84.254241776912, 84.242992129011, 84.231742481107, 84.220492833201, 84.209243185293, 84.197993537383, 84.186743889471, 84.175494241557, 84.164244593641, 84.152994945723, 84.141745297803, 84.130495649880, 84.119246001956, 84.107996354030, 84.096746706102, 84.085497058171, 84.074247410239, 84.062997762305, 84.051748114369, 84.040498466431, 84.029248818491, 84.017999170549, 84.006749522606, 83.995499874660, 83.984250226713, 83.973000578763, 83.961750930812, 83.950501282859, 83.939251634904, 83.928001986948, 83.916752338989, 83.905502691029, 83.894253043067, 83.883003395103, 83.871753747137, 83.860504099170, 83.849254451201, 83.838004803230, 83.826755155257, 83.815505507283, 83.804255859307, 83.793006211329, 83.781756563349, 83.770506915368, 83.759257267385, 83.748007619401, 83.736757971415, 83.725508323427, 83.714258675437, 83.703009027446, 83.691759379453, 83.680509731459, 83.669260083463, 83.658010435466, 83.646760787467, 83.635511139466, 83.624261491464, 83.613011843460, 83.601762195454, 83.590512547447, 83.579262899439, 83.568013251429, 83.556763603417, 83.545513955404, 83.534264307390, 83.523014659373, 83.511765011356, 83.500515363337, 83.489265715316, 83.478016067294, 83.466766419271, 83.455516771246, 83.444267123219, 83.433017475192, 83.421767827162, 83.410518179132, 83.399268531100, 83.388018883066, 83.376769235031, 83.365519586995, 83.354269938957, 83.343020290918, 83.331770642878, 83.320520994836, 83.309271346793, 83.298021698748, 83.286772050702, 83.275522402655, 83.264272754606, 83.253023106557, 83.241773458505, 83.230523810453, 83.219274162399, 83.208024514344, 83.196774866287, 83.185525218230, 83.174275570171, 83.163025922110, 83.151776274049, 83.140526625986, 83.129276977922, 83.118027329857, 83.106777681790, 83.095528033722, 83.084278385653, 83.073028737583, 83.061779089512, 83.050529441439, 83.039279793365, 83.028030145290, 83.016780497214, 83.005530849136, 82.994281201058, 82.983031552978, 82.971781904897, 82.960532256814, 82.949282608731, 82.938032960647, 82.926783312561, 82.915533664474, 82.904284016386, 82.893034368297, 82.881784720207, 82.870535072115, 82.859285424023, 82.848035775929, 82.836786127835, 82.825536479739, 82.814286831642, 82.803037183544, 82.791787535445, 82.780537887345, 82.769288239244, 82.758038591141, 82.746788943038, 82.735539294933, 82.724289646828, 82.713039998721, 82.701790350614, 82.690540702505, 82.679291054396, 82.668041406285, 82.656791758173, 82.645542110060, 82.634292461947, 82.623042813832, 82.611793165716, 82.600543517599, 82.589293869481, 82.578044221362, 82.566794573243, 82.555544925122, 82.544295277000, 82.533045628877, 82.521795980754, 82.510546332629, 82.499296684503, 82.488047036377, 82.476797388249, 82.465547740120, 82.454298091991, 82.443048443860, 82.431798795729, 82.420549147597, 82.409299499464, 82.398049851329, 82.386800203194, 82.375550555058, 82.364300906921, 82.353051258784, 82.341801610645, 82.330551962505, 82.319302314365, 82.308052666223, 82.296803018081, 82.285553369938, 82.274303721793, 82.263054073648, 82.251804425503, 82.240554777356, 82.229305129208, 82.218055481060, 82.206805832910, 82.195556184760, 82.184306536609, 82.173056888457, 82.161807240305, 82.150557592151, 82.139307943997, 82.128058295841, 82.116808647685, 82.105558999528, 82.094309351371, 82.083059703212, 82.071810055053, 82.060560406893, 82.049310758732, 82.038061110570, 82.026811462407, 82.015561814244, 82.004312166079, 81.993062517914, 81.981812869749, 81.970563221582, 81.959313573415, 81.948063925247, 81.936814277078, 81.925564628908, 81.914314980738, 81.903065332566, 81.891815684394, 81.880566036222, 81.869316388048, 81.858066739874, 81.846817091699, 81.835567443523, 81.824317795346, 81.813068147169, 81.801818498991, 81.790568850812, 81.779319202633, 81.768069554453, 81.756819906272, 81.745570258090, 81.734320609908, 81.723070961725, 81.711821313541, 81.700571665356, 81.689322017171, 81.678072368985, 81.666822720799, 81.655573072611, 81.644323424423, 81.633073776235, 81.621824128045, 81.610574479855, 81.599324831664, 81.588075183473, 81.576825535281, 81.565575887088, 81.554326238894, 81.543076590700, 81.531826942505, 81.520577294310, 81.509327646113, 81.498077997917, 81.486828349719, 81.475578701521, 81.464329053322, 81.453079405123, 81.441829756922, 81.430580108722, 81.419330460520, 81.408080812318, 81.396831164115, 81.385581515912, 81.374331867708, 81.363082219503, 81.351832571298, 81.340582923092, 81.329333274886, 81.318083626679, 81.306833978471, 81.295584330263, 81.284334682054, 81.273085033844, 81.261835385634, 81.250585737423, 81.239336089212, 81.228086441000, 81.216836792787, 81.205587144574, 81.194337496360, 81.183087848146, 81.171838199931, 81.160588551715, 81.149338903499, 81.138089255282, 81.126839607065, 81.115589958847, 81.104340310628, 81.093090662409, 81.081841014190, 81.070591365969, 81.059341717749, 81.048092069527, 81.036842421305, 81.025592773083, 81.014343124860, 81.003093476636, 80.991843828412, 80.980594180187, 80.969344531962, 80.958094883736, 80.946845235510, 80.935595587283, 80.924345939056, 80.913096290828, 80.901846642599, 80.890596994370, 80.879347346140, 80.868097697910, 80.856848049679, 80.845598401448, 80.834348753217, 80.823099104984, 80.811849456751, 80.800599808518, 80.789350160284, 80.778100512050, 80.766850863815, 80.755601215580, 80.744351567344, 80.733101919107, 80.721852270870, 80.710602622633, 80.699352974395, 80.688103326157, 80.676853677918, 80.665604029678, 80.654354381438, 80.643104733198, 80.631855084957, 80.620605436715, 80.609355788473, 80.598106140231, 80.586856491988, 80.575606843745, 80.564357195501, 80.553107547256, 80.541857899012, 80.530608250766, 80.519358602521, 80.508108954274, 80.496859306028, 80.485609657780, 80.474360009533, 80.463110361284, 80.451860713036, 80.440611064787, 80.429361416537, 80.418111768287, 80.406862120037, 80.395612471786, 80.384362823534, 80.373113175282, 80.361863527030, 80.350613878777, 80.339364230524, 80.328114582270, 80.316864934016, 80.305615285762, 80.294365637507, 80.283115989251, 80.271866340995, 80.260616692739, 80.249367044482, 80.238117396225, 80.226867747968, 80.215618099709, 80.204368451451, 80.193118803192, 80.181869154933, 80.170619506673, 80.159369858413, 80.148120210152, 80.136870561891, 80.125620913629, 80.114371265367, 80.103121617105, 80.091871968842, 80.080622320579, 80.069372672315, 80.058123024052, 80.046873375787, 80.035623727522, 80.024374079257, 80.013124430991, 80.001874782725, 79.990625134459, 79.979375486192, 79.968125837925, 79.956876189657, 79.945626541389, 79.934376893120, 79.923127244852, 79.911877596582, 79.900627948313, 79.889378300043, 79.878128651772, 79.866879003501, 79.855629355230, 79.844379706958, 79.833130058686, 79.821880410414, 79.810630762141, 79.799381113868, 79.788131465595, 79.776881817321, 79.765632169046, 79.754382520772, 79.743132872497, 79.731883224221, 79.720633575945, 79.709383927669, 79.698134279393, 79.686884631116, 79.675634982838, 79.664385334561, 79.653135686283, 79.641886038004, 79.630636389726, 79.619386741446, 79.608137093167, 79.596887444887, 79.585637796607, 79.574388148326, 79.563138500045, 79.551888851764, 79.540639203482, 79.529389555200, 79.518139906918, 79.506890258635, 79.495640610352, 79.484390962069, 79.473141313785, 79.461891665501, 79.450642017217, 79.439392368932, 79.428142720647, 79.416893072361, 79.405643424075, 79.394393775789, 79.383144127503, 79.371894479216, 79.360644830929, 79.349395182641, 79.338145534353, 79.326895886065, 79.315646237777, 79.304396589488, 79.293146941199, 79.281897292909, 79.270647644619, 79.259397996329, 79.248148348038, 79.236898699748, 79.225649051457, 79.214399403165, 79.203149754873, 79.191900106581, 79.180650458289, 79.169400809996, 79.158151161703, 79.146901513409, 79.135651865116, 79.124402216822, 79.113152568527, 79.101902920233, 79.090653271938, 79.079403623642, 79.068153975347, 79.056904327051, 79.045654678755, 79.034405030458, 79.023155382161, 79.011905733864, 79.000656085567, 78.989406437269, 78.978156788971, 78.966907140672, 78.955657492374, 78.944407844075, 78.933158195776, 78.921908547476, 78.910658899176, 78.899409250876, 78.888159602576, 78.876909954275, 78.865660305974, 78.854410657672, 78.843161009371, 78.831911361069, 78.820661712767, 78.809412064464, 78.798162416161, 78.786912767858, 78.775663119555, 78.764413471251, 78.753163822947, 78.741914174643, 78.730664526338, 78.719414878034, 78.708165229729, 78.696915581423, 78.685665933118, 78.674416284812, 78.663166636505, 78.651916988199, 78.640667339892, 78.629417691585, 78.618168043278, 78.606918394970, 78.595668746662, 78.584419098354, 78.573169450046, 78.561919801737, 78.550670153428, 78.539420505119, 78.528170856809, 78.516921208500, 78.505671560190, 78.494421911879, 78.483172263569, 78.471922615258, 78.460672966947, 78.449423318636, 78.438173670324, 78.426924022012, 78.415674373700, 78.404424725387, 78.393175077075, 78.381925428762, 78.370675780449, 78.359426132135, 78.348176483822, 78.336926835508, 78.325677187193, 78.314427538879, 78.303177890564, 78.291928242249, 78.280678593934, 78.269428945619, 78.258179297303, 78.246929648987, 78.235680000671, 78.224430352354, 78.213180704037, 78.201931055720, 78.190681407403, 78.179431759086, 78.168182110768, 78.156932462450, 78.145682814132, 78.134433165813, 78.123183517495, 78.111933869176, 78.100684220857, 78.089434572537, 78.078184924218, 78.066935275898, 78.055685627578, 78.044435979257, 78.033186330937, 78.021936682616, 78.010687034295, 77.999437385973, 77.988187737652, 77.976938089330, 77.965688441008, 77.954438792686, 77.943189144363, 77.931939496040, 77.920689847718, 77.909440199394, 77.898190551071, 77.886940902747, 77.875691254423, 77.864441606099, 77.853191957775, 77.841942309451, 77.830692661126, 77.819443012801, 77.808193364476, 77.796943716150, 77.785694067824, 77.774444419499, 77.763194771172, 77.751945122846, 77.740695474520, 77.729445826193, 77.718196177866, 77.706946529539, 77.695696881211, 77.684447232884, 77.673197584556, 77.661947936228, 77.650698287899, 77.639448639571, 77.628198991242, 77.616949342913, 77.605699694584, 77.594450046255, 77.583200397925, 77.571950749595, 77.560701101265, 77.549451452935, 77.538201804605, 77.526952156274, 77.515702507943, 77.504452859612, 77.493203211281, 77.481953562949, 77.470703914618, 77.459454266286, 77.448204617954, 77.436954969621, 77.425705321289, 77.414455672956, 77.403206024623, 77.391956376290, 77.380706727957, 77.369457079623, 77.358207431290, 77.346957782956, 77.335708134622, 77.324458486287, 77.313208837953, 77.301959189618, 77.290709541283, 77.279459892948, 77.268210244613, 77.256960596277, 77.245710947942, 77.234461299606, 77.223211651270, 77.211962002933, 77.200712354597, 77.189462706260, 77.178213057923, 77.166963409586, 77.155713761249, 77.144464112912, 77.133214464574, 77.121964816236, 77.110715167898, 77.099465519560, 77.088215871222, 77.076966222883, 77.065716574544, 77.054466926205, 77.043217277866, 77.031967629527, 77.020717981187, 77.009468332848, 76.998218684508, 76.986969036168, 76.975719387827, 76.964469739487, 76.953220091146, 76.941970442806, 76.930720794465, 76.919471146123, 76.908221497782, 76.896971849441, 76.885722201099, 76.874472552757, 76.863222904415, 76.851973256073, 76.840723607730, 76.829473959388, 76.818224311045, 76.806974662702, 76.795725014359, 76.784475366016, 76.773225717672, 76.761976069328, 76.750726420985, 76.739476772641, 76.728227124296, 76.716977475952, 76.705727827607, 76.694478179263, 76.683228530918, 76.671978882573, 76.660729234228, 76.649479585882, 76.638229937537, 76.626980289191, 76.615730640845, 76.604480992499, 76.593231344153, 76.581981695806, 76.570732047460, 76.559482399113, 76.548232750766, 76.536983102419, 76.525733454072, 76.514483805724, 76.503234157377, 76.491984509029, 76.480734860681, 76.469485212333, 76.458235563985, 76.446985915637, 76.435736267288, 76.424486618939, 76.413236970590, 76.401987322241, 76.390737673892, 76.379488025543, 76.368238377193, 76.356988728844, 76.345739080494, 76.334489432144, 76.323239783794, 76.311990135443, 76.300740487093, 76.289490838742, 76.278241190391, 76.266991542041, 76.255741893689, 76.244492245338, 76.233242596987, 76.221992948635, 76.210743300283, 76.199493651932, 76.188244003580, 76.176994355227, 76.165744706875, 76.154495058523, 76.143245410170, 76.131995761817, 76.120746113464, 76.109496465111, 76.098246816758, 76.086997168404, 76.075747520051, 76.064497871697, 76.053248223343, 76.041998574989, 76.030748926635, 76.019499278281, 76.008249629926, 75.996999981572, 75.985750333217, 75.974500684862, 75.963251036507, 75.952001388152, 75.940751739796, 75.929502091441, 75.918252443085, 75.907002794730, 75.895753146374, 75.884503498018, 75.873253849661, 75.862004201305, 75.850754552949, 75.839504904592, 75.828255256235, 75.817005607878, 75.805755959521, 75.794506311164, 75.783256662807, 75.772007014449, 75.760757366091, 75.749507717734, 75.738258069376, 75.727008421018, 75.715758772660, 75.704509124301, 75.693259475943, 75.682009827584, 75.670760179225, 75.659510530866, 75.648260882507, 75.637011234148, 75.625761585789, 75.614511937430, 75.603262289070, 75.592012640710, 75.580762992350, 75.569513343991, 75.558263695630, 75.547014047270, 75.535764398910, 75.524514750549, 75.513265102189, 75.502015453828, 75.490765805467, 75.479516157106, 75.468266508745, 75.457016860383, 75.445767212022, 75.434517563660, 75.423267915299, 75.412018266937, 75.400768618575, 75.389518970213, 75.378269321851, 75.367019673488, 75.355770025126, 75.344520376763, 75.333270728400, 75.322021080037, 75.310771431674, 75.299521783311, 75.288272134948, 75.277022486585, 75.265772838221, 75.254523189858, 75.243273541494, 75.232023893130, 75.220774244766, 75.209524596402, 75.198274948037, 75.187025299673, 75.175775651309, 75.164526002944, 75.153276354579, 75.142026706214, 75.130777057849, 75.119527409484, 75.108277761119, 75.097028112753, 75.085778464388, 75.074528816022, 75.063279167657, 75.052029519291, 75.040779870925, 75.029530222559, 75.018280574192, 75.007030925826, 74.995781277460, 74.984531629093, 74.973281980726, 74.962032332359, 74.950782683992, 74.939533035625, 74.928283387258, 74.917033738891, 74.905784090523, 74.894534442156, 74.883284793788, 74.872035145420, 74.860785497053, 74.849535848685, 74.838286200316, 74.827036551948, 74.815786903580, 74.804537255211, 74.793287606843, 74.782037958474, 74.770788310105, 74.759538661736, 74.748289013367, 74.737039364998, 74.725789716629, 74.714540068259, 74.703290419890, 74.692040771520, 74.680791123150, 74.669541474781, 74.658291826411, 74.647042178041, 74.635792529670, 74.624542881300, 74.613293232930, 74.602043584559, 74.590793936189, 74.579544287818, 74.568294639447, 74.557044991076, 74.545795342705, 74.534545694334, 74.523296045962, 74.512046397591, 74.500796749219, 74.489547100848, 74.478297452476, 74.467047804104, 74.455798155732, 74.444548507360, 74.433298858988, 74.422049210616, 74.410799562243, 74.399549913871, 74.388300265498, 74.377050617126, 74.365800968753, 74.354551320380, 74.343301672007, 74.332052023634, 74.320802375261, 74.309552726887, 74.298303078514, 74.287053430140, 74.275803781767, 74.264554133393, 74.253304485019, 74.242054836645, 74.230805188271, 74.219555539897, 74.208305891523, 74.197056243148, 74.185806594774, 74.174556946399, 74.163307298024, 74.152057649650, 74.140808001275, 74.129558352900, 74.118308704525, 74.107059056150, 74.095809407774, 74.084559759399, 74.073310111023, 74.062060462648, 74.050810814272, 74.039561165896, 74.028311517520, 74.017061869144, 74.005812220768, 73.994562572392, 73.983312924016, 73.972063275639, 73.960813627263, 73.949563978886, 73.938314330510, 73.927064682133, 73.915815033756, 73.904565385379, 73.893315737002, 73.882066088625, 73.870816440248, 73.859566791870, 73.848317143493, 73.837067495115, 73.825817846738, 73.814568198360, 73.803318549982, 73.792068901604, 73.780819253226, 73.769569604848, 73.758319956470, 73.747070308091, 73.735820659713, 73.724571011334, 73.713321362956, 73.702071714577, 73.690822066198, 73.679572417819, 73.668322769440, 73.657073121061, 73.645823472682, 73.634573824303, 73.623324175924, 73.612074527544, 73.600824879165, 73.589575230785, 73.578325582405, 73.567075934025, 73.555826285646, 73.544576637266, 73.533326988885, 73.522077340505, 73.510827692125, 73.499578043745, 73.488328395364, 73.477078746984, 73.465829098603, 73.454579450222, 73.443329801842, 73.432080153461, 73.420830505080, 73.409580856699, 73.398331208318, 73.387081559936, 73.375831911555, 73.364582263174, 73.353332614792, 73.342082966410, 73.330833318029, 73.319583669647, 73.308334021265, 73.297084372883, 73.285834724501, 73.274585076119, 73.263335427737, 73.252085779354, 73.240836130972, 73.229586482590, 73.218336834207, 73.207087185824, 73.195837537442, 73.184587889059, 73.173338240676, 73.162088592293, 73.150838943910, 73.139589295527, 73.128339647144, 73.117089998760, 73.105840350377, 73.094590701993, 73.083341053610, 73.072091405226, 73.060841756842, 73.049592108459, 73.038342460075, 73.027092811691, 73.015843163307, 73.004593514922, 72.993343866538, 72.982094218154, 72.970844569769, 72.959594921385, 72.948345273000, 72.937095624616, 72.925845976231, 72.914596327846, 72.903346679461, 72.892097031076, 72.880847382691, 72.869597734306, 72.858348085921, 72.847098437536, 72.835848789150, 72.824599140765, 72.813349492379, 72.802099843994, 72.790850195608, 72.779600547222, 72.768350898836, 72.757101250450, 72.745851602064, 72.734601953678, 72.723352305292, 72.712102656906, 72.700853008520, 72.689603360133, 72.678353711747, 72.667104063360, 72.655854414973, 72.644604766587, 72.633355118200, 72.622105469813, 72.610855821426, 72.599606173039, 72.588356524652, 72.577106876265, 72.565857227878, 72.554607579490, 72.543357931103, 72.532108282715, 72.520858634328, 72.509608985940, 72.498359337552, 72.487109689165, 72.475860040777, 72.464610392389, 72.453360744001, 72.442111095613, 72.430861447224, 72.419611798836, 72.408362150448, 72.397112502060, 72.385862853671, 72.374613205283, 72.363363556894, 72.352113908505, 72.340864260116, 72.329614611728, 72.318364963339, 72.307115314950, 72.295865666561, 72.284616018172, 72.273366369782, 72.262116721393, 72.250867073004, 72.239617424614, 72.228367776225, 72.217118127835, 72.205868479446, 72.194618831056, 72.183369182666, 72.172119534276, 72.160869885886, 72.149620237496, 72.138370589106, 72.127120940716, 72.115871292326, 72.104621643936, 72.093371995545, 72.082122347155, 72.070872698764, 72.059623050374, 72.048373401983, 72.037123753593, 72.025874105202, 72.014624456811, 72.003374808420, 71.992125160029, 71.980875511638, 71.969625863247, 71.958376214856, 71.947126566464, 71.935876918073, 71.924627269682, 71.913377621290, 71.902127972899, 71.890878324507, 71.879628676115, 71.868379027724, 71.857129379332, 71.845879730940, 71.834630082548, 71.823380434156, 71.812130785764, 71.800881137372, 71.789631488980, 71.778381840587, 71.767132192195, 71.755882543803, 71.744632895410, 71.733383247018, 71.722133598625, 71.710883950232, 71.699634301840, 71.688384653447, 71.677135005054, 71.665885356661, 71.654635708268, 71.643386059875, 71.632136411482, 71.620886763088, 71.609637114695, 71.598387466302, 71.587137817908, 71.575888169515, 71.564638521121, 71.553388872728, 71.542139224334, 71.530889575941, 71.519639927547, 71.508390279153, 71.497140630759, 71.485890982365, 71.474641333971, 71.463391685577, 71.452142037183, 71.440892388788, 71.429642740394, 71.418393092000, 71.407143443605, 71.395893795211, 71.384644146816, 71.373394498422, 71.362144850027, 71.350895201632, 71.339645553237, 71.328395904843, 71.317146256448, 71.305896608053, 71.294646959658, 71.283397311263, 71.272147662867, 71.260898014472, 71.249648366077, 71.238398717681, 71.227149069286, 71.215899420891, 71.204649772495, 71.193400124099, 71.182150475704, 71.170900827308, 71.159651178912, 71.148401530516, 71.137151882120, 71.125902233725, 71.114652585328, 71.103402936932, 71.092153288536, 71.080903640140, 71.069653991744, 71.058404343347, 71.047154694951, 71.035905046555, 71.024655398158, 71.013405749762, 71.002156101365, 70.990906452968, 70.979656804571, 70.968407156175, 70.957157507778, 70.945907859381, 70.934658210984, 70.923408562587, 70.912158914190, 70.900909265793, 70.889659617395, 70.878409968998, 70.867160320601, 70.855910672203, 70.844661023806, 70.833411375409, 70.822161727011, 70.810912078613, 70.799662430216, 70.788412781818, 70.777163133420, 70.765913485022, 70.754663836624, 70.743414188226, 70.732164539828, 70.720914891430, 70.709665243032, 70.698415594634, 70.687165946236, 70.675916297837, 70.664666649439, 70.653417001041, 70.642167352642, 70.630917704244, 70.619668055845, 70.608418407447, 70.597168759048, 70.585919110649, 70.574669462250, 70.563419813851, 70.552170165453, 70.540920517054, 70.529670868655, 70.518421220255, 70.507171571856, 70.495921923457, 70.484672275058, 70.473422626659, 70.462172978259, 70.450923329860, 70.439673681460, 70.428424033061, 70.417174384661, 70.405924736262, 70.394675087862, 70.383425439462, 70.372175791063, 70.360926142663, 70.349676494263, 70.338426845863, 70.327177197463, 70.315927549063, 70.304677900663, 70.293428252263, 70.282178603862, 70.270928955462, 70.259679307062, 70.248429658661, 70.237180010261, 70.225930361861, 70.214680713460, 70.203431065059, 70.192181416659, 70.180931768258, 70.169682119857, 70.158432471457, 70.147182823056, 70.135933174655, 70.124683526254, 70.113433877853, 70.102184229452, 70.090934581051, 70.079684932650, 70.068435284248, 70.057185635847, 70.045935987446, 70.034686339045, 70.023436690643, 70.012187042242, 70.000937393840, 69.989687745439, 69.978438097037, 69.967188448635, 69.955938800234, 69.944689151832, 69.933439503430, 69.922189855028, 69.910940206626, 69.899690558224, 69.888440909822, 69.877191261420, 69.865941613018, 69.854691964616, 69.843442316214, 69.832192667812, 69.820943019409, 69.809693371007, 69.798443722605, 69.787194074202, 69.775944425800, 69.764694777397, 69.753445128995, 69.742195480592, 69.730945832189, 69.719696183786, 69.708446535384, 69.697196886981, 69.685947238578, 69.674697590175, 69.663447941772, 69.652198293369, 69.640948644966, 69.629698996563, 69.618449348160, 69.607199699756, 69.595950051353, 69.584700402950, 69.573450754546, 69.562201106143, 69.550951457740, 69.539701809336, 69.528452160932, 69.517202512529, 69.505952864125, 69.494703215722, 69.483453567318, 69.472203918914, 69.460954270510, 69.449704622106, 69.438454973702, 69.427205325298, 69.415955676894, 69.404706028490, 69.393456380086, 69.382206731682, 69.370957083278, 69.359707434873, 69.348457786469, 69.337208138065, 69.325958489660, 69.314708841256, 69.303459192851, 69.292209544447, 69.280959896042, 69.269710247638, 69.258460599233, 69.247210950828, 69.235961302424, 69.224711654019, 69.213462005614, 69.202212357209, 69.190962708804, 69.179713060399, 69.168463411994, 69.157213763589, 69.145964115184, 69.134714466779, 69.123464818373, 69.112215169968, 69.100965521563, 69.089715873158, 69.078466224752, 69.067216576347, 69.055966927941, 69.044717279536, 69.033467631130, 69.022217982725, 69.010968334319, 68.999718685913, 68.988469037508, 68.977219389102, 68.965969740696, 68.954720092290, 68.943470443884, 68.932220795478, 68.920971147072, 68.909721498666, 68.898471850260, 68.887222201854, 68.875972553448, 68.864722905042, 68.853473256635, 68.842223608229, 68.830973959823, 68.819724311416, 68.808474663010, 68.797225014603, 68.785975366197, 68.774725717790, 68.763476069384, 68.752226420977, 68.740976772570, 68.729727124164, 68.718477475757, 68.707227827350, 68.695978178943, 68.684728530536, 68.673478882129, 68.662229233722, 68.650979585315, 68.639729936908, 68.628480288501, 68.617230640094, 68.605980991687, 68.594731343280, 68.583481694872, 68.572232046465, 68.560982398058, 68.549732749650, 68.538483101243, 68.527233452835, 68.515983804428, 68.504734156020, 68.493484507613, 68.482234859205, 68.470985210797, 68.459735562390, 68.448485913982, 68.437236265574, 68.425986617166, 68.414736968758, 68.403487320350, 68.392237671942, 68.380988023534, 68.369738375126, 68.358488726718, 68.347239078310, 68.335989429902, 68.324739781494, 68.313490133086, 68.302240484677, 68.290990836269, 68.279741187861, 68.268491539452, 68.257241891044, 68.245992242635, 68.234742594227, 68.223492945818, 68.212243297410, 68.200993649001, 68.189744000592, 68.178494352184, 68.167244703775, 68.155995055366, 68.144745406957, 68.133495758548, 68.122246110139, 68.110996461731, 68.099746813322, 68.088497164913, 68.077247516503, 68.065997868094, 68.054748219685, 68.043498571276, 68.032248922867, 68.020999274458, 68.009749626048, 67.998499977639, 67.987250329230, 67.976000680820, 67.964751032411, 67.953501384001, 67.942251735592, 67.931002087182, 67.919752438773, 67.908502790363, 67.897253141953, 67.886003493544, 67.874753845134, 67.863504196724, 67.852254548314, 67.841004899904, 67.829755251494, 67.818505603085, 67.807255954675, 67.796006306265, 67.784756657854, 67.773507009444, 67.762257361034, 67.751007712624, 67.739758064214, 67.728508415804, 67.717258767393, 67.706009118983, 67.694759470573, 67.683509822162, 67.672260173752, 67.661010525342, 67.649760876931, 67.638511228521, 67.627261580110, 67.616011931699, 67.604762283289, 67.593512634878, 67.582262986467, 67.571013338057, 67.559763689646, 67.548514041235, 67.537264392824, 67.526014744413, 67.514765096002, 67.503515447592, 67.492265799181, 67.481016150770, 67.469766502358, 67.458516853947, 67.447267205536, 67.436017557125, 67.424767908714, 67.413518260303, 67.402268611891, 67.391018963480, 67.379769315069, 67.368519666657, 67.357270018246, 67.346020369834, 67.334770721423, 67.323521073012, 67.312271424600, 67.301021776188, 67.289772127777, 67.278522479365, 67.267272830953, 67.256023182542, 67.244773534130, 67.233523885718, 67.222274237306, 67.211024588894, 67.199774940483, 67.188525292071, 67.177275643659, 67.166025995247, 67.154776346835, 67.143526698423, 67.132277050010, 67.121027401598, 67.109777753186, 67.098528104774, 67.087278456362, 67.076028807950, 67.064779159537, 67.053529511125, 67.042279862713, 67.031030214300, 67.019780565888, 67.008530917475, 66.997281269063, 66.986031620650, 66.974781972238, 66.963532323825, 66.952282675412, 66.941033027000, 66.929783378587, 66.918533730174, 66.907284081762, 66.896034433349, 66.884784784936, 66.873535136523, 66.862285488110, 66.851035839697, 66.839786191284, 66.828536542871, 66.817286894458, 66.806037246045, 66.794787597632, 66.783537949219, 66.772288300806, 66.761038652393, 66.749789003979, 66.738539355566, 66.727289707153, 66.716040058740, 66.704790410326, 66.693540761913, 66.682291113499, 66.671041465086, 66.659791816672, 66.648542168259, 66.637292519845, 66.626042871432, 66.614793223018, 66.603543574605, 66.592293926191, 66.581044277777, 66.569794629363, 66.558544980950, 66.547295332536, 66.536045684122, 66.524796035708, 66.513546387294, 66.502296738880, 66.491047090466, 66.479797442053, 66.468547793638, 66.457298145224, 66.446048496810, 66.434798848396, 66.423549199982, 66.412299551568, 66.401049903154, 66.389800254740, 66.378550606325, 66.367300957911, 66.356051309497, 66.344801661082, 66.333552012668, 66.322302364254, 66.311052715839, 66.299803067425, 66.288553419010, 66.277303770596, 66.266054122181, 66.254804473766, 66.243554825352, 66.232305176937, 66.221055528522, 66.209805880108, 66.198556231693, 66.187306583278, 66.176056934863, 66.164807286449, 66.153557638034, 66.142307989619, 66.131058341204, 66.119808692789, 66.108559044374, 66.097309395959, 66.086059747544, 66.074810099129, 66.063560450714, 66.052310802299, 66.041061153883, 66.029811505468, 66.018561857053, 66.007312208638, 65.996062560222, 65.984812911807, 65.973563263392, 65.962313614976, 65.951063966561, 65.939814318146, 65.928564669730, 65.917315021315, 65.906065372899, 65.894815724484, 65.883566076068, 65.872316427652, 65.861066779237, 65.849817130821, 65.838567482405, 65.827317833990, 65.816068185574, 65.804818537158, 65.793568888742, 65.782319240327, 65.771069591911, 65.759819943495, 65.748570295079, 65.737320646663, 65.726070998247, 65.714821349831, 65.703571701415, 65.692322052999, 65.681072404583, 65.669822756167, 65.658573107751, 65.647323459334, 65.636073810918, 65.624824162502, 65.613574514086, 65.602324865669, 65.591075217253, 65.579825568837, 65.568575920420, 65.557326272004, 65.546076623588, 65.534826975171, 65.523577326755, 65.512327678338, 65.501078029922, 65.489828381505, 65.478578733089, 65.467329084672, 65.456079436255, 65.444829787839, 65.433580139422, 65.422330491005, 65.411080842588, 65.399831194172, 65.388581545755, 65.377331897338, 65.366082248921, 65.354832600504, 65.343582952087, 65.332333303670, 65.321083655253, 65.309834006837, 65.298584358419, 65.287334710002, 65.276085061585, 65.264835413168, 65.253585764751, 65.242336116334, 65.231086467917, 65.219836819500, 65.208587171082, 65.197337522665, 65.186087874248, 65.174838225830, 65.163588577413, 65.152338928996, 65.141089280578, 65.129839632161, 65.118589983744, 65.107340335326, 65.096090686909, 65.084841038491, 65.073591390073, 65.062341741656, 65.051092093238, 65.039842444821, 65.028592796403, 65.017343147985, 65.006093499568, 64.994843851150, 64.983594202732, 64.972344554314, 64.961094905897, 64.949845257479, 64.938595609061, 64.927345960643, 64.916096312225, 64.904846663807, 64.893597015389, 64.882347366971, 64.871097718553, 64.859848070135, 64.848598421717, 64.837348773299, 64.826099124881, 64.814849476463, 64.803599828045, 64.792350179626, 64.781100531208, 64.769850882790, 64.758601234372, 64.747351585953, 64.736101937535, 64.724852289117, 64.713602640698, 64.702352992280, 64.691103343861, 64.679853695443, 64.668604047025, 64.657354398606, 64.646104750188, 64.634855101769, 64.623605453350, 64.612355804932, 64.601106156513, 64.589856508095, 64.578606859676, 64.567357211257, 64.556107562839, 64.544857914420, 64.533608266001, 64.522358617582, 64.511108969163, 64.499859320745, 64.488609672326, 64.477360023907, 64.466110375488, 64.454860727069, 64.443611078650, 64.432361430231, 64.421111781812, 64.409862133393, 64.398612484974, 64.387362836555, 64.376113188136, 64.364863539717, 64.353613891297, 64.342364242878, 64.331114594459, 64.319864946040, 64.308615297621, 64.297365649201, 64.286116000782, 64.274866352363, 64.263616703943, 64.252367055524, 64.241117407105, 64.229867758685, 64.218618110266, 64.207368461846, 64.196118813427, 64.184869165007, 64.173619516588, 64.162369868168, 64.151120219749, 64.139870571329, 64.128620922909, 64.117371274490, 64.106121626070, 64.094871977650, 64.083622329231, 64.072372680811, 64.061123032391, 64.049873383971, 64.038623735552, 64.027374087132, 64.016124438712, 64.004874790292, 63.993625141872, 63.982375493452, 63.971125845032, 63.959876196612, 63.948626548192, 63.937376899772, 63.926127251352, 63.914877602932, 63.903627954512, 63.892378306092, 63.881128657672, 63.869879009252, 63.858629360831, 63.847379712411, 63.836130063991, 63.824880415571, 63.813630767150, 63.802381118730, 63.791131470310, 63.779881821890, 63.768632173469, 63.757382525049, 63.746132876628, 63.734883228208, 63.723633579788, 63.712383931367, 63.701134282947, 63.689884634526, 63.678634986106, 63.667385337685, 63.656135689264, 63.644886040844, 63.633636392423, 63.622386744002, 63.611137095582, 63.599887447161, 63.588637798740, 63.577388150320, 63.566138501899, 63.554888853478, 63.543639205057, 63.532389556636, 63.521139908216, 63.509890259795, 63.498640611374, 63.487390962953, 63.476141314532, 63.464891666111, 63.453642017690, 63.442392369269, 63.431142720848, 63.419893072427, 63.408643424006, 63.397393775585, 63.386144127164, 63.374894478743, 63.363644830322, 63.352395181900, 63.341145533479, 63.329895885058, 63.318646236637, 63.307396588215, 63.296146939794, 63.284897291373, 63.273647642952, 63.262397994530, 63.251148346109, 63.239898697688, 63.228649049266, 63.217399400845, 63.206149752423, 63.194900104002, 63.183650455580, 63.172400807159, 63.161151158737, 63.149901510316, 63.138651861894, 63.127402213473, 63.116152565051, 63.104902916629, 63.093653268208, 63.082403619786, 63.071153971364, 63.059904322943, 63.048654674521, 63.037405026099, 63.026155377677, 63.014905729255, 63.003656080834, 62.992406432412, 62.981156783990, 62.969907135568, 62.958657487146, 62.947407838724, 62.936158190302, 62.924908541880, 62.913658893458, 62.902409245036, 62.891159596614, 62.879909948192, 62.868660299770, 62.857410651348, 62.846161002926, 62.834911354504, 62.823661706082, 62.812412057660, 62.801162409237, 62.789912760815, 62.778663112393, 62.767413463971, 62.756163815548, 62.744914167126, 62.733664518704, 62.722414870282, 62.711165221859, 62.699915573437, 62.688665925014, 62.677416276592, 62.666166628170, 62.654916979747, 62.643667331325, 62.632417682902, 62.621168034480, 62.609918386057, 62.598668737635, 62.587419089212, 62.576169440790, 62.564919792367, 62.553670143944, 62.542420495522, 62.531170847099, 62.519921198676, 62.508671550254, 62.497421901831, 62.486172253408, 62.474922604985, 62.463672956563, 62.452423308140, 62.441173659717, 62.429924011294, 62.418674362871, 62.407424714448, 62.396175066026, 62.384925417603, 62.373675769180, 62.362426120757, 62.351176472334, 62.339926823911, 62.328677175488, 62.317427527065, 62.306177878642, 62.294928230219, 62.283678581796, 62.272428933372, 62.261179284949, 62.249929636526, 62.238679988103, 62.227430339680, 62.216180691257, 62.204931042833, 62.193681394410, 62.182431745987, 62.171182097564, 62.159932449140, 62.148682800717, 62.137433152294, 62.126183503870, 62.114933855447, 62.103684207024, 62.092434558600, 62.081184910177, 62.069935261753, 62.058685613330, 62.047435964906, 62.036186316483, 62.024936668059, 62.013687019636, 62.002437371212, 61.991187722789, 61.979938074365, 61.968688425942, 61.957438777518, 61.946189129094, 61.934939480671, 61.923689832247, 61.912440183823, 61.901190535400, 61.889940886976, 61.878691238552, 61.867441590128, 61.856191941705, 61.844942293281, 61.833692644857, 61.822442996433, 61.811193348009, 61.799943699585, 61.788694051161, 61.777444402737, 61.766194754314, 61.754945105890, 61.743695457466, 61.732445809042, 61.721196160618, 61.709946512194, 61.698696863770, 61.687447215346, 61.676197566921, 61.664947918497, 61.653698270073, 61.642448621649, 61.631198973225, 61.619949324801, 61.608699676377, 61.597450027952, 61.586200379528, 61.574950731104, 61.563701082680, 61.552451434255, 61.541201785831, 61.529952137407, 61.518702488982, 61.507452840558, 61.496203192134, 61.484953543709, 61.473703895285, 61.462454246861, 61.451204598436, 61.439954950012, 61.428705301587, 61.417455653163, 61.406206004738, 61.394956356314, 61.383706707889, 61.372457059465, 61.361207411040, 61.349957762616, 61.338708114191, 61.327458465766, 61.316208817342, 61.304959168917, 61.293709520492, 61.282459872068, 61.271210223643, 61.259960575218, 61.248710926794, 61.237461278369, 61.226211629944, 61.214961981519, 61.203712333094, 61.192462684670, 61.181213036245, 61.169963387820, 61.158713739395, 61.147464090970, 61.136214442545, 61.124964794120, 61.113715145695, 61.102465497270, 61.091215848845, 61.079966200420, 61.068716551995, 61.057466903570, 61.046217255145, 61.034967606720, 61.023717958295, 61.012468309870, 61.001218661445, 60.989969013020, 60.978719364595, 60.967469716170, 60.956220067744, 60.944970419319, 60.933720770894, 60.922471122469, 60.911221474044, 60.899971825618, 60.888722177193, 60.877472528768, 60.866222880342, 60.854973231917, 60.843723583492, 60.832473935066, 60.821224286641, 60.809974638216, 60.798724989790, 60.787475341365, 60.776225692939, 60.764976044514, 60.753726396089, 60.742476747663, 60.731227099238, 60.719977450812, 60.708727802386, 60.697478153961, 60.686228505535, 60.674978857110, 60.663729208684, 60.652479560259, 60.641229911833, 60.629980263407, 60.618730614982, 60.607480966556, 60.596231318130, 60.584981669705, 60.573732021279, 60.562482372853, 60.551232724427, 60.539983076002, 60.528733427576, 60.517483779150, 60.506234130724, 60.494984482298, 60.483734833873, 60.472485185447, 60.461235537021, 60.449985888595, 60.438736240169, 60.427486591743, 60.416236943317, 60.404987294891, 60.393737646465, 60.382487998039, 60.371238349613, 60.359988701187, 60.348739052761, 60.337489404335, 60.326239755909, 60.314990107483, 60.303740459057, 60.292490810631, 60.281241162205, 60.269991513778, 60.258741865352, 60.247492216926, 60.236242568500, 60.224992920074, 60.213743271647, 60.202493623221, 60.191243974795, 60.179994326369, 60.168744677942, 60.157495029516, 60.146245381090, 60.134995732663, 60.123746084237, 60.112496435811, 60.101246787384, 60.089997138958, 60.078747490532, 60.067497842105, 60.056248193679, 60.044998545252, 60.033748896826, 60.022499248399, 60.011249599973, 59.999999951546, 59.988750303120, 59.977500654693, 59.966251006267, 59.955001357840, 59.943751709414, 59.932502060987, 59.921252412560, 59.910002764134, 59.898753115707, 59.887503467280, 59.876253818854, 59.865004170427, 59.853754522000, 59.842504873574, 59.831255225147, 59.820005576720, 59.808755928293, 59.797506279867, 59.786256631440, 59.775006983013, 59.763757334586, 59.752507686159, 59.741258037733, 59.730008389306, 59.718758740879, 59.707509092452, 59.696259444025, 59.685009795598, 59.673760147171, 59.662510498744, 59.651260850317, 59.640011201890, 59.628761553463, 59.617511905036, 59.606262256609, 59.595012608182, 59.583762959755, 59.572513311328, 59.561263662901, 59.550014014474, 59.538764366047, 59.527514717620, 59.516265069193, 59.505015420765, 59.493765772338, 59.482516123911, 59.471266475484, 59.460016827057, 59.448767178630, 59.437517530202, 59.426267881775, 59.415018233348, 59.403768584920, 59.392518936493, 59.381269288066, 59.370019639639, 59.358769991211, 59.347520342784, 59.336270694357, 59.325021045929, 59.313771397502, 59.302521749074, 59.291272100647, 59.280022452220, 59.268772803792, 59.257523155365, 59.246273506937, 59.235023858510, 59.223774210082, 59.212524561655, 59.201274913227, 59.190025264800, 59.178775616372, 59.167525967944, 59.156276319517, 59.145026671089, 59.133777022662, 59.122527374234, 59.111277725806, 59.100028077379, 59.088778428951, 59.077528780523, 59.066279132096, 59.055029483668, 59.043779835240, 59.032530186813, 59.021280538385, 59.010030889957, 58.998781241529, 58.987531593101, 58.976281944674, 58.965032296246, 58.953782647818, 58.942532999390, 58.931283350962, 58.920033702534, 58.908784054107, 58.897534405679, 58.886284757251, 58.875035108823, 58.863785460395, 58.852535811967, 58.841286163539, 58.830036515111, 58.818786866683, 58.807537218255, 58.796287569827, 58.785037921399, 58.773788272971, 58.762538624543, 58.751288976115, 58.740039327687, 58.728789679259, 58.717540030831, 58.706290382402, 58.695040733974, 58.683791085546, 58.672541437118, 58.661291788690, 58.650042140262, 58.638792491833, 58.627542843405, 58.616293194977, 58.605043546549, 58.593793898120, 58.582544249692, 58.571294601264, 58.560044952836, 58.548795304407, 58.537545655979, 58.526296007551, 58.515046359122, 58.503796710694, 58.492547062266, 58.481297413837, 58.470047765409, 58.458798116980, 58.447548468552, 58.436298820124, 58.425049171695, 58.413799523267, 58.402549874838, 58.391300226410, 58.380050577981, 58.368800929553, 58.357551281124, 58.346301632696, 58.335051984267, 58.323802335839, 58.312552687410, 58.301303038981, 58.290053390553, 58.278803742124, 58.267554093696, 58.256304445267, 58.245054796838, 58.233805148410, 58.222555499981, 58.211305851552, 58.200056203124, 58.188806554695, 58.177556906266, 58.166307257837, 58.155057609409, 58.143807960980, 58.132558312551, 58.121308664122, 58.110059015694, 58.098809367265, 58.087559718836, 58.076310070407, 58.065060421978, 58.053810773549, 58.042561125120, 58.031311476692, 58.020061828263, 58.008812179834, 57.997562531405, 57.986312882976, 57.975063234547, 57.963813586118, 57.952563937689, 57.941314289260, 57.930064640831, 57.918814992402, 57.907565343973, 57.896315695544, 57.885066047115, 57.873816398686, 57.862566750257, 57.851317101828, 57.840067453399, 57.828817804970, 57.817568156540, 57.806318508111, 57.795068859682, 57.783819211253, 57.772569562824, 57.761319914395, 57.750070265965, 57.738820617536, 57.727570969107, 57.716321320678, 57.705071672249, 57.693822023819, 57.682572375390, 57.671322726961, 57.660073078532, 57.648823430102, 57.637573781673, 57.626324133244, 57.615074484814, 57.603824836385, 57.592575187956, 57.581325539526, 57.570075891097, 57.558826242667, 57.547576594238, 57.536326945809, 57.525077297379, 57.513827648950, 57.502578000520, 57.491328352091, 57.480078703661, 57.468829055232, 57.457579406802, 57.446329758373, 57.435080109943, 57.423830461514, 57.412580813084, 57.401331164655, 57.390081516225, 57.378831867796, 57.367582219366, 57.356332570936, 57.345082922507, 57.333833274077, 57.322583625647, 57.311333977218, 57.300084328788, 57.288834680358, 57.277585031929, 57.266335383499, 57.255085735069, 57.243836086640, 57.232586438210, 57.221336789780, 57.210087141350, 57.198837492921, 57.187587844491, 57.176338196061, 57.165088547631, 57.153838899202, 57.142589250772, 57.131339602342, 57.120089953912, 57.108840305482, 57.097590657052, 57.086341008622, 57.075091360193, 57.063841711763, 57.052592063333, 57.041342414903, 57.030092766473, 57.018843118043, 57.007593469613, 56.996343821183, 56.985094172753, 56.973844524323, 56.962594875893, 56.951345227463, 56.940095579033, 56.928845930603, 56.917596282173, 56.906346633743, 56.895096985313, 56.883847336883, 56.872597688453, 56.861348040023, 56.850098391592, 56.838848743162, 56.827599094732, 56.816349446302, 56.805099797872, 56.793850149442, 56.782600501011, 56.771350852581, 56.760101204151, 56.748851555721, 56.737601907291, 56.726352258860, 56.715102610430, 56.703852962000, 56.692603313570, 56.681353665139, 56.670104016709, 56.658854368279, 56.647604719849, 56.636355071418, 56.625105422988, 56.613855774558, 56.602606126127, 56.591356477697, 56.580106829266, 56.568857180836, 56.557607532406, 56.546357883975, 56.535108235545, 56.523858587114, 56.512608938684, 56.501359290254, 56.490109641823, 56.478859993393, 56.467610344962, 56.456360696532, 56.445111048101, 56.433861399671, 56.422611751240, 56.411362102810, 56.400112454379, 56.388862805948, 56.377613157518, 56.366363509087, 56.355113860657, 56.343864212226, 56.332614563796, 56.321364915365, 56.310115266934, 56.298865618504, 56.287615970073, 56.276366321642, 56.265116673212, 56.253867024781, 56.242617376350, 56.231367727920, 56.220118079489, 56.208868431058, 56.197618782627, 56.186369134197, 56.175119485766, 56.163869837335, 56.152620188904, 56.141370540474, 56.130120892043, 56.118871243612, 56.107621595181, 56.096371946750, 56.085122298319, 56.073872649889, 56.062623001458, 56.051373353027, 56.040123704596, 56.028874056165, 56.017624407734, 56.006374759303, 55.995125110872, 55.983875462441, 55.972625814011, 55.961376165580, 55.950126517149, 55.938876868718, 55.927627220287, 55.916377571856, 55.905127923425, 55.893878274994, 55.882628626563, 55.871378978132, 55.860129329701, 55.848879681270, 55.837630032838, 55.826380384407, 55.815130735976, 55.803881087545, 55.792631439114, 55.781381790683, 55.770132142252, 55.758882493821, 55.747632845390, 55.736383196958, 55.725133548527, 55.713883900096, 55.702634251665, 55.691384603234, 55.680134954803, 55.668885306371, 55.657635657940, 55.646386009509, 55.635136361078, 55.623886712646, 55.612637064215, 55.601387415784, 55.590137767353, 55.578888118921, 55.567638470490, 55.556388822059, 55.545139173627, 55.533889525196, 55.522639876765, 55.511390228333, 55.500140579902, 55.488890931471, 55.477641283039, 55.466391634608, 55.455141986176, 55.443892337745, 55.432642689314, 55.421393040882, 55.410143392451, 55.398893744019, 55.387644095588, 55.376394447156, 55.365144798725, 55.353895150293, 55.342645501862, 55.331395853430, 55.320146204999, 55.308896556567, 55.297646908136, 55.286397259704, 55.275147611273, 55.263897962841, 55.252648314410, 55.241398665978, 55.230149017546, 55.218899369115, 55.207649720683, 55.196400072252, 55.185150423820, 55.173900775388, 55.162651126957, 55.151401478525, 55.140151830093, 55.128902181662, 55.117652533230, 55.106402884798, 55.095153236367, 55.083903587935, 55.072653939503, 55.061404291071, 55.050154642640, 55.038904994208, 55.027655345776, 55.016405697344, 55.005156048913, 54.993906400481, 54.982656752049, 54.971407103617, 54.960157455185, 54.948907806754, 54.937658158322, 54.926408509890, 54.915158861458, 54.903909213026, 54.892659564594, 54.881409916162, 54.870160267730, 54.858910619299, 54.847660970867, 54.836411322435, 54.825161674003, 54.813912025571, 54.802662377139, 54.791412728707, 54.780163080275, 54.768913431843, 54.757663783411, 54.746414134979, 54.735164486547, 54.723914838115, 54.712665189683, 54.701415541251, 54.690165892819, 54.678916244387, 54.667666595955, 54.656416947523, 54.645167299091, 54.633917650659, 54.622668002226, 54.611418353794, 54.600168705362, 54.588919056930, 54.577669408498, 54.566419760066, 54.555170111634, 54.543920463202, 54.532670814769, 54.521421166337, 54.510171517905, 54.498921869473, 54.487672221041, 54.476422572608, 54.465172924176, 54.453923275744, 54.442673627312, 54.431423978879, 54.420174330447, 54.408924682015, 54.397675033583, 54.386425385150, 54.375175736718, 54.363926088286, 54.352676439853, 54.341426791421, 54.330177142989, 54.318927494557, 54.307677846124, 54.296428197692, 54.285178549259, 54.273928900827, 54.262679252395, 54.251429603962, 54.240179955530, 54.228930307097, 54.217680658665, 54.206431010233, 54.195181361800, 54.183931713368, 54.172682064935, 54.161432416503, 54.150182768070, 54.138933119638, 54.127683471205, 54.116433822773, 54.105184174340, 54.093934525908, 54.082684877475, 54.071435229043, 54.060185580610, 54.048935932178, 54.037686283745, 54.026436635313, 54.015186986880, 54.003937338448, 53.992687690015, 53.981438041582, 53.970188393150, 53.958938744717, 53.947689096284, 53.936439447852, 53.925189799419, 53.913940150987, 53.902690502554, 53.891440854121, 53.880191205689, 53.868941557256, 53.857691908823, 53.846442260391, 53.835192611958, 53.823942963525, 53.812693315092, 53.801443666660, 53.790194018227, 53.778944369794, 53.767694721361, 53.756445072929, 53.745195424496, 53.733945776063, 53.722696127630, 53.711446479197, 53.700196830765, 53.688947182332, 53.677697533899, 53.666447885466, 53.655198237033, 53.643948588600, 53.632698940168, 53.621449291735, 53.610199643302, 53.598949994869, 53.587700346436, 53.576450698003, 53.565201049570, 53.553951401137, 53.542701752704, 53.531452104271, 53.520202455839, 53.508952807406, 53.497703158973, 53.486453510540, 53.475203862107, 53.463954213674, 53.452704565241, 53.441454916808, 53.430205268375, 53.418955619942, 53.407705971509, 53.396456323076, 53.385206674643, 53.373957026210, 53.362707377776, 53.351457729343, 53.340208080910, 53.328958432477, 53.317708784044, 53.306459135611, 53.295209487178, 53.283959838745, 53.272710190312, 53.261460541879, 53.250210893445, 53.238961245012, 53.227711596579, 53.216461948146, 53.205212299713, 53.193962651280, 53.182713002846, 53.171463354413, 53.160213705980, 53.148964057547, 53.137714409114, 53.126464760680, 53.115215112247, 53.103965463814, 53.092715815381, 53.081466166947, 53.070216518514, 53.058966870081, 53.047717221648, 53.036467573214, 53.025217924781, 53.013968276348, 53.002718627914, 52.991468979481, 52.980219331048, 52.968969682614, 52.957720034181, 52.946470385748, 52.935220737314, 52.923971088881, 52.912721440448, 52.901471792014, 52.890222143581, 52.878972495147, 52.867722846714, 52.856473198281, 52.845223549847, 52.833973901414, 52.822724252980, 52.811474604547, 52.800224956113, 52.788975307680, 52.777725659246, 52.766476010813, 52.755226362379, 52.743976713946, 52.732727065512, 52.721477417079, 52.710227768645, 52.698978120212, 52.687728471778, 52.676478823345, 52.665229174911, 52.653979526478, 52.642729878044, 52.631480229611, 52.620230581177, 52.608980932743, 52.597731284310, 52.586481635876, 52.575231987443, 52.563982339009, 52.552732690575, 52.541483042142, 52.530233393708, 52.518983745274, 52.507734096841, 52.496484448407, 52.485234799973, 52.473985151540, 52.462735503106, 52.451485854672, 52.440236206239, 52.428986557805, 52.417736909371, 52.406487260938, 52.395237612504, 52.383987964070, 52.372738315636, 52.361488667203, 52.350239018769, 52.338989370335, 52.327739721901, 52.316490073467, 52.305240425034, 52.293990776600, 52.282741128166, 52.271491479732, 52.260241831298, 52.248992182865, 52.237742534431, 52.226492885997, 52.215243237563, 52.203993589129, 52.192743940695, 52.181494292262, 52.170244643828, 52.158994995394, 52.147745346960, 52.136495698526, 52.125246050092, 52.113996401658, 52.102746753224, 52.091497104790, 52.080247456356, 52.068997807922, 52.057748159488, 52.046498511054, 52.035248862620, 52.023999214187, 52.012749565753, 52.001499917319, 51.990250268885, 51.979000620451, 51.967750972017, 51.956501323583, 51.945251675148, 51.934002026714, 51.922752378280, 51.911502729846, 51.900253081412, 51.889003432978, 51.877753784544, 51.866504136110, 51.855254487676, 51.844004839242, 51.832755190808, 51.821505542374, 51.810255893940, 51.799006245505, 51.787756597071, 51.776506948637, 51.765257300203, 51.754007651769, 51.742758003335, 51.731508354901, 51.720258706466, 51.709009058032, 51.697759409598, 51.686509761164, 51.675260112730, 51.664010464295, 51.652760815861, 51.641511167427, 51.630261518993, 51.619011870559, 51.607762222124, 51.596512573690, 51.585262925256, 51.574013276822, 51.562763628387, 51.551513979953, 51.540264331519, 51.529014683084, 51.517765034650, 51.506515386216, 51.495265737782, 51.484016089347, 51.472766440913, 51.461516792479, 51.450267144044, 51.439017495610, 51.427767847176, 51.416518198741, 51.405268550307, 51.394018901872, 51.382769253438, 51.371519605004, 51.360269956569, 51.349020308135, 51.337770659700, 51.326521011266, 51.315271362832, 51.304021714397, 51.292772065963, 51.281522417528, 51.270272769094, 51.259023120659, 51.247773472225, 51.236523823790, 51.225274175356, 51.214024526921, 51.202774878487, 51.191525230052, 51.180275581618, 51.169025933183, 51.157776284749, 51.146526636314, 51.135276987880, 51.124027339445, 51.112777691011, 51.101528042576, 51.090278394142, 51.079028745707, 51.067779097273, 51.056529448838, 51.045279800403, 51.034030151969, 51.022780503534, 51.011530855100, 51.000281206665, 50.989031558230, 50.977781909796, 50.966532261361, 50.955282612926, 50.944032964492, 50.932783316057, 50.921533667622, 50.910284019188, 50.899034370753, 50.887784722318, 50.876535073884, 50.865285425449, 50.854035777014, 50.842786128580, 50.831536480145, 50.820286831710, 50.809037183276, 50.797787534841, 50.786537886406, 50.775288237971, 50.764038589537, 50.752788941102, 50.741539292667, 50.730289644232, 50.719039995797, 50.707790347363, 50.696540698928, 50.685291050493, 50.674041402058, 50.662791753623, 50.651542105189, 50.640292456754, 50.629042808319, 50.617793159884, 50.606543511449, 50.595293863014, 50.584044214580, 50.572794566145, 50.561544917710, 50.550295269275, 50.539045620840, 50.527795972405, 50.516546323970, 50.505296675535, 50.494047027100, 50.482797378666, 50.471547730231, 50.460298081796, 50.449048433361, 50.437798784926, 50.426549136491, 50.415299488056, 50.404049839621, 50.392800191186, 50.381550542751, 50.370300894316, 50.359051245881, 50.347801597446, 50.336551949011, 50.325302300576, 50.314052652141, 50.302803003706, 50.291553355271, 50.280303706836, 50.269054058401, 50.257804409966, 50.246554761531, 50.235305113096, 50.224055464661, 50.212805816226, 50.201556167791, 50.190306519355, 50.179056870920, 50.167807222485, 50.156557574050, 50.145307925615, 50.134058277180, 50.122808628745, 50.111558980310, 50.100309331875, 50.089059683439, 50.077810035004, 50.066560386569, 50.055310738134, 50.044061089699, 50.032811441264, 50.021561792828, 50.010312144393, 49.999062495958, 49.987812847523, 49.976563199088, 49.965313550653, 49.954063902217, 49.942814253782, 49.931564605347, 49.920314956912, 49.909065308476, 49.897815660041, 49.886566011606, 49.875316363171, 49.864066714735, 49.852817066300, 49.841567417865, 49.830317769430, 49.819068120994, 49.807818472559, 49.796568824124, 49.785319175688, 49.774069527253, 49.762819878818, 49.751570230382, 49.740320581947, 49.729070933512, 49.717821285076, 49.706571636641, 49.695321988206, 49.684072339770, 49.672822691335, 49.661573042900, 49.650323394464, 49.639073746029, 49.627824097593, 49.616574449158, 49.605324800723, 49.594075152287, 49.582825503852, 49.571575855416, 49.560326206981, 49.549076558545, 49.537826910110, 49.526577261675, 49.515327613239, 49.504077964804, 49.492828316368, 49.481578667933, 49.470329019497, 49.459079371062, 49.447829722626, 49.436580074191, 49.425330425755, 49.414080777320, 49.402831128884, 49.391581480449, 49.380331832013, 49.369082183578, 49.357832535142, 49.346582886707, 49.335333238271, 49.324083589836, 49.312833941400, 49.301584292964, 49.290334644529, 49.279084996093, 49.267835347658, 49.256585699222, 49.245336050787, 49.234086402351, 49.222836753915, 49.211587105480, 49.200337457044, 49.189087808608, 49.177838160173, 49.166588511737, 49.155338863302, 49.144089214866, 49.132839566430, 49.121589917995, 49.110340269559, 49.099090621123, 49.087840972688, 49.076591324252, 49.065341675816, 49.054092027380, 49.042842378945, 49.031592730509, 49.020343082073, 49.009093433638, 48.997843785202, 48.986594136766, 48.975344488330, 48.964094839895, 48.952845191459, 48.941595543023, 48.930345894587, 48.919096246152, 48.907846597716, 48.896596949280, 48.885347300844, 48.874097652409, 48.862848003973, 48.851598355537, 48.840348707101, 48.829099058665, 48.817849410230, 48.806599761794, 48.795350113358, 48.784100464922, 48.772850816486, 48.761601168050, 48.750351519615, 48.739101871179, 48.727852222743, 48.716602574307, 48.705352925871, 48.694103277435, 48.682853628999, 48.671603980563, 48.660354332128, 48.649104683692, 48.637855035256, 48.626605386820, 48.615355738384, 48.604106089948, 48.592856441512, 48.581606793076, 48.570357144640, 48.559107496204, 48.547857847768, 48.536608199332, 48.525358550896, 48.514108902460, 48.502859254024, 48.491609605589, 48.480359957153, 48.469110308717, 48.457860660281, 48.446611011845, 48.435361363409, 48.424111714973, 48.412862066537, 48.401612418100, 48.390362769664, 48.379113121228, 48.367863472792, 48.356613824356, 48.345364175920, 48.334114527484, 48.322864879048, 48.311615230612, 48.300365582176, 48.289115933740, 48.277866285304, 48.266616636868, 48.255366988432, 48.244117339996, 48.232867691560, 48.221618043123, 48.210368394687, 48.199118746251, 48.187869097815, 48.176619449379, 48.165369800943, 48.154120152507, 48.142870504070, 48.131620855634, 48.120371207198, 48.109121558762, 48.097871910326, 48.086622261890, 48.075372613453, 48.064122965017, 48.052873316581, 48.041623668145, 48.030374019709, 48.019124371273, 48.007874722836, 47.996625074400, 47.985375425964, 47.974125777528, 47.962876129091, 47.951626480655, 47.940376832219, 47.929127183783, 47.917877535346, 47.906627886910, 47.895378238474, 47.884128590038, 47.872878941601, 47.861629293165, 47.850379644729, 47.839129996292, 47.827880347856, 47.816630699420, 47.805381050984, 47.794131402547, 47.782881754111, 47.771632105675, 47.760382457238, 47.749132808802, 47.737883160366, 47.726633511929, 47.715383863493, 47.704134215057, 47.692884566620, 47.681634918184, 47.670385269747, 47.659135621311, 47.647885972875, 47.636636324438, 47.625386676002, 47.614137027566, 47.602887379129, 47.591637730693, 47.580388082256, 47.569138433820, 47.557888785383, 47.546639136947, 47.535389488511, 47.524139840074, 47.512890191638, 47.501640543201, 47.490390894765, 47.479141246328, 47.467891597892, 47.456641949455, 47.445392301019, 47.434142652582, 47.422893004146, 47.411643355710, 47.400393707273, 47.389144058837, 47.377894410400, 47.366644761963, 47.355395113527, 47.344145465090, 47.332895816654, 47.321646168217, 47.310396519781, 47.299146871344, 47.287897222908, 47.276647574471, 47.265397926035, 47.254148277598, 47.242898629162, 47.231648980725, 47.220399332288, 47.209149683852, 47.197900035415, 47.186650386979, 47.175400738542, 47.164151090105, 47.152901441669, 47.141651793232, 47.130402144796, 47.119152496359, 47.107902847922, 47.096653199486, 47.085403551049, 47.074153902612, 47.062904254176, 47.051654605739, 47.040404957302, 47.029155308866, 47.017905660429, 47.006656011993, 46.995406363556, 46.984156715119, 46.972907066682, 46.961657418246, 46.950407769809, 46.939158121372, 46.927908472936, 46.916658824499, 46.905409176062, 46.894159527626, 46.882909879189, 46.871660230752, 46.860410582315, 46.849160933879, 46.837911285442, 46.826661637005, 46.815411988568, 46.804162340132, 46.792912691695, 46.781663043258, 46.770413394821, 46.759163746384, 46.747914097948, 46.736664449511, 46.725414801074, 46.714165152637, 46.702915504201, 46.691665855764, 46.680416207327, 46.669166558890, 46.657916910453, 46.646667262016, 46.635417613580, 46.624167965143, 46.612918316706, 46.601668668269, 46.590419019832, 46.579169371395, 46.567919722958, 46.556670074522, 46.545420426085, 46.534170777648, 46.522921129211, 46.511671480774, 46.500421832337, 46.489172183900, 46.477922535463, 46.466672887026, 46.455423238590, 46.444173590153, 46.432923941716, 46.421674293279, 46.410424644842, 46.399174996405, 46.387925347968, 46.376675699531, 46.365426051094, 46.354176402657, 46.342926754220, 46.331677105783, 46.320427457346, 46.309177808909, 46.297928160472, 46.286678512035, 46.275428863598, 46.264179215161, 46.252929566724, 46.241679918287, 46.230430269850, 46.219180621413, 46.207930972976, 46.196681324539, 46.185431676102, 46.174182027665, 46.162932379228, 46.151682730791, 46.140433082354, 46.129183433917, 46.117933785480, 46.106684137043, 46.095434488606, 46.084184840169, 46.072935191732, 46.061685543295, 46.050435894858, 46.039186246421, 46.027936597983, 46.016686949546, 46.005437301109, 45.994187652672, 45.982938004235, 45.971688355798, 45.960438707361, 45.949189058924, 45.937939410487, 45.926689762049, 45.915440113612, 45.904190465175, 45.892940816738, 45.881691168301, 45.870441519864, 45.859191871427, 45.847942222989, 45.836692574552, 45.825442926115, 45.814193277678, 45.802943629241, 45.791693980804, 45.780444332366, 45.769194683929, 45.757945035492, 45.746695387055, 45.735445738618, 45.724196090180, 45.712946441743, 45.701696793306, 45.690447144869, 45.679197496431, 45.667947847994, 45.656698199557, 45.645448551120, 45.634198902682, 45.622949254245, 45.611699605808, 45.600449957371, 45.589200308933, 45.577950660496, 45.566701012059, 45.555451363622, 45.544201715184, 45.532952066747, 45.521702418310, 45.510452769872, 45.499203121435, 45.487953472998, 45.476703824560, 45.465454176123, 45.454204527686, 45.442954879249, 45.431705230811, 45.420455582374, 45.409205933936, 45.397956285499, 45.386706637062, 45.375456988624, 45.364207340187, 45.352957691750, 45.341708043312, 45.330458394875, 45.319208746438, 45.307959098000, 45.296709449563, 45.285459801125, 45.274210152688, 45.262960504251, 45.251710855813, 45.240461207376, 45.229211558938, 45.217961910501, 45.206712262064, 45.195462613626, 45.184212965189, 45.172963316751, 45.161713668314, 45.150464019876, 45.139214371439, 45.127964723002, 45.116715074564, 45.105465426127, 45.094215777689, 45.082966129252, 45.071716480814, 45.060466832377, 45.049217183939, 45.037967535502, 45.026717887064, 45.015468238627, 45.004218590189, 44.992968941752, 44.981719293314, 44.970469644877, 44.959219996439, 44.947970348002, 44.936720699564, 44.925471051127, 44.914221402689, 44.902971754252, 44.891722105814, 44.880472457377, 44.869222808939, 44.857973160502, 44.846723512064, 44.835473863626, 44.824224215189, 44.812974566751, 44.801724918314, 44.790475269876, 44.779225621439, 44.767975973001, 44.756726324563, 44.745476676126, 44.734227027688, 44.722977379251, 44.711727730813, 44.700478082375, 44.689228433938, 44.677978785500, 44.666729137062, 44.655479488625, 44.644229840187, 44.632980191750, 44.621730543312, 44.610480894874, 44.599231246437, 44.587981597999, 44.576731949561, 44.565482301124, 44.554232652686, 44.542983004248, 44.531733355811, 44.520483707373, 44.509234058935, 44.497984410498, 44.486734762060, 44.475485113622, 44.464235465184, 44.452985816747, 44.441736168309, 44.430486519871, 44.419236871434, 44.407987222996, 44.396737574558, 44.385487926120, 44.374238277683, 44.362988629245, 44.351738980807, 44.340489332370, 44.329239683932, 44.317990035494, 44.306740387056, 44.295490738618, 44.284241090181, 44.272991441743, 44.261741793305, 44.250492144867, 44.239242496430, 44.227992847992, 44.216743199554, 44.205493551116, 44.194243902678, 44.182994254241, 44.171744605803, 44.160494957365, 44.149245308927, 44.137995660489, 44.126746012052, 44.115496363614, 44.104246715176, 44.092997066738, 44.081747418300, 44.070497769862, 44.059248121425, 44.047998472987, 44.036748824549, 44.025499176111, 44.014249527673, 44.002999879235, 43.991750230797, 43.980500582359, 43.969250933922, 43.958001285484, 43.946751637046, 43.935501988608, 43.924252340170, 43.913002691732, 43.901753043294, 43.890503394856, 43.879253746418, 43.868004097981, 43.856754449543, 43.845504801105, 43.834255152667, 43.823005504229, 43.811755855791, 43.800506207353, 43.789256558915, 43.778006910477, 43.766757262039, 43.755507613601, 43.744257965163, 43.733008316725, 43.721758668287, 43.710509019849, 43.699259371411, 43.688009722973, 43.676760074535, 43.665510426097, 43.654260777659, 43.643011129221, 43.631761480783, 43.620511832345, 43.609262183907, 43.598012535469, 43.586762887031, 43.575513238593, 43.564263590155, 43.553013941717, 43.541764293279, 43.530514644841, 43.519264996403, 43.508015347965, 43.496765699527, 43.485516051089, 43.474266402651, 43.463016754213, 43.451767105775, 43.440517457337, 43.429267808899, 43.418018160461, 43.406768512023, 43.395518863584, 43.384269215146, 43.373019566708, 43.361769918270, 43.350520269832, 43.339270621394, 43.328020972956, 43.316771324518, 43.305521676080, 43.294272027642, 43.283022379203, 43.271772730765, 43.260523082327, 43.249273433889, 43.238023785451, 43.226774137013, 43.215524488575, 43.204274840136, 43.193025191698, 43.181775543260, 43.170525894822, 43.159276246384, 43.148026597946, 43.136776949508, 43.125527301069, 43.114277652631, 43.103028004193, 43.091778355755, 43.080528707317, 43.069279058878, 43.058029410440, 43.046779762002, 43.035530113564, 43.024280465126, 43.013030816687, 43.001781168249, 42.990531519811, 42.979281871373, 42.968032222935, 42.956782574496, 42.945532926058, 42.934283277620, 42.923033629182, 42.911783980743, 42.900534332305, 42.889284683867, 42.878035035429, 42.866785386990, 42.855535738552, 42.844286090114, 42.833036441676, 42.821786793237, 42.810537144799, 42.799287496361, 42.788037847922, 42.776788199484, 42.765538551046, 42.754288902608, 42.743039254169, 42.731789605731, 42.720539957293, 42.709290308854, 42.698040660416, 42.686791011978, 42.675541363539, 42.664291715101, 42.653042066663, 42.641792418224, 42.630542769786, 42.619293121348, 42.608043472909, 42.596793824471, 42.585544176033, 42.574294527594, 42.563044879156, 42.551795230718, 42.540545582279, 42.529295933841, 42.518046285402, 42.506796636964, 42.495546988526, 42.484297340087, 42.473047691649, 42.461798043211, 42.450548394772, 42.439298746334, 42.428049097895, 42.416799449457, 42.405549801019, 42.394300152580, 42.383050504142, 42.371800855703, 42.360551207265, 42.349301558826, 42.338051910388, 42.326802261950, 42.315552613511, 42.304302965073, 42.293053316634, 42.281803668196, 42.270554019757, 42.259304371319, 42.248054722880, 42.236805074442, 42.225555426003, 42.214305777565, 42.203056129127, 42.191806480688, 42.180556832250, 42.169307183811, 42.158057535373, 42.146807886934, 42.135558238496, 42.124308590057, 42.113058941619, 42.101809293180, 42.090559644742, 42.079309996303, 42.068060347865, 42.056810699426, 42.045561050988, 42.034311402549, 42.023061754110, 42.011812105672, 42.000562457233, 41.989312808795, 41.978063160356, 41.966813511918, 41.955563863479, 41.944314215041, 41.933064566602, 41.921814918164, 41.910565269725, 41.899315621286, 41.888065972848, 41.876816324409, 41.865566675971, 41.854317027532, 41.843067379093, 41.831817730655, 41.820568082216, 41.809318433778, 41.798068785339, 41.786819136900, 41.775569488462, 41.764319840023, 41.753070191585, 41.741820543146, 41.730570894707, 41.719321246269, 41.708071597830, 41.696821949392, 41.685572300953, 41.674322652514, 41.663073004076, 41.651823355637, 41.640573707198, 41.629324058760, 41.618074410321, 41.606824761882, 41.595575113444, 41.584325465005, 41.573075816566, 41.561826168128, 41.550576519689, 41.539326871250, 41.528077222812, 41.516827574373, 41.505577925934, 41.494328277496, 41.483078629057, 41.471828980618, 41.460579332179, 41.449329683741, 41.438080035302, 41.426830386863, 41.415580738425, 41.404331089986, 41.393081441547, 41.381831793108, 41.370582144670, 41.359332496231, 41.348082847792, 41.336833199353, 41.325583550915, 41.314333902476, 41.303084254037, 41.291834605598, 41.280584957160, 41.269335308721, 41.258085660282, 41.246836011843, 41.235586363405, 41.224336714966, 41.213087066527, 41.201837418088, 41.190587769650, 41.179338121211, 41.168088472772, 41.156838824333, 41.145589175894, 41.134339527456, 41.123089879017, 41.111840230578, 41.100590582139, 41.089340933700, 41.078091285262, 41.066841636823, 41.055591988384, 41.044342339945, 41.033092691506, 41.021843043067, 41.010593394629, 40.999343746190, 40.988094097751, 40.976844449312, 40.965594800873, 40.954345152434, 40.943095503995, 40.931845855557, 40.920596207118, 40.909346558679, 40.898096910240, 40.886847261801, 40.875597613362, 40.864347964923, 40.853098316484, 40.841848668046, 40.830599019607, 40.819349371168, 40.808099722729, 40.796850074290, 40.785600425851, 40.774350777412, 40.763101128973, 40.751851480534, 40.740601832095, 40.729352183657, 40.718102535218, 40.706852886779, 40.695603238340, 40.684353589901, 40.673103941462, 40.661854293023, 40.650604644584, 40.639354996145, 40.628105347706, 40.616855699267, 40.605606050828, 40.594356402389, 40.583106753950, 40.571857105511, 40.560607457072, 40.549357808633, 40.538108160194, 40.526858511755, 40.515608863316, 40.504359214877, 40.493109566438, 40.481859917999, 40.470610269560, 40.459360621121, 40.448110972682, 40.436861324243, 40.425611675804, 40.414362027365, 40.403112378926, 40.391862730487, 40.380613082048, 40.369363433609, 40.358113785170, 40.346864136731, 40.335614488292, 40.324364839853, 40.313115191414, 40.301865542975, 40.290615894536, 40.279366246097, 40.268116597658, 40.256866949219, 40.245617300780, 40.234367652341, 40.223118003902, 40.211868355463, 40.200618707024, 40.189369058585, 40.178119410146, 40.166869761706, 40.155620113267, 40.144370464828, 40.133120816389, 40.121871167950, 40.110621519511, 40.099371871072, 40.088122222633, 40.076872574194, 40.065622925755, 40.054373277316, 40.043123628876, 40.031873980437, 40.020624331998, 40.009374683559, 39.998125035120, 39.986875386681, 39.975625738242, 39.964376089803, 39.953126441363, 39.941876792924, 39.930627144485, 39.919377496046, 39.908127847607, 39.896878199168, 39.885628550728, 39.874378902289, 39.863129253850, 39.851879605411, 39.840629956972, 39.829380308533, 39.818130660093, 39.806881011654, 39.795631363215, 39.784381714776, 39.773132066337, 39.761882417898, 39.750632769458, 39.739383121019, 39.728133472580, 39.716883824141, 39.705634175702, 39.694384527262, 39.683134878823, 39.671885230384, 39.660635581945, 39.649385933506, 39.638136285066, 39.626886636627, 39.615636988188, 39.604387339749, 39.593137691309, 39.581888042870, 39.570638394431, 39.559388745992, 39.548139097552, 39.536889449113, 39.525639800674, 39.514390152235, 39.503140503795, 39.491890855356, 39.480641206917, 39.469391558478, 39.458141910038, 39.446892261599, 39.435642613160, 39.424392964720, 39.413143316281, 39.401893667842, 39.390644019403, 39.379394370963, 39.368144722524, 39.356895074085, 39.345645425645, 39.334395777206, 39.323146128767, 39.311896480328, 39.300646831888, 39.289397183449, 39.278147535010, 39.266897886570, 39.255648238131, 39.244398589692, 39.233148941252, 39.221899292813, 39.210649644374, 39.199399995934, 39.188150347495, 39.176900699056, 39.165651050616, 39.154401402177, 39.143151753738, 39.131902105298, 39.120652456859, 39.109402808419, 39.098153159980, 39.086903511541, 39.075653863101, 39.064404214662, 39.053154566223, 39.041904917783, 39.030655269344, 39.019405620904, 39.008155972465, 38.996906324026, 38.985656675586, 38.974407027147, 38.963157378708, 38.951907730268, 38.940658081829, 38.929408433389, 38.918158784950, 38.906909136510, 38.895659488071, 38.884409839632, 38.873160191192, 38.861910542753, 38.850660894313, 38.839411245874, 38.828161597434, 38.816911948995, 38.805662300556, 38.794412652116, 38.783163003677, 38.771913355237, 38.760663706798, 38.749414058358, 38.738164409919, 38.726914761479, 38.715665113040, 38.704415464601, 38.693165816161, 38.681916167722, 38.670666519282, 38.659416870843, 38.648167222403, 38.636917573964, 38.625667925524, 38.614418277085, 38.603168628645, 38.591918980206, 38.580669331766, 38.569419683327, 38.558170034887, 38.546920386448, 38.535670738008, 38.524421089569, 38.513171441129, 38.501921792690, 38.490672144250, 38.479422495811, 38.468172847371, 38.456923198932, 38.445673550492, 38.434423902053, 38.423174253613, 38.411924605173, 38.400674956734, 38.389425308294, 38.378175659855, 38.366926011415, 38.355676362976, 38.344426714536, 38.333177066097, 38.321927417657, 38.310677769217, 38.299428120778, 38.288178472338, 38.276928823899, 38.265679175459, 38.254429527020, 38.243179878580, 38.231930230140, 38.220680581701, 38.209430933261, 38.198181284822, 38.186931636382, 38.175681987942, 38.164432339503, 38.153182691063, 38.141933042624, 38.130683394184, 38.119433745744, 38.108184097305, 38.096934448865, 38.085684800426, 38.074435151986, 38.063185503546, 38.051935855107, 38.040686206667, 38.029436558227, 38.018186909788, 38.006937261348, 37.995687612909, 37.984437964469, 37.973188316029, 37.961938667590, 37.950689019150, 37.939439370710, 37.928189722271, 37.916940073831, 37.905690425391, 37.894440776952, 37.883191128512, 37.871941480072, 37.860691831633, 37.849442183193, 37.838192534753, 37.826942886314, 37.815693237874, 37.804443589434, 37.793193940995, 37.781944292555, 37.770694644115, 37.759444995676, 37.748195347236, 37.736945698796, 37.725696050356, 37.714446401917, 37.703196753477, 37.691947105037, 37.680697456598, 37.669447808158, 37.658198159718, 37.646948511278, 37.635698862839, 37.624449214399, 37.613199565959, 37.601949917519, 37.590700269080, 37.579450620640, 37.568200972200, 37.556951323761, 37.545701675321, 37.534452026881, 37.523202378441, 37.511952730001, 37.500703081562, 37.489453433122, 37.478203784682, 37.466954136242, 37.455704487803, 37.444454839363, 37.433205190923, 37.421955542483, 37.410705894044, 37.399456245604, 37.388206597164, 37.376956948724, 37.365707300284, 37.354457651845, 37.343208003405, 37.331958354965, 37.320708706525, 37.309459058085, 37.298209409646, 37.286959761206, 37.275710112766, 37.264460464326, 37.253210815886, 37.241961167447, 37.230711519007, 37.219461870567, 37.208212222127, 37.196962573687, 37.185712925247, 37.174463276808, 37.163213628368, 37.151963979928, 37.140714331488, 37.129464683048, 37.118215034608, 37.106965386169, 37.095715737729, 37.084466089289, 37.073216440849, 37.061966792409, 37.050717143969, 37.039467495529, 37.028217847090, 37.016968198650, 37.005718550210, 36.994468901770, 36.983219253330, 36.971969604890, 36.960719956450, 36.949470308010, 36.938220659571, 36.926971011131, 36.915721362691, 36.904471714251, 36.893222065811, 36.881972417371, 36.870722768931, 36.859473120491, 36.848223472051, 36.836973823611, 36.825724175172, 36.814474526732, 36.803224878292, 36.791975229852, 36.780725581412, 36.769475932972, 36.758226284532, 36.746976636092, 36.735726987652, 36.724477339212, 36.713227690772, 36.701978042332, 36.690728393892, 36.679478745452, 36.668229097012, 36.656979448573, 36.645729800133, 36.634480151693, 36.623230503253, 36.611980854813, 36.600731206373, 36.589481557933, 36.578231909493, 36.566982261053, 36.555732612613, 36.544482964173, 36.533233315733, 36.521983667293, 36.510734018853, 36.499484370413, 36.488234721973, 36.476985073533, 36.465735425093, 36.454485776653, 36.443236128213, 36.431986479773, 36.420736831333, 36.409487182893, 36.398237534453, 36.386987886013, 36.375738237573, 36.364488589133, 36.353238940693, 36.341989292253, 36.330739643813, 36.319489995373, 36.308240346933, 36.296990698493, 36.285741050053, 36.274491401613, 36.263241753173, 36.251992104733, 36.240742456293, 36.229492807853, 36.218243159413, 36.206993510973, 36.195743862532, 36.184494214092, 36.173244565652, 36.161994917212, 36.150745268772, 36.139495620332, 36.128245971892, 36.116996323452, 36.105746675012, 36.094497026572, 36.083247378132, 36.071997729692, 36.060748081252, 36.049498432812, 36.038248784371, 36.026999135931, 36.015749487491, 36.004499839051, 35.993250190611, 35.982000542171, 35.970750893731, 35.959501245291, 35.948251596851, 35.937001948411, 35.925752299971, 35.914502651530, 35.903253003090, 35.892003354650, 35.880753706210, 35.869504057770, 35.858254409330, 35.847004760890, 35.835755112450, 35.824505464009, 35.813255815569, 35.802006167129, 35.790756518689, 35.779506870249, 35.768257221809, 35.757007573369, 35.745757924928, 35.734508276488, 35.723258628048, 35.712008979608, 35.700759331168, 35.689509682728, 35.678260034288, 35.667010385847, 35.655760737407, 35.644511088967, 35.633261440527, 35.622011792087, 35.610762143647, 35.599512495206, 35.588262846766, 35.577013198326, 35.565763549886, 35.554513901446, 35.543264253005, 35.532014604565, 35.520764956125, 35.509515307685, 35.498265659245, 35.487016010804, 35.475766362364, 35.464516713924, 35.453267065484, 35.442017417044, 35.430767768603, 35.419518120163, 35.408268471723, 35.397018823283, 35.385769174843, 35.374519526402, 35.363269877962, 35.352020229522, 35.340770581082, 35.329520932641, 35.318271284201, 35.307021635761, 35.295771987321, 35.284522338880, 35.273272690440, 35.262023042000, 35.250773393560, 35.239523745119, 35.228274096679, 35.217024448239, 35.205774799799, 35.194525151358, 35.183275502918, 35.172025854478, 35.160776206038, 35.149526557597, 35.138276909157, 35.127027260717, 35.115777612277, 35.104527963836, 35.093278315396, 35.082028666956, 35.070779018515, 35.059529370075, 35.048279721635, 35.037030073195, 35.025780424754, 35.014530776314, 35.003281127874, 34.992031479433, 34.980781830993, 34.969532182553, 34.958282534112, 34.947032885672, 34.935783237232, 34.924533588792, 34.913283940351, 34.902034291911, 34.890784643471, 34.879534995030, 34.868285346590, 34.857035698150, 34.845786049709, 34.834536401269, 34.823286752829, 34.812037104388, 34.800787455948, 34.789537807508, 34.778288159067, 34.767038510627, 34.755788862187, 34.744539213746, 34.733289565306, 34.722039916865, 34.710790268425, 34.699540619985, 34.688290971544, 34.677041323104, 34.665791674664, 34.654542026223, 34.643292377783, 34.632042729343, 34.620793080902, 34.609543432462, 34.598293784021, 34.587044135581, 34.575794487141, 34.564544838700, 34.553295190260, 34.542045541819, 34.530795893379, 34.519546244939, 34.508296596498, 34.497046948058, 34.485797299618, 34.474547651177, 34.463298002737, 34.452048354296, 34.440798705856, 34.429549057415, 34.418299408975, 34.407049760535, 34.395800112094, 34.384550463654, 34.373300815213, 34.362051166773, 34.350801518333, 34.339551869892, 34.328302221452, 34.317052573011, 34.305802924571, 34.294553276130, 34.283303627690, 34.272053979249, 34.260804330809, 34.249554682369, 34.238305033928, 34.227055385488, 34.215805737047, 34.204556088607, 34.193306440166, 34.182056791726, 34.170807143285, 34.159557494845, 34.148307846404, 34.137058197964, 34.125808549524, 34.114558901083, 34.103309252643, 34.092059604202, 34.080809955762, 34.069560307321, 34.058310658881, 34.047061010440, 34.035811362000, 34.024561713559, 34.013312065119, 34.002062416678, 33.990812768238, 33.979563119797, 33.968313471357, 33.957063822916, 33.945814174476, 33.934564526035, 33.923314877595, 33.912065229154, 33.900815580714, 33.889565932273, 33.878316283833, 33.867066635392, 33.855816986952, 33.844567338511, 33.833317690071, 33.822068041630, 33.810818393190, 33.799568744749, 33.788319096308, 33.777069447868, 33.765819799427, 33.754570150987, 33.743320502546, 33.732070854106, 33.720821205665, 33.709571557225, 33.698321908784, 33.687072260344, 33.675822611903, 33.664572963462, 33.653323315022, 33.642073666581, 33.630824018141, 33.619574369700, 33.608324721260, 33.597075072819, 33.585825424379, 33.574575775938, 33.563326127497, 33.552076479057, 33.540826830616, 33.529577182176, 33.518327533735, 33.507077885294, 33.495828236854, 33.484578588413, 33.473328939973, 33.462079291532, 33.450829643092, 33.439579994651, 33.428330346210, 33.417080697770, 33.405831049329, 33.394581400889, 33.383331752448, 33.372082104007, 33.360832455567, 33.349582807126, 33.338333158685, 33.327083510245, 33.315833861804, 33.304584213364, 33.293334564923, 33.282084916482, 33.270835268042, 33.259585619601, 33.248335971160, 33.237086322720, 33.225836674279, 33.214587025839, 33.203337377398, 33.192087728957, 33.180838080517, 33.169588432076, 33.158338783635, 33.147089135195, 33.135839486754, 33.124589838313, 33.113340189873, 33.102090541432, 33.090840892991, 33.079591244551, 33.068341596110, 33.057091947669, 33.045842299229, 33.034592650788, 33.023343002347, 33.012093353907, 33.000843705466, 32.989594057025, 32.978344408585, 32.967094760144, 32.955845111703, 32.944595463263, 32.933345814822, 32.922096166381, 32.910846517941, 32.899596869500, 32.888347221059, 32.877097572619, 32.865847924178, 32.854598275737, 32.843348627296, 32.832098978856, 32.820849330415, 32.809599681974, 32.798350033534, 32.787100385093, 32.775850736652, 32.764601088211, 32.753351439771, 32.742101791330, 32.730852142889, 32.719602494449, 32.708352846008, 32.697103197567, 32.685853549126, 32.674603900686, 32.663354252245, 32.652104603804, 32.640854955363, 32.629605306923, 32.618355658482, 32.607106010041, 32.595856361600, 32.584606713160, 32.573357064719, 32.562107416278, 32.550857767837, 32.539608119397, 32.528358470956, 32.517108822515, 32.505859174074, 32.494609525634, 32.483359877193, 32.472110228752, 32.460860580311, 32.449610931871, 32.438361283430, 32.427111634989, 32.415861986548, 32.404612338107, 32.393362689667, 32.382113041226, 32.370863392785, 32.359613744344, 32.348364095904, 32.337114447463, 32.325864799022, 32.314615150581, 32.303365502140, 32.292115853700, 32.280866205259, 32.269616556818, 32.258366908377, 32.247117259936, 32.235867611496, 32.224617963055, 32.213368314614, 32.202118666173, 32.190869017732, 32.179619369291, 32.168369720851, 32.157120072410, 32.145870423969, 32.134620775528, 32.123371127087, 32.112121478646, 32.100871830206, 32.089622181765, 32.078372533324, 32.067122884883, 32.055873236442, 32.044623588001, 32.033373939561, 32.022124291120, 32.010874642679, 31.999624994238, 31.988375345797, 31.977125697356, 31.965876048916, 31.954626400475, 31.943376752034, 31.932127103593, 31.920877455152, 31.909627806711, 31.898378158270, 31.887128509830, 31.875878861389, 31.864629212948, 31.853379564507, 31.842129916066, 31.830880267625, 31.819630619184, 31.808380970743, 31.797131322303, 31.785881673862, 31.774632025421, 31.763382376980, 31.752132728539, 31.740883080098, 31.729633431657, 31.718383783216, 31.707134134775, 31.695884486335, 31.684634837894, 31.673385189453, 31.662135541012, 31.650885892571, 31.639636244130, 31.628386595689, 31.617136947248, 31.605887298807, 31.594637650366, 31.583388001925, 31.572138353485, 31.560888705044, 31.549639056603, 31.538389408162, 31.527139759721, 31.515890111280, 31.504640462839, 31.493390814398, 31.482141165957, 31.470891517516, 31.459641869075, 31.448392220634, 31.437142572193, 31.425892923752, 31.414643275311, 31.403393626871, 31.392143978430, 31.380894329989, 31.369644681548, 31.358395033107, 31.347145384666, 31.335895736225, 31.324646087784, 31.313396439343, 31.302146790902, 31.290897142461, 31.279647494020, 31.268397845579, 31.257148197138, 31.245898548697, 31.234648900256, 31.223399251815, 31.212149603374, 31.200899954933, 31.189650306492, 31.178400658051, 31.167151009610, 31.155901361169, 31.144651712728, 31.133402064287, 31.122152415846, 31.110902767405, 31.099653118964, 31.088403470523, 31.077153822082, 31.065904173641, 31.054654525200, 31.043404876759, 31.032155228318, 31.020905579877, 31.009655931436, 30.998406282995, 30.987156634554, 30.975906986113, 30.964657337672, 30.953407689231, 30.942158040790, 30.930908392349, 30.919658743908, 30.908409095467, 30.897159447026, 30.885909798585, 30.874660150144, 30.863410501703, 30.852160853262, 30.840911204821, 30.829661556380, 30.818411907939, 30.807162259498, 30.795912611057, 30.784662962616, 30.773413314175, 30.762163665734, 30.750914017293, 30.739664368852, 30.728414720411, 30.717165071969, 30.705915423528, 30.694665775087, 30.683416126646, 30.672166478205, 30.660916829764, 30.649667181323, 30.638417532882, 30.627167884441, 30.615918236000, 30.604668587559, 30.593418939118, 30.582169290677, 30.570919642236, 30.559669993795, 30.548420345353, 30.537170696912, 30.525921048471, 30.514671400030, 30.503421751589, 30.492172103148, 30.480922454707, 30.469672806266, 30.458423157825, 30.447173509384, 30.435923860943, 30.424674212501, 30.413424564060, 30.402174915619, 30.390925267178, 30.379675618737, 30.368425970296, 30.357176321855, 30.345926673414, 30.334677024973, 30.323427376532, 30.312177728090, 30.300928079649, 30.289678431208, 30.278428782767, 30.267179134326, 30.255929485885, 30.244679837444, 30.233430189003, 30.222180540561, 30.210930892120, 30.199681243679, 30.188431595238, 30.177181946797, 30.165932298356, 30.154682649915, 30.143433001473, 30.132183353032, 30.120933704591, 30.109684056150, 30.098434407709, 30.087184759268, 30.075935110827, 30.064685462385, 30.053435813944, 30.042186165503, 30.030936517062, 30.019686868621, 30.008437220180, 29.997187571739, 29.985937923297, 29.974688274856, 29.963438626415, 29.952188977974, 29.940939329533, 29.929689681091, 29.918440032650, 29.907190384209, 29.895940735768, 29.884691087327, 29.873441438886, 29.862191790444, 29.850942142003, 29.839692493562, 29.828442845121, 29.817193196680, 29.805943548238, 29.794693899797, 29.783444251356, 29.772194602915, 29.760944954474, 29.749695306033, 29.738445657591, 29.727196009150, 29.715946360709, 29.704696712268, 29.693447063827, 29.682197415385, 29.670947766944, 29.659698118503, 29.648448470062, 29.637198821620, 29.625949173179, 29.614699524738, 29.603449876297, 29.592200227856, 29.580950579414, 29.569700930973, 29.558451282532, 29.547201634091, 29.535951985649, 29.524702337208, 29.513452688767, 29.502203040326, 29.490953391885, 29.479703743443, 29.468454095002, 29.457204446561, 29.445954798120, 29.434705149678, 29.423455501237, 29.412205852796, 29.400956204355, 29.389706555913, 29.378456907472, 29.367207259031, 29.355957610590, 29.344707962148, 29.333458313707, 29.322208665266, 29.310959016825, 29.299709368383, 29.288459719942, 29.277210071501, 29.265960423059, 29.254710774618, 29.243461126177, 29.232211477736, 29.220961829294, 29.209712180853, 29.198462532412, 29.187212883971, 29.175963235529, 29.164713587088, 29.153463938647, 29.142214290205, 29.130964641764, 29.119714993323, 29.108465344882, 29.097215696440, 29.085966047999, 29.074716399558, 29.063466751116, 29.052217102675, 29.040967454234, 29.029717805792, 29.018468157351, 29.007218508910, 28.995968860469, 28.984719212027, 28.973469563586, 28.962219915145, 28.950970266703, 28.939720618262, 28.928470969821, 28.917221321379, 28.905971672938, 28.894722024497, 28.883472376055, 28.872222727614, 28.860973079173, 28.849723430731, 28.838473782290, 28.827224133849, 28.815974485407, 28.804724836966, 28.793475188525, 28.782225540083, 28.770975891642, 28.759726243201, 28.748476594759, 28.737226946318, 28.725977297877, 28.714727649435, 28.703478000994, 28.692228352553, 28.680978704111, 28.669729055670, 28.658479407229, 28.647229758787, 28.635980110346, 28.624730461905, 28.613480813463, 28.602231165022, 28.590981516581, 28.579731868139, 28.568482219698, 28.557232571256, 28.545982922815, 28.534733274374, 28.523483625932, 28.512233977491, 28.500984329050, 28.489734680608, 28.478485032167, 28.467235383725, 28.455985735284, 28.444736086843, 28.433486438401, 28.422236789960, 28.410987141519, 28.399737493077, 28.388487844636, 28.377238196194, 28.365988547753, 28.354738899312, 28.343489250870, 28.332239602429, 28.320989953987, 28.309740305546, 28.298490657105, 28.287241008663, 28.275991360222, 28.264741711780, 28.253492063339, 28.242242414898, 28.230992766456, 28.219743118015, 28.208493469573, 28.197243821132, 28.185994172691, 28.174744524249, 28.163494875808, 28.152245227366, 28.140995578925, 28.129745930483, 28.118496282042, 28.107246633601, 28.095996985159, 28.084747336718, 28.073497688276, 28.062248039835, 28.050998391393, 28.039748742952, 28.028499094511, 28.017249446069, 28.005999797628, 27.994750149186, 27.983500500745, 27.972250852303, 27.961001203862, 27.949751555420, 27.938501906979, 27.927252258538, 27.916002610096, 27.904752961655, 27.893503313213, 27.882253664772, 27.871004016330, 27.859754367889, 27.848504719447, 27.837255071006, 27.826005422564, 27.814755774123, 27.803506125682, 27.792256477240, 27.781006828799, 27.769757180357, 27.758507531916, 27.747257883474, 27.736008235033, 27.724758586591, 27.713508938150, 27.702259289708, 27.691009641267, 27.679759992825, 27.668510344384, 27.657260695942, 27.646011047501, 27.634761399059, 27.623511750618, 27.612262102176, 27.601012453735, 27.589762805293, 27.578513156852, 27.567263508410, 27.556013859969, 27.544764211527, 27.533514563086, 27.522264914644, 27.511015266203, 27.499765617761, 27.488515969320, 27.477266320878, 27.466016672437, 27.454767023995, 27.443517375554, 27.432267727112, 27.421018078671, 27.409768430229, 27.398518781788, 27.387269133346, 27.376019484905, 27.364769836463, 27.353520188022, 27.342270539580, 27.331020891139, 27.319771242697, 27.308521594256, 27.297271945814, 27.286022297373, 27.274772648931, 27.263523000490, 27.252273352048, 27.241023703606, 27.229774055165, 27.218524406723, 27.207274758282, 27.196025109840, 27.184775461399, 27.173525812957, 27.162276164516, 27.151026516074, 27.139776867633, 27.128527219191, 27.117277570749, 27.106027922308, 27.094778273866, 27.083528625425, 27.072278976983, 27.061029328542, 27.049779680100, 27.038530031659, 27.027280383217, 27.016030734775, 27.004781086334, 26.993531437892, 26.982281789451, 26.971032141009, 26.959782492568, 26.948532844126, 26.937283195684, 26.926033547243, 26.914783898801, 26.903534250360, 26.892284601918, 26.881034953477, 26.869785305035, 26.858535656593, 26.847286008152, 26.836036359710, 26.824786711269, 26.813537062827, 26.802287414385, 26.791037765944, 26.779788117502, 26.768538469061, 26.757288820619, 26.746039172177, 26.734789523736, 26.723539875294, 26.712290226853, 26.701040578411, 26.689790929969, 26.678541281528, 26.667291633086, 26.656041984645, 26.644792336203, 26.633542687761, 26.622293039320, 26.611043390878, 26.599793742437, 26.588544093995, 26.577294445553, 26.566044797112, 26.554795148670, 26.543545500228, 26.532295851787, 26.521046203345, 26.509796554904, 26.498546906462, 26.487297258020, 26.476047609579, 26.464797961137, 26.453548312695, 26.442298664254, 26.431049015812, 26.419799367371, 26.408549718929, 26.397300070487, 26.386050422046, 26.374800773604, 26.363551125162, 26.352301476721, 26.341051828279, 26.329802179837, 26.318552531396, 26.307302882954, 26.296053234512, 26.284803586071, 26.273553937629, 26.262304289187, 26.251054640746, 26.239804992304, 26.228555343862, 26.217305695421, 26.206056046979, 26.194806398538, 26.183556750096, 26.172307101654, 26.161057453213, 26.149807804771, 26.138558156329, 26.127308507887, 26.116058859446, 26.104809211004, 26.093559562562, 26.082309914121, 26.071060265679, 26.059810617237, 26.048560968796, 26.037311320354, 26.026061671912, 26.014812023471, 26.003562375029, 25.992312726587, 25.981063078146, 25.969813429704, 25.958563781262, 25.947314132821, 25.936064484379, 25.924814835937, 25.913565187495, 25.902315539054, 25.891065890612, 25.879816242170, 25.868566593729, 25.857316945287, 25.846067296845, 25.834817648404, 25.823567999962, 25.812318351520, 25.801068703078, 25.789819054637, 25.778569406195, 25.767319757753, 25.756070109312, 25.744820460870, 25.733570812428, 25.722321163986, 25.711071515545, 25.699821867103, 25.688572218661, 25.677322570220, 25.666072921778, 25.654823273336, 25.643573624894, 25.632323976453, 25.621074328011, 25.609824679569, 25.598575031127, 25.587325382686, 25.576075734244, 25.564826085802, 25.553576437361, 25.542326788919, 25.531077140477, 25.519827492035, 25.508577843594, 25.497328195152, 25.486078546710, 25.474828898268, 25.463579249827, 25.452329601385, 25.441079952943, 25.429830304501, 25.418580656060, 25.407331007618, 25.396081359176, 25.384831710734, 25.373582062293, 25.362332413851, 25.351082765409, 25.339833116967, 25.328583468526, 25.317333820084, 25.306084171642, 25.294834523200, 25.283584874758, 25.272335226317, 25.261085577875, 25.249835929433, 25.238586280991, 25.227336632550, 25.216086984108, 25.204837335666, 25.193587687224, 25.182338038783, 25.171088390341, 25.159838741899, 25.148589093457, 25.137339445015, 25.126089796574, 25.114840148132, 25.103590499690, 25.092340851248, 25.081091202806, 25.069841554365, 25.058591905923, 25.047342257481, 25.036092609039, 25.024842960597, 25.013593312156, 25.002343663714, 24.991094015272, 24.979844366830, 24.968594718388, 24.957345069947, 24.946095421505, 24.934845773063, 24.923596124621, 24.912346476179, 24.901096827738, 24.889847179296, 24.878597530854, 24.867347882412, 24.856098233970, 24.844848585529, 24.833598937087, 24.822349288645, 24.811099640203, 24.799849991761, 24.788600343319, 24.777350694878, 24.766101046436, 24.754851397994, 24.743601749552, 24.732352101110, 24.721102452669, 24.709852804227, 24.698603155785, 24.687353507343, 24.676103858901, 24.664854210459, 24.653604562018, 24.642354913576, 24.631105265134, 24.619855616692, 24.608605968250, 24.597356319808, 24.586106671367, 24.574857022925, 24.563607374483, 24.552357726041, 24.541108077599, 24.529858429157, 24.518608780715, 24.507359132274, 24.496109483832, 24.484859835390, 24.473610186948, 24.462360538506, 24.451110890064, 24.439861241622, 24.428611593181, 24.417361944739, 24.406112296297, 24.394862647855, 24.383612999413, 24.372363350971, 24.361113702529, 24.349864054088, 24.338614405646, 24.327364757204, 24.316115108762, 24.304865460320, 24.293615811878, 24.282366163436, 24.271116514994, 24.259866866553, 24.248617218111, 24.237367569669, 24.226117921227, 24.214868272785, 24.203618624343, 24.192368975901, 24.181119327459, 24.169869679017, 24.158620030576, 24.147370382134, 24.136120733692, 24.124871085250, 24.113621436808, 24.102371788366, 24.091122139924, 24.079872491482, 24.068622843040, 24.057373194599, 24.046123546157, 24.034873897715, 24.023624249273, 24.012374600831, 24.001124952389, 23.989875303947, 23.978625655505, 23.967376007063, 23.956126358621, 23.944876710180, 23.933627061738, 23.922377413296, 23.911127764854, 23.899878116412, 23.888628467970, 23.877378819528, 23.866129171086, 23.854879522644, 23.843629874202, 23.832380225760, 23.821130577318, 23.809880928877, 23.798631280435, 23.787381631993, 23.776131983551, 23.764882335109, 23.753632686667, 23.742383038225, 23.731133389783, 23.719883741341, 23.708634092899, 23.697384444457, 23.686134796015, 23.674885147573, 23.663635499131, 23.652385850689, 23.641136202248, 23.629886553806, 23.618636905364, 23.607387256922, 23.596137608480, 23.584887960038, 23.573638311596, 23.562388663154, 23.551139014712, 23.539889366270, 23.528639717828, 23.517390069386, 23.506140420944, 23.494890772502, 23.483641124060, 23.472391475618, 23.461141827176, 23.449892178734, 23.438642530292, 23.427392881850, 23.416143233408, 23.404893584967, 23.393643936525, 23.382394288083, 23.371144639641, 23.359894991199, 23.348645342757, 23.337395694315, 23.326146045873, 23.314896397431, 23.303646748989, 23.292397100547, 23.281147452105, 23.269897803663, 23.258648155221, 23.247398506779, 23.236148858337, 23.224899209895, 23.213649561453, 23.202399913011, 23.191150264569, 23.179900616127, 23.168650967685, 23.157401319243, 23.146151670801, 23.134902022359, 23.123652373917, 23.112402725475, 23.101153077033, 23.089903428591, 23.078653780149, 23.067404131707, 23.056154483265, 23.044904834823, 23.033655186381, 23.022405537939, 23.011155889497, 22.999906241055, 22.988656592613, 22.977406944171, 22.966157295729, 22.954907647287, 22.943657998845, 22.932408350403, 22.921158701961, 22.909909053519, 22.898659405077, 22.887409756635, 22.876160108193, 22.864910459751, 22.853660811309, 22.842411162867, 22.831161514425, 22.819911865983, 22.808662217541, 22.797412569099, 22.786162920657, 22.774913272215, 22.763663623773, 22.752413975331, 22.741164326889, 22.729914678447, 22.718665030005, 22.707415381563, 22.696165733121, 22.684916084679, 22.673666436237, 22.662416787794, 22.651167139352, 22.639917490910, 22.628667842468, 22.617418194026, 22.606168545584, 22.594918897142, 22.583669248700, 22.572419600258, 22.561169951816, 22.549920303374, 22.538670654932, 22.527421006490, 22.516171358048, 22.504921709606, 22.493672061164, 22.482422412722, 22.471172764280, 22.459923115838, 22.448673467396, 22.437423818954, 22.426174170512, 22.414924522069, 22.403674873627, 22.392425225185, 22.381175576743, 22.369925928301, 22.358676279859, 22.347426631417, 22.336176982975, 22.324927334533, 22.313677686091, 22.302428037649, 22.291178389207, 22.279928740765, 22.268679092323, 22.257429443881, 22.246179795438, 22.234930146996, 22.223680498554, 22.212430850112, 22.201181201670, 22.189931553228, 22.178681904786, 22.167432256344, 22.156182607902, 22.144932959460, 22.133683311018, 22.122433662576, 22.111184014133, 22.099934365691, 22.088684717249, 22.077435068807, 22.066185420365, 22.054935771923, 22.043686123481, 22.032436475039, 22.021186826597, 22.009937178155, 21.998687529713, 21.987437881270, 21.976188232828, 21.964938584386, 21.953688935944, 21.942439287502, 21.931189639060, 21.919939990618, 21.908690342176, 21.897440693734, 21.886191045292, 21.874941396849, 21.863691748407, 21.852442099965, 21.841192451523, 21.829942803081, 21.818693154639, 21.807443506197, 21.796193857755, 21.784944209313, 21.773694560870, 21.762444912428, 21.751195263986, 21.739945615544, 21.728695967102, 21.717446318660, 21.706196670218, 21.694947021776, 21.683697373333, 21.672447724891, 21.661198076449, 21.649948428007, 21.638698779565, 21.627449131123, 21.616199482681, 21.604949834239, 21.593700185796, 21.582450537354, 21.571200888912, 21.559951240470, 21.548701592028, 21.537451943586, 21.526202295144, 21.514952646701, 21.503702998259, 21.492453349817, 21.481203701375, 21.469954052933, 21.458704404491, 21.447454756049, 21.436205107606, 21.424955459164, 21.413705810722, 21.402456162280, 21.391206513838, 21.379956865396, 21.368707216954, 21.357457568511, 21.346207920069, 21.334958271627, 21.323708623185, 21.312458974743, 21.301209326301, 21.289959677858, 21.278710029416, 21.267460380974, 21.256210732532, 21.244961084090, 21.233711435648, 21.222461787206, 21.211212138763, 21.199962490321, 21.188712841879, 21.177463193437, 21.166213544995, 21.154963896553, 21.143714248110, 21.132464599668, 21.121214951226, 21.109965302784, 21.098715654342, 21.087466005899, 21.076216357457, 21.064966709015, 21.053717060573, 21.042467412131, 21.031217763689, 21.019968115246, 21.008718466804, 20.997468818362, 20.986219169920, 20.974969521478, 20.963719873035, 20.952470224593, 20.941220576151, 20.929970927709, 20.918721279267, 20.907471630825, 20.896221982382, 20.884972333940, 20.873722685498, 20.862473037056, 20.851223388614, 20.839973740171, 20.828724091729, 20.817474443287, 20.806224794845, 20.794975146403, 20.783725497960, 20.772475849518, 20.761226201076, 20.749976552634, 20.738726904192, 20.727477255749, 20.716227607307, 20.704977958865, 20.693728310423, 20.682478661980, 20.671229013538, 20.659979365096, 20.648729716654, 20.637480068212, 20.626230419769, 20.614980771327, 20.603731122885, 20.592481474443, 20.581231826001, 20.569982177558, 20.558732529116, 20.547482880674, 20.536233232232, 20.524983583789, 20.513733935347, 20.502484286905, 20.491234638463, 20.479984990021, 20.468735341578, 20.457485693136, 20.446236044694, 20.434986396252, 20.423736747809, 20.412487099367, 20.401237450925, 20.389987802483, 20.378738154040, 20.367488505598, 20.356238857156, 20.344989208714, 20.333739560272, 20.322489911829, 20.311240263387, 20.299990614945, 20.288740966503, 20.277491318060, 20.266241669618, 20.254992021176, 20.243742372734, 20.232492724291, 20.221243075849, 20.209993427407, 20.198743778965, 20.187494130522, 20.176244482080, 20.164994833638, 20.153745185196, 20.142495536753, 20.131245888311, 20.119996239869, 20.108746591427, 20.097496942984, 20.086247294542, 20.074997646100, 20.063747997658, 20.052498349215, 20.041248700773, 20.029999052331, 20.018749403888, 20.007499755446, 19.996250107004, 19.985000458562, 19.973750810119, 19.962501161677, 19.951251513235, 19.940001864793, 19.928752216350, 19.917502567908, 19.906252919466, 19.895003271024, 19.883753622581, 19.872503974139, 19.861254325697, 19.850004677254, 19.838755028812, 19.827505380370, 19.816255731928, 19.805006083485, 19.793756435043, 19.782506786601, 19.771257138158, 19.760007489716, 19.748757841274, 19.737508192832, 19.726258544389, 19.715008895947, 19.703759247505, 19.692509599062, 19.681259950620, 19.670010302178, 19.658760653736, 19.647511005293, 19.636261356851, 19.625011708409, 19.613762059966, 19.602512411524, 19.591262763082, 19.580013114640, 19.568763466197, 19.557513817755, 19.546264169313, 19.535014520870, 19.523764872428, 19.512515223986, 19.501265575543, 19.490015927101, 19.478766278659, 19.467516630216, 19.456266981774, 19.445017333332, 19.433767684890, 19.422518036447, 19.411268388005, 19.400018739563, 19.388769091120, 19.377519442678, 19.366269794236, 19.355020145793, 19.343770497351, 19.332520848909, 19.321271200466, 19.310021552024, 19.298771903582, 19.287522255139, 19.276272606697, 19.265022958255, 19.253773309812, 19.242523661370, 19.231274012928, 19.220024364486, 19.208774716043, 19.197525067601, 19.186275419159, 19.175025770716, 19.163776122274, 19.152526473832, 19.141276825389, 19.130027176947, 19.118777528505, 19.107527880062, 19.096278231620, 19.085028583178, 19.073778934735, 19.062529286293, 19.051279637851, 19.040029989408, 19.028780340966, 19.017530692524, 19.006281044081, 18.995031395639, 18.983781747196, 18.972532098754, 18.961282450312, 18.950032801869, 18.938783153427, 18.927533504985, 18.916283856542, 18.905034208100, 18.893784559658, 18.882534911215, 18.871285262773, 18.860035614331, 18.848785965888, 18.837536317446, 18.826286669004, 18.815037020561, 18.803787372119, 18.792537723677, 18.781288075234, 18.770038426792, 18.758788778349, 18.747539129907, 18.736289481465, 18.725039833022, 18.713790184580, 18.702540536138, 18.691290887695, 18.680041239253, 18.668791590811, 18.657541942368, 18.646292293926, 18.635042645483, 18.623792997041, 18.612543348599, 18.601293700156, 18.590044051714, 18.578794403272, 18.567544754829, 18.556295106387, 18.545045457944, 18.533795809502, 18.522546161060, 18.511296512617, 18.500046864175, 18.488797215733, 18.477547567290, 18.466297918848, 18.455048270405, 18.443798621963, 18.432548973521, 18.421299325078, 18.410049676636, 18.398800028194, 18.387550379751, 18.376300731309, 18.365051082866, 18.353801434424, 18.342551785982, 18.331302137539, 18.320052489097, 18.308802840654, 18.297553192212, 18.286303543770, 18.275053895327, 18.263804246885, 18.252554598442, 18.241304950000, 18.230055301558, 18.218805653115, 18.207556004673, 18.196306356230, 18.185056707788, 18.173807059346, 18.162557410903, 18.151307762461, 18.140058114018, 18.128808465576, 18.117558817134, 18.106309168691, 18.095059520249, 18.083809871806, 18.072560223364, 18.061310574922, 18.050060926479, 18.038811278037, 18.027561629594, 18.016311981152, 18.005062332710, 17.993812684267, 17.982563035825, 17.971313387382, 17.960063738940, 17.948814090497, 17.937564442055, 17.926314793613, 17.915065145170, 17.903815496728, 17.892565848285, 17.881316199843, 17.870066551401, 17.858816902958, 17.847567254516, 17.836317606073, 17.825067957631, 17.813818309188, 17.802568660746, 17.791319012304, 17.780069363861, 17.768819715419, 17.757570066976, 17.746320418534, 17.735070770091, 17.723821121649, 17.712571473207, 17.701321824764, 17.690072176322, 17.678822527879, 17.667572879437, 17.656323230994, 17.645073582552, 17.633823934110, 17.622574285667, 17.611324637225, 17.600074988782, 17.588825340340, 17.577575691897, 17.566326043455, 17.555076395012, 17.543826746570, 17.532577098128, 17.521327449685, 17.510077801243, 17.498828152800, 17.487578504358, 17.476328855915, 17.465079207473, 17.453829559030, 17.442579910588, 17.431330262145, 17.420080613703, 17.408830965261, 17.397581316818, 17.386331668376, 17.375082019933, 17.363832371491, 17.352582723048, 17.341333074606, 17.330083426163, 17.318833777721, 17.307584129278, 17.296334480836, 17.285084832394, 17.273835183951, 17.262585535509, 17.251335887066, 17.240086238624, 17.228836590181, 17.217586941739, 17.206337293296, 17.195087644854, 17.183837996411, 17.172588347969, 17.161338699526, 17.150089051084, 17.138839402641, 17.127589754199, 17.116340105757, 17.105090457314, 17.093840808872, 17.082591160429, 17.071341511987, 17.060091863544, 17.048842215102, 17.037592566659, 17.026342918217, 17.015093269774, 17.003843621332, 16.992593972889, 16.981344324447, 16.970094676004, 16.958845027562, 16.947595379119, 16.936345730677, 16.925096082234, 16.913846433792, 16.902596785349, 16.891347136907, 16.880097488464, 16.868847840022, 16.857598191579, 16.846348543137, 16.835098894695, 16.823849246252, 16.812599597810, 16.801349949367, 16.790100300925, 16.778850652482, 16.767601004040, 16.756351355597, 16.745101707155, 16.733852058712, 16.722602410270, 16.711352761827, 16.700103113385, 16.688853464942, 16.677603816500, 16.666354168057, 16.655104519615, 16.643854871172, 16.632605222730, 16.621355574287, 16.610105925845, 16.598856277402, 16.587606628960, 16.576356980517, 16.565107332075, 16.553857683632, 16.542608035190, 16.531358386747, 16.520108738305, 16.508859089862, 16.497609441419, 16.486359792977, 16.475110144534, 16.463860496092, 16.452610847649, 16.441361199207, 16.430111550764, 16.418861902322, 16.407612253879, 16.396362605437, 16.385112956994, 16.373863308552, 16.362613660109, 16.351364011667, 16.340114363224, 16.328864714782, 16.317615066339, 16.306365417897, 16.295115769454, 16.283866121012, 16.272616472569, 16.261366824127, 16.250117175684, 16.238867527242, 16.227617878799, 16.216368230356, 16.205118581914, 16.193868933471, 16.182619285029, 16.171369636586, 16.160119988144, 16.148870339701, 16.137620691259, 16.126371042816, 16.115121394374, 16.103871745931, 16.092622097489, 16.081372449046, 16.070122800604, 16.058873152161, 16.047623503718, 16.036373855276, 16.025124206833, 16.013874558391, 16.002624909948, 15.991375261506, 15.980125613063, 15.968875964621, 15.957626316178, 15.946376667736, 15.935127019293, 15.923877370851, 15.912627722408, 15.901378073965, 15.890128425523, 15.878878777080, 15.867629128638, 15.856379480195, 15.845129831753, 15.833880183310, 15.822630534868, 15.811380886425, 15.800131237982, 15.788881589540, 15.777631941097, 15.766382292655, 15.755132644212, 15.743882995770, 15.732633347327, 15.721383698885, 15.710134050442, 15.698884401999, 15.687634753557, 15.676385105114, 15.665135456672, 15.653885808229, 15.642636159787, 15.631386511344, 15.620136862902, 15.608887214459, 15.597637566016, 15.586387917574, 15.575138269131, 15.563888620689, 15.552638972246, 15.541389323804, 15.530139675361, 15.518890026918, 15.507640378476, 15.496390730033, 15.485141081591, 15.473891433148, 15.462641784706, 15.451392136263, 15.440142487820, 15.428892839378, 15.417643190935, 15.406393542493, 15.395143894050, 15.383894245608, 15.372644597165, 15.361394948722, 15.350145300280, 15.338895651837, 15.327646003395, 15.316396354952, 15.305146706509, 15.293897058067, 15.282647409624, 15.271397761182, 15.260148112739, 15.248898464297, 15.237648815854, 15.226399167411, 15.215149518969, 15.203899870526, 15.192650222084, 15.181400573641, 15.170150925198, 15.158901276756, 15.147651628313, 15.136401979871, 15.125152331428, 15.113902682985, 15.102653034543, 15.091403386100, 15.080153737658, 15.068904089215, 15.057654440773, 15.046404792330, 15.035155143887, 15.023905495445, 15.012655847002, 15.001406198560, 14.990156550117, 14.978906901674, 14.967657253232, 14.956407604789, 14.945157956347, 14.933908307904, 14.922658659461, 14.911409011019, 14.900159362576, 14.888909714134, 14.877660065691, 14.866410417248, 14.855160768806, 14.843911120363, 14.832661471920, 14.821411823478, 14.810162175035, 14.798912526593, 14.787662878150, 14.776413229707, 14.765163581265, 14.753913932822, 14.742664284380, 14.731414635937, 14.720164987494, 14.708915339052, 14.697665690609, 14.686416042167, 14.675166393724, 14.663916745281, 14.652667096839, 14.641417448396, 14.630167799953, 14.618918151511, 14.607668503068, 14.596418854626, 14.585169206183, 14.573919557740, 14.562669909298, 14.551420260855, 14.540170612412, 14.528920963970, 14.517671315527, 14.506421667085, 14.495172018642, 14.483922370199, 14.472672721757, 14.461423073314, 14.450173424871, 14.438923776429, 14.427674127986, 14.416424479544, 14.405174831101, 14.393925182658, 14.382675534216, 14.371425885773, 14.360176237330, 14.348926588888, 14.337676940445, 14.326427292002, 14.315177643560, 14.303927995117, 14.292678346675, 14.281428698232, 14.270179049789, 14.258929401347, 14.247679752904, 14.236430104461, 14.225180456019, 14.213930807576, 14.202681159133, 14.191431510691, 14.180181862248, 14.168932213806, 14.157682565363, 14.146432916920, 14.135183268478, 14.123933620035, 14.112683971592, 14.101434323150, 14.090184674707, 14.078935026264, 14.067685377822, 14.056435729379, 14.045186080936, 14.033936432494, 14.022686784051, 14.011437135608, 14.000187487166, 13.988937838723, 13.977688190281, 13.966438541838, 13.955188893395, 13.943939244953, 13.932689596510, 13.921439948067, 13.910190299625, 13.898940651182, 13.887691002739, 13.876441354297, 13.865191705854, 13.853942057411, 13.842692408969, 13.831442760526, 13.820193112083, 13.808943463641, 13.797693815198, 13.786444166755, 13.775194518313, 13.763944869870, 13.752695221427, 13.741445572985, 13.730195924542, 13.718946276099, 13.707696627657, 13.696446979214, 13.685197330771, 13.673947682329, 13.662698033886, 13.651448385443, 13.640198737001, 13.628949088558, 13.617699440115, 13.606449791673, 13.595200143230, 13.583950494787, 13.572700846345, 13.561451197902, 13.550201549459, 13.538951901017, 13.527702252574, 13.516452604131, 13.505202955689, 13.493953307246, 13.482703658803, 13.471454010361, 13.460204361918, 13.448954713475, 13.437705065033, 13.426455416590, 13.415205768147, 13.403956119704, 13.392706471262, 13.381456822819, 13.370207174376, 13.358957525934, 13.347707877491, 13.336458229048, 13.325208580606, 13.313958932163, 13.302709283720, 13.291459635278, 13.280209986835, 13.268960338392, 13.257710689950, 13.246461041507, 13.235211393064, 13.223961744622, 13.212712096179, 13.201462447736, 13.190212799293, 13.178963150851, 13.167713502408, 13.156463853965, 13.145214205523, 13.133964557080, 13.122714908637, 13.111465260195, 13.100215611752, 13.088965963309, 13.077716314867, 13.066466666424, 13.055217017981, 13.043967369538, 13.032717721096, 13.021468072653, 13.010218424210, 12.998968775768, 12.987719127325, 12.976469478882, 12.965219830440, 12.953970181997, 12.942720533554, 12.931470885111, 12.920221236669, 12.908971588226, 12.897721939783, 12.886472291341, 12.875222642898, 12.863972994455, 12.852723346012, 12.841473697570, 12.830224049127, 12.818974400684, 12.807724752242, 12.796475103799, 12.785225455356, 12.773975806913, 12.762726158471, 12.751476510028, 12.740226861585, 12.728977213143, 12.717727564700, 12.706477916257, 12.695228267815, 12.683978619372, 12.672728970929, 12.661479322486, 12.650229674044, 12.638980025601, 12.627730377158, 12.616480728715, 12.605231080273, 12.593981431830, 12.582731783387, 12.571482134945, 12.560232486502, 12.548982838059, 12.537733189616, 12.526483541174, 12.515233892731, 12.503984244288, 12.492734595846, 12.481484947403, 12.470235298960, 12.458985650517, 12.447736002075, 12.436486353632, 12.425236705189, 12.413987056746, 12.402737408304, 12.391487759861, 12.380238111418, 12.368988462976, 12.357738814533, 12.346489166090, 12.335239517647, 12.323989869205, 12.312740220762, 12.301490572319, 12.290240923876, 12.278991275434, 12.267741626991, 12.256491978548, 12.245242330106, 12.233992681663, 12.222743033220, 12.211493384777, 12.200243736335, 12.188994087892, 12.177744439449, 12.166494791006, 12.155245142564, 12.143995494121, 12.132745845678, 12.121496197235, 12.110246548793, 12.098996900350, 12.087747251907, 12.076497603464, 12.065247955022, 12.053998306579, 12.042748658136, 12.031499009693, 12.020249361251, 12.008999712808, 11.997750064365, 11.986500415922, 11.975250767480, 11.964001119037, 11.952751470594, 11.941501822152, 11.930252173709, 11.919002525266, 11.907752876823, 11.896503228381, 11.885253579938, 11.874003931495, 11.862754283052, 11.851504634610, 11.840254986167, 11.829005337724, 11.817755689281, 11.806506040839, 11.795256392396, 11.784006743953, 11.772757095510, 11.761507447067, 11.750257798625, 11.739008150182, 11.727758501739, 11.716508853296, 11.705259204854, 11.694009556411, 11.682759907968, 11.671510259525, 11.660260611083, 11.649010962640, 11.637761314197, 11.626511665754, 11.615262017312, 11.604012368869, 11.592762720426, 11.581513071983, 11.570263423541, 11.559013775098, 11.547764126655, 11.536514478212, 11.525264829770, 11.514015181327, 11.502765532884, 11.491515884441, 11.480266235998, 11.469016587556, 11.457766939113, 11.446517290670, 11.435267642227, 11.424017993785, 11.412768345342, 11.401518696899, 11.390269048456, 11.379019400014, 11.367769751571, 11.356520103128, 11.345270454685, 11.334020806242, 11.322771157800, 11.311521509357, 11.300271860914, 11.289022212471, 11.277772564029, 11.266522915586, 11.255273267143, 11.244023618700, 11.232773970257, 11.221524321815, 11.210274673372, 11.199025024929, 11.187775376486, 11.176525728044, 11.165276079601, 11.154026431158, 11.142776782715, 11.131527134272, 11.120277485830, 11.109027837387, 11.097778188944, 11.086528540501, 11.075278892059, 11.064029243616, 11.052779595173, 11.041529946730, 11.030280298287, 11.019030649845, 11.007781001402, 10.996531352959, 10.985281704516, 10.974032056074, 10.962782407631, 10.951532759188, 10.940283110745, 10.929033462302, 10.917783813860, 10.906534165417, 10.895284516974, 10.884034868531, 10.872785220088, 10.861535571646, 10.850285923203, 10.839036274760, 10.827786626317, 10.816536977874, 10.805287329432, 10.794037680989, 10.782788032546, 10.771538384103, 10.760288735660, 10.749039087218, 10.737789438775, 10.726539790332, 10.715290141889, 10.704040493446, 10.692790845004, 10.681541196561, 10.670291548118, 10.659041899675, 10.647792251233, 10.636542602790, 10.625292954347, 10.614043305904, 10.602793657461, 10.591544009019, 10.580294360576, 10.569044712133, 10.557795063690, 10.546545415247, 10.535295766804, 10.524046118362, 10.512796469919, 10.501546821476, 10.490297173033, 10.479047524590, 10.467797876148, 10.456548227705, 10.445298579262, 10.434048930819, 10.422799282376, 10.411549633934, 10.400299985491, 10.389050337048, 10.377800688605, 10.366551040162, 10.355301391720, 10.344051743277, 10.332802094834, 10.321552446391, 10.310302797948, 10.299053149506, 10.287803501063, 10.276553852620, 10.265304204177, 10.254054555734, 10.242804907291, 10.231555258849, 10.220305610406, 10.209055961963, 10.197806313520, 10.186556665077, 10.175307016635, 10.164057368192, 10.152807719749, 10.141558071306, 10.130308422863, 10.119058774420, 10.107809125978, 10.096559477535, 10.085309829092, 10.074060180649, 10.062810532206, 10.051560883764, 10.040311235321, 10.029061586878, 10.017811938435, 10.006562289992, 9.995312641549, 9.984062993107, 9.972813344664, 9.961563696221, 9.950314047778, 9.939064399335, 9.927814750893, 9.916565102450, 9.905315454007, 9.894065805564, 9.882816157121, 9.871566508678, 9.860316860236, 9.849067211793, 9.837817563350, 9.826567914907, 9.815318266464, 9.804068618021, 9.792818969579, 9.781569321136, 9.770319672693, 9.759070024250, 9.747820375807, 9.736570727364, 9.725321078922, 9.714071430479, 9.702821782036, 9.691572133593, 9.680322485150, 9.669072836707, 9.657823188265, 9.646573539822, 9.635323891379, 9.624074242936, 9.612824594493, 9.601574946050, 9.590325297608, 9.579075649165, 9.567826000722, 9.556576352279, 9.545326703836, 9.534077055393, 9.522827406951, 9.511577758508, 9.500328110065, 9.489078461622, 9.477828813179, 9.466579164736, 9.455329516294, 9.444079867851, 9.432830219408, 9.421580570965, 9.410330922522, 9.399081274079, 9.387831625636, 9.376581977194, 9.365332328751, 9.354082680308, 9.342833031865, 9.331583383422, 9.320333734979, 9.309084086537, 9.297834438094, 9.286584789651, 9.275335141208, 9.264085492765, 9.252835844322, 9.241586195879, 9.230336547437, 9.219086898994, 9.207837250551, 9.196587602108, 9.185337953665, 9.174088305222, 9.162838656780, 9.151589008337, 9.140339359894, 9.129089711451, 9.117840063008, 9.106590414565, 9.095340766122, 9.084091117680, 9.072841469237, 9.061591820794, 9.050342172351, 9.039092523908, 9.027842875465, 9.016593227022, 9.005343578580, 8.994093930137, 8.982844281694, 8.971594633251, 8.960344984808, 8.949095336365, 8.937845687922, 8.926596039480, 8.915346391037, 8.904096742594, 8.892847094151, 8.881597445708, 8.870347797265, 8.859098148822, 8.847848500380, 8.836598851937, 8.825349203494, 8.814099555051, 8.802849906608, 8.791600258165, 8.780350609722, 8.769100961280, 8.757851312837, 8.746601664394, 8.735352015951, 8.724102367508, 8.712852719065, 8.701603070622, 8.690353422180, 8.679103773737, 8.667854125294, 8.656604476851, 8.645354828408, 8.634105179965, 8.622855531522, 8.611605883079, 8.600356234637, 8.589106586194, 8.577856937751, 8.566607289308, 8.555357640865, 8.544107992422, 8.532858343979, 8.521608695537, 8.510359047094, 8.499109398651, 8.487859750208, 8.476610101765, 8.465360453322, 8.454110804879, 8.442861156436, 8.431611507994, 8.420361859551, 8.409112211108, 8.397862562665, 8.386612914222, 8.375363265779, 8.364113617336, 8.352863968893, 8.341614320451, 8.330364672008, 8.319115023565, 8.307865375122, 8.296615726679, 8.285366078236, 8.274116429793, 8.262866781350, 8.251617132908, 8.240367484465, 8.229117836022, 8.217868187579, 8.206618539136, 8.195368890693, 8.184119242250, 8.172869593807, 8.161619945364, 8.150370296922, 8.139120648479, 8.127871000036, 8.116621351593, 8.105371703150, 8.094122054707, 8.082872406264, 8.071622757821, 8.060373109379, 8.049123460936, 8.037873812493, 8.026624164050, 8.015374515607, 8.004124867164, 7.992875218721, 7.981625570278, 7.970375921835, 7.959126273393, 7.947876624950, 7.936626976507, 7.925377328064, 7.914127679621, 7.902878031178, 7.891628382735, 7.880378734292, 7.869129085849, 7.857879437407, 7.846629788964, 7.835380140521, 7.824130492078, 7.812880843635, 7.801631195192, 7.790381546749, 7.779131898306, 7.767882249863, 7.756632601421, 7.745382952978, 7.734133304535, 7.722883656092, 7.711634007649, 7.700384359206, 7.689134710763, 7.677885062320, 7.666635413877, 7.655385765435, 7.644136116992, 7.632886468549, 7.621636820106, 7.610387171663, 7.599137523220, 7.587887874777, 7.576638226334, 7.565388577891, 7.554138929449, 7.542889281006, 7.531639632563, 7.520389984120, 7.509140335677, 7.497890687234, 7.486641038791, 7.475391390348, 7.464141741905, 7.452892093462, 7.441642445020, 7.430392796577, 7.419143148134, 7.407893499691, 7.396643851248, 7.385394202805, 7.374144554362, 7.362894905919, 7.351645257476, 7.340395609033, 7.329145960591, 7.317896312148, 7.306646663705, 7.295397015262, 7.284147366819, 7.272897718376, 7.261648069933, 7.250398421490, 7.239148773047, 7.227899124604, 7.216649476161, 7.205399827719, 7.194150179276, 7.182900530833, 7.171650882390, 7.160401233947, 7.149151585504, 7.137901937061, 7.126652288618, 7.115402640175, 7.104152991732, 7.092903343289, 7.081653694847, 7.070404046404, 7.059154397961, 7.047904749518, 7.036655101075, 7.025405452632, 7.014155804189, 7.002906155746, 6.991656507303, 6.980406858860, 6.969157210417, 6.957907561975, 6.946657913532, 6.935408265089, 6.924158616646, 6.912908968203, 6.901659319760, 6.890409671317, 6.879160022874, 6.867910374431, 6.856660725988, 6.845411077545, 6.834161429103, 6.822911780660, 6.811662132217, 6.800412483774, 6.789162835331, 6.777913186888, 6.766663538445, 6.755413890002, 6.744164241559, 6.732914593116, 6.721664944673, 6.710415296230, 6.699165647788, 6.687915999345, 6.676666350902, 6.665416702459, 6.654167054016, 6.642917405573, 6.631667757130, 6.620418108687, 6.609168460244, 6.597918811801, 6.586669163358, 6.575419514915, 6.564169866473, 6.552920218030, 6.541670569587, 6.530420921144, 6.519171272701, 6.507921624258, 6.496671975815, 6.485422327372, 6.474172678929, 6.462923030486, 6.451673382043, 6.440423733600, 6.429174085157, 6.417924436715, 6.406674788272, 6.395425139829, 6.384175491386, 6.372925842943, 6.361676194500, 6.350426546057, 6.339176897614, 6.327927249171, 6.316677600728, 6.305427952285, 6.294178303842, 6.282928655399, 6.271679006956, 6.260429358514, 6.249179710071, 6.237930061628, 6.226680413185, 6.215430764742, 6.204181116299, 6.192931467856, 6.181681819413, 6.170432170970, 6.159182522527, 6.147932874084, 6.136683225641, 6.125433577198, 6.114183928755, 6.102934280313, 6.091684631870, 6.080434983427, 6.069185334984, 6.057935686541, 6.046686038098, 6.035436389655, 6.024186741212, 6.012937092769, 6.001687444326, 5.990437795883, 5.979188147440, 5.967938498997, 5.956688850554, 5.945439202112, 5.934189553669, 5.922939905226, 5.911690256783, 5.900440608340, 5.889190959897, 5.877941311454, 5.866691663011, 5.855442014568, 5.844192366125, 5.832942717682, 5.821693069239, 5.810443420796, 5.799193772353, 5.787944123910, 5.776694475467, 5.765444827025, 5.754195178582, 5.742945530139, 5.731695881696, 5.720446233253, 5.709196584810, 5.697946936367, 5.686697287924, 5.675447639481, 5.664197991038, 5.652948342595, 5.641698694152, 5.630449045709, 5.619199397266, 5.607949748823, 5.596700100380, 5.585450451938, 5.574200803495, 5.562951155052, 5.551701506609, 5.540451858166, 5.529202209723, 5.517952561280, 5.506702912837, 5.495453264394, 5.484203615951, 5.472953967508, 5.461704319065, 5.450454670622, 5.439205022179, 5.427955373736, 5.416705725293, 5.405456076850, 5.394206428407, 5.382956779965, 5.371707131522, 5.360457483079, 5.349207834636, 5.337958186193, 5.326708537750, 5.315458889307, 5.304209240864, 5.292959592421, 5.281709943978, 5.270460295535, 5.259210647092, 5.247960998649, 5.236711350206, 5.225461701763, 5.214212053320, 5.202962404877, 5.191712756434, 5.180463107992, 5.169213459549, 5.157963811106, 5.146714162663, 5.135464514220, 5.124214865777, 5.112965217334, 5.101715568891, 5.090465920448, 5.079216272005, 5.067966623562, 5.056716975119, 5.045467326676, 5.034217678233, 5.022968029790, 5.011718381347, 5.000468732904, 4.989219084461, 4.977969436018, 4.966719787575, 4.955470139133, 4.944220490690, 4.932970842247, 4.921721193804, 4.910471545361, 4.899221896918, 4.887972248475, 4.876722600032, 4.865472951589, 4.854223303146, 4.842973654703, 4.831724006260, 4.820474357817, 4.809224709374, 4.797975060931, 4.786725412488, 4.775475764045, 4.764226115602, 4.752976467159, 4.741726818716, 4.730477170273, 4.719227521830, 4.707977873388, 4.696728224945, 4.685478576502, 4.674228928059, 4.662979279616, 4.651729631173, 4.640479982730, 4.629230334287, 4.617980685844, 4.606731037401, 4.595481388958, 4.584231740515, 4.572982092072, 4.561732443629, 4.550482795186, 4.539233146743, 4.527983498300, 4.516733849857, 4.505484201414, 4.494234552971, 4.482984904528, 4.471735256085, 4.460485607642, 4.449235959199, 4.437986310757, 4.426736662314, 4.415487013871, 4.404237365428, 4.392987716985, 4.381738068542, 4.370488420099, 4.359238771656, 4.347989123213, 4.336739474770, 4.325489826327, 4.314240177884, 4.302990529441, 4.291740880998, 4.280491232555, 4.269241584112, 4.257991935669, 4.246742287226, 4.235492638783, 4.224242990340, 4.212993341897, 4.201743693454, 4.190494045011, 4.179244396568, 4.167994748125, 4.156745099682, 4.145495451240, 4.134245802797, 4.122996154354, 4.111746505911, 4.100496857468, 4.089247209025, 4.077997560582, 4.066747912139, 4.055498263696, 4.044248615253, 4.032998966810, 4.021749318367, 4.010499669924, 3.999250021481, 3.988000373038, 3.976750724595, 3.965501076152, 3.954251427709, 3.943001779266, 3.931752130823, 3.920502482380, 3.909252833937, 3.898003185494, 3.886753537051, 3.875503888608, 3.864254240165, 3.853004591722, 3.841754943279, 3.830505294836, 3.819255646393, 3.808005997951, 3.796756349508, 3.785506701065, 3.774257052622, 3.763007404179, 3.751757755736, 3.740508107293, 3.729258458850, 3.718008810407, 3.706759161964, 3.695509513521, 3.684259865078, 3.673010216635, 3.661760568192, 3.650510919749, 3.639261271306, 3.628011622863, 3.616761974420, 3.605512325977, 3.594262677534, 3.583013029091, 3.571763380648, 3.560513732205, 3.549264083762, 3.538014435319, 3.526764786876, 3.515515138433, 3.504265489990, 3.493015841547, 3.481766193104, 3.470516544661, 3.459266896218, 3.448017247775, 3.436767599332, 3.425517950889, 3.414268302447, 3.403018654004, 3.391769005561, 3.380519357118, 3.369269708675, 3.358020060232, 3.346770411789, 3.335520763346, 3.324271114903, 3.313021466460, 3.301771818017, 3.290522169574, 3.279272521131, 3.268022872688, 3.256773224245, 3.245523575802, 3.234273927359, 3.223024278916, 3.211774630473, 3.200524982030, 3.189275333587, 3.178025685144, 3.166776036701, 3.155526388258, 3.144276739815, 3.133027091372, 3.121777442929, 3.110527794486, 3.099278146043, 3.088028497600, 3.076778849157, 3.065529200714, 3.054279552271, 3.043029903828, 3.031780255385, 3.020530606942, 3.009280958499, 2.998031310056, 2.986781661613, 2.975532013170, 2.964282364727, 2.953032716284, 2.941783067841, 2.930533419399, 2.919283770956, 2.908034122513, 2.896784474070, 2.885534825627, 2.874285177184, 2.863035528741, 2.851785880298, 2.840536231855, 2.829286583412, 2.818036934969, 2.806787286526, 2.795537638083, 2.784287989640, 2.773038341197, 2.761788692754, 2.750539044311, 2.739289395868, 2.728039747425, 2.716790098982, 2.705540450539, 2.694290802096, 2.683041153653, 2.671791505210, 2.660541856767, 2.649292208324, 2.638042559881, 2.626792911438, 2.615543262995, 2.604293614552, 2.593043966109, 2.581794317666, 2.570544669223, 2.559295020780, 2.548045372337, 2.536795723894, 2.525546075451, 2.514296427008, 2.503046778565, 2.491797130122, 2.480547481679, 2.469297833236, 2.458048184793, 2.446798536350, 2.435548887907, 2.424299239464, 2.413049591021, 2.401799942578, 2.390550294135, 2.379300645692, 2.368050997249, 2.356801348806, 2.345551700363, 2.334302051920, 2.323052403478, 2.311802755035, 2.300553106592, 2.289303458149, 2.278053809706, 2.266804161263, 2.255554512820, 2.244304864377, 2.233055215934, 2.221805567491, 2.210555919048, 2.199306270605, 2.188056622162, 2.176806973719, 2.165557325276, 2.154307676833, 2.143058028390, 2.131808379947, 2.120558731504, 2.109309083061, 2.098059434618, 2.086809786175, 2.075560137732, 2.064310489289, 2.053060840846, 2.041811192403, 2.030561543960, 2.019311895517, 2.008062247074, 1.996812598631, 1.985562950188, 1.974313301745, 1.963063653302, 1.951814004859, 1.940564356416, 1.929314707973, 1.918065059530, 1.906815411087, 1.895565762644, 1.884316114201, 1.873066465758, 1.861816817315, 1.850567168872, 1.839317520429, 1.828067871986, 1.816818223543, 1.805568575100, 1.794318926657, 1.783069278214, 1.771819629771, 1.760569981328, 1.749320332885, 1.738070684442, 1.726821035999, 1.715571387556, 1.704321739113, 1.693072090670, 1.681822442227, 1.670572793784, 1.659323145341, 1.648073496898, 1.636823848455, 1.625574200012, 1.614324551569, 1.603074903126, 1.591825254683, 1.580575606240, 1.569325957797, 1.558076309354, 1.546826660911, 1.535577012468, 1.524327364025, 1.513077715582, 1.501828067139, 1.490578418696, 1.479328770253, 1.468079121810, 1.456829473367, 1.445579824924, 1.434330176482, 1.423080528039, 1.411830879596, 1.400581231153, 1.389331582710, 1.378081934267, 1.366832285824, 1.355582637381, 1.344332988938, 1.333083340495, 1.321833692052, 1.310584043609, 1.299334395166, 1.288084746723, 1.276835098280, 1.265585449837, 1.254335801394, 1.243086152951, 1.231836504508, 1.220586856065, 1.209337207622, 1.198087559179, 1.186837910736, 1.175588262293, 1.164338613850, 1.153088965407, 1.141839316964, 1.130589668521, 1.119340020078, 1.108090371635, 1.096840723192, 1.085591074749, 1.074341426306, 1.063091777863, 1.051842129420, 1.040592480977, 1.029342832534, 1.018093184091, 1.006843535648, 0.995593887205, 0.984344238762, 0.973094590319, 0.961844941876, 0.950595293433, 0.939345644990, 0.928095996547, 0.916846348104, 0.905596699661, 0.894347051218, 0.883097402775, 0.871847754332, 0.860598105889, 0.849348457446, 0.838098809003, 0.826849160560, 0.815599512117, 0.804349863674, 0.793100215231, 0.781850566788, 0.770600918345, 0.759351269902, 0.748101621459, 0.736851973016, 0.725602324573, 0.714352676130, 0.703103027687, 0.691853379244, 0.680603730801, 0.669354082358, 0.658104433915, 0.646854785472, 0.635605137029, 0.624355488586, 0.613105840143, 0.601856191700, 0.590606543257, 0.579356894814, 0.568107246371, 0.556857597928, 0.545607949485, 0.534358301042, 0.523108652599, 0.511859004156, 0.500609355713, 0.489359707270, 0.478110058827, 0.466860410384, 0.455610761941, 0.444361113498, 0.433111465055, 0.421861816612, 0.410612168169, 0.399362519726, 0.388112871283, 0.376863222840, 0.365613574397, 0.354363925954, 0.343114277511, 0.331864629068, 0.320614980625, 0.309365332182, 0.298115683739, 0.286866035296, 0.275616386853, 0.264366738410, 0.253117089967, 0.241867441524, 0.230617793081, 0.219368144638, 0.208118496195, 0.196868847752, 0.185619199309, 0.174369550866, 0.163119902423, 0.151870253980, 0.140620605537, 0.129370957094, 0.118121308651, 0.106871660208, 0.095622011765, 0.084372363322, 0.073122714879, 0.061873066436, 0.050623417993, 0.039373769550, 0.028124121107, 0.016874472664, 0.005624824221)) } // namespace gaussian } // namespace spacing } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/spacing/gaussian/N.h0000664000175000017500000000645415160212070022762 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ /// @author Willem Deconinck /// @date Nov 2014 #pragma once #include #include #include "atlas/util/Factory.h" namespace atlas { namespace grid { namespace spacing { namespace gaussian { class GaussianLatitudes { public: /// @pre nlats has enough allocated memory to store the latitudes /// @param size of lats vector void assign(double lats[], const size_t size) const; /// @post resizes the vector to the number of latitutes void assign(std::vector& lats) const; size_t N() const { return lats_.size(); } protected: std::vector lats_; }; class GaussianLatitudesFactory : public util::Factory { public: static std::string className() { return "GaussianLatitudesFactory"; } static const GaussianLatitudes* build(const std::string& builder) { return get(builder)->make(); } using Factory::Factory; private: virtual const GaussianLatitudes* make() const = 0; }; template class GaussianLatitudesBuilder : public GaussianLatitudesFactory { public: using GaussianLatitudesFactory::GaussianLatitudesFactory; private: virtual const GaussianLatitudes* make() const override { return new T(); } }; #define DECLARE_GAUSSIAN_LATITUDES(NUMBER) \ class N##NUMBER : public GaussianLatitudes { \ public: \ N##NUMBER(); \ }; #define LIST(...) __VA_ARGS__ #define DEFINE_GAUSSIAN_LATITUDES(NUMBER, LATS) \ N##NUMBER::N##NUMBER() { \ size_t N = NUMBER; \ double lat[] = {LATS}; \ lats_.assign(lat, lat + N); \ } \ namespace { \ static GaussianLatitudesBuilder builder_N##NUMBER(#NUMBER); \ } DECLARE_GAUSSIAN_LATITUDES(16); DECLARE_GAUSSIAN_LATITUDES(24); DECLARE_GAUSSIAN_LATITUDES(32); DECLARE_GAUSSIAN_LATITUDES(48); DECLARE_GAUSSIAN_LATITUDES(64); DECLARE_GAUSSIAN_LATITUDES(80); DECLARE_GAUSSIAN_LATITUDES(96); DECLARE_GAUSSIAN_LATITUDES(128); DECLARE_GAUSSIAN_LATITUDES(160); DECLARE_GAUSSIAN_LATITUDES(200); DECLARE_GAUSSIAN_LATITUDES(256); DECLARE_GAUSSIAN_LATITUDES(320); DECLARE_GAUSSIAN_LATITUDES(400); DECLARE_GAUSSIAN_LATITUDES(512); DECLARE_GAUSSIAN_LATITUDES(576); DECLARE_GAUSSIAN_LATITUDES(640); DECLARE_GAUSSIAN_LATITUDES(800); DECLARE_GAUSSIAN_LATITUDES(1024); DECLARE_GAUSSIAN_LATITUDES(1280); DECLARE_GAUSSIAN_LATITUDES(1600); DECLARE_GAUSSIAN_LATITUDES(2000); DECLARE_GAUSSIAN_LATITUDES(4000); DECLARE_GAUSSIAN_LATITUDES(8000); #undef DECLARE_GAUSSIAN_LATITUDES } // namespace gaussian } // namespace spacing } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/spacing/gaussian/Latitudes.cc0000664000175000017500000002347615160212070024664 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ /// @file Latitidus.cc /// @author Willem Deconinck /// @date Jan 2014 #include #include #include "eckit/log/Bytes.h" #include "atlas/grid/detail/spacing/gaussian/Latitudes.h" #include "atlas/grid/detail/spacing/gaussian/N.h" #include "atlas/library/config.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Log.h" #include "atlas/runtime/Trace.h" #include "atlas/util/Constants.h" #include "atlas/util/CoordinateEnums.h" namespace atlas { namespace grid { namespace spacing { namespace gaussian { //----------------------------------------------------------------------------- void compute_gaussian_quadrature_npole_equator(const size_t N, double lat[], double weights[]); //----------------------------------------------------------------------------- void gaussian_latitudes_npole_equator(const size_t N, double lats[]) { std::stringstream Nstream; Nstream << N; std::string Nstr = Nstream.str(); if (GaussianLatitudesFactory::has(Nstr)) { std::unique_ptr gl(GaussianLatitudesFactory::build(Nstr)); gl->assign(lats, N); } // if ( Factory::instance().exists( Nstr ) ) { // std::unique_ptr gl( Factory::instance().get( Nstr ).create() ); // gl->assign( lats, N ); // } else { std::vector weights(N); compute_gaussian_quadrature_npole_equator(N, lats, weights.data()); } } //----------------------------------------------------------------------------- void gaussian_latitudes_npole_spole(const size_t N, double lats[]) { gaussian_latitudes_npole_equator(N, lats); size_t end = 2 * N - 1; for (size_t jlat = 0; jlat < N; ++jlat) { lats[end--] = -lats[jlat]; } } //----------------------------------------------------------------------------- void gaussian_quadrature_npole_equator(const size_t N, double lats[], double weights[]) { compute_gaussian_quadrature_npole_equator(N, lats, weights); } //----------------------------------------------------------------------------- void gaussian_quadrature_npole_spole(const size_t N, double lats[], double weights[]) { gaussian_quadrature_npole_equator(N, lats, weights); size_t end = 2 * N - 1; for (size_t jlat = 0; jlat < N; ++jlat) { lats[end] = -lats[jlat]; weights[end] = weights[jlat]; end--; } } //----------------------------------------------------------------------------- namespace { // Anonymous namespace //----------------------------------------------------------------------------- void legpol_newton_iteration(size_t kn, const double pfn[], double px, double& pxn, double& pxmod) { // Routine to perform a single Newton iteration step to find // the zero of the ordinary Legendre polynomial of degree N // Explicit arguments : // -------------------- // KN : Degree of the Legendre polynomial (in) // KODD : odd or even number of latitudes (in) // PFN : Fourier coefficients of series expansion (in) // for the ordinary Legendre polynomials // PX : abcissa where the computations are performed (in) // PXN : new abscissa (Newton iteration) (out) // PXMOD : PXN-PX (out) double zdlx, zdlk, zdlldn; double zdlmod = 0; size_t ik; size_t kodd = kn % 2; // mod(kn,2) zdlx = px; zdlk = 0.; if (kodd == 0) { zdlk = 0.5 * pfn[0]; } zdlldn = 0.; ik = 1; // Newton interation step // ---------------------- for (size_t jn = 2 - kodd; jn <= kn; jn += 2) { // normalised ordinary Legendre polynomial == \overbar{P_n}^0 zdlk += pfn[ik] * std::cos(static_cast(jn) * zdlx); // normalised derivative == d/d\theta(\overbar{P_n}^0) zdlldn -= pfn[ik] * static_cast(jn) * std::sin(static_cast(jn) * zdlx); ++ik; } // Newton method if( zdlldn != 0 ) { zdlmod = -zdlk / zdlldn; } pxn = zdlx + zdlmod; pxmod = zdlmod; } void legpol_weight(const size_t kn, const double pfn[], const double px, double& pw) { // Routine to compute the quadrature weight of the legendre polynomial // Explicit arguments : // -------------------- // KN : Degree of the Legendre polynomial (in) // KODD : odd or even number of latitudes (in) // PFN : Fourier coefficients of series expansion (in) // for the ordinary Legendre polynomials // PX : abcissa where the computations are performed (in) // KFLAG : When KFLAG.EQ.1 computes the weights (in) // PXN : new abscissa (Newton iteration) (out) // PXMOD : PXN-PX (out) double zdlx, zdlldn; size_t ik; size_t kodd = kn % 2; zdlx = px; zdlldn = 0.; ik = 1; // Compute weights // --------------- for (size_t jn = 2 - kodd; jn <= kn; jn += 2) { zdlldn -= pfn[ik] * static_cast(jn) * std::sin(static_cast(jn) * zdlx); ++ik; } pw = static_cast(2 * kn + 1) / (zdlldn * zdlldn); } //----------------------------------------------------------------------------- void legpol_quadrature(const size_t kn, const double pfn[], double& pl, double& pw, size_t& kiter, double& pmod) { //**** *GAWL * - Routine to perform the Newton loop // Purpose. // -------- // Find 0 of Legendre polynomial with Newton loop //** Interface. // ---------- // *CALL* *GAWL(PFN,PL,PW,PEPS,KN,KITER,PMOD) // Explicit arguments : // -------------------- // PFN Fourier coefficients of series expansion // for the ordinary Legendre polynomials (in) // PL Gaussian latitude (inout) // PEPS 0 of the machine (in) // KN Truncation (in) // KITER Number of iterations (out) // PMOD Last modification (inout) constexpr size_t itemax = 20; constexpr double ztol = std::numeric_limits::epsilon() * 1000.; double zx = pl; double zxn; double zw = 0; bool tol_reached = false; for (size_t jter = 1; jter <= itemax + 1; ++jter) { kiter = jter; legpol_newton_iteration(kn, pfn, zx, zxn, pmod); zx = zxn; if (tol_reached) { legpol_weight(kn, pfn, zx, zw); break; } if (std::abs(pmod) <= ztol) { tol_reached = true; } } if (not tol_reached) { std::stringstream s; s << "Could not converge gaussian latitude to accuracy [" << ztol << "]\n"; s << "after " << itemax << " iterations. Consequently also failed to compute quadrature weight."; throw_Exception(s.str(), Here()); } pl = zxn; pw = zw; } //----------------------------------------------------------------------------- } // anonymous namespace //----------------------------------------------------------------------------- void compute_gaussian_quadrature_npole_equator(const size_t N, double lats[], double weights[]) { Log::debug() << "Atlas computing Gaussian latitudes for N " << N << std::endl; ATLAS_TRACE(); size_t kdgl = 2 * N; // Computation of Fourier coefficients of series expansion for the ordinary Legendre polynomials std::vector zzfn(N + 1); { // Belousov, Swarztrauber use zfn(0)=std::sqrt(2.) // IFS normalisation chosen to be 0.5*Integral(Pnm**2) = 1 constexpr double zfn_0 = 2.; std::vector zfn(kdgl+1); zfn[0] = zfn_0; for (size_t jn = 1; jn <= kdgl; ++jn) { zfn[jn] = zfn_0; for (size_t jgl = 1; jgl <= jn; ++jgl) { zfn[jn] *= std::sqrt(1. - 0.25 / (static_cast(jgl * jgl))); } size_t iodd = jn % 2; for (size_t jgl = 2; jgl <= jn - iodd; jgl += 2) { zfn[jn - jgl] = zfn[jn - jgl + 2] * static_cast((jgl - 1) * (2 * jn - jgl + 2)) / static_cast(jgl * (2 * jn - jgl + 1)); } } size_t iodd = kdgl % 2; for (size_t jgl = iodd, ik = iodd; jgl <= kdgl; jgl += 2, ++ik) { zzfn[ik] = zfn[jgl]; } } for (size_t jgl = 0; jgl < N; ++jgl) { // Compute first guess for colatitudes in radians double z = (4. * (jgl + 1.) - 1.) * M_PI / (4. * 2. * N + 2.); lats[jgl] = (z + 1. / (std::tan(z) * (8. * (2. * N) * (2. * N)))); // refine colat first guess here via Newton's method size_t iter; double zmod; legpol_quadrature(kdgl, zzfn.data(), lats[jgl], weights[jgl], iter, zmod); // Convert colat to lat, in degrees constexpr double pole = 90.; lats[jgl] = pole - lats[jgl] * util::Constants::radiansToDegrees(); } } //----------------------------------------------------------------------------- } // namespace gaussian } // namespace spacing } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/spacing/gaussian/N32.cc0000664000175000017500000000260015160212070023252 0ustar alastairalastair/* * (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. */ // TL63 #include "atlas/grid/detail/spacing/gaussian/N.h" namespace atlas { namespace grid { namespace spacing { namespace gaussian { DEFINE_GAUSSIAN_LATITUDES(32, LIST(87.863798839233, 85.096526988317, 82.312912947886, 79.525606572659, 76.736899680368, 73.947515153990, 71.157752011587, 68.367756108313, 65.577607010828, 62.787351798963, 59.997020108491, 57.206631527643, 54.416199526086, 51.625733674938, 48.835240966251, 46.044726631102, 43.254194665351, 40.463648178115, 37.673089629045, 34.882520993773, 32.091943881744, 29.301359621763, 26.510769325211, 23.720173933535, 20.929574254490, 18.138970990239, 15.348364759491, 12.557756115231, 9.767145559196, 6.976533553949, 4.185920533189, 1.395306910819)) } // namespace gaussian } // namespace spacing } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/spacing/gaussian/N48.cc0000664000175000017500000000337015160212070023266 0ustar alastairalastair/* * (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. */ // TL95 #include "atlas/grid/detail/spacing/gaussian/N.h" namespace atlas { namespace grid { namespace spacing { namespace gaussian { DEFINE_GAUSSIAN_LATITUDES(48, LIST(88.572168514007, 86.722530954668, 84.861970292042, 82.998941642838, 81.134976837677, 79.270559034860, 77.405888082079, 75.541061452879, 73.676132313209, 71.811132114274, 69.946080646983, 68.080990985651, 66.215872113999, 64.350730408872, 62.485570522036, 60.620395926826, 58.755209269380, 56.890012601357, 55.024807538312, 53.159595370020, 51.294377138951, 49.429153697123, 47.563925747979, 45.698693877702, 43.833458578951, 41.968220269075, 40.102979304249, 38.237735990565, 36.372490592812, 34.507243341501, 32.641994438518, 30.776744061723, 28.911492368718, 27.046239499945, 25.180985581271, 23.315730726141, 21.450475037398, 19.585218608822, 17.719961526447, 15.854703869695, 13.989445712357, 12.124187123456, 10.258928168006, 8.393668907692, 6.528409401480, 4.663149706178, 2.797889876957, 0.932629967838)) } // namespace gaussian } // namespace spacing } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/spacing/gaussian/N200.cc0000664000175000017500000001035715160212070023337 0ustar alastairalastair/* * (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. */ // TL399 #include "atlas/grid/detail/spacing/gaussian/N.h" namespace atlas { namespace grid { namespace spacing { namespace gaussian { DEFINE_GAUSSIAN_LATITUDES( 200, LIST(89.655964246870, 89.210294391654, 88.761992615075, 88.313096175209, 87.863974165523, 87.414743002061, 86.965450837218, 86.516121156344, 86.066766768868, 85.617395252044, 85.168011373562, 84.718618282731, 84.269218143214, 83.819812490525, 83.370402444494, 82.920988840982, 82.471572316507, 82.022153364315, 81.572732372538, 81.123309650771, 80.673885448980, 80.224459971196, 79.775033385601, 79.325605832072, 78.876177427890, 78.426748272132, 77.977318449063, 77.527888030811, 77.078457079472, 76.629025648798, 76.179593785550, 75.730161530601, 75.280728919836, 74.831295984884, 74.381862753740, 73.932429251264, 73.482995499613, 73.033561518596, 72.584127325979, 72.134692937741, 71.685258368293, 71.235823630664, 70.786388736668, 70.336953697036, 69.887518521543, 69.438083219108, 68.988647797888, 68.539212265356, 68.089776628374, 67.640340893252, 67.190905065802, 66.741469151388, 66.292033154968, 65.842597081130, 65.393160934128, 64.943724717911, 64.494288436150, 64.044852092262, 63.595415689431, 63.145979230630, 62.696542718633, 62.247106156039, 61.797669545278, 61.348232888629, 60.898796188231, 60.449359446093, 59.999922664103, 59.550485844039, 59.101048987575, 58.651612096289, 58.202175171671, 57.752738215127, 57.303301227987, 56.853864211509, 56.404427166883, 55.954990095237, 55.505552997640, 55.056115875108, 54.606678728603, 54.157241559042, 53.707804367293, 53.258367154186, 52.808929920507, 52.359492667008, 51.910055394404, 51.460618103377, 51.011180794579, 50.561743468631, 50.112306126128, 49.662868767636, 49.213431393698, 48.763994004835, 48.314556601543, 47.865119184298, 47.415681753557, 46.966244309758, 46.516806853321, 46.067369384648, 45.617931904125, 45.168494412125, 44.719056909005, 44.269619395107, 43.820181870762, 43.370744336287, 42.921306791988, 42.471869238160, 42.022431675086, 41.572994103039, 41.123556522283, 40.674118933073, 40.224681335653, 39.775243730260, 39.325806117123, 38.876368496463, 38.426930868493, 37.977493233419, 37.528055591442, 37.078617942753, 36.629180287541, 36.179742625985, 35.730304958260, 35.280867284537, 34.831429604979, 34.381991919747, 33.932554228994, 33.483116532871, 33.033678831522, 32.584241125090, 32.134803413711, 31.685365697518, 31.235927976641, 30.786490251206, 30.337052521334, 29.887614787146, 29.438177048755, 28.988739306277, 28.539301559819, 28.089863809488, 27.640426055390, 27.190988297625, 26.741550536291, 26.292112771486, 25.842675003304, 25.393237231836, 24.943799457171, 24.494361679398, 24.044923898602, 23.595486114866, 23.146048328272, 22.696610538899, 22.247172746826, 21.797734952130, 21.348297154884, 20.898859355163, 20.449421553037, 19.999983748578, 19.550545941853, 19.101108132931, 18.651670321878, 18.202232508759, 17.752794693638, 17.303356876577, 16.853919057638, 16.404481236882, 15.955043414368, 15.505605590154, 15.056167764299, 14.606729936858, 14.157292107888, 13.707854277444, 13.258416445580, 12.808978612349, 12.359540777805, 11.910102941998, 11.460665104981, 11.011227266804, 10.561789427517, 10.112351587170, 9.662913745812, 9.213475903492, 8.764038060256, 8.314600216153, 7.865162371230, 7.415724525534, 6.966286679110, 6.516848832004, 6.067410984263, 5.617973135931, 5.168535287054, 4.719097437675, 4.269659587839, 3.820221737591, 3.370783886975, 2.921346036033, 2.471908184811, 2.022470333351, 1.573032481696, 1.123594629891, 0.674156777978, 0.224718926000)) } // namespace gaussian } // namespace spacing } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/spacing/gaussian/N.cc0000664000175000017500000000323315160212070023110 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ /// @author Willem Deconinck /// @date Jan 2015 #include "atlas/grid/detail/spacing/gaussian/N.h" #include "atlas/runtime/Exception.h" namespace atlas { namespace grid { namespace spacing { namespace gaussian { #if 0 std ::string GaussianLatitudes::className() { return "GaussianLatitudes"; } #endif void GaussianLatitudes::assign(double lats[], const size_t size) const { ATLAS_ASSERT(size >= lats_.size()); for (size_t jlat = 0; jlat < lats_.size(); ++jlat) { lats[jlat] = lats_[jlat]; } } void GaussianLatitudes::assign(std::vector& lats) const { lats = lats_; } template void load() { // eckit::ConcreteBuilderT0 builder( "tmp" ); GaussianLatitudesBuilder(); } void regist() { load(); load(); load(); load(); load(); load(); load(); load(); load(); load(); load(); load(); load(); load(); load(); load(); load(); load(); load(); load(); load(); load(); load(); } } // namespace gaussian } // namespace spacing } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/spacing/gaussian/N512.cc0000664000175000017500000002353115160212070023343 0ustar alastairalastair/* * (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. */ // TL1023 #include "atlas/grid/detail/spacing/gaussian/N.h" namespace atlas { namespace grid { namespace spacing { namespace gaussian { DEFINE_GAUSSIAN_LATITUDES( 512, LIST(89.865508687700, 89.691286319427, 89.516035073908, 89.340551361134, 89.164979468040, 88.989364904277, 88.813726493695, 88.638073417245, 88.462410682421, 88.286741251340, 88.111066987798, 87.935389122929, 87.759708502558, 87.584025726950, 87.408341233871, 87.232655350076, 87.056968324394, 86.881280349644, 86.705591577554, 86.529902129152, 86.354212102154, 86.178521576318, 86.002830617390, 85.827139280050, 85.651447610144, 85.475755646402, 85.300063421765, 85.124370964427, 84.948678298664, 84.772985445485, 84.597292423170, 84.421599247696, 84.245905933089, 84.070212491715, 83.894518934514, 83.718825271204, 83.543131510446, 83.367437659983, 83.191743726757, 83.016049717015, 82.840355636389, 82.664661489971, 82.488967282378, 82.313273017804, 82.137578700070, 81.961884332659, 81.786189918760, 81.610495461294, 81.434800962941, 81.259106426166, 81.083411853242, 80.907717246262, 80.732022607164, 80.556327937741, 80.380633239654, 80.204938514445, 80.029243763549, 79.853548988299, 79.677854189940, 79.502159369631, 79.326464528457, 79.150769667431, 78.975074787502, 78.799379889560, 78.623684974440, 78.447990042926, 78.272295095754, 78.096600133617, 77.920905157170, 77.745210167027, 77.569515163769, 77.393820147944, 77.218125120072, 77.042430080642, 76.866735030118, 76.691039968939, 76.515344897522, 76.339649816263, 76.163954725535, 75.988259625697, 75.812564517085, 75.636869400022, 75.461174274815, 75.285479141756, 75.109784001121, 74.934088853178, 74.758393698178, 74.582698536362, 74.407003367962, 74.231308193198, 74.055613012279, 73.879917825408, 73.704222632776, 73.528527434569, 73.352832230963, 73.177137022127, 73.001441808224, 72.825746589409, 72.650051365831, 72.474356137634, 72.298660904956, 72.122965667928, 71.947270426678, 71.771575181328, 71.595879931995, 71.420184678794, 71.244489421831, 71.068794161213, 70.893098897040, 70.717403629410, 70.541708358415, 70.366013084148, 70.190317806694, 70.014622526138, 69.838927242562, 69.663231956043, 69.487536666658, 69.311841374478, 69.136146079577, 68.960450782020, 68.784755481875, 68.609060179206, 68.433364874073, 68.257669566538, 68.081974256658, 67.906278944489, 67.730583630085, 67.554888313498, 67.379192994781, 67.203497673982, 67.027802351149, 66.852107026329, 66.676411699566, 66.500716370904, 66.325021040386, 66.149325708053, 65.973630373944, 65.797935038099, 65.622239700555, 65.446544361348, 65.270849020515, 65.095153678090, 64.919458334106, 64.743762988596, 64.568067641592, 64.392372293124, 64.216676943223, 64.040981591918, 63.865286239237, 63.689590885209, 63.513895529859, 63.338200173214, 63.162504815300, 62.986809456141, 62.811114095763, 62.635418734187, 62.459723371438, 62.284028007538, 62.108332642509, 61.932637276372, 61.756941909148, 61.581246540857, 61.405551171518, 61.229855801152, 61.054160429778, 60.878465057412, 60.702769684074, 60.527074309781, 60.351378934550, 60.175683558397, 59.999988181339, 59.824292803393, 59.648597424573, 59.472902044894, 59.297206664372, 59.121511283021, 58.945815900855, 58.770120517889, 58.594425134135, 58.418729749607, 58.243034364318, 58.067338978280, 57.891643591507, 57.715948204009, 57.540252815800, 57.364557426890, 57.188862037291, 57.013166647015, 56.837471256071, 56.661775864471, 56.486080472225, 56.310385079344, 56.134689685836, 55.958994291713, 55.783298896984, 55.607603501658, 55.431908105745, 55.256212709253, 55.080517312191, 54.904821914568, 54.729126516393, 54.553431117674, 54.377735718419, 54.202040318635, 54.026344918332, 53.850649517516, 53.674954116196, 53.499258714378, 53.323563312070, 53.147867909279, 52.972172506012, 52.796477102275, 52.620781698077, 52.445086293423, 52.269390888319, 52.093695482773, 51.918000076789, 51.742304670376, 51.566609263538, 51.390913856281, 51.215218448612, 51.039523040536, 50.863827632058, 50.688132223185, 50.512436813921, 50.336741404272, 50.161045994243, 49.985350583839, 49.809655173065, 49.633959761927, 49.458264350429, 49.282568938576, 49.106873526372, 48.931178113822, 48.755482700932, 48.579787287704, 48.404091874145, 48.228396460257, 48.052701046046, 47.877005631515, 47.701310216668, 47.525614801510, 47.349919386045, 47.174223970276, 46.998528554208, 46.822833137844, 46.647137721187, 46.471442304243, 46.295746887013, 46.120051469502, 45.944356051714, 45.768660633651, 45.592965215317, 45.417269796716, 45.241574377850, 45.065878958724, 44.890183539339, 44.714488119700, 44.538792699810, 44.363097279670, 44.187401859285, 44.011706438658, 43.836011017791, 43.660315596687, 43.484620175349, 43.308924753780, 43.133229331982, 42.957533909959, 42.781838487713, 42.606143065246, 42.430447642561, 42.254752219661, 42.079056796548, 41.903361373225, 41.727665949694, 41.551970525957, 41.376275102018, 41.200579677877, 41.024884253538, 40.849188829003, 40.673493404274, 40.497797979354, 40.322102554244, 40.146407128946, 39.970711703463, 39.795016277797, 39.619320851950, 39.443625425924, 39.267929999721, 39.092234573343, 38.916539146792, 38.740843720069, 38.565148293178, 38.389452866119, 38.213757438895, 38.038062011506, 37.862366583957, 37.686671156247, 37.510975728378, 37.335280300353, 37.159584872174, 36.983889443841, 36.808194015357, 36.632498586723, 36.456803157940, 36.281107729011, 36.105412299938, 35.929716870721, 35.754021441362, 35.578326011862, 35.402630582224, 35.226935152449, 35.051239722538, 34.875544292492, 34.699848862314, 34.524153432004, 34.348458001564, 34.172762570996, 33.997067140300, 33.821371709478, 33.645676278532, 33.469980847462, 33.294285416271, 33.118589984959, 32.942894553528, 32.767199121978, 32.591503690312, 32.415808258530, 32.240112826634, 32.064417394624, 31.888721962503, 31.713026530271, 31.537331097929, 31.361635665479, 31.185940232922, 31.010244800259, 30.834549367490, 30.658853934618, 30.483158501643, 30.307463068566, 30.131767635389, 29.956072202111, 29.780376768736, 29.604681335263, 29.428985901693, 29.253290468028, 29.077595034269, 28.901899600416, 28.726204166471, 28.550508732434, 28.374813298307, 28.199117864090, 28.023422429785, 27.847726995392, 27.672031560912, 27.496336126347, 27.320640691697, 27.144945256962, 26.969249822145, 26.793554387245, 26.617858952264, 26.442163517203, 26.266468082062, 26.090772646842, 25.915077211544, 25.739381776169, 25.563686340718, 25.387990905192, 25.212295469591, 25.036600033916, 24.860904598168, 24.685209162348, 24.509513726456, 24.333818290494, 24.158122854462, 23.982427418361, 23.806731982191, 23.631036545954, 23.455341109649, 23.279645673279, 23.103950236843, 22.928254800343, 22.752559363778, 22.576863927150, 22.401168490460, 22.225473053708, 22.049777616894, 21.874082180020, 21.698386743087, 21.522691306094, 21.346995869042, 21.171300431933, 20.995604994767, 20.819909557544, 20.644214120265, 20.468518682931, 20.292823245543, 20.117127808100, 19.941432370604, 19.765736933056, 19.590041495455, 19.414346057803, 19.238650620101, 19.062955182348, 18.887259744545, 18.711564306693, 18.535868868793, 18.360173430845, 18.184477992850, 18.008782554808, 17.833087116719, 17.657391678586, 17.481696240407, 17.306000802184, 17.130305363917, 16.954609925607, 16.778914487254, 16.603219048858, 16.427523610421, 16.251828171943, 16.076132733425, 15.900437294866, 15.724741856268, 15.549046417630, 15.373350978955, 15.197655540241, 15.021960101490, 14.846264662702, 14.670569223878, 14.494873785017, 14.319178346122, 14.143482907191, 13.967787468226, 13.792092029227, 13.616396590195, 13.440701151130, 13.265005712032, 13.089310272903, 12.913614833742, 12.737919394550, 12.562223955327, 12.386528516075, 12.210833076793, 12.035137637482, 11.859442198142, 11.683746758774, 11.508051319379, 11.332355879956, 11.156660440507, 10.980965001031, 10.805269561530, 10.629574122003, 10.453878682451, 10.278183242874, 10.102487803274, 9.926792363650, 9.751096924003, 9.575401484333, 9.399706044641, 9.224010604927, 9.048315165192, 8.872619725435, 8.696924285659, 8.521228845862, 8.345533406045, 8.169837966209, 7.994142526355, 7.818447086482, 7.642751646591, 7.467056206682, 7.291360766757, 7.115665326815, 6.939969886856, 6.764274446882, 6.588579006892, 6.412883566887, 6.237188126868, 6.061492686834, 5.885797246787, 5.710101806726, 5.534406366652, 5.358710926566, 5.183015486468, 5.007320046358, 4.831624606236, 4.655929166104, 4.480233725961, 4.304538285808, 4.128842845645, 3.953147405473, 3.777451965292, 3.601756525102, 3.426061084905, 3.250365644700, 3.074670204487, 2.898974764267, 2.723279324041, 2.547583883809, 2.371888443571, 2.196193003328, 2.020497563080, 1.844802122827, 1.669106682571, 1.493411242310, 1.317715802046, 1.142020361779, 0.966324921510, 0.790629481238, 0.614934040965, 0.439238600690, 0.263543160415, 0.087847720138)) } // namespace gaussian } // namespace spacing } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/spacing/gaussian/N1024.cc0000664000175000017500000004602515160212070023425 0ustar alastairalastair/* * (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. */ // TL2047 #include "atlas/grid/detail/spacing/gaussian/N.h" namespace atlas { namespace grid { namespace spacing { namespace gaussian { DEFINE_GAUSSIAN_LATITUDES( 1024, LIST(89.932737928460, 89.845605479540, 89.757958466419, 89.670195191296, 89.582387815250, 89.494559098662, 89.406718455753, 89.318870478120, 89.231017670122, 89.143161513177, 89.055302939412, 88.967442564544, 88.879580811588, 88.791717980751, 88.703854290968, 88.615989905657, 88.528124949263, 88.440259518219, 88.352393688409, 88.264527520359, 88.176661062939, 88.088794356040, 88.000927432542, 87.913060319791, 87.825193040716, 87.737325614688, 87.649458058179, 87.561590385292, 87.473722608166, 87.385854737308, 87.297986781862, 87.210118749818, 87.122250648189, 87.034382483161, 86.946514260205, 86.858645984181, 86.770777659421, 86.682909289796, 86.595040878780, 86.507172429496, 86.419303944761, 86.331435427121, 86.243566878886, 86.155698302153, 86.067829698833, 85.979961070667, 85.892092419252, 85.804223746047, 85.716355052393, 85.628486339523, 85.540617608572, 85.452748860590, 85.364880096544, 85.277011317330, 85.189142523781, 85.101273716667, 85.013404896705, 84.925536064563, 84.837667220863, 84.749798366184, 84.661929501069, 84.574060626026, 84.486191741528, 84.398322848020, 84.310453945921, 84.222585035621, 84.134716117490, 84.046847191874, 83.958978259099, 83.871109319475, 83.783240373290, 83.695371420820, 83.607502462324, 83.519633498046, 83.431764528220, 83.343895553063, 83.256026572786, 83.168157587584, 83.080288597647, 82.992419603152, 82.904550604269, 82.816681601158, 82.728812593972, 82.640943582859, 82.553074567956, 82.465205549396, 82.377336527307, 82.289467501807, 82.201598473014, 82.113729441036, 82.025860405980, 81.937991367945, 81.850122327028, 81.762253283322, 81.674384236914, 81.586515187890, 81.498646136330, 81.410777082312, 81.322908025911, 81.235038967199, 81.147169906244, 81.059300843113, 80.971431777869, 80.883562710572, 80.795693641283, 80.707824570058, 80.619955496950, 80.532086422013, 80.444217345296, 80.356348266849, 80.268479186718, 80.180610104949, 80.092741021585, 80.004871936668, 79.917002850239, 79.829133762336, 79.741264672999, 79.653395582263, 79.565526490164, 79.477657396735, 79.389788302011, 79.301919206022, 79.214050108800, 79.126181010375, 79.038311910775, 78.950442810029, 78.862573708164, 78.774704605205, 78.686835501180, 78.598966396111, 78.511097290023, 78.423228182940, 78.335359074884, 78.247489965876, 78.159620855938, 78.071751745091, 77.983882633354, 77.896013520746, 77.808144407288, 77.720275292996, 77.632406177888, 77.544537061983, 77.456667945296, 77.368798827844, 77.280929709643, 77.193060590708, 77.105191471054, 77.017322350696, 76.929453229649, 76.841584107925, 76.753714985538, 76.665845862502, 76.577976738829, 76.490107614531, 76.402238489621, 76.314369364111, 76.226500238012, 76.138631111335, 76.050761984091, 75.962892856291, 75.875023727945, 75.787154599064, 75.699285469656, 75.611416339733, 75.523547209302, 75.435678078374, 75.347808946958, 75.259939815061, 75.172070682693, 75.084201549862, 74.996332416576, 74.908463282843, 74.820594148671, 74.732725014067, 74.644855879040, 74.556986743595, 74.469117607740, 74.381248471483, 74.293379334829, 74.205510197785, 74.117641060359, 74.029771922556, 73.941902784382, 73.854033645844, 73.766164506947, 73.678295367697, 73.590426228100, 73.502557088162, 73.414687947887, 73.326818807282, 73.238949666350, 73.151080525099, 73.063211383532, 72.975342241654, 72.887473099470, 72.799603956985, 72.711734814204, 72.623865671130, 72.535996527769, 72.448127384125, 72.360258240201, 72.272389096003, 72.184519951533, 72.096650806797, 72.008781661798, 71.920912516539, 71.833043371026, 71.745174225260, 71.657305079247, 71.569435932989, 71.481566786490, 71.393697639753, 71.305828492782, 71.217959345580, 71.130090198151, 71.042221050496, 70.954351902621, 70.866482754527, 70.778613606218, 70.690744457696, 70.602875308964, 70.515006160026, 70.427137010884, 70.339267861541, 70.251398711999, 70.163529562262, 70.075660412331, 69.987791262209, 69.899922111899, 69.812052961404, 69.724183810725, 69.636314659865, 69.548445508827, 69.460576357612, 69.372707206223, 69.284838054662, 69.196968902932, 69.109099751034, 69.021230598970, 68.933361446744, 68.845492294356, 68.757623141808, 68.669753989104, 68.581884836244, 68.494015683230, 68.406146530065, 68.318277376750, 68.230408223287, 68.142539069678, 68.054669915925, 67.966800762029, 67.878931607992, 67.791062453816, 67.703193299502, 67.615324145052, 67.527454990468, 67.439585835750, 67.351716680902, 67.263847525924, 67.175978370817, 67.088109215584, 67.000240060226, 66.912370904743, 66.824501749138, 66.736632593412, 66.648763437566, 66.560894281602, 66.473025125521, 66.385155969324, 66.297286813013, 66.209417656589, 66.121548500052, 66.033679343405, 65.945810186649, 65.857941029784, 65.770071872812, 65.682202715735, 65.594333558552, 65.506464401266, 65.418595243877, 65.330726086387, 65.242856928797, 65.154987771107, 65.067118613319, 64.979249455434, 64.891380297453, 64.803511139377, 64.715641981206, 64.627772822942, 64.539903664587, 64.452034506139, 64.364165347602, 64.276296188975, 64.188427030260, 64.100557871457, 64.012688712568, 63.924819553592, 63.836950394532, 63.749081235388, 63.661212076161, 63.573342916851, 63.485473757459, 63.397604597987, 63.309735438435, 63.221866278804, 63.133997119094, 63.046127959307, 62.958258799443, 62.870389639503, 62.782520479487, 62.694651319397, 62.606782159233, 62.518912998996, 62.431043838687, 62.343174678305, 62.255305517853, 62.167436357330, 62.079567196737, 61.991698036075, 61.903828875345, 61.815959714547, 61.728090553681, 61.640221392750, 61.552352231752, 61.464483070689, 61.376613909561, 61.288744748369, 61.200875587114, 61.113006425796, 61.025137264415, 60.937268102973, 60.849398941469, 60.761529779905, 60.673660618281, 60.585791456597, 60.497922294854, 60.410053133052, 60.322183971193, 60.234314809276, 60.146445647302, 60.058576485271, 59.970707323185, 59.882838161043, 59.794968998847, 59.707099836595, 59.619230674290, 59.531361511931, 59.443492349520, 59.355623187055, 59.267754024539, 59.179884861970, 59.092015699351, 59.004146536680, 58.916277373959, 58.828408211189, 58.740539048368, 58.652669885499, 58.564800722581, 58.476931559614, 58.389062396600, 58.301193233538, 58.213324070430, 58.125454907274, 58.037585744072, 57.949716580824, 57.861847417531, 57.773978254193, 57.686109090809, 57.598239927382, 57.510370763910, 57.422501600395, 57.334632436836, 57.246763273234, 57.158894109590, 57.071024945903, 56.983155782175, 56.895286618404, 56.807417454593, 56.719548290741, 56.631679126848, 56.543809962914, 56.455940798941, 56.368071634928, 56.280202470876, 56.192333306784, 56.104464142654, 56.016594978486, 55.928725814279, 55.840856650035, 55.752987485753, 55.665118321433, 55.577249157077, 55.489379992684, 55.401510828255, 55.313641663790, 55.225772499289, 55.137903334752, 55.050034170180, 54.962165005573, 54.874295840932, 54.786426676256, 54.698557511545, 54.610688346801, 54.522819182023, 54.434950017212, 54.347080852367, 54.259211687489, 54.171342522579, 54.083473357636, 53.995604192661, 53.907735027655, 53.819865862616, 53.731996697546, 53.644127532444, 53.556258367312, 53.468389202149, 53.380520036955, 53.292650871731, 53.204781706476, 53.116912541192, 53.029043375878, 52.941174210535, 52.853305045162, 52.765435879760, 52.677566714330, 52.589697548871, 52.501828383383, 52.413959217867, 52.326090052323, 52.238220886751, 52.150351721152, 52.062482555525, 51.974613389871, 51.886744224189, 51.798875058481, 51.711005892747, 51.623136726985, 51.535267561198, 51.447398395384, 51.359529229545, 51.271660063679, 51.183790897788, 51.095921731872, 51.008052565931, 50.920183399964, 50.832314233973, 50.744445067957, 50.656575901916, 50.568706735851, 50.480837569762, 50.392968403648, 50.305099237511, 50.217230071350, 50.129360905166, 50.041491738958, 49.953622572727, 49.865753406473, 49.777884240196, 49.690015073896, 49.602145907574, 49.514276741229, 49.426407574862, 49.338538408472, 49.250669242061, 49.162800075628, 49.074930909173, 48.987061742696, 48.899192576198, 48.811323409679, 48.723454243139, 48.635585076577, 48.547715909995, 48.459846743392, 48.371977576768, 48.284108410124, 48.196239243460, 48.108370076775, 48.020500910070, 47.932631743346, 47.844762576601, 47.756893409837, 47.669024243053, 47.581155076250, 47.493285909427, 47.405416742586, 47.317547575725, 47.229678408845, 47.141809241947, 47.053940075030, 46.966070908094, 46.878201741140, 46.790332574167, 46.702463407176, 46.614594240167, 46.526725073140, 46.438855906096, 46.350986739033, 46.263117571953, 46.175248404855, 46.087379237740, 45.999510070607, 45.911640903457, 45.823771736290, 45.735902569106, 45.648033401905, 45.560164234688, 45.472295067453, 45.384425900202, 45.296556732935, 45.208687565651, 45.120818398351, 45.032949231035, 44.945080063702, 44.857210896354, 44.769341728989, 44.681472561609, 44.593603394213, 44.505734226802, 44.417865059375, 44.329995891933, 44.242126724475, 44.154257557002, 44.066388389514, 43.978519222011, 43.890650054493, 43.802780886960, 43.714911719412, 43.627042551849, 43.539173384272, 43.451304216681, 43.363435049075, 43.275565881454, 43.187696713820, 43.099827546171, 43.011958378508, 42.924089210831, 42.836220043140, 42.748350875436, 42.660481707717, 42.572612539985, 42.484743372240, 42.396874204480, 42.309005036708, 42.221135868922, 42.133266701123, 42.045397533310, 41.957528365484, 41.869659197646, 41.781790029794, 41.693920861930, 41.606051694052, 41.518182526162, 41.430313358259, 41.342444190344, 41.254575022416, 41.166705854475, 41.078836686523, 40.990967518558, 40.903098350580, 40.815229182591, 40.727360014589, 40.639490846575, 40.551621678550, 40.463752510512, 40.375883342463, 40.288014174402, 40.200145006329, 40.112275838244, 40.024406670148, 39.936537502041, 39.848668333922, 39.760799165792, 39.672929997650, 39.585060829497, 39.497191661333, 39.409322493158, 39.321453324972, 39.233584156775, 39.145714988566, 39.057845820347, 38.969976652118, 38.882107483877, 38.794238315626, 38.706369147364, 38.618499979092, 38.530630810809, 38.442761642515, 38.354892474212, 38.267023305898, 38.179154137573, 38.091284969239, 38.003415800894, 37.915546632539, 37.827677464174, 37.739808295800, 37.651939127415, 37.564069959020, 37.476200790616, 37.388331622201, 37.300462453777, 37.212593285344, 37.124724116900, 37.036854948448, 36.948985779985, 36.861116611513, 36.773247443032, 36.685378274542, 36.597509106042, 36.509639937533, 36.421770769014, 36.333901600487, 36.246032431950, 36.158163263405, 36.070294094850, 35.982424926287, 35.894555757714, 35.806686589133, 35.718817420543, 35.630948251944, 35.543079083336, 35.455209914720, 35.367340746095, 35.279471577462, 35.191602408820, 35.103733240170, 35.015864071511, 34.927994902843, 34.840125734168, 34.752256565484, 34.664387396792, 34.576518228092, 34.488649059383, 34.400779890666, 34.312910721942, 34.225041553209, 34.137172384468, 34.049303215720, 33.961434046963, 33.873564878199, 33.785695709426, 33.697826540646, 33.609957371859, 33.522088203063, 33.434219034260, 33.346349865450, 33.258480696631, 33.170611527805, 33.082742358972, 32.994873190131, 32.907004021283, 32.819134852428, 32.731265683565, 32.643396514695, 32.555527345817, 32.467658176933, 32.379789008041, 32.291919839142, 32.204050670236, 32.116181501323, 32.028312332403, 31.940443163476, 31.852573994542, 31.764704825601, 31.676835656653, 31.588966487698, 31.501097318737, 31.413228149768, 31.325358980793, 31.237489811812, 31.149620642823, 31.061751473829, 30.973882304827, 30.886013135819, 30.798143966804, 30.710274797783, 30.622405628756, 30.534536459722, 30.446667290681, 30.358798121634, 30.270928952581, 30.183059783522, 30.095190614457, 30.007321445385, 29.919452276307, 29.831583107223, 29.743713938132, 29.655844769036, 29.567975599934, 29.480106430825, 29.392237261711, 29.304368092591, 29.216498923464, 29.128629754332, 29.040760585194, 28.952891416050, 28.865022246901, 28.777153077745, 28.689283908584, 28.601414739417, 28.513545570245, 28.425676401067, 28.337807231883, 28.249938062693, 28.162068893498, 28.074199724298, 27.986330555092, 27.898461385880, 27.810592216664, 27.722723047441, 27.634853878214, 27.546984708981, 27.459115539742, 27.371246370498, 27.283377201249, 27.195508031995, 27.107638862736, 27.019769693471, 26.931900524202, 26.844031354927, 26.756162185647, 26.668293016362, 26.580423847071, 26.492554677776, 26.404685508476, 26.316816339171, 26.228947169861, 26.141078000546, 26.053208831226, 25.965339661902, 25.877470492572, 25.789601323238, 25.701732153899, 25.613862984555, 25.525993815206, 25.438124645853, 25.350255476495, 25.262386307132, 25.174517137765, 25.086647968393, 24.998778799016, 24.910909629635, 24.823040460250, 24.735171290859, 24.647302121465, 24.559432952066, 24.471563782662, 24.383694613254, 24.295825443842, 24.207956274425, 24.120087105005, 24.032217935579, 23.944348766150, 23.856479596716, 23.768610427278, 23.680741257835, 23.592872088389, 23.505002918938, 23.417133749483, 23.329264580024, 23.241395410561, 23.153526241094, 23.065657071623, 22.977787902148, 22.889918732668, 22.802049563185, 22.714180393698, 22.626311224207, 22.538442054711, 22.450572885212, 22.362703715710, 22.274834546203, 22.186965376692, 22.099096207178, 22.011227037659, 21.923357868137, 21.835488698612, 21.747619529082, 21.659750359549, 21.571881190012, 21.484012020472, 21.396142850927, 21.308273681379, 21.220404511828, 21.132535342273, 21.044666172714, 20.956797003152, 20.868927833587, 20.781058664017, 20.693189494445, 20.605320324869, 20.517451155289, 20.429581985706, 20.341712816120, 20.253843646530, 20.165974476937, 20.078105307340, 19.990236137740, 19.902366968137, 19.814497798531, 19.726628628921, 19.638759459308, 19.550890289692, 19.463021120072, 19.375151950450, 19.287282780824, 19.199413611195, 19.111544441563, 19.023675271928, 18.935806102290, 18.847936932648, 18.760067763004, 18.672198593357, 18.584329423706, 18.496460254053, 18.408591084396, 18.320721914737, 18.232852745075, 18.144983575409, 18.057114405741, 17.969245236070, 17.881376066396, 17.793506896719, 17.705637727039, 17.617768557357, 17.529899387672, 17.442030217984, 17.354161048293, 17.266291878599, 17.178422708903, 17.090553539204, 17.002684369502, 16.914815199797, 16.826946030090, 16.739076860380, 16.651207690668, 16.563338520953, 16.475469351235, 16.387600181515, 16.299731011792, 16.211861842067, 16.123992672339, 16.036123502609, 15.948254332876, 15.860385163141, 15.772515993403, 15.684646823662, 15.596777653920, 15.508908484175, 15.421039314427, 15.333170144677, 15.245300974925, 15.157431805170, 15.069562635413, 14.981693465654, 14.893824295893, 14.805955126129, 14.718085956363, 14.630216786594, 14.542347616824, 14.454478447051, 14.366609277276, 14.278740107498, 14.190870937719, 14.103001767937, 14.015132598154, 13.927263428368, 13.839394258580, 13.751525088790, 13.663655918998, 13.575786749203, 13.487917579407, 13.400048409609, 13.312179239809, 13.224310070006, 13.136440900202, 13.048571730396, 12.960702560587, 12.872833390777, 12.784964220965, 12.697095051151, 12.609225881335, 12.521356711517, 12.433487541698, 12.345618371876, 12.257749202053, 12.169880032227, 12.082010862400, 11.994141692572, 11.906272522741, 11.818403352909, 11.730534183074, 11.642665013239, 11.554795843401, 11.466926673562, 11.379057503721, 11.291188333878, 11.203319164034, 11.115449994188, 11.027580824340, 10.939711654491, 10.851842484640, 10.763973314787, 10.676104144933, 10.588234975078, 10.500365805220, 10.412496635362, 10.324627465501, 10.236758295640, 10.148889125776, 10.061019955912, 9.973150786046, 9.885281616178, 9.797412446309, 9.709543276438, 9.621674106566, 9.533804936693, 9.445935766818, 9.358066596942, 9.270197427065, 9.182328257186, 9.094459087306, 9.006589917424, 8.918720747542, 8.830851577657, 8.742982407772, 8.655113237886, 8.567244067998, 8.479374898109, 8.391505728218, 8.303636558327, 8.215767388434, 8.127898218540, 8.040029048645, 7.952159878749, 7.864290708852, 7.776421538953, 7.688552369054, 7.600683199153, 7.512814029251, 7.424944859348, 7.337075689444, 7.249206519539, 7.161337349633, 7.073468179726, 6.985599009818, 6.897729839909, 6.809860669999, 6.721991500088, 6.634122330176, 6.546253160263, 6.458383990349, 6.370514820434, 6.282645650519, 6.194776480602, 6.106907310685, 6.019038140766, 5.931168970847, 5.843299800927, 5.755430631006, 5.667561461084, 5.579692291162, 5.491823121239, 5.403953951315, 5.316084781390, 5.228215611464, 5.140346441538, 5.052477271611, 4.964608101683, 4.876738931755, 4.788869761826, 4.701000591896, 4.613131421965, 4.525262252034, 4.437393082102, 4.349523912170, 4.261654742237, 4.173785572303, 4.085916402369, 3.998047232434, 3.910178062499, 3.822308892563, 3.734439722626, 3.646570552689, 3.558701382752, 3.470832212814, 3.382963042876, 3.295093872937, 3.207224702997, 3.119355533057, 3.031486363117, 2.943617193176, 2.855748023235, 2.767878853294, 2.680009683352, 2.592140513409, 2.504271343467, 2.416402173524, 2.328533003580, 2.240663833637, 2.152794663692, 2.064925493748, 1.977056323804, 1.889187153859, 1.801317983914, 1.713448813968, 1.625579644023, 1.537710474077, 1.449841304131, 1.361972134184, 1.274102964238, 1.186233794291, 1.098364624344, 1.010495454397, 0.922626284450, 0.834757114503, 0.746887944556, 0.659018774608, 0.571149604661, 0.483280434713, 0.395411264765, 0.307542094817, 0.219672924870, 0.131803754922, 0.043934584974)) } // namespace gaussian } // namespace spacing } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/spacing/gaussian/N24.cc0000664000175000017500000000246615160212070023265 0ustar alastairalastair/* * (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. */ // TL47 #include "atlas/grid/detail/spacing/gaussian/N.h" namespace atlas { namespace grid { namespace spacing { namespace gaussian { DEFINE_GAUSSIAN_LATITUDES(24, LIST(87.1590945558628505, 83.4789366693171644, 79.7770456548256419, 76.0702444625451335, 72.3615810293448476, 68.6520167895174893, 64.9419494887575155, 61.2315731880771352, 57.5209937979699646, 53.8102740319414252, 50.0994534129868470, 46.3885581116054269, 42.6776061726049036, 38.9666104694540252, 35.2555804613681829, 31.5445232840216754, 27.8334444519932376, 24.1223483260879874, 20.4112384335677852, 16.7001176938426745, 12.9889885820881474, 9.2778532515078656, 5.5667136279135834, 1.8555714859932551)) } // namespace gaussian } // namespace spacing } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/spacing/gaussian/N160.cc0000664000175000017500000000722315160212070023342 0ustar alastairalastair/* * (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. */ // TL319 #include "atlas/grid/detail/spacing/gaussian/N.h" namespace atlas { namespace grid { namespace spacing { namespace gaussian { DEFINE_GAUSSIAN_LATITUDES( 160, LIST(89.570089550607, 89.013176131022, 88.452973836713, 87.892028445344, 87.330801179738, 86.769437514528, 86.207997621423, 85.646510847953, 85.084993200912, 84.523454148914, 83.961899649718, 83.400333638737, 82.838758819710, 82.277177111434, 81.715589913266, 81.153998269713, 80.592402976178, 80.030804649031, 79.469203773292, 78.907600735838, 78.345995849036, 77.784389367849, 77.222781502445, 76.661172427620, 76.099562289938, 75.537951213208, 74.976339302737, 74.414726648662, 73.853113328584, 73.291499409676, 72.729884950380, 72.168270001775, 71.606654608708, 71.045038810711, 70.483422642771, 69.921806135960, 69.360189317972, 68.798572213565, 68.236954844948, 67.675337232092, 67.113719393011, 66.552101343996, 65.990483099813, 65.428864673879, 64.867246078414, 64.305627324571, 63.744008422549, 63.182389381694, 62.620770210586, 62.059150917117, 61.497531508556, 60.935911991615, 60.374292372493, 59.812672656933, 59.251052850257, 58.689432957406, 58.127812982976, 57.566192931243, 57.004572806194, 56.442952611549, 55.881332350787, 55.319712027158, 54.758091643709, 54.196471203297, 53.634850708600, 53.073230162138, 52.511609566278, 51.949988923250, 51.388368235154, 50.826747503971, 50.265126731571, 49.703505919720, 49.141885070089, 48.580264184257, 48.018643263723, 47.457022309904, 46.895401324147, 46.333780307729, 45.772159261862, 45.210538187702, 44.648917086344, 44.087295958835, 43.525674806167, 42.964053629291, 42.402432429111, 41.840811206489, 41.279189962251, 40.717568697184, 40.155947412042, 39.594326107546, 39.032704784385, 38.471083443221, 37.909462084686, 37.347840709389, 36.786219317912, 36.224597910814, 35.662976488634, 35.101355051887, 34.539733601070, 33.978112136661, 33.416490659120, 32.854869168890, 32.293247666397, 31.731626152053, 31.170004626255, 30.608383089386, 30.046761541816, 29.485139983902, 28.923518415990, 28.361896838413, 27.800275251495, 27.238653655548, 26.677032050875, 26.115410437771, 25.553788816520, 24.992167187398, 24.430545550672, 23.868923906604, 23.307302255447, 22.745680597445, 22.184058932838, 21.622437261859, 21.060815584735, 20.499193901686, 19.937572212927, 19.375950518668, 18.814328819115, 18.252707114466, 17.691085404918, 17.129463690661, 16.567841971882, 16.006220248764, 15.444598521486, 14.882976790224, 14.321355055149, 13.759733316430, 13.198111574234, 12.636489828723, 12.074868080057, 11.513246328393, 10.951624573887, 10.390002816691, 9.828381056956, 9.266759294829, 8.705137530459, 8.143515763989, 7.581893995562, 7.020272225320, 6.458650453403, 5.897028679950, 5.335406905098, 4.773785128984, 4.212163351743, 3.650541573510, 3.088919794419, 2.527298014602, 1.965676234193, 1.404054453324, 0.842432672126, 0.280810890730)) } // namespace gaussian } // namespace spacing } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/spacing/gaussian/N640.cc0000664000175000017500000003021015160212070023335 0ustar alastairalastair/* * (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. */ // TL1279 #include "atlas/grid/detail/spacing/gaussian/N.h" namespace atlas { namespace grid { namespace spacing { namespace gaussian { DEFINE_GAUSSIAN_LATITUDES( 640, LIST(89.892396445590, 89.753004943174, 89.612790258599, 89.472389582061, 89.331918354382, 89.191412986832, 89.050888539966, 88.910352359260, 88.769808451100, 88.629259185412, 88.488706053376, 88.348150039999, 88.207591822004, 88.067031879651, 87.926470563186, 87.785908134041, 87.645344791296, 87.504780689222, 87.364215949215, 87.223650668104, 87.083084924071, 86.942518780929, 86.801952291278, 86.661385498868, 86.520818440380, 86.380251146799, 86.239683644481, 86.099115955985, 85.958548100731, 85.817980095530, 85.677411955006, 85.536843691943, 85.396275317563, 85.255706841758, 85.115138273282, 84.974569619910, 84.834000888572, 84.693432085462, 84.552863216136, 84.412294285589, 84.271725298330, 84.131156258431, 83.990587169587, 83.850018035154, 83.709448858186, 83.568879641474, 83.428310387568, 83.287741098803, 83.147171777324, 83.006602425105, 82.866033043963, 82.725463635573, 82.584894201486, 82.444324743135, 82.303755261850, 82.163185758865, 82.022616235328, 81.882046692304, 81.741477130791, 81.600907551716, 81.460337955946, 81.319768344292, 81.179198717514, 81.038629076323, 80.898059421388, 80.757489753335, 80.616920072753, 80.476350380198, 80.335780676193, 80.195210961228, 80.054641235771, 79.914071500258, 79.773501755105, 79.632932000703, 79.492362237425, 79.351792465622, 79.211222685626, 79.070652897754, 78.930083102307, 78.789513299568, 78.648943489809, 78.508373673288, 78.367803850250, 78.227234020928, 78.086664185545, 77.946094344312, 77.805524497433, 77.664954645100, 77.524384787497, 77.383814924802, 77.243245057181, 77.102675184796, 76.962105307802, 76.821535426346, 76.680965540569, 76.540395650606, 76.399825756588, 76.259255858639, 76.118685956878, 75.978116051420, 75.837546142375, 75.696976229850, 75.556406313944, 75.415836394757, 75.275266472383, 75.134696546911, 74.994126618429, 74.853556687021, 74.712986752768, 74.572416815746, 74.431846876032, 74.291276933698, 74.150706988813, 74.010137041445, 73.869567091658, 73.728997139516, 73.588427185079, 73.447857228405, 73.307287269551, 73.166717308572, 73.026147345520, 72.885577380447, 72.745007413401, 72.604437444432, 72.463867473585, 72.323297500905, 72.182727526435, 72.042157550217, 71.901587572293, 71.761017592701, 71.620447611481, 71.479877628669, 71.339307644300, 71.198737658411, 71.058167671035, 70.917597682205, 70.777027691952, 70.636457700309, 70.495887707304, 70.355317712967, 70.214747717328, 70.074177720412, 69.933607722247, 69.793037722860, 69.652467722275, 69.511897720517, 69.371327717611, 69.230757713579, 69.090187708444, 68.949617702229, 68.809047694955, 68.668477686643, 68.527907677314, 68.387337666986, 68.246767655681, 68.106197643416, 67.965627630209, 67.825057616080, 67.684487601045, 67.543917585122, 67.403347568326, 67.262777550675, 67.122207532184, 66.981637512868, 66.841067492743, 66.700497471823, 66.559927450122, 66.419357427655, 66.278787404436, 66.138217380477, 65.997647355791, 65.857077330392, 65.716507304291, 65.575937277502, 65.435367250035, 65.294797221902, 65.154227193115, 65.013657163685, 64.873087133622, 64.732517102938, 64.591947071642, 64.451377039745, 64.310807007256, 64.170236974186, 64.029666940544, 63.889096906338, 63.748526871579, 63.607956836274, 63.467386800434, 63.326816764065, 63.186246727177, 63.045676689778, 62.905106651876, 62.764536613478, 62.623966574592, 62.483396535226, 62.342826495387, 62.202256455083, 62.061686414319, 61.921116373105, 61.780546331445, 61.639976289347, 61.499406246817, 61.358836203862, 61.218266160488, 61.077696116701, 60.937126072507, 60.796556027912, 60.655985982922, 60.515415937542, 60.374845891778, 60.234275845637, 60.093705799122, 59.953135752239, 59.812565704993, 59.671995657391, 59.531425609435, 59.390855561132, 59.250285512486, 59.109715463502, 58.969145414185, 58.828575364539, 58.688005314568, 58.547435264277, 58.406865213671, 58.266295162752, 58.125725111527, 57.985155059998, 57.844585008170, 57.704014956047, 57.563444903632, 57.422874850930, 57.282304797944, 57.141734744678, 57.001164691135, 56.860594637319, 56.720024583233, 56.579454528882, 56.438884474268, 56.298314419394, 56.157744364265, 56.017174308882, 55.876604253250, 55.736034197372, 55.595464141249, 55.454894084887, 55.314324028286, 55.173753971452, 55.033183914385, 54.892613857089, 54.752043799568, 54.611473741823, 54.470903683857, 54.330333625673, 54.189763567274, 54.049193508662, 53.908623449839, 53.768053390809, 53.627483331573, 53.486913272134, 53.346343212494, 53.205773152657, 53.065203092623, 52.924633032395, 52.784062971976, 52.643492911368, 52.502922850573, 52.362352789593, 52.221782728430, 52.081212667086, 51.940642605563, 51.800072543864, 51.659502481990, 51.518932419943, 51.378362357725, 51.237792295338, 51.097222232785, 50.956652170066, 50.816082107184, 50.675512044140, 50.534941980936, 50.394371917574, 50.253801854056, 50.113231790383, 49.972661726557, 49.832091662580, 49.691521598453, 49.550951534178, 49.410381469756, 49.269811405190, 49.129241340479, 48.988671275628, 48.848101210635, 48.707531145504, 48.566961080235, 48.426391014830, 48.285820949291, 48.145250883618, 48.004680817814, 47.864110751879, 47.723540685814, 47.582970619622, 47.442400553304, 47.301830486860, 47.161260420293, 47.020690353603, 46.880120286791, 46.739550219859, 46.598980152808, 46.458410085640, 46.317840018355, 46.177269950954, 46.036699883439, 45.896129815811, 45.755559748071, 45.614989680220, 45.474419612259, 45.333849544190, 45.193279476013, 45.052709407729, 44.912139339340, 44.771569270846, 44.630999202249, 44.490429133549, 44.349859064748, 44.209288995846, 44.068718926845, 43.928148857745, 43.787578788547, 43.647008719253, 43.506438649863, 43.365868580378, 43.225298510799, 43.084728441127, 42.944158371362, 42.803588301507, 42.663018231561, 42.522448161525, 42.381878091401, 42.241308021188, 42.100737950889, 41.960167880503, 41.819597810032, 41.679027739476, 41.538457668836, 41.397887598112, 41.257317527307, 41.116747456420, 40.976177385452, 40.835607314404, 40.695037243276, 40.554467172070, 40.413897100786, 40.273327029425, 40.132756957987, 39.992186886473, 39.851616814884, 39.711046743221, 39.570476671484, 39.429906599674, 39.289336527791, 39.148766455836, 39.008196383811, 38.867626311714, 38.727056239548, 38.586486167313, 38.445916095009, 38.305346022636, 38.164775950197, 38.024205877690, 37.883635805117, 37.743065732479, 37.602495659776, 37.461925587007, 37.321355514176, 37.180785441280, 37.040215368322, 36.899645295301, 36.759075222219, 36.618505149076, 36.477935075872, 36.337365002607, 36.196794929284, 36.056224855901, 35.915654782459, 35.775084708960, 35.634514635403, 35.493944561788, 35.353374488118, 35.212804414391, 35.072234340608, 34.931664266771, 34.791094192879, 34.650524118932, 34.509954044932, 34.369383970879, 34.228813896773, 34.088243822614, 33.947673748404, 33.807103674142, 33.666533599830, 33.525963525466, 33.385393451053, 33.244823376590, 33.104253302077, 32.963683227516, 32.823113152906, 32.682543078249, 32.541973003543, 32.401402928791, 32.260832853991, 32.120262779145, 31.979692704254, 31.839122629316, 31.698552554333, 31.557982479306, 31.417412404234, 31.276842329118, 31.136272253958, 30.995702178755, 30.855132103508, 30.714562028220, 30.573991952888, 30.433421877515, 30.292851802101, 30.152281726645, 30.011711651148, 29.871141575611, 29.730571500034, 29.590001424417, 29.449431348760, 29.308861273064, 29.168291197330, 29.027721121557, 28.887151045746, 28.746580969896, 28.606010894010, 28.465440818086, 28.324870742125, 28.184300666128, 28.043730590094, 27.903160514025, 27.762590437920, 27.622020361779, 27.481450285604, 27.340880209393, 27.200310133149, 27.059740056870, 26.919169980557, 26.778599904211, 26.638029827831, 26.497459751418, 26.356889674973, 26.216319598495, 26.075749521985, 25.935179445443, 25.794609368869, 25.654039292264, 25.513469215628, 25.372899138962, 25.232329062264, 25.091758985537, 24.951188908779, 24.810618831992, 24.670048755175, 24.529478678328, 24.388908601453, 24.248338524549, 24.107768447617, 23.967198370656, 23.826628293668, 23.686058216651, 23.545488139607, 23.404918062536, 23.264347985438, 23.123777908313, 22.983207831162, 22.842637753984, 22.702067676780, 22.561497599550, 22.420927522295, 22.280357445014, 22.139787367708, 21.999217290377, 21.858647213022, 21.718077135642, 21.577507058237, 21.436936980809, 21.296366903357, 21.155796825881, 21.015226748382, 20.874656670859, 20.734086593314, 20.593516515746, 20.452946438155, 20.312376360542, 20.171806282907, 20.031236205250, 19.890666127571, 19.750096049871, 19.609525972149, 19.468955894407, 19.328385816643, 19.187815738859, 19.047245661054, 18.906675583229, 18.766105505383, 18.625535427518, 18.484965349633, 18.344395271729, 18.203825193805, 18.063255115862, 17.922685037900, 17.782114959919, 17.641544881920, 17.500974803902, 17.360404725866, 17.219834647812, 17.079264569740, 16.938694491650, 16.798124413543, 16.657554335419, 16.516984257277, 16.376414179119, 16.235844100943, 16.095274022751, 15.954703944543, 15.814133866318, 15.673563788078, 15.532993709821, 15.392423631549, 15.251853553261, 15.111283474957, 14.970713396639, 14.830143318305, 14.689573239957, 14.549003161593, 14.408433083215, 14.267863004823, 14.127292926417, 13.986722847996, 13.846152769562, 13.705582691113, 13.565012612652, 13.424442534176, 13.283872455688, 13.143302377186, 13.002732298672, 12.862162220144, 12.721592141604, 12.581022063052, 12.440451984487, 12.299881905910, 12.159311827321, 12.018741748721, 11.878171670108, 11.737601591484, 11.597031512849, 11.456461434202, 11.315891355544, 11.175321276875, 11.034751198196, 10.894181119506, 10.753611040805, 10.613040962094, 10.472470883373, 10.331900804641, 10.191330725900, 10.050760647149, 9.910190568389, 9.769620489619, 9.629050410840, 9.488480332051, 9.347910253253, 9.207340174447, 9.066770095632, 8.926200016808, 8.785629937976, 8.645059859135, 8.504489780287, 8.363919701430, 8.223349622565, 8.082779543693, 7.942209464813, 7.801639385925, 7.661069307030, 7.520499228128, 7.379929149219, 7.239359070303, 7.098788991380, 6.958218912450, 6.817648833514, 6.677078754572, 6.536508675623, 6.395938596669, 6.255368517708, 6.114798438741, 5.974228359769, 5.833658280791, 5.693088201808, 5.552518122820, 5.411948043826, 5.271377964827, 5.130807885824, 4.990237806815, 4.849667727802, 4.709097648785, 4.568527569763, 4.427957490737, 4.287387411707, 4.146817332673, 4.006247253635, 3.865677174593, 3.725107095548, 3.584537016499, 3.443966937447, 3.303396858392, 3.162826779334, 3.022256700273, 2.881686621209, 2.741116542142, 2.600546463073, 2.459976384002, 2.319406304928, 2.178836225852, 2.038266146774, 1.897696067694, 1.757125988613, 1.616555909530, 1.475985830445, 1.335415751359, 1.194845672272, 1.054275593184, 0.913705514095, 0.773135435005, 0.632565355914, 0.491995276822, 0.351425197731, 0.210855118639, 0.070285039546)) } // namespace gaussian } // namespace spacing } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/spacing/gaussian/N576.cc0000664000175000017500000002575415160212070023366 0ustar alastairalastair/* * (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. */ // TL1151 #include "atlas/grid/detail/spacing/gaussian/N.h" namespace atlas { namespace grid { namespace spacing { namespace gaussian { DEFINE_GAUSSIAN_LATITUDES( 576, LIST(89.880445682778, 89.725572955853, 89.569785621770, 89.413791638920, 89.257719269313, 89.101608968163, 88.945477468692, 88.789332932188, 88.633179809996, 88.477020735252, 88.320857364755, 88.164690792905, 88.008521771586, 87.852350834397, 87.696178370484, 87.540004670312, 87.383829955074, 87.227654396174, 87.071478128490, 86.915301259606, 86.759123876386, 86.602946049731, 86.446767838084, 86.290589290052, 86.134410446389, 85.978231341525, 85.822052004742, 85.665872461102, 85.509692732184, 85.353512836665, 85.197332790795, 85.041152608776, 84.884972303076, 84.728791884688, 84.572611363337, 84.416430747661, 84.260250045360, 84.104069263315, 83.947888407701, 83.791707484067, 83.635526497422, 83.479345452291, 83.323164352780, 83.166983202616, 83.010802005194, 82.854620763613, 82.698439480708, 82.542258159072, 82.386076801091, 82.229895408955, 82.073713984683, 81.917532530138, 81.761351047042, 81.605169536987, 81.448988001452, 81.292806441807, 81.136624859327, 80.980443255198, 80.824261630525, 80.668079986340, 80.511898323606, 80.355716643225, 80.199534946040, 80.043353232842, 79.887171504373, 79.730989761330, 79.574808004367, 79.418626234102, 79.262444451114, 79.106262655951, 78.950080849129, 78.793899031136, 78.637717202433, 78.481535363454, 78.325353514613, 78.169171656300, 78.012989788886, 77.856807912721, 77.700626028139, 77.544444135457, 77.388262234976, 77.232080326982, 77.075898411747, 76.919716489531, 76.763534560580, 76.607352625132, 76.451170683410, 76.294988735628, 76.138806781993, 75.982624822699, 75.826442857934, 75.670260887876, 75.514078912697, 75.357896932561, 75.201714947624, 75.045532958038, 74.889350963947, 74.733168965488, 74.576986962794, 74.420804955994, 74.264622945209, 74.108440930556, 73.952258912150, 73.796076890098, 73.639894864504, 73.483712835471, 73.327530803093, 73.171348767464, 73.015166728675, 72.858984686811, 72.702802641956, 72.546620594190, 72.390438543591, 72.234256490233, 72.078074434189, 71.921892375528, 71.765710314318, 71.609528250623, 71.453346184507, 71.297164116031, 71.140982045252, 70.984799972227, 70.828617897013, 70.672435819661, 70.516253740223, 70.360071658750, 70.203889575289, 70.047707489886, 69.891525402589, 69.735343313439, 69.579161222480, 69.422979129753, 69.266797035298, 69.110614939154, 68.954432841359, 68.798250741949, 68.642068640959, 68.485886538424, 68.329704434378, 68.173522328853, 68.017340221880, 67.861158113491, 67.704976003715, 67.548793892581, 67.392611780117, 67.236429666351, 67.080247551309, 66.924065435018, 66.767883317503, 66.611701198788, 66.455519078897, 66.299336957853, 66.143154835680, 65.986972712399, 65.830790588032, 65.674608462599, 65.518426336122, 65.362244208621, 65.206062080113, 65.049879950620, 64.893697820158, 64.737515688747, 64.581333556403, 64.425151423144, 64.268969288986, 64.112787153947, 63.956605018041, 63.800422881284, 63.644240743692, 63.488058605279, 63.331876466060, 63.175694326050, 63.019512185260, 62.863330043707, 62.707147901401, 62.550965758357, 62.394783614587, 62.238601470103, 62.082419324918, 61.926237179043, 61.770055032489, 61.613872885268, 61.457690737392, 61.301508588870, 61.145326439713, 60.989144289931, 60.832962139536, 60.676779988535, 60.520597836940, 60.364415684760, 60.208233532004, 60.052051378680, 59.895869224799, 59.739687070368, 59.583504915396, 59.427322759892, 59.271140603863, 59.114958447319, 58.958776290265, 58.802594132711, 58.646411974664, 58.490229816131, 58.334047657120, 58.177865497637, 58.021683337689, 57.865501177284, 57.709319016429, 57.553136855128, 57.396954693390, 57.240772531221, 57.084590368626, 56.928408205612, 56.772226042184, 56.616043878350, 56.459861714113, 56.303679549481, 56.147497384458, 55.991315219050, 55.835133053263, 55.678950887101, 55.522768720570, 55.366586553675, 55.210404386421, 55.054222218812, 54.898040050854, 54.741857882551, 54.585675713908, 54.429493544930, 54.273311375620, 54.117129205983, 53.960947036024, 53.804764865747, 53.648582695156, 53.492400524255, 53.336218353048, 53.180036181539, 53.023854009733, 52.867671837632, 52.711489665240, 52.555307492562, 52.399125319601, 52.242943146360, 52.086760972843, 51.930578799054, 51.774396624996, 51.618214450672, 51.462032276086, 51.305850101240, 51.149667926139, 50.993485750784, 50.837303575180, 50.681121399330, 50.524939223235, 50.368757046900, 50.212574870327, 50.056392693519, 49.900210516479, 49.744028339210, 49.587846161714, 49.431663983994, 49.275481806052, 49.119299627893, 48.963117449517, 48.806935270927, 48.650753092126, 48.494570913117, 48.338388733901, 48.182206554482, 48.026024374861, 47.869842195041, 47.713660015024, 47.557477834813, 47.401295654409, 47.245113473815, 47.088931293033, 46.932749112065, 46.776566930913, 46.620384749579, 46.464202568066, 46.308020386375, 46.151838204508, 45.995656022467, 45.839473840254, 45.683291657872, 45.527109475321, 45.370927292603, 45.214745109722, 45.058562926678, 44.902380743472, 44.746198560108, 44.590016376586, 44.433834192908, 44.277652009077, 44.121469825092, 43.965287640958, 43.809105456673, 43.652923272242, 43.496741087664, 43.340558902942, 43.184376718076, 43.028194533070, 42.872012347923, 42.715830162638, 42.559647977216, 42.403465791658, 42.247283605966, 42.091101420141, 41.934919234185, 41.778737048098, 41.622554861883, 41.466372675541, 41.310190489072, 41.154008302479, 40.997826115762, 40.841643928923, 40.685461741962, 40.529279554882, 40.373097367684, 40.216915180368, 40.060732992935, 39.904550805388, 39.748368617727, 39.592186429953, 39.436004242067, 39.279822054071, 39.123639865966, 38.967457677752, 38.811275489432, 38.655093301005, 38.498911112472, 38.342728923836, 38.186546735097, 38.030364546256, 37.874182357314, 37.718000168271, 37.561817979130, 37.405635789891, 37.249453600554, 37.093271411121, 36.937089221594, 36.780907031971, 36.624724842256, 36.468542652448, 36.312360462548, 36.156178272558, 35.999996082478, 35.843813892309, 35.687631702051, 35.531449511707, 35.375267321276, 35.219085130760, 35.062902940159, 34.906720749474, 34.750538558705, 34.594356367855, 34.438174176923, 34.281991985910, 34.125809794817, 33.969627603645, 33.813445412394, 33.657263221065, 33.501081029660, 33.344898838178, 33.188716646621, 33.032534454989, 32.876352263283, 32.720170071503, 32.563987879651, 32.407805687726, 32.251623495730, 32.095441303664, 31.939259111527, 31.783076919322, 31.626894727047, 31.470712534704, 31.314530342294, 31.158348149817, 31.002165957274, 30.845983764666, 30.689801571992, 30.533619379254, 30.377437186453, 30.221254993588, 30.065072800661, 29.908890607672, 29.752708414622, 29.596526221510, 29.440344028339, 29.284161835108, 29.127979641818, 28.971797448469, 28.815615255063, 28.659433061599, 28.503250868078, 28.347068674501, 28.190886480867, 28.034704287179, 27.878522093436, 27.722339899639, 27.566157705788, 27.409975511883, 27.253793317926, 27.097611123917, 26.941428929856, 26.785246735744, 26.629064541581, 26.472882347367, 26.316700153104, 26.160517958792, 26.004335764431, 25.848153570021, 25.691971375563, 25.535789181058, 25.379606986506, 25.223424791908, 25.067242597263, 24.911060402572, 24.754878207837, 24.598696013056, 24.442513818231, 24.286331623362, 24.130149428450, 23.973967233495, 23.817785038496, 23.661602843456, 23.505420648374, 23.349238453250, 23.193056258085, 23.036874062880, 22.880691867634, 22.724509672349, 22.568327477024, 22.412145281660, 22.255963086258, 22.099780890817, 21.943598695338, 21.787416499822, 21.631234304268, 21.475052108678, 21.318869913051, 21.162687717389, 21.006505521690, 20.850323325957, 20.694141130188, 20.537958934385, 20.381776738547, 20.225594542676, 20.069412346771, 19.913230150833, 19.757047954862, 19.600865758859, 19.444683562824, 19.288501366756, 19.132319170658, 18.976136974528, 18.819954778367, 18.663772582176, 18.507590385954, 18.351408189703, 18.195225993422, 18.039043797112, 17.882861600774, 17.726679404406, 17.570497208011, 17.414315011587, 17.258132815136, 17.101950618658, 16.945768422153, 16.789586225620, 16.633404029062, 16.477221832478, 16.321039635867, 16.164857439232, 16.008675242571, 15.852493045885, 15.696310849174, 15.540128652440, 15.383946455681, 15.227764258899, 15.071582062093, 14.915399865264, 14.759217668412, 14.603035471538, 14.446853274641, 14.290671077722, 14.134488880782, 13.978306683820, 13.822124486837, 13.665942289833, 13.509760092808, 13.353577895763, 13.197395698698, 13.041213501613, 12.885031304509, 12.728849107385, 12.572666910242, 12.416484713080, 12.260302515900, 12.104120318702, 11.947938121486, 11.791755924252, 11.635573727000, 11.479391529732, 11.323209332446, 11.167027135144, 11.010844937825, 10.854662740490, 10.698480543140, 10.542298345773, 10.386116148391, 10.229933950994, 10.073751753582, 9.917569556155, 9.761387358714, 9.605205161259, 9.449022963790, 9.292840766307, 9.136658568810, 8.980476371300, 8.824294173778, 8.668111976242, 8.511929778694, 8.355747581134, 8.199565383562, 8.043383185978, 7.887200988383, 7.731018790776, 7.574836593158, 7.418654395529, 7.262472197890, 7.106290000240, 6.950107802580, 6.793925604910, 6.637743407231, 6.481561209542, 6.325379011844, 6.169196814136, 6.013014616420, 5.856832418696, 5.700650220963, 5.544468023222, 5.388285825474, 5.232103627717, 5.075921429954, 4.919739232183, 4.763557034405, 4.607374836620, 4.451192638829, 4.295010441032, 4.138828243229, 3.982646045419, 3.826463847605, 3.670281649784, 3.514099451959, 3.357917254129, 3.201735056294, 3.045552858454, 2.889370660610, 2.733188462763, 2.577006264911, 2.420824067056, 2.264641869197, 2.108459671335, 1.952277473470, 1.796095275603, 1.639913077733, 1.483730879860, 1.327548681986, 1.171366484109, 1.015184286231, 0.859002088352, 0.702819890472, 0.546637692590, 0.390455494708, 0.234273296825, 0.078091098942)) } // namespace gaussian } // namespace spacing } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/spacing/gaussian/N2000.cc0000664000175000017500000011103115160212070023406 0ustar alastairalastair/* * (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. */ // TL3999 #include "atlas/grid/detail/spacing/gaussian/N.h" namespace atlas { namespace grid { namespace spacing { namespace gaussian { DEFINE_GAUSSIAN_LATITUDES( 2000, LIST(89.965557716640, 89.920940588056, 89.876059971209, 89.831119821127, 89.786157088682, 89.741183428577, 89.696203661469, 89.651220138535, 89.606234142158, 89.561246430917, 89.516257482116, 89.471267611042, 89.426277034302, 89.381285905620, 89.336294337107, 89.291302412440, 89.246310195345, 89.201317735199, 89.156325070860, 89.111332233323, 89.066339247611, 89.021346134150, 88.976352909776, 88.931359588494, 88.886366182044, 88.841372700344, 88.796379151831, 88.751385543725, 88.706391882242, 88.661398172763, 88.616404419970, 88.571410627953, 88.526416800305, 88.481422940193, 88.436429050418, 88.391435133469, 88.346441191564, 88.301447226685, 88.256453240612, 88.211459234943, 88.166465211121, 88.121471170450, 88.076477114112, 88.031483043182, 87.986488958637, 87.941494861370, 87.896500752197, 87.851506631867, 87.806512501066, 87.761518360424, 87.716524210525, 87.671530051904, 87.626535885058, 87.581541710445, 87.536547528491, 87.491553339591, 87.446559144112, 87.401564942396, 87.356570734762, 87.311576521506, 87.266582302906, 87.221588079222, 87.176593850696, 87.131599617557, 87.086605380019, 87.041611138281, 86.996616892534, 86.951622642953, 86.906628389707, 86.861634132954, 86.816639872841, 86.771645609509, 86.726651343092, 86.681657073714, 86.636662801495, 86.591668526547, 86.546674248976, 86.501679968884, 86.456685686367, 86.411691401516, 86.366697114417, 86.321702825154, 86.276708533805, 86.231714240444, 86.186719945143, 86.141725647969, 86.096731348988, 86.051737048260, 86.006742745846, 85.961748441801, 85.916754136180, 85.871759829034, 85.826765520411, 85.781771210361, 85.736776898926, 85.691782586152, 85.646788272080, 85.601793956749, 85.556799640198, 85.511805322463, 85.466811003580, 85.421816683582, 85.376822362502, 85.331828040372, 85.286833717221, 85.241839393078, 85.196845067971, 85.151850741928, 85.106856414973, 85.061862087133, 85.016867758430, 84.971873428888, 84.926879098529, 84.881884767375, 84.836890435447, 84.791896102764, 84.746901769347, 84.701907435213, 84.656913100381, 84.611918764868, 84.566924428692, 84.521930091868, 84.476935754413, 84.431941416342, 84.386947077669, 84.341952738409, 84.296958398576, 84.251964058183, 84.206969717244, 84.161975375770, 84.116981033775, 84.071986691270, 84.026992348266, 83.981998004775, 83.937003660808, 83.892009316375, 83.847014971487, 83.802020626152, 83.757026280382, 83.712031934185, 83.667037587570, 83.622043240547, 83.577048893124, 83.532054545308, 83.487060197109, 83.442065848534, 83.397071499592, 83.352077150288, 83.307082800631, 83.262088450628, 83.217094100285, 83.172099749610, 83.127105398609, 83.082111047287, 83.037116695653, 82.992122343710, 82.947127991466, 82.902133638926, 82.857139286096, 82.812144932981, 82.767150579586, 82.722156225917, 82.677161871979, 82.632167517776, 82.587173163314, 82.542178808596, 82.497184453629, 82.452190098415, 82.407195742961, 82.362201387269, 82.317207031344, 82.272212675190, 82.227218318811, 82.182223962211, 82.137229605394, 82.092235248363, 82.047240891122, 82.002246533675, 81.957252176025, 81.912257818176, 81.867263460130, 81.822269101891, 81.777274743462, 81.732280384847, 81.687286026048, 81.642291667068, 81.597297307910, 81.552302948577, 81.507308589073, 81.462314229398, 81.417319869557, 81.372325509552, 81.327331149384, 81.282336789058, 81.237342428575, 81.192348067938, 81.147353707149, 81.102359346210, 81.057364985124, 81.012370623893, 80.967376262518, 80.922381901003, 80.877387539349, 80.832393177558, 80.787398815633, 80.742404453575, 80.697410091386, 80.652415729068, 80.607421366622, 80.562427004052, 80.517432641358, 80.472438278542, 80.427443915607, 80.382449552553, 80.337455189382, 80.292460826096, 80.247466462697, 80.202472099185, 80.157477735564, 80.112483371833, 80.067489007995, 80.022494644051, 79.977500280003, 79.932505915852, 79.887511551598, 79.842517187245, 79.797522822792, 79.752528458242, 79.707534093595, 79.662539728853, 79.617545364016, 79.572550999088, 79.527556634067, 79.482562268956, 79.437567903756, 79.392573538468, 79.347579173093, 79.302584807632, 79.257590442086, 79.212596076457, 79.167601710745, 79.122607344951, 79.077612979076, 79.032618613122, 78.987624247089, 78.942629880979, 78.897635514791, 78.852641148528, 78.807646782190, 78.762652415777, 78.717658049292, 78.672663682734, 78.627669316105, 78.582674949405, 78.537680582636, 78.492686215798, 78.447691848891, 78.402697481917, 78.357703114877, 78.312708747771, 78.267714380599, 78.222720013364, 78.177725646065, 78.132731278703, 78.087736911279, 78.042742543794, 77.997748176248, 77.952753808642, 77.907759440976, 77.862765073252, 77.817770705470, 77.772776337630, 77.727781969733, 77.682787601781, 77.637793233773, 77.592798865709, 77.547804497592, 77.502810129421, 77.457815761196, 77.412821392919, 77.367827024590, 77.322832656210, 77.277838287778, 77.232843919296, 77.187849550764, 77.142855182183, 77.097860813553, 77.052866444875, 77.007872076149, 76.962877707375, 76.917883338555, 76.872888969688, 76.827894600776, 76.782900231818, 76.737905862815, 76.692911493767, 76.647917124676, 76.602922755540, 76.557928386362, 76.512934017141, 76.467939647878, 76.422945278573, 76.377950909226, 76.332956539838, 76.287962170410, 76.242967800941, 76.197973431433, 76.152979061885, 76.107984692297, 76.062990322672, 76.017995953007, 75.973001583305, 75.928007213566, 75.883012843789, 75.838018473975, 75.793024104124, 75.748029734238, 75.703035364315, 75.658040994357, 75.613046624364, 75.568052254336, 75.523057884273, 75.478063514177, 75.433069144046, 75.388074773882, 75.343080403684, 75.298086033454, 75.253091663191, 75.208097292895, 75.163102922567, 75.118108552208, 75.073114181817, 75.028119811395, 74.983125440942, 74.938131070459, 74.893136699945, 74.848142329401, 74.803147958827, 74.758153588223, 74.713159217590, 74.668164846929, 74.623170476238, 74.578176105519, 74.533181734771, 74.488187363996, 74.443192993192, 74.398198622361, 74.353204251503, 74.308209880618, 74.263215509706, 74.218221138767, 74.173226767802, 74.128232396810, 74.083238025793, 74.038243654750, 73.993249283681, 73.948254912587, 73.903260541468, 73.858266170324, 73.813271799156, 73.768277427963, 73.723283056746, 73.678288685504, 73.633294314239, 73.588299942950, 73.543305571638, 73.498311200302, 73.453316828943, 73.408322457562, 73.363328086157, 73.318333714730, 73.273339343281, 73.228344971810, 73.183350600316, 73.138356228801, 73.093361857264, 73.048367485706, 73.003373114126, 72.958378742525, 72.913384370904, 72.868389999261, 72.823395627598, 72.778401255914, 72.733406884210, 72.688412512486, 72.643418140742, 72.598423768978, 72.553429397194, 72.508435025391, 72.463440653568, 72.418446281727, 72.373451909866, 72.328457537986, 72.283463166087, 72.238468794170, 72.193474422234, 72.148480050280, 72.103485678307, 72.058491306317, 72.013496934308, 71.968502562282, 71.923508190238, 71.878513818176, 71.833519446097, 71.788525074001, 71.743530701887, 71.698536329757, 71.653541957609, 71.608547585445, 71.563553213264, 71.518558841066, 71.473564468852, 71.428570096622, 71.383575724376, 71.338581352113, 71.293586979834, 71.248592607540, 71.203598235230, 71.158603862904, 71.113609490563, 71.068615118206, 71.023620745834, 70.978626373447, 70.933632001045, 70.888637628628, 70.843643256196, 70.798648883749, 70.753654511288, 70.708660138812, 70.663665766321, 70.618671393817, 70.573677021298, 70.528682648765, 70.483688276217, 70.438693903656, 70.393699531081, 70.348705158492, 70.303710785890, 70.258716413274, 70.213722040644, 70.168727668002, 70.123733295345, 70.078738922676, 70.033744549993, 69.988750177298, 69.943755804589, 69.898761431868, 69.853767059134, 69.808772686387, 69.763778313628, 69.718783940856, 69.673789568072, 69.628795195275, 69.583800822466, 69.538806449645, 69.493812076812, 69.448817703966, 69.403823331109, 69.358828958240, 69.313834585359, 69.268840212466, 69.223845839562, 69.178851466646, 69.133857093719, 69.088862720780, 69.043868347830, 68.998873974869, 68.953879601897, 68.908885228913, 68.863890855918, 68.818896482913, 68.773902109896, 68.728907736869, 68.683913363831, 68.638918990782, 68.593924617722, 68.548930244652, 68.503935871572, 68.458941498480, 68.413947125379, 68.368952752267, 68.323958379145, 68.278964006013, 68.233969632871, 68.188975259719, 68.143980886557, 68.098986513384, 68.053992140202, 68.008997767010, 67.964003393809, 67.919009020597, 67.874014647376, 67.829020274146, 67.784025900906, 67.739031527656, 67.694037154397, 67.649042781129, 67.604048407852, 67.559054034565, 67.514059661269, 67.469065287964, 67.424070914650, 67.379076541327, 67.334082167995, 67.289087794654, 67.244093421304, 67.199099047946, 67.154104674578, 67.109110301202, 67.064115927817, 67.019121554424, 66.974127181022, 66.929132807612, 66.884138434193, 66.839144060766, 66.794149687331, 66.749155313887, 66.704160940435, 66.659166566975, 66.614172193507, 66.569177820030, 66.524183446546, 66.479189073053, 66.434194699553, 66.389200326044, 66.344205952528, 66.299211579004, 66.254217205472, 66.209222831933, 66.164228458385, 66.119234084830, 66.074239711268, 66.029245337698, 65.984250964120, 65.939256590535, 65.894262216943, 65.849267843343, 65.804273469735, 65.759279096121, 65.714284722499, 65.669290348870, 65.624295975233, 65.579301601590, 65.534307227939, 65.489312854282, 65.444318480617, 65.399324106945, 65.354329733267, 65.309335359581, 65.264340985889, 65.219346612190, 65.174352238484, 65.129357864771, 65.084363491051, 65.039369117325, 64.994374743592, 64.949380369852, 64.904385996106, 64.859391622354, 64.814397248594, 64.769402874829, 64.724408501057, 64.679414127278, 64.634419753493, 64.589425379702, 64.544431005904, 64.499436632100, 64.454442258290, 64.409447884474, 64.364453510652, 64.319459136823, 64.274464762988, 64.229470389147, 64.184476015301, 64.139481641448, 64.094487267589, 64.049492893724, 64.004498519853, 63.959504145977, 63.914509772094, 63.869515398206, 63.824521024312, 63.779526650412, 63.734532276506, 63.689537902595, 63.644543528678, 63.599549154755, 63.554554780827, 63.509560406893, 63.464566032953, 63.419571659008, 63.374577285058, 63.329582911102, 63.284588537140, 63.239594163174, 63.194599789201, 63.149605415224, 63.104611041241, 63.059616667252, 63.014622293259, 62.969627919260, 62.924633545256, 62.879639171246, 62.834644797232, 62.789650423212, 62.744656049187, 62.699661675157, 62.654667301123, 62.609672927082, 62.564678553037, 62.519684178987, 62.474689804932, 62.429695430872, 62.384701056807, 62.339706682737, 62.294712308663, 62.249717934583, 62.204723560498, 62.159729186409, 62.114734812315, 62.069740438216, 62.024746064113, 61.979751690004, 61.934757315891, 61.889762941774, 61.844768567651, 61.799774193524, 61.754779819393, 61.709785445257, 61.664791071116, 61.619796696970, 61.574802322821, 61.529807948666, 61.484813574508, 61.439819200344, 61.394824826177, 61.349830452005, 61.304836077828, 61.259841703647, 61.214847329462, 61.169852955272, 61.124858581079, 61.079864206880, 61.034869832678, 60.989875458471, 60.944881084260, 60.899886710045, 60.854892335826, 60.809897961603, 60.764903587375, 60.719909213143, 60.674914838907, 60.629920464667, 60.584926090423, 60.539931716175, 60.494937341923, 60.449942967667, 60.404948593406, 60.359954219142, 60.314959844874, 60.269965470602, 60.224971096326, 60.179976722046, 60.134982347762, 60.089987973475, 60.044993599183, 59.999999224888, 59.955004850588, 59.910010476285, 59.865016101979, 59.820021727668, 59.775027353354, 59.730032979036, 59.685038604714, 59.640044230388, 59.595049856059, 59.550055481726, 59.505061107390, 59.460066733049, 59.415072358706, 59.370077984358, 59.325083610007, 59.280089235653, 59.235094861295, 59.190100486933, 59.145106112568, 59.100111738199, 59.055117363827, 59.010122989451, 58.965128615072, 58.920134240689, 58.875139866303, 58.830145491914, 58.785151117521, 58.740156743125, 58.695162368725, 58.650167994322, 58.605173619916, 58.560179245506, 58.515184871093, 58.470190496677, 58.425196122257, 58.380201747834, 58.335207373408, 58.290212998979, 58.245218624546, 58.200224250110, 58.155229875671, 58.110235501229, 58.065241126784, 58.020246752335, 57.975252377883, 57.930258003428, 57.885263628970, 57.840269254509, 57.795274880045, 57.750280505578, 57.705286131108, 57.660291756634, 57.615297382158, 57.570303007678, 57.525308633196, 57.480314258710, 57.435319884222, 57.390325509730, 57.345331135236, 57.300336760738, 57.255342386238, 57.210348011735, 57.165353637228, 57.120359262719, 57.075364888207, 57.030370513692, 56.985376139174, 56.940381764654, 56.895387390130, 56.850393015604, 56.805398641075, 56.760404266543, 56.715409892008, 56.670415517470, 56.625421142930, 56.580426768387, 56.535432393841, 56.490438019292, 56.445443644741, 56.400449270187, 56.355454895630, 56.310460521071, 56.265466146508, 56.220471771943, 56.175477397376, 56.130483022806, 56.085488648233, 56.040494273657, 55.995499899079, 55.950505524499, 55.905511149915, 55.860516775329, 55.815522400741, 55.770528026150, 55.725533651556, 55.680539276960, 55.635544902361, 55.590550527760, 55.545556153156, 55.500561778550, 55.455567403941, 55.410573029329, 55.365578654716, 55.320584280099, 55.275589905481, 55.230595530859, 55.185601156236, 55.140606781610, 55.095612406981, 55.050618032350, 55.005623657717, 54.960629283081, 54.915634908443, 54.870640533803, 54.825646159160, 54.780651784515, 54.735657409867, 54.690663035217, 54.645668660565, 54.600674285910, 54.555679911253, 54.510685536594, 54.465691161933, 54.420696787269, 54.375702412603, 54.330708037935, 54.285713663264, 54.240719288591, 54.195724913916, 54.150730539239, 54.105736164559, 54.060741789877, 54.015747415193, 53.970753040507, 53.925758665819, 53.880764291128, 53.835769916436, 53.790775541741, 53.745781167044, 53.700786792344, 53.655792417643, 53.610798042940, 53.565803668234, 53.520809293526, 53.475814918816, 53.430820544104, 53.385826169390, 53.340831794674, 53.295837419956, 53.250843045236, 53.205848670513, 53.160854295789, 53.115859921062, 53.070865546334, 53.025871171603, 52.980876796871, 52.935882422136, 52.890888047400, 52.845893672661, 52.800899297920, 52.755904923178, 52.710910548433, 52.665916173687, 52.620921798938, 52.575927424188, 52.530933049435, 52.485938674681, 52.440944299925, 52.395949925166, 52.350955550406, 52.305961175644, 52.260966800880, 52.215972426114, 52.170978051346, 52.125983676577, 52.080989301805, 52.035994927032, 51.991000552256, 51.946006177479, 51.901011802700, 51.856017427919, 51.811023053137, 51.766028678352, 51.721034303566, 51.676039928777, 51.631045553987, 51.586051179196, 51.541056804402, 51.496062429606, 51.451068054809, 51.406073680010, 51.361079305209, 51.316084930407, 51.271090555603, 51.226096180796, 51.181101805989, 51.136107431179, 51.091113056368, 51.046118681555, 51.001124306740, 50.956129931923, 50.911135557105, 50.866141182285, 50.821146807464, 50.776152432640, 50.731158057815, 50.686163682989, 50.641169308160, 50.596174933330, 50.551180558498, 50.506186183665, 50.461191808830, 50.416197433993, 50.371203059155, 50.326208684315, 50.281214309473, 50.236219934630, 50.191225559785, 50.146231184939, 50.101236810091, 50.056242435241, 50.011248060390, 49.966253685537, 49.921259310682, 49.876264935826, 49.831270560969, 49.786276186110, 49.741281811249, 49.696287436387, 49.651293061523, 49.606298686657, 49.561304311790, 49.516309936922, 49.471315562052, 49.426321187180, 49.381326812307, 49.336332437432, 49.291338062556, 49.246343687679, 49.201349312800, 49.156354937919, 49.111360563037, 49.066366188153, 49.021371813268, 48.976377438381, 48.931383063493, 48.886388688604, 48.841394313713, 48.796399938820, 48.751405563927, 48.706411189031, 48.661416814134, 48.616422439236, 48.571428064337, 48.526433689435, 48.481439314533, 48.436444939629, 48.391450564724, 48.346456189817, 48.301461814909, 48.256467439999, 48.211473065088, 48.166478690176, 48.121484315262, 48.076489940347, 48.031495565431, 47.986501190513, 47.941506815594, 47.896512440673, 47.851518065751, 47.806523690828, 47.761529315903, 47.716534940977, 47.671540566050, 47.626546191121, 47.581551816191, 47.536557441260, 47.491563066327, 47.446568691393, 47.401574316458, 47.356579941521, 47.311585566583, 47.266591191644, 47.221596816704, 47.176602441762, 47.131608066819, 47.086613691874, 47.041619316929, 46.996624941982, 46.951630567034, 46.906636192084, 46.861641817133, 46.816647442181, 46.771653067228, 46.726658692274, 46.681664317318, 46.636669942361, 46.591675567403, 46.546681192443, 46.501686817482, 46.456692442521, 46.411698067557, 46.366703692593, 46.321709317627, 46.276714942661, 46.231720567693, 46.186726192723, 46.141731817753, 46.096737442781, 46.051743067809, 46.006748692835, 45.961754317859, 45.916759942883, 45.871765567906, 45.826771192927, 45.781776817947, 45.736782442966, 45.691788067984, 45.646793693000, 45.601799318016, 45.556804943030, 45.511810568043, 45.466816193055, 45.421821818066, 45.376827443076, 45.331833068085, 45.286838693092, 45.241844318099, 45.196849943104, 45.151855568108, 45.106861193111, 45.061866818113, 45.016872443114, 44.971878068113, 44.926883693112, 44.881889318109, 44.836894943106, 44.791900568101, 44.746906193095, 44.701911818088, 44.656917443080, 44.611923068071, 44.566928693061, 44.521934318050, 44.476939943038, 44.431945568025, 44.386951193010, 44.341956817995, 44.296962442978, 44.251968067961, 44.206973692942, 44.161979317923, 44.116984942902, 44.071990567880, 44.026996192857, 43.982001817834, 43.937007442809, 43.892013067783, 43.847018692756, 43.802024317728, 43.757029942699, 43.712035567670, 43.667041192639, 43.622046817607, 43.577052442574, 43.532058067540, 43.487063692505, 43.442069317469, 43.397074942432, 43.352080567394, 43.307086192355, 43.262091817315, 43.217097442275, 43.172103067233, 43.127108692190, 43.082114317146, 43.037119942102, 42.992125567056, 42.947131192009, 42.902136816962, 42.857142441913, 42.812148066863, 42.767153691813, 42.722159316762, 42.677164941709, 42.632170566656, 42.587176191602, 42.542181816546, 42.497187441490, 42.452193066433, 42.407198691375, 42.362204316316, 42.317209941257, 42.272215566196, 42.227221191134, 42.182226816072, 42.137232441008, 42.092238065944, 42.047243690878, 42.002249315812, 41.957254940745, 41.912260565677, 41.867266190608, 41.822271815539, 41.777277440468, 41.732283065396, 41.687288690324, 41.642294315251, 41.597299940176, 41.552305565101, 41.507311190025, 41.462316814949, 41.417322439871, 41.372328064792, 41.327333689713, 41.282339314633, 41.237344939551, 41.192350564469, 41.147356189387, 41.102361814303, 41.057367439218, 41.012373064133, 40.967378689047, 40.922384313960, 40.877389938872, 40.832395563783, 40.787401188693, 40.742406813603, 40.697412438512, 40.652418063420, 40.607423688327, 40.562429313233, 40.517434938139, 40.472440563043, 40.427446187947, 40.382451812850, 40.337457437752, 40.292463062654, 40.247468687554, 40.202474312454, 40.157479937353, 40.112485562251, 40.067491187149, 40.022496812045, 39.977502436941, 39.932508061836, 39.887513686731, 39.842519311624, 39.797524936517, 39.752530561409, 39.707536186300, 39.662541811190, 39.617547436080, 39.572553060968, 39.527558685857, 39.482564310744, 39.437569935630, 39.392575560516, 39.347581185401, 39.302586810285, 39.257592435169, 39.212598060052, 39.167603684934, 39.122609309815, 39.077614934695, 39.032620559575, 38.987626184454, 38.942631809332, 38.897637434210, 38.852643059087, 38.807648683963, 38.762654308838, 38.717659933713, 38.672665558587, 38.627671183460, 38.582676808332, 38.537682433204, 38.492688058075, 38.447693682945, 38.402699307815, 38.357704932684, 38.312710557552, 38.267716182419, 38.222721807286, 38.177727432152, 38.132733057017, 38.087738681882, 38.042744306746, 37.997749931609, 37.952755556471, 37.907761181333, 37.862766806194, 37.817772431055, 37.772778055915, 37.727783680774, 37.682789305632, 37.637794930490, 37.592800555347, 37.547806180203, 37.502811805059, 37.457817429914, 37.412823054768, 37.367828679622, 37.322834304475, 37.277839929327, 37.232845554179, 37.187851179030, 37.142856803880, 37.097862428730, 37.052868053579, 37.007873678428, 36.962879303275, 36.917884928122, 36.872890552969, 36.827896177815, 36.782901802660, 36.737907427504, 36.692913052348, 36.647918677191, 36.602924302034, 36.557929926876, 36.512935551717, 36.467941176558, 36.422946801398, 36.377952426237, 36.332958051076, 36.287963675914, 36.242969300752, 36.197974925589, 36.152980550425, 36.107986175261, 36.062991800096, 36.017997424930, 35.973003049764, 35.928008674597, 35.883014299430, 35.838019924262, 35.793025549093, 35.748031173924, 35.703036798754, 35.658042423584, 35.613048048413, 35.568053673241, 35.523059298069, 35.478064922896, 35.433070547723, 35.388076172549, 35.343081797374, 35.298087422199, 35.253093047024, 35.208098671847, 35.163104296670, 35.118109921493, 35.073115546315, 35.028121171136, 34.983126795957, 34.938132420777, 34.893138045597, 34.848143670416, 34.803149295234, 34.758154920052, 34.713160544869, 34.668166169686, 34.623171794502, 34.578177419318, 34.533183044133, 34.488188668948, 34.443194293762, 34.398199918575, 34.353205543388, 34.308211168200, 34.263216793012, 34.218222417823, 34.173228042634, 34.128233667444, 34.083239292254, 34.038244917063, 33.993250541871, 33.948256166679, 33.903261791486, 33.858267416293, 33.813273041099, 33.768278665905, 33.723284290711, 33.678289915515, 33.633295540319, 33.588301165123, 33.543306789926, 33.498312414729, 33.453318039531, 33.408323664332, 33.363329289133, 33.318334913934, 33.273340538734, 33.228346163533, 33.183351788332, 33.138357413131, 33.093363037929, 33.048368662726, 33.003374287523, 32.958379912319, 32.913385537115, 32.868391161911, 32.823396786705, 32.778402411500, 32.733408036294, 32.688413661087, 32.643419285880, 32.598424910672, 32.553430535464, 32.508436160255, 32.463441785046, 32.418447409837, 32.373453034627, 32.328458659416, 32.283464284205, 32.238469908993, 32.193475533781, 32.148481158569, 32.103486783356, 32.058492408142, 32.013498032928, 31.968503657714, 31.923509282499, 31.878514907283, 31.833520532067, 31.788526156851, 31.743531781634, 31.698537406417, 31.653543031199, 31.608548655981, 31.563554280762, 31.518559905543, 31.473565530323, 31.428571155103, 31.383576779882, 31.338582404661, 31.293588029440, 31.248593654218, 31.203599278995, 31.158604903772, 31.113610528549, 31.068616153325, 31.023621778101, 30.978627402876, 30.933633027651, 30.888638652425, 30.843644277199, 30.798649901973, 30.753655526746, 30.708661151519, 30.663666776291, 30.618672401062, 30.573678025834, 30.528683650604, 30.483689275375, 30.438694900145, 30.393700524914, 30.348706149683, 30.303711774452, 30.258717399220, 30.213723023988, 30.168728648755, 30.123734273522, 30.078739898289, 30.033745523055, 29.988751147821, 29.943756772586, 29.898762397351, 29.853768022115, 29.808773646879, 29.763779271642, 29.718784896406, 29.673790521168, 29.628796145931, 29.583801770692, 29.538807395454, 29.493813020215, 29.448818644975, 29.403824269736, 29.358829894496, 29.313835519255, 29.268841144014, 29.223846768772, 29.178852393531, 29.133858018288, 29.088863643046, 29.043869267803, 28.998874892559, 28.953880517315, 28.908886142071, 28.863891766827, 28.818897391582, 28.773903016336, 28.728908641090, 28.683914265844, 28.638919890598, 28.593925515351, 28.548931140103, 28.503936764855, 28.458942389607, 28.413948014359, 28.368953639110, 28.323959263860, 28.278964888611, 28.233970513361, 28.188976138110, 28.143981762859, 28.098987387608, 28.053993012356, 28.008998637104, 27.964004261852, 27.919009886599, 27.874015511346, 27.829021136093, 27.784026760839, 27.739032385585, 27.694038010330, 27.649043635075, 27.604049259820, 27.559054884564, 27.514060509308, 27.469066134051, 27.424071758795, 27.379077383538, 27.334083008280, 27.289088633022, 27.244094257764, 27.199099882505, 27.154105507246, 27.109111131987, 27.064116756727, 27.019122381467, 26.974128006207, 26.929133630946, 26.884139255685, 26.839144880423, 26.794150505162, 26.749156129900, 26.704161754637, 26.659167379374, 26.614173004111, 26.569178628847, 26.524184253584, 26.479189878319, 26.434195503055, 26.389201127790, 26.344206752525, 26.299212377259, 26.254218001993, 26.209223626727, 26.164229251460, 26.119234876193, 26.074240500926, 26.029246125658, 25.984251750390, 25.939257375122, 25.894262999853, 25.849268624584, 25.804274249315, 25.759279874046, 25.714285498776, 25.669291123505, 25.624296748235, 25.579302372964, 25.534307997693, 25.489313622421, 25.444319247149, 25.399324871877, 25.354330496604, 25.309336121332, 25.264341746058, 25.219347370785, 25.174352995511, 25.129358620237, 25.084364244962, 25.039369869688, 24.994375494413, 24.949381119137, 24.904386743862, 24.859392368586, 24.814397993309, 24.769403618033, 24.724409242756, 24.679414867478, 24.634420492201, 24.589426116923, 24.544431741645, 24.499437366366, 24.454442991088, 24.409448615808, 24.364454240529, 24.319459865249, 24.274465489969, 24.229471114689, 24.184476739409, 24.139482364128, 24.094487988847, 24.049493613565, 24.004499238283, 23.959504863001, 23.914510487719, 23.869516112436, 23.824521737153, 23.779527361870, 23.734532986587, 23.689538611303, 23.644544236019, 23.599549860734, 23.554555485450, 23.509561110165, 23.464566734879, 23.419572359594, 23.374577984308, 23.329583609022, 23.284589233736, 23.239594858449, 23.194600483162, 23.149606107875, 23.104611732587, 23.059617357299, 23.014622982011, 22.969628606723, 22.924634231434, 22.879639856146, 22.834645480856, 22.789651105567, 22.744656730277, 22.699662354987, 22.654667979697, 22.609673604406, 22.564679229116, 22.519684853825, 22.474690478533, 22.429696103242, 22.384701727950, 22.339707352658, 22.294712977365, 22.249718602073, 22.204724226780, 22.159729851487, 22.114735476193, 22.069741100899, 22.024746725605, 21.979752350311, 21.934757975017, 21.889763599722, 21.844769224427, 21.799774849132, 21.754780473836, 21.709786098540, 21.664791723244, 21.619797347948, 21.574802972652, 21.529808597355, 21.484814222058, 21.439819846761, 21.394825471463, 21.349831096165, 21.304836720867, 21.259842345569, 21.214847970270, 21.169853594972, 21.124859219673, 21.079864844373, 21.034870469074, 20.989876093774, 20.944881718474, 20.899887343174, 20.854892967873, 20.809898592573, 20.764904217272, 20.719909841970, 20.674915466669, 20.629921091367, 20.584926716065, 20.539932340763, 20.494937965461, 20.449943590158, 20.404949214855, 20.359954839552, 20.314960464249, 20.269966088945, 20.224971713642, 20.179977338338, 20.134982963033, 20.089988587729, 20.044994212424, 19.999999837119, 19.955005461814, 19.910011086509, 19.865016711203, 19.820022335897, 19.775027960591, 19.730033585285, 19.685039209978, 19.640044834672, 19.595050459365, 19.550056084057, 19.505061708750, 19.460067333442, 19.415072958135, 19.370078582826, 19.325084207518, 19.280089832210, 19.235095456901, 19.190101081592, 19.145106706283, 19.100112330974, 19.055117955664, 19.010123580354, 18.965129205044, 18.920134829734, 18.875140454424, 18.830146079113, 18.785151703802, 18.740157328491, 18.695162953180, 18.650168577868, 18.605174202557, 18.560179827245, 18.515185451933, 18.470191076620, 18.425196701308, 18.380202325995, 18.335207950682, 18.290213575369, 18.245219200056, 18.200224824742, 18.155230449429, 18.110236074115, 18.065241698801, 18.020247323486, 17.975252948172, 17.930258572857, 17.885264197542, 17.840269822227, 17.795275446912, 17.750281071596, 17.705286696281, 17.660292320965, 17.615297945649, 17.570303570332, 17.525309195016, 17.480314819699, 17.435320444382, 17.390326069065, 17.345331693748, 17.300337318431, 17.255342943113, 17.210348567795, 17.165354192477, 17.120359817159, 17.075365441841, 17.030371066522, 16.985376691203, 16.940382315885, 16.895387940565, 16.850393565246, 16.805399189927, 16.760404814607, 16.715410439287, 16.670416063967, 16.625421688647, 16.580427313327, 16.535432938006, 16.490438562685, 16.445444187365, 16.400449812043, 16.355455436722, 16.310461061401, 16.265466686079, 16.220472310757, 16.175477935435, 16.130483560113, 16.085489184791, 16.040494809469, 15.995500434146, 15.950506058823, 15.905511683500, 15.860517308177, 15.815522932854, 15.770528557530, 15.725534182206, 15.680539806883, 15.635545431559, 15.590551056234, 15.545556680910, 15.500562305586, 15.455567930261, 15.410573554936, 15.365579179611, 15.320584804286, 15.275590428961, 15.230596053635, 15.185601678310, 15.140607302984, 15.095612927658, 15.050618552332, 15.005624177005, 14.960629801679, 14.915635426352, 14.870641051026, 14.825646675699, 14.780652300372, 14.735657925045, 14.690663549717, 14.645669174390, 14.600674799062, 14.555680423734, 14.510686048406, 14.465691673078, 14.420697297750, 14.375702922421, 14.330708547093, 14.285714171764, 14.240719796435, 14.195725421106, 14.150731045777, 14.105736670447, 14.060742295118, 14.015747919788, 13.970753544459, 13.925759169129, 13.880764793799, 13.835770418468, 13.790776043138, 13.745781667807, 13.700787292477, 13.655792917146, 13.610798541815, 13.565804166484, 13.520809791153, 13.475815415821, 13.430821040490, 13.385826665158, 13.340832289826, 13.295837914495, 13.250843539163, 13.205849163830, 13.160854788498, 13.115860413166, 13.070866037833, 13.025871662500, 12.980877287167, 12.935882911834, 12.890888536501, 12.845894161168, 12.800899785835, 12.755905410501, 12.710911035167, 12.665916659834, 12.620922284500, 12.575927909166, 12.530933533831, 12.485939158497, 12.440944783163, 12.395950407828, 12.350956032493, 12.305961657159, 12.260967281824, 12.215972906489, 12.170978531153, 12.125984155818, 12.080989780483, 12.035995405147, 11.991001029811, 11.946006654476, 11.901012279140, 11.856017903804, 11.811023528467, 11.766029153131, 11.721034777795, 11.676040402458, 11.631046027121, 11.586051651785, 11.541057276448, 11.496062901111, 11.451068525774, 11.406074150436, 11.361079775099, 11.316085399762, 11.271091024424, 11.226096649086, 11.181102273748, 11.136107898411, 11.091113523072, 11.046119147734, 11.001124772396, 10.956130397058, 10.911136021719, 10.866141646381, 10.821147271042, 10.776152895703, 10.731158520364, 10.686164145025, 10.641169769686, 10.596175394347, 10.551181019007, 10.506186643668, 10.461192268328, 10.416197892989, 10.371203517649, 10.326209142309, 10.281214766969, 10.236220391629, 10.191226016289, 10.146231640948, 10.101237265608, 10.056242890267, 10.011248514927, 9.966254139586, 9.921259764245, 9.876265388904, 9.831271013563, 9.786276638222, 9.741282262881, 9.696287887540, 9.651293512198, 9.606299136857, 9.561304761515, 9.516310386174, 9.471316010832, 9.426321635490, 9.381327260148, 9.336332884806, 9.291338509464, 9.246344134122, 9.201349758779, 9.156355383437, 9.111361008094, 9.066366632752, 9.021372257409, 8.976377882066, 8.931383506723, 8.886389131380, 8.841394756037, 8.796400380694, 8.751406005351, 8.706411630008, 8.661417254664, 8.616422879321, 8.571428503977, 8.526434128633, 8.481439753290, 8.436445377946, 8.391451002602, 8.346456627258, 8.301462251914, 8.256467876570, 8.211473501225, 8.166479125881, 8.121484750537, 8.076490375192, 8.031495999848, 7.986501624503, 7.941507249158, 7.896512873813, 7.851518498469, 7.806524123124, 7.761529747779, 7.716535372433, 7.671540997088, 7.626546621743, 7.581552246398, 7.536557871052, 7.491563495707, 7.446569120361, 7.401574745016, 7.356580369670, 7.311585994324, 7.266591618978, 7.221597243632, 7.176602868286, 7.131608492940, 7.086614117594, 7.041619742248, 6.996625366902, 6.951630991555, 6.906636616209, 6.861642240862, 6.816647865516, 6.771653490169, 6.726659114822, 6.681664739476, 6.636670364129, 6.591675988782, 6.546681613435, 6.501687238088, 6.456692862741, 6.411698487394, 6.366704112047, 6.321709736699, 6.276715361352, 6.231720986005, 6.186726610657, 6.141732235310, 6.096737859962, 6.051743484615, 6.006749109267, 5.961754733919, 5.916760358571, 5.871765983223, 5.826771607875, 5.781777232528, 5.736782857179, 5.691788481831, 5.646794106483, 5.601799731135, 5.556805355787, 5.511810980438, 5.466816605090, 5.421822229742, 5.376827854393, 5.331833479045, 5.286839103696, 5.241844728348, 5.196850352999, 5.151855977650, 5.106861602301, 5.061867226953, 5.016872851604, 4.971878476255, 4.926884100906, 4.881889725557, 4.836895350208, 4.791900974859, 4.746906599509, 4.701912224160, 4.656917848811, 4.611923473462, 4.566929098112, 4.521934722763, 4.476940347414, 4.431945972064, 4.386951596715, 4.341957221365, 4.296962846015, 4.251968470666, 4.206974095316, 4.161979719966, 4.116985344617, 4.071990969267, 4.026996593917, 3.982002218567, 3.937007843217, 3.892013467867, 3.847019092517, 3.802024717167, 3.757030341817, 3.712035966467, 3.667041591117, 3.622047215767, 3.577052840416, 3.532058465066, 3.487064089716, 3.442069714366, 3.397075339015, 3.352080963665, 3.307086588314, 3.262092212964, 3.217097837613, 3.172103462263, 3.127109086912, 3.082114711562, 3.037120336211, 2.992125960861, 2.947131585510, 2.902137210159, 2.857142834808, 2.812148459458, 2.767154084107, 2.722159708756, 2.677165333405, 2.632170958055, 2.587176582704, 2.542182207353, 2.497187832002, 2.452193456651, 2.407199081300, 2.362204705949, 2.317210330598, 2.272215955247, 2.227221579896, 2.182227204545, 2.137232829194, 2.092238453842, 2.047244078491, 2.002249703140, 1.957255327789, 1.912260952438, 1.867266577087, 1.822272201735, 1.777277826384, 1.732283451033, 1.687289075681, 1.642294700330, 1.597300324979, 1.552305949627, 1.507311574276, 1.462317198925, 1.417322823573, 1.372328448222, 1.327334072871, 1.282339697519, 1.237345322168, 1.192350946816, 1.147356571465, 1.102362196113, 1.057367820762, 1.012373445410, 0.967379070059, 0.922384694707, 0.877390319356, 0.832395944004, 0.787401568653, 0.742407193301, 0.697412817950, 0.652418442598, 0.607424067247, 0.562429691895, 0.517435316543, 0.472440941192, 0.427446565840, 0.382452190489, 0.337457815137, 0.292463439785, 0.247469064434, 0.202474689082, 0.157480313731, 0.112485938379, 0.067491563027, 0.022497187676)) } // namespace gaussian } // namespace spacing } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/spacing/gaussian/N400.cc0000664000175000017500000001751215160212070023341 0ustar alastairalastair/* * (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. */ // TL799 #include "atlas/grid/detail/spacing/gaussian/N.h" namespace atlas { namespace grid { namespace spacing { namespace gaussian { DEFINE_GAUSSIAN_LATITUDES( 400, LIST(89.827874645894, 89.604900490023, 89.380609551250, 89.156021095055, 88.931319783477, 88.706563860905, 88.481777418577, 88.256972206505, 88.032154633403, 87.807328490264, 87.582496162420, 87.357659225510, 87.132818762040, 86.907975540243, 86.683130120384, 86.458282920653, 86.233434259507, 86.008584383722, 85.783733487483, 85.558881725682, 85.334029223381, 85.109176082657, 84.884322387651, 84.659468208345, 84.434613603414, 84.209758622426, 83.984903307540, 83.760047694839, 83.535191815391, 83.310335696085, 83.085479360317, 82.860622828534, 82.635766118687, 82.410909246602, 82.186052226280, 81.961195070160, 81.736337789325, 81.511480393685, 81.286622892127, 81.061765292642, 80.836907602441, 80.612049828040, 80.387191975348, 80.162334049733, 79.937476056084, 79.712617998861, 79.487759882144, 79.262901709670, 79.038043484872, 78.813185210903, 78.588326890672, 78.363468526859, 78.138610121943, 77.913751678220, 77.688893197814, 77.464034682701, 77.239176134715, 77.014317555565, 76.789458946841, 76.564600310030, 76.339741646518, 76.114882957602, 75.890024244498, 75.665165508344, 75.440306750209, 75.215447971095, 74.990589171946, 74.765730353650, 74.540871517041, 74.316012662908, 74.091153791995, 73.866294905004, 73.641436002597, 73.416577085402, 73.191718154013, 72.966859208993, 72.742000250874, 72.517141280163, 72.292282297339, 72.067423302858, 71.842564297155, 71.617705280640, 71.392846253707, 71.167987216729, 70.943128170062, 70.718269114045, 70.493410049002, 70.268550975241, 70.043691893059, 69.818832802736, 69.593973704543, 69.369114598736, 69.144255485563, 68.919396365260, 68.694537238053, 68.469678104158, 68.244818963783, 68.019959817128, 67.795100664383, 67.570241505733, 67.345382341353, 67.120523171413, 66.895663996075, 66.670804815496, 66.445945629826, 66.221086439211, 65.996227243789, 65.771368043695, 65.546508839058, 65.321649630002, 65.096790416648, 64.871931199112, 64.647071977504, 64.422212751934, 64.197353522504, 63.972494289316, 63.747635052466, 63.522775812048, 63.297916568152, 63.073057320866, 62.848198070275, 62.623338816461, 62.398479559501, 62.173620299475, 61.948761036454, 61.723901770512, 61.499042501718, 61.274183230138, 61.049323955840, 60.824464678885, 60.599605399335, 60.374746117249, 60.149886832686, 59.925027545701, 59.700168256349, 59.475308964682, 59.250449670751, 59.025590374607, 58.800731076297, 58.575871775869, 58.351012473367, 58.126153168837, 57.901293862321, 57.676434553861, 57.451575243498, 57.226715931271, 57.001856617219, 56.776997301380, 56.552137983789, 56.327278664483, 56.102419343496, 55.877560020861, 55.652700696612, 55.427841370780, 55.202982043397, 54.978122714492, 54.753263384095, 54.528404052236, 54.303544718941, 54.078685384240, 53.853826048157, 53.628966710720, 53.404107371953, 53.179248031881, 52.954388690530, 52.729529347921, 52.504670004079, 52.279810659026, 52.054951312783, 51.830091965373, 51.605232616816, 51.380373267132, 51.155513916343, 50.930654564466, 50.705795211522, 50.480935857528, 50.256076502504, 50.031217146467, 49.806357789435, 49.581498431424, 49.356639072452, 49.131779712534, 48.906920351687, 48.682060989925, 48.457201627266, 48.232342263723, 48.007482899311, 47.782623534045, 47.557764167938, 47.332904801005, 47.108045433258, 46.883186064711, 46.658326695378, 46.433467325269, 46.208607954399, 45.983748582779, 45.758889210421, 45.534029837336, 45.309170463537, 45.084311089034, 44.859451713839, 44.634592337961, 44.409732961412, 44.184873584203, 43.960014206342, 43.735154827841, 43.510295448709, 43.285436068955, 43.060576688590, 42.835717307622, 42.610857926060, 42.385998543914, 42.161139161191, 41.936279777901, 41.711420394052, 41.486561009653, 41.261701624711, 41.036842239234, 40.811982853231, 40.587123466709, 40.362264079675, 40.137404692137, 39.912545304103, 39.687685915579, 39.462826526572, 39.237967137090, 39.013107747139, 38.788248356727, 38.563388965859, 38.338529574541, 38.113670182782, 37.888810790586, 37.663951397960, 37.439092004910, 37.214232611441, 36.989373217561, 36.764513823274, 36.539654428586, 36.314795033503, 36.089935638030, 35.865076242173, 35.640216845937, 35.415357449327, 35.190498052348, 34.965638655006, 34.740779257306, 34.515919859252, 34.291060460849, 34.066201062102, 33.841341663016, 33.616482263595, 33.391622863845, 33.166763463768, 32.941904063371, 32.717044662657, 32.492185261630, 32.267325860296, 32.042466458657, 31.817607056718, 31.592747654483, 31.367888251957, 31.143028849143, 30.918169446044, 30.693310042666, 30.468450639011, 30.243591235084, 30.018731830887, 29.793872426425, 29.569013021702, 29.344153616720, 29.119294211484, 28.894434805996, 28.669575400260, 28.444715994280, 28.219856588059, 27.994997181599, 27.770137774905, 27.545278367979, 27.320418960825, 27.095559553446, 26.870700145844, 26.645840738023, 26.420981329986, 26.196121921735, 25.971262513275, 25.746403104606, 25.521543695733, 25.296684286658, 25.071824877385, 24.846965467914, 24.622106058250, 24.397246648396, 24.172387238353, 23.947527828124, 23.722668417712, 23.497809007120, 23.272949596350, 23.048090185404, 22.823230774285, 22.598371362996, 22.373511951538, 22.148652539915, 21.923793128129, 21.698933716181, 21.474074304075, 21.249214891812, 21.024355479395, 20.799496066827, 20.574636654109, 20.349777241243, 20.124917828232, 19.900058415078, 19.675199001783, 19.450339588349, 19.225480174778, 19.000620761073, 18.775761347235, 18.550901933267, 18.326042519170, 18.101183104946, 17.876323690598, 17.651464276127, 17.426604861536, 17.201745446826, 16.976886031999, 16.752026617057, 16.527167202002, 16.302307786836, 16.077448371561, 15.852588956178, 15.627729540690, 15.402870125097, 15.178010709403, 14.953151293609, 14.728291877716, 14.503432461726, 14.278573045641, 14.053713629463, 13.828854213193, 13.603994796833, 13.379135380386, 13.154275963851, 12.929416547232, 12.704557130530, 12.479697713746, 12.254838296882, 12.029978879940, 11.805119462921, 11.580260045827, 11.355400628659, 11.130541211420, 10.905681794110, 10.680822376731, 10.455962959286, 10.231103541774, 10.006244124198, 9.781384706560, 9.556525288861, 9.331665871102, 9.106806453284, 8.881947035411, 8.657087617482, 8.432228199499, 8.207368781465, 7.982509363379, 7.757649945245, 7.532790527062, 7.307931108834, 7.083071690560, 6.858212272243, 6.633352853884, 6.408493435484, 6.183634017046, 5.958774598569, 5.733915180056, 5.509055761509, 5.284196342927, 5.059336924314, 4.834477505670, 4.609618086996, 4.384758668295, 4.159899249567, 3.935039830814, 3.710180412037, 3.485320993238, 3.260461574418, 3.035602155578, 2.810742736719, 2.585883317844, 2.361023898953, 2.136164480048, 1.911305061130, 1.686445642201, 1.461586223261, 1.236726804313, 1.011867385357, 0.787007966395, 0.562148547428, 0.337289128458, 0.112429709486)) } // namespace gaussian } // namespace spacing } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/spacing/gaussian/N256.cc0000664000175000017500000001271115160212070023346 0ustar alastairalastair/* * (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. */ // TL511 #include "atlas/grid/detail/spacing/gaussian/N.h" namespace atlas { namespace grid { namespace spacing { namespace gaussian { DEFINE_GAUSSIAN_LATITUDES( 256, LIST(89.731148618413, 89.382873896334, 89.032542423790, 88.681746243591, 88.330773788807, 87.979716034326, 87.628610609484, 87.277475867224, 86.926321817646, 86.575154382095, 86.223977286346, 85.872792991467, 85.521603188281, 85.170409076734, 84.819211531931, 84.468011207066, 84.116808599553, 83.765604094844, 83.414397996248, 83.063190545702, 82.711981938543, 82.360772334213, 82.009561864138, 81.658350637624, 81.307138746324, 80.955926267657, 80.604713267476, 80.253499802142, 79.902285920181, 79.551071663595, 79.199857068926, 78.848642168112, 78.497426989195, 78.146211556892, 77.794995893075, 77.443780017172, 77.092563946496, 76.741347696526, 76.390131281143, 76.038914712832, 75.687698002854, 75.336481161390, 74.985264197669, 74.634047120076, 74.282829936248, 73.931612653153, 73.580395277164, 73.229177814120, 72.877960269381, 72.526742647876, 72.175524954146, 71.824307192381, 71.473089366452, 71.121871479946, 70.770653536183, 70.419435538248, 70.068217489009, 69.716999391133, 69.365781247107, 69.014563059252, 68.663344829736, 68.312126560585, 67.960908253699, 67.609689910856, 67.258471533726, 66.907253123876, 66.556034682780, 66.204816211827, 65.853597712321, 65.502379185494, 65.151160632510, 64.799942054463, 64.448723452393, 64.097504827279, 63.746286180050, 63.395067511586, 63.043848822719, 62.692630114242, 62.341411386903, 61.990192641418, 61.638973878463, 61.287755098684, 60.936536302693, 60.585317491076, 60.234098664389, 59.882879823163, 59.531660967905, 59.180442099098, 58.829223217203, 58.478004322663, 58.126785415899, 57.775566497315, 57.424347567296, 57.073128626212, 56.721909674418, 56.370690712253, 56.019471740043, 55.668252758099, 55.317033766721, 54.965814766198, 54.614595756804, 54.263376738806, 53.912157712459, 53.560938678008, 53.209719635690, 52.858500585731, 52.507281528350, 52.156062463759, 51.804843392159, 51.453624313747, 51.102405228712, 50.751186137234, 50.399967039491, 50.048747935650, 49.697528825877, 49.346309710328, 48.995090589156, 48.643871462509, 48.292652330530, 47.941433193356, 47.590214051120, 47.238994903953, 46.887775751977, 46.536556595315, 46.185337434084, 45.834118268396, 45.482899098363, 45.131679924089, 44.780460745679, 44.429241563233, 44.078022376847, 43.726803186616, 43.375583992632, 43.024364794983, 42.673145593755, 42.321926389033, 41.970707180896, 41.619487969425, 41.268268754697, 40.917049536785, 40.565830315762, 40.214611091700, 39.863391864666, 39.512172634728, 39.160953401950, 38.809734166396, 38.458514928128, 38.107295687205, 37.756076443686, 37.404857197628, 37.053637949087, 36.702418698117, 36.351199444770, 35.999980189098, 35.648760931151, 35.297541670979, 34.946322408628, 34.595103144147, 34.243883877579, 33.892664608970, 33.541445338363, 33.190226065800, 32.839006791323, 32.487787514973, 32.136568236789, 31.785348956809, 31.434129675072, 31.082910391614, 30.731691106472, 30.380471819681, 30.029252531276, 29.678033241291, 29.326813949758, 28.975594656711, 28.624375362181, 28.273156066200, 27.921936768798, 27.570717470004, 27.219498169849, 26.868278868361, 26.517059565568, 26.165840261498, 25.814620956179, 25.463401649636, 25.112182341895, 24.760963032984, 24.409743722926, 24.058524411747, 23.707305099470, 23.356085786120, 23.004866471720, 22.653647156293, 22.302427839862, 21.951208522449, 21.599989204076, 21.248769884764, 20.897550564535, 20.546331243409, 20.195111921408, 19.843892598551, 19.492673274857, 19.141453950348, 18.790234625041, 18.439015298957, 18.087795972114, 17.736576644529, 17.385357316222, 17.034137987211, 16.682918657513, 16.331699327146, 15.980479996126, 15.629260664472, 15.278041332199, 14.926821999325, 14.575602665866, 14.224383331838, 13.873163997257, 13.521944662139, 13.170725326500, 12.819505990355, 12.468286653719, 12.117067316609, 11.765847979038, 11.414628641021, 11.063409302574, 10.712189963711, 10.360970624447, 10.009751284795, 9.658531944770, 9.307312604387, 8.956093263659, 8.604873922599, 8.253654581223, 7.902435239543, 7.551215897573, 7.199996555326, 6.848777212817, 6.497557870058, 6.146338527062, 5.795119183843, 5.443899840414, 5.092680496788, 4.741461152978, 4.390241808997, 4.039022464858, 3.687803120573, 3.336583776155, 2.985364431618, 2.634145086974, 2.282925742235, 1.931706397414, 1.580487052524, 1.229267707577, 0.878048362586, 0.526829017564, 0.175609672524)) } // namespace gaussian } // namespace spacing } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/spacing/gaussian/N80.cc0000664000175000017500000000474715160212070023273 0ustar alastairalastair/* * (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. */ // TL159 #include "atlas/grid/detail/spacing/gaussian/N.h" namespace atlas { namespace grid { namespace spacing { namespace gaussian { DEFINE_GAUSSIAN_LATITUDES(80, LIST(89.141519426461, 88.029428867952, 86.910770814124, 85.790628883637, 84.669924084447, 83.548946912542, 82.427817524008, 81.306594522669, 80.185309872477, 79.063982481409, 77.942624246673, 76.821243027100, 75.699844222011, 74.578431663296, 73.457008145583, 72.335575754909, 71.214136079887, 70.092690351624, 68.971239538936, 67.849784414670, 66.728325602882, 65.606863613010, 64.485398865043, 63.363931708341, 62.242462435891, 61.120991295252, 59.999518497040, 58.878044221583, 57.756568624184, 56.635091839330, 55.513613984077, 54.392135160792, 53.270655459398, 52.149174959220, 51.027693730509, 49.906211835711, 48.784729330535, 47.663246264843, 46.541762683406, 45.420278626548, 44.298794130694, 43.177309228835, 42.055823950935, 40.934338324279, 39.812852373771, 38.691366122202, 37.569879590471, 36.448392797794, 35.326905761872, 34.205418499049, 33.083931024447, 31.962443352088, 30.840955495002, 29.719467465319, 28.597979274357, 27.476490932696, 26.355002450251, 25.233513836324, 24.112025099671, 22.990536248541, 21.869047290730, 20.747558233616, 19.626069084199, 18.504579849136, 17.383090534771, 16.261601147162, 15.140111692111, 14.018622175186, 12.897132601745, 11.775642976956, 10.654153305818, 9.532663593176, 8.411173843743, 7.289684062115, 6.168194252784, 5.046704420157, 3.925214568566, 2.803724702287, 1.682234825547, 0.560744942544)) } // namespace gaussian } // namespace spacing } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/spacing/gaussian/N1600.cc0000664000175000017500000007254315160212070023431 0ustar alastairalastair/* * (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. */ // TL3199 #include "atlas/grid/detail/spacing/gaussian/N.h" namespace atlas { namespace grid { namespace spacing { namespace gaussian { DEFINE_GAUSSIAN_LATITUDES( 1600, LIST(89.956948491058, 89.901178822991, 89.845079804890, 89.788906372571, 89.732704713178, 89.676489394638, 89.620266442582, 89.564038795892, 89.507808057494, 89.451575175583, 89.395340746771, 89.339105165153, 89.282868701480, 89.226631547902, 89.170393844551, 89.114155696023, 89.057917181969, 89.001678364113, 88.945439291023, 88.889200001442, 88.832960526648, 88.776720892172, 88.720481119062, 88.664241224818, 88.608001224118, 88.551761129360, 88.495520951086, 88.439280698324, 88.383040378843, 88.326799999369, 88.270559565753, 88.214319083110, 88.158078555930, 88.101837988170, 88.045597383333, 87.989356744529, 87.933116074532, 87.876875375818, 87.820634650612, 87.764393900912, 87.708153128521, 87.651912335070, 87.595671522036, 87.539430690761, 87.483189842469, 87.426948978275, 87.370708099200, 87.314467206178, 87.258226300067, 87.201985381657, 87.145744451675, 87.089503510791, 87.033262559625, 86.977021598752, 86.920780628702, 86.864539649971, 86.808298663016, 86.752057668265, 86.695816666116, 86.639575656940, 86.583334641085, 86.527093618874, 86.470852590613, 86.414611556584, 86.358370517056, 86.302129472280, 86.245888422491, 86.189647367911, 86.133406308749, 86.077165245203, 86.020924177459, 85.964683105691, 85.908442030065, 85.852200950740, 85.795959867862, 85.739718781574, 85.683477692007, 85.627236599289, 85.570995503540, 85.514754404873, 85.458513303398, 85.402272199216, 85.346031092427, 85.289789983123, 85.233548871394, 85.177307757325, 85.121066640996, 85.064825522485, 85.008584401865, 84.952343279207, 84.896102154578, 84.839861028043, 84.783619899663, 84.727378769498, 84.671137637604, 84.614896504035, 84.558655368843, 84.502414232077, 84.446173093787, 84.389931954017, 84.333690812811, 84.277449670213, 84.221208526262, 84.164967380998, 84.108726234457, 84.052485086678, 83.996243937694, 83.940002787538, 83.883761636244, 83.827520483842, 83.771279330362, 83.715038175834, 83.658797020285, 83.602555863742, 83.546314706230, 83.490073547776, 83.433832388404, 83.377591228136, 83.321350066995, 83.265108905004, 83.208867742183, 83.152626578553, 83.096385414133, 83.040144248943, 82.983903083002, 82.927661916327, 82.871420748935, 82.815179580843, 82.758938412069, 82.702697242626, 82.646456072532, 82.590214901800, 82.533973730445, 82.477732558482, 82.421491385923, 82.365250212781, 82.309009039070, 82.252767864802, 82.196526689989, 82.140285514643, 82.084044338775, 82.027803162396, 81.971561985516, 81.915320808147, 81.859079630299, 81.802838451980, 81.746597273202, 81.690356093972, 81.634114914301, 81.577873734197, 81.521632553669, 81.465391372725, 81.409150191374, 81.352909009622, 81.296667827479, 81.240426644952, 81.184185462047, 81.127944278772, 81.071703095135, 81.015461911141, 80.959220726798, 80.902979542112, 80.846738357090, 80.790497171737, 80.734255986059, 80.678014800063, 80.621773613754, 80.565532427138, 80.509291240220, 80.453050053006, 80.396808865500, 80.340567677708, 80.284326489635, 80.228085301286, 80.171844112665, 80.115602923777, 80.059361734627, 80.003120545219, 79.946879355557, 79.890638165646, 79.834396975489, 79.778155785091, 79.721914594456, 79.665673403587, 79.609432212489, 79.553191021165, 79.496949829619, 79.440708637854, 79.384467445874, 79.328226253682, 79.271985061281, 79.215743868675, 79.159502675867, 79.103261482861, 79.047020289658, 78.990779096262, 78.934537902677, 78.878296708904, 78.822055514948, 78.765814320810, 78.709573126493, 78.653331932000, 78.597090737333, 78.540849542496, 78.484608347490, 78.428367152319, 78.372125956984, 78.315884761487, 78.259643565832, 78.203402370020, 78.147161174054, 78.090919977936, 78.034678781667, 77.978437585251, 77.922196388689, 77.865955191983, 77.809713995135, 77.753472798147, 77.697231601021, 77.640990403760, 77.584749206363, 77.528508008835, 77.472266811176, 77.416025613388, 77.359784415473, 77.303543217432, 77.247302019268, 77.191060820981, 77.134819622574, 77.078578424048, 77.022337225405, 76.966096026645, 76.909854827771, 76.853613628784, 76.797372429686, 76.741131230477, 76.684890031160, 76.628648831736, 76.572407632205, 76.516166432570, 76.459925232831, 76.403684032991, 76.347442833049, 76.291201633008, 76.234960432869, 76.178719232632, 76.122478032300, 76.066236831873, 76.009995631352, 75.953754430738, 75.897513230033, 75.841272029238, 75.785030828353, 75.728789627381, 75.672548426321, 75.616307225175, 75.560066023944, 75.503824822628, 75.447583621230, 75.391342419749, 75.335101218187, 75.278860016545, 75.222618814823, 75.166377613023, 75.110136411145, 75.053895209190, 74.997654007160, 74.941412805054, 74.885171602875, 74.828930400621, 74.772689198296, 74.716447995898, 74.660206793430, 74.603965590891, 74.547724388284, 74.491483185607, 74.435241982862, 74.379000780051, 74.322759577172, 74.266518374228, 74.210277171219, 74.154035968146, 74.097794765009, 74.041553561809, 73.985312358547, 73.929071155223, 73.872829951838, 73.816588748392, 73.760347544887, 73.704106341323, 73.647865137700, 73.591623934019, 73.535382730281, 73.479141526486, 73.422900322635, 73.366659118728, 73.310417914766, 73.254176710750, 73.197935506680, 73.141694302556, 73.085453098380, 73.029211894151, 72.972970689870, 72.916729485538, 72.860488281156, 72.804247076723, 72.748005872240, 72.691764667707, 72.635523463126, 72.579282258497, 72.523041053820, 72.466799849095, 72.410558644323, 72.354317439505, 72.298076234640, 72.241835029730, 72.185593824775, 72.129352619775, 72.073111414731, 72.016870209642, 71.960629004510, 71.904387799335, 71.848146594117, 71.791905388857, 71.735664183555, 71.679422978211, 71.623181772826, 71.566940567400, 71.510699361934, 71.454458156428, 71.398216950882, 71.341975745297, 71.285734539673, 71.229493334010, 71.173252128309, 71.117010922570, 71.060769716793, 71.004528510979, 70.948287305128, 70.892046099240, 70.835804893316, 70.779563687357, 70.723322481361, 70.667081275330, 70.610840069264, 70.554598863164, 70.498357657029, 70.442116450859, 70.385875244656, 70.329634038420, 70.273392832150, 70.217151625847, 70.160910419512, 70.104669213144, 70.048428006743, 69.992186800311, 69.935945593848, 69.879704387353, 69.823463180827, 69.767221974270, 69.710980767683, 69.654739561065, 69.598498354417, 69.542257147739, 69.486015941032, 69.429774734296, 69.373533527530, 69.317292320736, 69.261051113913, 69.204809907061, 69.148568700182, 69.092327493274, 69.036086286339, 68.979845079376, 68.923603872387, 68.867362665370, 68.811121458326, 68.754880251256, 68.698639044159, 68.642397837036, 68.586156629887, 68.529915422712, 68.473674215512, 68.417433008286, 68.361191801036, 68.304950593760, 68.248709386459, 68.192468179134, 68.136226971784, 68.079985764411, 68.023744557013, 67.967503349591, 67.911262142146, 67.855020934677, 67.798779727185, 67.742538519670, 67.686297312132, 67.630056104571, 67.573814896987, 67.517573689381, 67.461332481753, 67.405091274103, 67.348850066431, 67.292608858736, 67.236367651021, 67.180126443284, 67.123885235525, 67.067644027746, 67.011402819945, 66.955161612124, 66.898920404282, 66.842679196420, 66.786437988537, 66.730196780634, 66.673955572711, 66.617714364767, 66.561473156805, 66.505231948822, 66.448990740820, 66.392749532799, 66.336508324758, 66.280267116698, 66.224025908620, 66.167784700522, 66.111543492406, 66.055302284271, 65.999061076118, 65.942819867947, 65.886578659757, 65.830337451550, 65.774096243324, 65.717855035081, 65.661613826820, 65.605372618542, 65.549131410246, 65.492890201932, 65.436648993602, 65.380407785255, 65.324166576890, 65.267925368509, 65.211684160111, 65.155442951697, 65.099201743266, 65.042960534818, 64.986719326355, 64.930478117875, 64.874236909379, 64.817995700867, 64.761754492340, 64.705513283796, 64.649272075237, 64.593030866663, 64.536789658073, 64.480548449468, 64.424307240847, 64.368066032212, 64.311824823562, 64.255583614896, 64.199342406216, 64.143101197521, 64.086859988811, 64.030618780087, 63.974377571349, 63.918136362596, 63.861895153829, 63.805653945048, 63.749412736253, 63.693171527443, 63.636930318620, 63.580689109783, 63.524447900933, 63.468206692069, 63.411965483191, 63.355724274300, 63.299483065395, 63.243241856477, 63.187000647546, 63.130759438602, 63.074518229645, 63.018277020675, 62.962035811692, 62.905794602697, 62.849553393688, 62.793312184667, 62.737070975634, 62.680829766588, 62.624588557529, 62.568347348459, 62.512106139376, 62.455864930281, 62.399623721174, 62.343382512055, 62.287141302924, 62.230900093781, 62.174658884626, 62.118417675459, 62.062176466281, 62.005935257092, 61.949694047891, 61.893452838678, 61.837211629454, 61.780970420219, 61.724729210972, 61.668488001715, 61.612246792446, 61.556005583166, 61.499764373875, 61.443523164574, 61.387281955261, 61.331040745938, 61.274799536604, 61.218558327259, 61.162317117904, 61.106075908538, 61.049834699162, 60.993593489776, 60.937352280379, 60.881111070972, 60.824869861554, 60.768628652127, 60.712387442689, 60.656146233241, 60.599905023784, 60.543663814316, 60.487422604839, 60.431181395352, 60.374940185855, 60.318698976348, 60.262457766832, 60.206216557306, 60.149975347770, 60.093734138225, 60.037492928671, 59.981251719107, 59.925010509534, 59.868769299952, 59.812528090360, 59.756286880760, 59.700045671150, 59.643804461531, 59.587563251903, 59.531322042266, 59.475080832621, 59.418839622966, 59.362598413303, 59.306357203630, 59.250115993950, 59.193874784260, 59.137633574562, 59.081392364855, 59.025151155140, 58.968909945416, 58.912668735684, 58.856427525944, 58.800186316195, 58.743945106438, 58.687703896672, 58.631462686899, 58.575221477117, 58.518980267327, 58.462739057529, 58.406497847723, 58.350256637909, 58.294015428087, 58.237774218258, 58.181533008420, 58.125291798575, 58.069050588721, 58.012809378861, 57.956568168992, 57.900326959116, 57.844085749232, 57.787844539340, 57.731603329441, 57.675362119535, 57.619120909621, 57.562879699700, 57.506638489771, 57.450397279835, 57.394156069892, 57.337914859941, 57.281673649983, 57.225432440018, 57.169191230046, 57.112950020067, 57.056708810081, 57.000467600088, 56.944226390087, 56.887985180080, 56.831743970066, 56.775502760045, 56.719261550017, 56.663020339982, 56.606779129941, 56.550537919892, 56.494296709838, 56.438055499776, 56.381814289708, 56.325573079633, 56.269331869551, 56.213090659463, 56.156849449369, 56.100608239268, 56.044367029160, 55.988125819046, 55.931884608926, 55.875643398799, 55.819402188666, 55.763160978527, 55.706919768382, 55.650678558230, 55.594437348072, 55.538196137908, 55.481954927738, 55.425713717562, 55.369472507379, 55.313231297191, 55.256990086997, 55.200748876796, 55.144507666590, 55.088266456378, 55.032025246159, 54.975784035935, 54.919542825706, 54.863301615470, 54.807060405229, 54.750819194981, 54.694577984728, 54.638336774470, 54.582095564206, 54.525854353936, 54.469613143660, 54.413371933379, 54.357130723092, 54.300889512800, 54.244648302502, 54.188407092199, 54.132165881890, 54.075924671576, 54.019683461257, 53.963442250932, 53.907201040602, 53.850959830266, 53.794718619925, 53.738477409579, 53.682236199228, 53.625994988871, 53.569753778510, 53.513512568143, 53.457271357770, 53.401030147393, 53.344788937011, 53.288547726623, 53.232306516231, 53.176065305833, 53.119824095431, 53.063582885023, 53.007341674611, 52.951100464193, 52.894859253771, 52.838618043344, 52.782376832912, 52.726135622475, 52.669894412033, 52.613653201586, 52.557411991135, 52.501170780679, 52.444929570218, 52.388688359752, 52.332447149282, 52.276205938807, 52.219964728328, 52.163723517843, 52.107482307354, 52.051241096861, 51.994999886363, 51.938758675860, 51.882517465353, 51.826276254842, 51.770035044326, 51.713793833805, 51.657552623280, 51.601311412751, 51.545070202217, 51.488828991679, 51.432587781136, 51.376346570589, 51.320105360038, 51.263864149482, 51.207622938923, 51.151381728358, 51.095140517790, 51.038899307217, 50.982658096641, 50.926416886060, 50.870175675474, 50.813934464885, 50.757693254292, 50.701452043694, 50.645210833092, 50.588969622486, 50.532728411876, 50.476487201263, 50.420245990645, 50.364004780023, 50.307763569397, 50.251522358767, 50.195281148133, 50.139039937495, 50.082798726853, 50.026557516207, 49.970316305558, 49.914075094904, 49.857833884247, 49.801592673586, 49.745351462921, 49.689110252252, 49.632869041579, 49.576627830903, 49.520386620223, 49.464145409539, 49.407904198851, 49.351662988160, 49.295421777465, 49.239180566766, 49.182939356064, 49.126698145358, 49.070456934648, 49.014215723935, 48.957974513218, 48.901733302498, 48.845492091774, 48.789250881046, 48.733009670315, 48.676768459580, 48.620527248842, 48.564286038100, 48.508044827355, 48.451803616606, 48.395562405854, 48.339321195099, 48.283079984340, 48.226838773577, 48.170597562811, 48.114356352042, 48.058115141270, 48.001873930494, 47.945632719714, 47.889391508932, 47.833150298146, 47.776909087357, 47.720667876564, 47.664426665768, 47.608185454969, 47.551944244167, 47.495703033362, 47.439461822553, 47.383220611741, 47.326979400926, 47.270738190108, 47.214496979286, 47.158255768461, 47.102014557634, 47.045773346803, 46.989532135969, 46.933290925132, 46.877049714291, 46.820808503448, 46.764567292602, 46.708326081752, 46.652084870900, 46.595843660044, 46.539602449186, 46.483361238324, 46.427120027460, 46.370878816592, 46.314637605722, 46.258396394849, 46.202155183972, 46.145913973093, 46.089672762211, 46.033431551326, 45.977190340438, 45.920949129547, 45.864707918653, 45.808466707756, 45.752225496857, 45.695984285954, 45.639743075049, 45.583501864141, 45.527260653230, 45.471019442317, 45.414778231400, 45.358537020481, 45.302295809559, 45.246054598635, 45.189813387707, 45.133572176777, 45.077330965844, 45.021089754909, 44.964848543971, 44.908607333030, 44.852366122086, 44.796124911140, 44.739883700191, 44.683642489239, 44.627401278285, 44.571160067328, 44.514918856368, 44.458677645406, 44.402436434442, 44.346195223474, 44.289954012505, 44.233712801532, 44.177471590557, 44.121230379580, 44.064989168600, 44.008747957617, 43.952506746632, 43.896265535644, 43.840024324654, 43.783783113662, 43.727541902667, 43.671300691669, 43.615059480669, 43.558818269667, 43.502577058662, 43.446335847655, 43.390094636645, 43.333853425633, 43.277612214618, 43.221371003601, 43.165129792582, 43.108888581560, 43.052647370536, 42.996406159510, 42.940164948481, 42.883923737450, 42.827682526417, 42.771441315381, 42.715200104343, 42.658958893302, 42.602717682260, 42.546476471215, 42.490235260168, 42.433994049118, 42.377752838066, 42.321511627012, 42.265270415956, 42.209029204898, 42.152787993837, 42.096546782774, 42.040305571709, 41.984064360641, 41.927823149572, 41.871581938500, 41.815340727426, 41.759099516350, 41.702858305272, 41.646617094191, 41.590375883109, 41.534134672024, 41.477893460937, 41.421652249848, 41.365411038757, 41.309169827664, 41.252928616569, 41.196687405472, 41.140446194372, 41.084204983271, 41.027963772167, 40.971722561062, 40.915481349954, 40.859240138844, 40.802998927732, 40.746757716619, 40.690516505503, 40.634275294385, 40.578034083265, 40.521792872143, 40.465551661019, 40.409310449894, 40.353069238766, 40.296828027636, 40.240586816504, 40.184345605371, 40.128104394235, 40.071863183097, 40.015621971958, 39.959380760816, 39.903139549673, 39.846898338528, 39.790657127381, 39.734415916231, 39.678174705080, 39.621933493928, 39.565692282773, 39.509451071616, 39.453209860458, 39.396968649297, 39.340727438135, 39.284486226971, 39.228245015805, 39.172003804637, 39.115762593468, 39.059521382297, 39.003280171123, 38.947038959948, 38.890797748772, 38.834556537593, 38.778315326413, 38.722074115230, 38.665832904047, 38.609591692861, 38.553350481673, 38.497109270484, 38.440868059293, 38.384626848100, 38.328385636906, 38.272144425710, 38.215903214512, 38.159662003312, 38.103420792111, 38.047179580908, 37.990938369703, 37.934697158497, 37.878455947289, 37.822214736079, 37.765973524867, 37.709732313654, 37.653491102439, 37.597249891223, 37.541008680005, 37.484767468785, 37.428526257564, 37.372285046341, 37.316043835116, 37.259802623890, 37.203561412662, 37.147320201432, 37.091078990201, 37.034837778968, 36.978596567734, 36.922355356498, 36.866114145260, 36.809872934021, 36.753631722781, 36.697390511538, 36.641149300295, 36.584908089049, 36.528666877802, 36.472425666554, 36.416184455304, 36.359943244052, 36.303702032799, 36.247460821545, 36.191219610289, 36.134978399031, 36.078737187772, 36.022495976511, 35.966254765249, 35.910013553986, 35.853772342721, 35.797531131454, 35.741289920186, 35.685048708916, 35.628807497645, 35.572566286373, 35.516325075099, 35.460083863824, 35.403842652547, 35.347601441269, 35.291360229989, 35.235119018708, 35.178877807425, 35.122636596141, 35.066395384856, 35.010154173569, 34.953912962281, 34.897671750991, 34.841430539700, 34.785189328408, 34.728948117114, 34.672706905819, 34.616465694522, 34.560224483224, 34.503983271925, 34.447742060625, 34.391500849323, 34.335259638019, 34.279018426714, 34.222777215408, 34.166536004101, 34.110294792792, 34.054053581482, 33.997812370171, 33.941571158858, 33.885329947544, 33.829088736229, 33.772847524912, 33.716606313594, 33.660365102275, 33.604123890954, 33.547882679632, 33.491641468309, 33.435400256985, 33.379159045659, 33.322917834332, 33.266676623004, 33.210435411675, 33.154194200344, 33.097952989012, 33.041711777679, 32.985470566344, 32.929229355008, 32.872988143671, 32.816746932333, 32.760505720994, 32.704264509653, 32.648023298311, 32.591782086968, 32.535540875624, 32.479299664278, 32.423058452931, 32.366817241583, 32.310576030234, 32.254334818884, 32.198093607532, 32.141852396180, 32.085611184826, 32.029369973471, 31.973128762114, 31.916887550757, 31.860646339398, 31.804405128039, 31.748163916678, 31.691922705316, 31.635681493953, 31.579440282588, 31.523199071223, 31.466957859856, 31.410716648488, 31.354475437119, 31.298234225749, 31.241993014378, 31.185751803006, 31.129510591633, 31.073269380258, 31.017028168883, 30.960786957506, 30.904545746128, 30.848304534749, 30.792063323369, 30.735822111988, 30.679580900606, 30.623339689223, 30.567098477839, 30.510857266453, 30.454616055067, 30.398374843679, 30.342133632291, 30.285892420901, 30.229651209510, 30.173409998119, 30.117168786726, 30.060927575332, 30.004686363937, 29.948445152541, 29.892203941144, 29.835962729746, 29.779721518347, 29.723480306947, 29.667239095546, 29.610997884144, 29.554756672741, 29.498515461337, 29.442274249932, 29.386033038526, 29.329791827119, 29.273550615711, 29.217309404302, 29.161068192891, 29.104826981480, 29.048585770068, 28.992344558655, 28.936103347241, 28.879862135826, 28.823620924410, 28.767379712994, 28.711138501576, 28.654897290157, 28.598656078737, 28.542414867316, 28.486173655895, 28.429932444472, 28.373691233049, 28.317450021624, 28.261208810199, 28.204967598772, 28.148726387345, 28.092485175917, 28.036243964487, 27.980002753057, 27.923761541626, 27.867520330194, 27.811279118762, 27.755037907328, 27.698796695893, 27.642555484458, 27.586314273021, 27.530073061584, 27.473831850146, 27.417590638707, 27.361349427267, 27.305108215826, 27.248867004384, 27.192625792941, 27.136384581498, 27.080143370053, 27.023902158608, 26.967660947162, 26.911419735715, 26.855178524267, 26.798937312818, 26.742696101369, 26.686454889918, 26.630213678467, 26.573972467015, 26.517731255562, 26.461490044108, 26.405248832653, 26.349007621198, 26.292766409742, 26.236525198285, 26.180283986827, 26.124042775368, 26.067801563908, 26.011560352448, 25.955319140987, 25.899077929525, 25.842836718062, 25.786595506598, 25.730354295134, 25.674113083668, 25.617871872202, 25.561630660735, 25.505389449268, 25.449148237799, 25.392907026330, 25.336665814860, 25.280424603389, 25.224183391918, 25.167942180446, 25.111700968972, 25.055459757499, 24.999218546024, 24.942977334548, 24.886736123072, 24.830494911595, 24.774253700118, 24.718012488639, 24.661771277160, 24.605530065680, 24.549288854199, 24.493047642718, 24.436806431236, 24.380565219753, 24.324324008269, 24.268082796785, 24.211841585300, 24.155600373814, 24.099359162327, 24.043117950840, 23.986876739352, 23.930635527863, 23.874394316374, 23.818153104884, 23.761911893393, 23.705670681901, 23.649429470409, 23.593188258916, 23.536947047422, 23.480705835928, 23.424464624433, 23.368223412937, 23.311982201441, 23.255740989943, 23.199499778446, 23.143258566947, 23.087017355448, 23.030776143948, 22.974534932447, 22.918293720946, 22.862052509444, 22.805811297942, 22.749570086438, 22.693328874935, 22.637087663430, 22.580846451925, 22.524605240419, 22.468364028912, 22.412122817405, 22.355881605897, 22.299640394389, 22.243399182880, 22.187157971370, 22.130916759859, 22.074675548348, 22.018434336837, 21.962193125324, 21.905951913811, 21.849710702298, 21.793469490784, 21.737228279269, 21.680987067753, 21.624745856237, 21.568504644721, 21.512263433203, 21.456022221685, 21.399781010167, 21.343539798648, 21.287298587128, 21.231057375607, 21.174816164087, 21.118574952565, 21.062333741043, 21.006092529520, 20.949851317997, 20.893610106473, 20.837368894948, 20.781127683423, 20.724886471897, 20.668645260371, 20.612404048844, 20.556162837317, 20.499921625789, 20.443680414260, 20.387439202731, 20.331197991201, 20.274956779671, 20.218715568140, 20.162474356609, 20.106233145077, 20.049991933544, 19.993750722011, 19.937509510477, 19.881268298943, 19.825027087408, 19.768785875873, 19.712544664337, 19.656303452801, 19.600062241264, 19.543821029726, 19.487579818188, 19.431338606650, 19.375097395110, 19.318856183571, 19.262614972031, 19.206373760490, 19.150132548949, 19.093891337407, 19.037650125865, 18.981408914322, 18.925167702779, 18.868926491235, 18.812685279690, 18.756444068146, 18.700202856600, 18.643961645054, 18.587720433508, 18.531479221961, 18.475238010414, 18.418996798866, 18.362755587317, 18.306514375769, 18.250273164219, 18.194031952669, 18.137790741119, 18.081549529568, 18.025308318017, 17.969067106465, 17.912825894913, 17.856584683360, 17.800343471807, 17.744102260253, 17.687861048699, 17.631619837144, 17.575378625589, 17.519137414033, 17.462896202477, 17.406654990920, 17.350413779363, 17.294172567806, 17.237931356248, 17.181690144690, 17.125448933131, 17.069207721571, 17.012966510012, 16.956725298451, 16.900484086891, 16.844242875330, 16.788001663768, 16.731760452206, 16.675519240644, 16.619278029081, 16.563036817517, 16.506795605954, 16.450554394389, 16.394313182825, 16.338071971260, 16.281830759694, 16.225589548128, 16.169348336562, 16.113107124995, 16.056865913428, 16.000624701860, 15.944383490292, 15.888142278724, 15.831901067155, 15.775659855586, 15.719418644016, 15.663177432446, 15.606936220876, 15.550695009305, 15.494453797733, 15.438212586162, 15.381971374590, 15.325730163017, 15.269488951444, 15.213247739871, 15.157006528297, 15.100765316723, 15.044524105149, 14.988282893574, 14.932041681998, 14.875800470423, 14.819559258847, 14.763318047270, 14.707076835694, 14.650835624116, 14.594594412539, 14.538353200961, 14.482111989383, 14.425870777804, 14.369629566225, 14.313388354646, 14.257147143066, 14.200905931486, 14.144664719905, 14.088423508324, 14.032182296743, 13.975941085162, 13.919699873580, 13.863458661998, 13.807217450415, 13.750976238832, 13.694735027249, 13.638493815665, 13.582252604081, 13.526011392497, 13.469770180912, 13.413528969327, 13.357287757741, 13.301046546156, 13.244805334570, 13.188564122983, 13.132322911396, 13.076081699809, 13.019840488222, 12.963599276634, 12.907358065046, 12.851116853458, 12.794875641869, 12.738634430280, 12.682393218691, 12.626152007101, 12.569910795511, 12.513669583921, 12.457428372330, 12.401187160739, 12.344945949148, 12.288704737557, 12.232463525965, 12.176222314373, 12.119981102780, 12.063739891187, 12.007498679594, 11.951257468001, 11.895016256407, 11.838775044813, 11.782533833219, 11.726292621625, 11.670051410030, 11.613810198435, 11.557568986839, 11.501327775244, 11.445086563648, 11.388845352051, 11.332604140455, 11.276362928858, 11.220121717261, 11.163880505664, 11.107639294066, 11.051398082468, 10.995156870870, 10.938915659271, 10.882674447672, 10.826433236073, 10.770192024474, 10.713950812875, 10.657709601275, 10.601468389675, 10.545227178074, 10.488985966474, 10.432744754873, 10.376503543272, 10.320262331670, 10.264021120069, 10.207779908467, 10.151538696865, 10.095297485262, 10.039056273660, 9.982815062057, 9.926573850454, 9.870332638851, 9.814091427247, 9.757850215643, 9.701609004039, 9.645367792435, 9.589126580830, 9.532885369225, 9.476644157620, 9.420402946015, 9.364161734410, 9.307920522804, 9.251679311198, 9.195438099592, 9.139196887985, 9.082955676379, 9.026714464772, 8.970473253165, 8.914232041558, 8.857990829950, 8.801749618343, 8.745508406735, 8.689267195127, 8.633025983518, 8.576784771910, 8.520543560301, 8.464302348692, 8.408061137083, 8.351819925473, 8.295578713864, 8.239337502254, 8.183096290644, 8.126855079034, 8.070613867424, 8.014372655813, 7.958131444202, 7.901890232591, 7.845649020980, 7.789407809369, 7.733166597758, 7.676925386146, 7.620684174534, 7.564442962922, 7.508201751310, 7.451960539697, 7.395719328085, 7.339478116472, 7.283236904859, 7.226995693246, 7.170754481632, 7.114513270019, 7.058272058405, 7.002030846792, 6.945789635178, 6.889548423563, 6.833307211949, 6.777066000335, 6.720824788720, 6.664583577105, 6.608342365490, 6.552101153875, 6.495859942260, 6.439618730644, 6.383377519029, 6.327136307413, 6.270895095797, 6.214653884181, 6.158412672565, 6.102171460949, 6.045930249332, 5.989689037715, 5.933447826099, 5.877206614482, 5.820965402865, 5.764724191248, 5.708482979630, 5.652241768013, 5.596000556395, 5.539759344777, 5.483518133160, 5.427276921542, 5.371035709923, 5.314794498305, 5.258553286687, 5.202312075068, 5.146070863450, 5.089829651831, 5.033588440212, 4.977347228593, 4.921106016974, 4.864864805355, 4.808623593735, 4.752382382116, 4.696141170496, 4.639899958877, 4.583658747257, 4.527417535637, 4.471176324017, 4.414935112397, 4.358693900777, 4.302452689156, 4.246211477536, 4.189970265915, 4.133729054295, 4.077487842674, 4.021246631053, 3.965005419432, 3.908764207811, 3.852522996190, 3.796281784569, 3.740040572948, 3.683799361327, 3.627558149705, 3.571316938084, 3.515075726462, 3.458834514840, 3.402593303218, 3.346352091597, 3.290110879975, 3.233869668353, 3.177628456730, 3.121387245108, 3.065146033486, 3.008904821864, 2.952663610241, 2.896422398619, 2.840181186996, 2.783939975374, 2.727698763751, 2.671457552128, 2.615216340506, 2.558975128883, 2.502733917260, 2.446492705637, 2.390251494014, 2.334010282391, 2.277769070768, 2.221527859144, 2.165286647521, 2.109045435898, 2.052804224275, 1.996563012651, 1.940321801028, 1.884080589404, 1.827839377781, 1.771598166157, 1.715356954533, 1.659115742910, 1.602874531286, 1.546633319662, 1.490392108039, 1.434150896415, 1.377909684791, 1.321668473167, 1.265427261543, 1.209186049919, 1.152944838295, 1.096703626671, 1.040462415047, 0.984221203423, 0.927979991799, 0.871738780175, 0.815497568551, 0.759256356927, 0.703015145303, 0.646773933679, 0.590532722054, 0.534291510430, 0.478050298806, 0.421809087182, 0.365567875558, 0.309326663933, 0.253085452309, 0.196844240685, 0.140603029061, 0.084361817436, 0.028120605812)) } // namespace gaussian } // namespace spacing } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/spacing/gaussian/N128.cc0000664000175000017500000000606115160212070023345 0ustar alastairalastair/* * (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. */ // TL255 #include "atlas/grid/detail/spacing/gaussian/N.h" namespace atlas { namespace grid { namespace spacing { namespace gaussian { DEFINE_GAUSSIAN_LATITUDES( 128, LIST(89.462821568577, 88.766951352842, 88.066971647431, 87.366063433082, 86.664803013441, 85.963372160880, 85.261846060713, 84.560261383053, 83.858638128608, 83.156988128542, 82.455318827164, 81.753635141838, 81.051940450936, 80.350237152036, 79.648526993666, 78.946811280967, 78.245091007821, 77.543366944409, 76.841639696782, 76.139909748368, 75.438177489488, 74.736443238739, 74.034707258747, 73.332969767937, 72.631230949459, 71.929490958033, 71.227749925260, 70.526007963782, 69.824265170583, 69.122521629614, 68.420777413915, 67.719032587332, 67.017287205917, 66.315541319089, 65.613794970579, 64.912048199233, 64.210301039674, 63.508553522860, 62.806805676556, 62.105057525741, 61.403309092943, 60.701560398538, 59.999811460996, 59.298062297105, 58.596312922156, 57.894563350105, 57.192813593719, 56.491063664698, 55.789313573786, 55.087563330867, 54.385812945049, 53.684062424740, 52.982311777713, 52.280561011166, 51.578810131776, 50.877059145743, 50.175308058833, 49.473556876418, 48.771805603506, 48.070054244773, 47.368302804592, 46.666551287055, 45.964799695997, 45.263048035017, 44.561296307493, 43.859544516602, 43.157792665336, 42.456040756510, 41.754288792783, 41.052536776662, 40.350784710518, 39.649032596592, 38.947280437007, 38.245528233774, 37.543775988799, 36.842023703894, 36.140271380778, 35.438519021086, 34.736766626375, 34.035014198127, 33.333261737756, 32.631509246608, 31.929756725971, 31.228004177076, 30.526251601098, 29.824498999163, 29.122746372350, 28.420993721692, 27.719241048181, 27.017488352770, 26.315735636373, 25.613982899871, 24.912230144110, 24.210477369908, 23.508724578050, 22.806971769296, 22.105218944378, 21.403466104006, 20.701713248864, 19.999960379617, 19.298207496906, 18.596454601357, 17.894701693573, 17.192948774143, 16.491195843640, 15.789442902620, 15.087689951625, 14.385936991186, 13.684184021817, 12.982431044024, 12.280678058301, 11.578925065131, 10.877172064989, 10.175419058338, 9.473666045636, 8.771913027332, 8.070160003868, 7.368406975680, 6.666653943198, 5.964900906845, 5.263147867043, 4.561394824207, 3.859641778748, 3.157888731076, 2.456135681596, 1.754382630712, 1.052629578828, 0.350876526343)) } // namespace gaussian } // namespace spacing } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/spacing/gaussian/N96.cc0000664000175000017500000000466015160212070023274 0ustar alastairalastair/* * (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. */ // TL191 #include "atlas/grid/detail/spacing/gaussian/N.h" namespace atlas { namespace grid { namespace spacing { namespace gaussian { DEFINE_GAUSSIAN_LATITUDES( 96, LIST(89.284227532514, 88.357003518665, 87.424303746070, 86.490366766281, 85.555960484893, 84.621327107649, 83.686566816564, 82.751728473431, 81.816838728603, 80.881913346797, 79.946962247386, 79.011991982672, 78.077007054304, 77.142010657054, 76.207005120861, 75.271992184860, 74.336973173452, 73.401949112957, 72.466920811004, 71.531888911827, 70.596853935603, 69.661816306938, 68.726776375860, 67.791734433501, 66.856690723993, 65.921645453587, 64.986598797727, 64.051550906600, 63.116501909529, 62.181451918473, 61.246401030854, 60.311349331843, 59.376296896232, 58.441243789970, 57.506190071435, 56.571135792495, 55.636080999393, 54.701025733491, 53.765970031901, 52.830913928020, 51.895857451987, 50.960800631070, 50.025743490008, 49.090686051296, 48.155628335437, 47.220570361160, 46.285512145613, 45.350453704525, 44.415395052354, 43.480336202416, 42.545277166998, 41.610217957456, 40.675158584305, 39.740099057298, 38.805039385498, 37.869979577337, 36.934919640674, 35.999859582846, 35.064799410712, 34.129739130695, 33.194678748816, 32.259618270730, 31.324557701757, 30.389497046905, 29.454436310897, 28.519375498194, 27.584314613017, 26.649253659362, 25.714192641019, 24.779131561590, 23.844070424500, 22.909009233010, 21.973947990236, 21.038886699150, 20.103825362598, 19.168763983309, 18.233702563901, 17.298641106891, 16.363579614703, 15.428518089674, 14.493456534065, 13.558394950061, 12.623333339782, 11.688271705287, 10.753210048579, 9.818148371612, 8.883086676292, 7.948024964486, 7.012963238024, 6.077901498705, 5.142839748298, 4.207777988549, 3.272716221183, 2.337654447911, 1.402592670428, 0.467530890423)) } // namespace gaussian } // namespace spacing } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/spacing/gaussian/Latitudes.h0000664000175000017500000000401415160212070024511 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ /// @file Latitudes.h /// @author Willem Deconinck /// @date Jan 2014 #pragma once #include namespace atlas { namespace grid { namespace spacing { namespace gaussian { /** * @brief Compute gaussian latitudes between North pole and equator * @param N [in] Number of latitudes between pole and equator (Gaussian * N number) * @param latitudes [out] latitudes in degrees */ void gaussian_latitudes_npole_equator(const size_t N, double latitudes[]); /** * @brief Compute gaussian latitudes and quadrature weights between North pole * and equator * @param N [in] Number of latitudes between pole and equator (Gaussian * N number) * @param latitudes [out] latitudes in degrees * @param weights [out] quadrature weights */ void gaussian_quadrature_npole_equator(const size_t N, double latitudes[], double weights[]); /** * @brief Compute gaussian latitudes between North pole and South pole * @param N [in] Number of latitudes between pole and equator (Gaussian * N number) * @param latitudes [out] latitudes in degrees (size 2*N) */ void gaussian_latitudes_npole_spole(const size_t N, double latitudes[]); /** * @brief Compute gaussian latitudes and quadrature weights between North pole * and South pole * @param N [in] Number of latitudes between pole and equator (Gaussian * N number) * @param latitudes [out] latitudes in degrees (size 2*N) * @param weights [out] quadrature weights (size 2*N) */ void gaussian_quadrature_npole_spole(const size_t N, double latitudes[], double weights[]); } // namespace gaussian } // namespace spacing } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/spacing/gaussian/N320.cc0000664000175000017500000001461515160212070023343 0ustar alastairalastair/* * (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. */ // TL639 #include "atlas/grid/detail/spacing/gaussian/N.h" namespace atlas { namespace grid { namespace spacing { namespace gaussian { DEFINE_GAUSSIAN_LATITUDES( 320, LIST(89.784876907219, 89.506202738206, 89.225882847612, 88.945191118316, 88.664358341823, 88.383457312248, 88.102518138937, 87.821555507111, 87.540577426411, 87.259588634840, 86.978592113597, 86.697589831922, 86.416583142736, 86.135573006184, 85.854560122485, 85.573545014297, 85.292528079627, 85.011509626898, 84.730489898803, 84.449469088929, 84.168447353577, 83.887424820323, 83.606401594330, 83.325377763059, 83.044353399845, 82.763328566637, 82.482303316125, 82.201277693408, 81.920251737312, 81.639225481447, 81.358198955050, 81.077172183679, 80.796145189767, 80.515117993094, 80.234090611161, 79.953063059512, 79.672035351999, 79.391007501008, 79.109979517646, 78.828951411902, 78.547923192786, 78.266894868445, 77.985866446261, 77.704837932945, 77.423809334606, 77.142780656820, 76.861751904686, 76.580723082875, 76.299694195676, 76.018665247032, 75.737636240576, 75.456607179661, 75.175578067384, 74.894548906612, 74.613519700005, 74.332490450030, 74.051461158979, 73.770431828988, 73.489402462046, 73.208373060010, 72.927343624612, 72.646314157476, 72.365284660120, 72.084255133966, 71.803225580350, 71.522196000526, 71.241166395673, 70.960136766901, 70.679107115253, 70.398077441716, 70.117047747219, 69.836018032638, 69.554988298803, 69.273958546499, 68.992928776468, 68.711898989413, 68.430869186000, 68.149839366862, 67.868809532599, 67.587779683782, 67.306749820951, 67.025719944623, 66.744690055288, 66.463660153412, 66.182630239442, 65.901600313801, 65.620570376894, 65.339540429107, 65.058510470810, 64.777480502355, 64.496450524078, 64.215420536303, 63.934390539337, 63.653360533475, 63.372330519002, 63.091300496187, 62.810270465291, 62.529240426562, 62.248210380240, 61.967180326555, 61.686150265726, 61.405120197967, 61.124090123479, 60.843060042460, 60.562029955097, 60.280999861572, 59.999969762058, 59.718939656725, 59.437909545733, 59.156879429239, 58.875849307392, 58.594819180339, 58.313789048218, 58.032758911165, 57.751728769309, 57.470698622777, 57.189668471689, 56.908638316164, 56.627608156315, 56.346577992251, 56.065547824077, 55.784517651898, 55.503487475812, 55.222457295914, 54.941427112298, 54.660396925054, 54.379366734270, 54.098336540028, 53.817306342412, 53.536276141501, 53.255245937372, 52.974215730098, 52.693185519753, 52.412155306407, 52.131125090128, 51.850094870983, 51.569064649034, 51.288034424345, 51.007004196977, 50.725973966988, 50.444943734435, 50.163913499374, 49.882883261860, 49.601853021944, 49.320822779679, 49.039792535112, 48.758762288294, 48.477732039272, 48.196701788090, 47.915671534794, 47.634641279427, 47.353611022031, 47.072580762649, 46.791550501320, 46.510520238082, 46.229489972975, 45.948459706036, 45.667429437301, 45.386399166805, 45.105368894583, 44.824338620668, 44.543308345094, 44.262278067892, 43.981247789094, 43.700217508730, 43.419187226830, 43.138156943424, 42.857126658539, 42.576096372204, 42.295066084446, 42.014035795291, 41.733005504765, 41.451975212893, 41.170944919701, 40.889914625212, 40.608884329450, 40.327854032439, 40.046823734201, 39.765793434758, 39.484763134131, 39.203732832343, 38.922702529414, 38.641672225364, 38.360641920213, 38.079611613981, 37.798581306687, 37.517550998349, 37.236520688986, 36.955490378616, 36.674460067255, 36.393429754923, 36.112399441635, 35.831369127408, 35.550338812258, 35.269308496201, 34.988278179253, 34.707247861429, 34.426217542744, 34.145187223213, 33.864156902850, 33.583126581669, 33.302096259685, 33.021065936911, 32.740035613361, 32.459005289046, 32.177974963982, 31.896944638180, 31.615914311652, 31.334883984411, 31.053853656469, 30.772823327839, 30.491792998530, 30.210762668555, 29.929732337926, 29.648702006652, 29.367671674745, 29.086641342216, 28.805611009075, 28.524580675333, 28.243550340999, 27.962520006084, 27.681489670597, 27.400459334548, 27.119428997946, 26.838398660802, 26.557368323123, 26.276337984920, 25.995307646201, 25.714277306975, 25.433246967251, 25.152216627037, 24.871186286341, 24.590155945173, 24.309125603539, 24.028095261449, 23.747064918910, 23.466034575929, 23.185004232515, 22.903973888676, 22.622943544418, 22.341913199749, 22.060882854677, 21.779852509208, 21.498822163351, 21.217791817111, 20.936761470495, 20.655731123512, 20.374700776166, 20.093670428466, 19.812640080417, 19.531609732026, 19.250579383300, 18.969549034245, 18.688518684866, 18.407488335171, 18.126457985166, 17.845427634856, 17.564397284248, 17.283366933347, 17.002336582159, 16.721306230691, 16.440275878947, 16.159245526934, 15.878215174657, 15.597184822122, 15.316154469334, 15.035124116299, 14.754093763022, 14.473063409508, 14.192033055763, 13.911002701792, 13.629972347600, 13.348941993193, 13.067911638575, 12.786881283751, 12.505850928727, 12.224820573508, 11.943790218098, 11.662759862502, 11.381729506726, 11.100699150774, 10.819668794650, 10.538638438360, 10.257608081908, 9.976577725300, 9.695547368539, 9.414517011630, 9.133486654578, 8.852456297388, 8.571425940064, 8.290395582610, 8.009365225031, 7.728334867332, 7.447304509516, 7.166274151589, 6.885243793555, 6.604213435417, 6.323183077181, 6.042152718851, 5.761122360431, 5.480092001926, 5.199061643339, 4.918031284674, 4.637000925938, 4.355970567132, 4.074940208262, 3.793909849332, 3.512879490347, 3.231849131309, 2.950818772224, 2.669788413095, 2.388758053927, 2.107727694724, 1.826697335490, 1.545666976229, 1.264636616946, 0.983606257644, 0.702575898327, 0.421545539000, 0.140515179668)) } // namespace gaussian } // namespace spacing } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/spacing/gaussian/N4000.cc0000664000175000017500000022062315160212070023420 0ustar alastairalastair/* * (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. */ // TL7999 #include "atlas/grid/detail/spacing/gaussian/N.h" namespace atlas { namespace grid { namespace spacing { namespace gaussian { DEFINE_GAUSSIAN_LATITUDES( 4000, LIST(89.982777782041, 89.960467823498, 89.938026112601, 89.915554633223, 89.893071861958, 89.870583626524, 89.848092337395, 89.825599170237, 89.803104766281, 89.780609504838, 89.758113624578, 89.735617283150, 89.713120588868, 89.690623618599, 89.668126428400, 89.645629060112, 89.623131545602, 89.600633909559, 89.578136171413, 89.555638346662, 89.533140447819, 89.510642485097, 89.488144466917, 89.465646400278, 89.443148291053, 89.420650144201, 89.398151963940, 89.375653753880, 89.353155517131, 89.330657256382, 89.308158973974, 89.285660671954, 89.263162352116, 89.240664016047, 89.218165665144, 89.195667300653, 89.173168923684, 89.150670535227, 89.128172136173, 89.105673727319, 89.083175309389, 89.060676883034, 89.038178448845, 89.015680007358, 88.993181559065, 88.970683104410, 88.948184643801, 88.925686177614, 88.903187706190, 88.880689229847, 88.858190748874, 88.835692263540, 88.813193774093, 88.790695280762, 88.768196783761, 88.745698283287, 88.723199779523, 88.700701272640, 88.678202762798, 88.655704250144, 88.633205734819, 88.610707216951, 88.588208696663, 88.565710174068, 88.543211649272, 88.520713122378, 88.498214593477, 88.475716062661, 88.453217530011, 88.430718995608, 88.408220459525, 88.385721921832, 88.363223382596, 88.340724841881, 88.318226299744, 88.295727756243, 88.273229211430, 88.250730665357, 88.228232118071, 88.205733569618, 88.183235020041, 88.160736469382, 88.138237917679, 88.115739364971, 88.093240811293, 88.070742256678, 88.048243701159, 88.025745144768, 88.003246587532, 87.980748029482, 87.958249470643, 87.935750911042, 87.913252350702, 87.890753789648, 87.868255227903, 87.845756665488, 87.823258102423, 87.800759538729, 87.778260974425, 87.755762409529, 87.733263844059, 87.710765278031, 87.688266711463, 87.665768144369, 87.643269576765, 87.620771008664, 87.598272440082, 87.575773871032, 87.553275301526, 87.530776731576, 87.508278161196, 87.485779590396, 87.463281019188, 87.440782447582, 87.418283875589, 87.395785303218, 87.373286730480, 87.350788157385, 87.328289583939, 87.305791010154, 87.283292436037, 87.260793861596, 87.238295286839, 87.215796711774, 87.193298136408, 87.170799560749, 87.148300984803, 87.125802408577, 87.103303832079, 87.080805255312, 87.058306678285, 87.035808101003, 87.013309523472, 86.990810945697, 86.968312367684, 86.945813789438, 86.923315210965, 86.900816632268, 86.878318053353, 86.855819474226, 86.833320894889, 86.810822315347, 86.788323735606, 86.765825155669, 86.743326575540, 86.720827995223, 86.698329414722, 86.675830834040, 86.653332253182, 86.630833672151, 86.608335090950, 86.585836509583, 86.563337928052, 86.540839346362, 86.518340764515, 86.495842182514, 86.473343600362, 86.450845018063, 86.428346435618, 86.405847853031, 86.383349270303, 86.360850687439, 86.338352104440, 86.315853521309, 86.293354938048, 86.270856354659, 86.248357771146, 86.225859187509, 86.203360603752, 86.180862019876, 86.158363435884, 86.135864851777, 86.113366267558, 86.090867683228, 86.068369098789, 86.045870514244, 86.023371929594, 86.000873344840, 85.978374759985, 85.955876175031, 85.933377589978, 85.910879004828, 85.888380419584, 85.865881834246, 85.843383248817, 85.820884663297, 85.798386077688, 85.775887491991, 85.753388906209, 85.730890320342, 85.708391734391, 85.685893148358, 85.663394562245, 85.640895976052, 85.618397389780, 85.595898803431, 85.573400217007, 85.550901630507, 85.528403043934, 85.505904457288, 85.483405870571, 85.460907283783, 85.438408696926, 85.415910110001, 85.393411523008, 85.370912935948, 85.348414348824, 85.325915761635, 85.303417174382, 85.280918587066, 85.258419999689, 85.235921412251, 85.213422824753, 85.190924237196, 85.168425649580, 85.145927061907, 85.123428474177, 85.100929886391, 85.078431298550, 85.055932710655, 85.033434122705, 85.010935534703, 84.988436946649, 84.965938358543, 84.943439770386, 84.920941182179, 84.898442593922, 84.875944005617, 84.853445417263, 84.830946828862, 84.808448240413, 84.785949651918, 84.763451063378, 84.740952474792, 84.718453886162, 84.695955297487, 84.673456708770, 84.650958120009, 84.628459531205, 84.605960942360, 84.583462353474, 84.560963764546, 84.538465175579, 84.515966586571, 84.493467997524, 84.470969408439, 84.448470819315, 84.425972230153, 84.403473640953, 84.380975051717, 84.358476462443, 84.335977873134, 84.313479283789, 84.290980694409, 84.268482104994, 84.245983515544, 84.223484926060, 84.200986336543, 84.178487746992, 84.155989157409, 84.133490567792, 84.110991978144, 84.088493388464, 84.065994798753, 84.043496209010, 84.020997619237, 83.998499029433, 83.976000439600, 83.953501849737, 83.931003259844, 83.908504669922, 83.886006079972, 83.863507489993, 83.841008899986, 83.818510309952, 83.796011719889, 83.773513129800, 83.751014539684, 83.728515949541, 83.706017359372, 83.683518769177, 83.661020178956, 83.638521588710, 83.616022998438, 83.593524408141, 83.571025817820, 83.548527227475, 83.526028637105, 83.503530046711, 83.481031456294, 83.458532865853, 83.436034275389, 83.413535684902, 83.391037094392, 83.368538503860, 83.346039913306, 83.323541322729, 83.301042732131, 83.278544141511, 83.256045550870, 83.233546960207, 83.211048369524, 83.188549778820, 83.166051188095, 83.143552597351, 83.121054006585, 83.098555415800, 83.076056824996, 83.053558234171, 83.031059643328, 83.008561052465, 82.986062461583, 82.963563870682, 82.941065279763, 82.918566688825, 82.896068097869, 82.873569506895, 82.851070915903, 82.828572324893, 82.806073733866, 82.783575142821, 82.761076551759, 82.738577960680, 82.716079369584, 82.693580778471, 82.671082187341, 82.648583596195, 82.626085005032, 82.603586413854, 82.581087822659, 82.558589231449, 82.536090640222, 82.513592048980, 82.491093457723, 82.468594866450, 82.446096275163, 82.423597683860, 82.401099092542, 82.378600501209, 82.356101909862, 82.333603318500, 82.311104727124, 82.288606135733, 82.266107544328, 82.243608952910, 82.221110361477, 82.198611770031, 82.176113178570, 82.153614587097, 82.131115995610, 82.108617404109, 82.086118812596, 82.063620221069, 82.041121629529, 82.018623037977, 81.996124446412, 81.973625854834, 81.951127263243, 81.928628671640, 81.906130080025, 81.883631488397, 81.861132896757, 81.838634305106, 81.816135713442, 81.793637121767, 81.771138530079, 81.748639938380, 81.726141346670, 81.703642754948, 81.681144163215, 81.658645571470, 81.636146979714, 81.613648387948, 81.591149796170, 81.568651204381, 81.546152612581, 81.523654020771, 81.501155428950, 81.478656837119, 81.456158245277, 81.433659653424, 81.411161061561, 81.388662469688, 81.366163877805, 81.343665285912, 81.321166694009, 81.298668102095, 81.276169510172, 81.253670918240, 81.231172326297, 81.208673734345, 81.186175142383, 81.163676550412, 81.141177958431, 81.118679366442, 81.096180774442, 81.073682182434, 81.051183590416, 81.028684998390, 81.006186406354, 80.983687814310, 80.961189222257, 80.938690630195, 80.916192038124, 80.893693446044, 80.871194853956, 80.848696261860, 80.826197669754, 80.803699077641, 80.781200485519, 80.758701893389, 80.736203301251, 80.713704709104, 80.691206116950, 80.668707524787, 80.646208932616, 80.623710340438, 80.601211748251, 80.578713156057, 80.556214563855, 80.533715971645, 80.511217379428, 80.488718787203, 80.466220194970, 80.443721602730, 80.421223010483, 80.398724418228, 80.376225825966, 80.353727233696, 80.331228641420, 80.308730049136, 80.286231456845, 80.263732864547, 80.241234272242, 80.218735679930, 80.196237087611, 80.173738495285, 80.151239902952, 80.128741310612, 80.106242718266, 80.083744125913, 80.061245533554, 80.038746941187, 80.016248348815, 79.993749756435, 79.971251164049, 79.948752571657, 79.926253979258, 79.903755386853, 79.881256794442, 79.858758202025, 79.836259609601, 79.813761017171, 79.791262424735, 79.768763832293, 79.746265239844, 79.723766647390, 79.701268054930, 79.678769462464, 79.656270869991, 79.633772277514, 79.611273685030, 79.588775092540, 79.566276500045, 79.543777907544, 79.521279315037, 79.498780722525, 79.476282130007, 79.453783537483, 79.431284944954, 79.408786352420, 79.386287759879, 79.363789167334, 79.341290574783, 79.318791982227, 79.296293389665, 79.273794797099, 79.251296204526, 79.228797611949, 79.206299019367, 79.183800426779, 79.161301834186, 79.138803241588, 79.116304648985, 79.093806056377, 79.071307463764, 79.048808871146, 79.026310278523, 79.003811685896, 78.981313093263, 78.958814500625, 78.936315907983, 78.913817315336, 78.891318722684, 78.868820130027, 78.846321537366, 78.823822944700, 78.801324352029, 78.778825759354, 78.756327166674, 78.733828573989, 78.711329981300, 78.688831388607, 78.666332795909, 78.643834203206, 78.621335610500, 78.598837017788, 78.576338425073, 78.553839832353, 78.531341239628, 78.508842646900, 78.486344054167, 78.463845461429, 78.441346868688, 78.418848275942, 78.396349683193, 78.373851090439, 78.351352497681, 78.328853904918, 78.306355312152, 78.283856719382, 78.261358126607, 78.238859533829, 78.216360941047, 78.193862348260, 78.171363755470, 78.148865162676, 78.126366569878, 78.103867977076, 78.081369384270, 78.058870791460, 78.036372198647, 78.013873605830, 77.991375013009, 77.968876420184, 77.946377827355, 77.923879234523, 77.901380641687, 77.878882048848, 77.856383456004, 77.833884863158, 77.811386270307, 77.788887677453, 77.766389084596, 77.743890491735, 77.721391898870, 77.698893306002, 77.676394713130, 77.653896120255, 77.631397527376, 77.608898934494, 77.586400341609, 77.563901748720, 77.541403155828, 77.518904562933, 77.496405970034, 77.473907377132, 77.451408784226, 77.428910191317, 77.406411598405, 77.383913005490, 77.361414412572, 77.338915819650, 77.316417226725, 77.293918633797, 77.271420040866, 77.248921447931, 77.226422854994, 77.203924262053, 77.181425669109, 77.158927076162, 77.136428483213, 77.113929890260, 77.091431297304, 77.068932704345, 77.046434111383, 77.023935518418, 77.001436925450, 76.978938332479, 76.956439739505, 76.933941146528, 76.911442553549, 76.888943960566, 76.866445367581, 76.843946774592, 76.821448181601, 76.798949588607, 76.776450995610, 76.753952402611, 76.731453809608, 76.708955216603, 76.686456623595, 76.663958030584, 76.641459437571, 76.618960844555, 76.596462251536, 76.573963658514, 76.551465065490, 76.528966472463, 76.506467879434, 76.483969286401, 76.461470693367, 76.438972100329, 76.416473507289, 76.393974914246, 76.371476321201, 76.348977728153, 76.326479135103, 76.303980542050, 76.281481948995, 76.258983355937, 76.236484762876, 76.213986169813, 76.191487576748, 76.168988983680, 76.146490390610, 76.123991797537, 76.101493204462, 76.078994611385, 76.056496018304, 76.033997425222, 76.011498832137, 75.989000239050, 75.966501645961, 75.944003052869, 75.921504459775, 75.899005866678, 75.876507273579, 75.854008680478, 75.831510087375, 75.809011494269, 75.786512901161, 75.764014308051, 75.741515714939, 75.719017121824, 75.696518528707, 75.674019935588, 75.651521342466, 75.629022749343, 75.606524156217, 75.584025563089, 75.561526969959, 75.539028376827, 75.516529783692, 75.494031190556, 75.471532597417, 75.449034004276, 75.426535411134, 75.404036817989, 75.381538224842, 75.359039631692, 75.336541038541, 75.314042445388, 75.291543852233, 75.269045259075, 75.246546665916, 75.224048072755, 75.201549479591, 75.179050886426, 75.156552293258, 75.134053700089, 75.111555106918, 75.089056513744, 75.066557920569, 75.044059327392, 75.021560734213, 74.999062141031, 74.976563547848, 74.954064954664, 74.931566361477, 74.909067768288, 74.886569175097, 74.864070581905, 74.841571988710, 74.819073395514, 74.796574802316, 74.774076209116, 74.751577615914, 74.729079022711, 74.706580429505, 74.684081836298, 74.661583243089, 74.639084649878, 74.616586056666, 74.594087463451, 74.571588870235, 74.549090277017, 74.526591683797, 74.504093090576, 74.481594497353, 74.459095904128, 74.436597310901, 74.414098717673, 74.391600124442, 74.369101531211, 74.346602937977, 74.324104344742, 74.301605751505, 74.279107158266, 74.256608565026, 74.234109971784, 74.211611378541, 74.189112785296, 74.166614192049, 74.144115598800, 74.121617005550, 74.099118412298, 74.076619819045, 74.054121225790, 74.031622632533, 74.009124039275, 73.986625446016, 73.964126852754, 73.941628259491, 73.919129666227, 73.896631072961, 73.874132479693, 73.851633886424, 73.829135293154, 73.806636699881, 73.784138106608, 73.761639513332, 73.739140920056, 73.716642326777, 73.694143733498, 73.671645140216, 73.649146546934, 73.626647953649, 73.604149360364, 73.581650767077, 73.559152173788, 73.536653580498, 73.514154987206, 73.491656393913, 73.469157800619, 73.446659207323, 73.424160614026, 73.401662020727, 73.379163427427, 73.356664834125, 73.334166240822, 73.311667647518, 73.289169054212, 73.266670460905, 73.244171867596, 73.221673274286, 73.199174680975, 73.176676087662, 73.154177494348, 73.131678901033, 73.109180307716, 73.086681714398, 73.064183121079, 73.041684527758, 73.019185934436, 72.996687341112, 72.974188747788, 72.951690154462, 72.929191561134, 72.906692967806, 72.884194374476, 72.861695781144, 72.839197187812, 72.816698594478, 72.794200001143, 72.771701407806, 72.749202814469, 72.726704221130, 72.704205627790, 72.681707034448, 72.659208441106, 72.636709847762, 72.614211254416, 72.591712661070, 72.569214067722, 72.546715474374, 72.524216881024, 72.501718287672, 72.479219694320, 72.456721100966, 72.434222507611, 72.411723914255, 72.389225320898, 72.366726727539, 72.344228134180, 72.321729540819, 72.299230947457, 72.276732354094, 72.254233760729, 72.231735167364, 72.209236573997, 72.186737980629, 72.164239387260, 72.141740793890, 72.119242200519, 72.096743607146, 72.074245013773, 72.051746420398, 72.029247827023, 72.006749233646, 71.984250640268, 71.961752046889, 71.939253453508, 71.916754860127, 71.894256266745, 71.871757673361, 71.849259079977, 71.826760486591, 71.804261893204, 71.781763299816, 71.759264706427, 71.736766113037, 71.714267519646, 71.691768926254, 71.669270332861, 71.646771739467, 71.624273146072, 71.601774552675, 71.579275959278, 71.556777365880, 71.534278772480, 71.511780179080, 71.489281585678, 71.466782992276, 71.444284398872, 71.421785805468, 71.399287212062, 71.376788618656, 71.354290025248, 71.331791431840, 71.309292838430, 71.286794245020, 71.264295651608, 71.241797058196, 71.219298464782, 71.196799871368, 71.174301277952, 71.151802684536, 71.129304091118, 71.106805497700, 71.084306904281, 71.061808310860, 71.039309717439, 71.016811124017, 70.994312530594, 70.971813937170, 70.949315343745, 70.926816750319, 70.904318156892, 70.881819563464, 70.859320970035, 70.836822376606, 70.814323783175, 70.791825189744, 70.769326596311, 70.746828002878, 70.724329409444, 70.701830816008, 70.679332222572, 70.656833629135, 70.634335035698, 70.611836442259, 70.589337848819, 70.566839255379, 70.544340661937, 70.521842068495, 70.499343475052, 70.476844881608, 70.454346288163, 70.431847694717, 70.409349101270, 70.386850507823, 70.364351914375, 70.341853320925, 70.319354727475, 70.296856134024, 70.274357540573, 70.251858947120, 70.229360353667, 70.206861760212, 70.184363166757, 70.161864573301, 70.139365979844, 70.116867386387, 70.094368792928, 70.071870199469, 70.049371606009, 70.026873012548, 70.004374419086, 69.981875825624, 69.959377232160, 69.936878638696, 69.914380045231, 69.891881451766, 69.869382858299, 69.846884264832, 69.824385671364, 69.801887077895, 69.779388484425, 69.756889890954, 69.734391297483, 69.711892704011, 69.689394110538, 69.666895517065, 69.644396923590, 69.621898330115, 69.599399736639, 69.576901143163, 69.554402549685, 69.531903956207, 69.509405362728, 69.486906769248, 69.464408175768, 69.441909582287, 69.419410988805, 69.396912395322, 69.374413801839, 69.351915208354, 69.329416614870, 69.306918021384, 69.284419427897, 69.261920834410, 69.239422240923, 69.216923647434, 69.194425053945, 69.171926460455, 69.149427866964, 69.126929273473, 69.104430679980, 69.081932086488, 69.059433492994, 69.036934899500, 69.014436306005, 68.991937712509, 68.969439119013, 68.946940525516, 68.924441932018, 68.901943338519, 68.879444745020, 68.856946151521, 68.834447558020, 68.811948964519, 68.789450371017, 68.766951777514, 68.744453184011, 68.721954590507, 68.699455997003, 68.676957403497, 68.654458809991, 68.631960216485, 68.609461622978, 68.586963029470, 68.564464435961, 68.541965842452, 68.519467248942, 68.496968655431, 68.474470061920, 68.451971468408, 68.429472874896, 68.406974281383, 68.384475687869, 68.361977094355, 68.339478500839, 68.316979907324, 68.294481313807, 68.271982720290, 68.249484126773, 68.226985533255, 68.204486939736, 68.181988346216, 68.159489752696, 68.136991159175, 68.114492565654, 68.091993972132, 68.069495378609, 68.046996785086, 68.024498191562, 68.001999598038, 67.979501004513, 67.957002410987, 67.934503817461, 67.912005223934, 67.889506630407, 67.867008036879, 67.844509443350, 67.822010849821, 67.799512256291, 67.777013662761, 67.754515069230, 67.732016475698, 67.709517882166, 67.687019288633, 67.664520695100, 67.642022101566, 67.619523508031, 67.597024914496, 67.574526320960, 67.552027727424, 67.529529133887, 67.507030540350, 67.484531946812, 67.462033353273, 67.439534759734, 67.417036166195, 67.394537572654, 67.372038979113, 67.349540385572, 67.327041792030, 67.304543198488, 67.282044604945, 67.259546011401, 67.237047417857, 67.214548824312, 67.192050230767, 67.169551637221, 67.147053043675, 67.124554450128, 67.102055856581, 67.079557263033, 67.057058669484, 67.034560075935, 67.012061482386, 66.989562888836, 66.967064295285, 66.944565701734, 66.922067108182, 66.899568514630, 66.877069921077, 66.854571327524, 66.832072733970, 66.809574140416, 66.787075546861, 66.764576953306, 66.742078359750, 66.719579766194, 66.697081172637, 66.674582579080, 66.652083985522, 66.629585391963, 66.607086798404, 66.584588204845, 66.562089611285, 66.539591017725, 66.517092424164, 66.494593830602, 66.472095237040, 66.449596643478, 66.427098049915, 66.404599456352, 66.382100862788, 66.359602269223, 66.337103675658, 66.314605082093, 66.292106488527, 66.269607894961, 66.247109301394, 66.224610707827, 66.202112114259, 66.179613520691, 66.157114927122, 66.134616333553, 66.112117739983, 66.089619146413, 66.067120552842, 66.044621959271, 66.022123365699, 65.999624772127, 65.977126178555, 65.954627584982, 65.932128991408, 65.909630397834, 65.887131804260, 65.864633210685, 65.842134617110, 65.819636023534, 65.797137429958, 65.774638836381, 65.752140242804, 65.729641649226, 65.707143055648, 65.684644462070, 65.662145868491, 65.639647274911, 65.617148681331, 65.594650087751, 65.572151494170, 65.549652900589, 65.527154307008, 65.504655713425, 65.482157119843, 65.459658526260, 65.437159932677, 65.414661339093, 65.392162745508, 65.369664151924, 65.347165558339, 65.324666964753, 65.302168371167, 65.279669777581, 65.257171183994, 65.234672590406, 65.212173996819, 65.189675403231, 65.167176809642, 65.144678216053, 65.122179622464, 65.099681028874, 65.077182435284, 65.054683841693, 65.032185248102, 65.009686654510, 64.987188060918, 64.964689467326, 64.942190873733, 64.919692280140, 64.897193686547, 64.874695092953, 64.852196499358, 64.829697905764, 64.807199312168, 64.784700718573, 64.762202124977, 64.739703531380, 64.717204937784, 64.694706344186, 64.672207750589, 64.649709156991, 64.627210563392, 64.604711969793, 64.582213376194, 64.559714782595, 64.537216188995, 64.514717595394, 64.492219001794, 64.469720408192, 64.447221814591, 64.424723220989, 64.402224627387, 64.379726033784, 64.357227440181, 64.334728846577, 64.312230252974, 64.289731659369, 64.267233065765, 64.244734472160, 64.222235878554, 64.199737284949, 64.177238691342, 64.154740097736, 64.132241504129, 64.109742910522, 64.087244316914, 64.064745723306, 64.042247129698, 64.019748536089, 63.997249942480, 63.974751348870, 63.952252755261, 63.929754161650, 63.907255568040, 63.884756974429, 63.862258380818, 63.839759787206, 63.817261193594, 63.794762599982, 63.772264006369, 63.749765412756, 63.727266819142, 63.704768225528, 63.682269631914, 63.659771038300, 63.637272444685, 63.614773851070, 63.592275257454, 63.569776663838, 63.547278070222, 63.524779476605, 63.502280882988, 63.479782289371, 63.457283695753, 63.434785102135, 63.412286508517, 63.389787914898, 63.367289321279, 63.344790727660, 63.322292134040, 63.299793540420, 63.277294946799, 63.254796353178, 63.232297759557, 63.209799165936, 63.187300572314, 63.164801978692, 63.142303385070, 63.119804791447, 63.097306197824, 63.074807604200, 63.052309010576, 63.029810416952, 63.007311823328, 62.984813229703, 62.962314636078, 62.939816042453, 62.917317448827, 62.894818855201, 62.872320261574, 62.849821667948, 62.827323074321, 62.804824480693, 62.782325887066, 62.759827293438, 62.737328699809, 62.714830106181, 62.692331512552, 62.669832918922, 62.647334325293, 62.624835731663, 62.602337138033, 62.579838544402, 62.557339950771, 62.534841357140, 62.512342763509, 62.489844169877, 62.467345576245, 62.444846982612, 62.422348388979, 62.399849795346, 62.377351201713, 62.354852608079, 62.332354014446, 62.309855420811, 62.287356827177, 62.264858233542, 62.242359639907, 62.219861046271, 62.197362452635, 62.174863858999, 62.152365265363, 62.129866671726, 62.107368078089, 62.084869484452, 62.062370890814, 62.039872297177, 62.017373703538, 61.994875109900, 61.972376516261, 61.949877922622, 61.927379328983, 61.904880735343, 61.882382141703, 61.859883548063, 61.837384954423, 61.814886360782, 61.792387767141, 61.769889173499, 61.747390579858, 61.724891986216, 61.702393392573, 61.679894798931, 61.657396205288, 61.634897611645, 61.612399018002, 61.589900424358, 61.567401830714, 61.544903237070, 61.522404643425, 61.499906049781, 61.477407456136, 61.454908862490, 61.432410268845, 61.409911675199, 61.387413081553, 61.364914487906, 61.342415894260, 61.319917300613, 61.297418706965, 61.274920113318, 61.252421519670, 61.229922926022, 61.207424332374, 61.184925738725, 61.162427145076, 61.139928551427, 61.117429957777, 61.094931364128, 61.072432770478, 61.049934176828, 61.027435583177, 61.004936989526, 60.982438395875, 60.959939802224, 60.937441208572, 60.914942614921, 60.892444021269, 60.869945427616, 60.847446833964, 60.824948240311, 60.802449646658, 60.779951053004, 60.757452459351, 60.734953865697, 60.712455272043, 60.689956678388, 60.667458084733, 60.644959491079, 60.622460897423, 60.599962303768, 60.577463710112, 60.554965116456, 60.532466522800, 60.509967929144, 60.487469335487, 60.464970741830, 60.442472148173, 60.419973554515, 60.397474960858, 60.374976367200, 60.352477773541, 60.329979179883, 60.307480586224, 60.284981992565, 60.262483398906, 60.239984805247, 60.217486211587, 60.194987617927, 60.172489024267, 60.149990430607, 60.127491836946, 60.104993243285, 60.082494649624, 60.059996055963, 60.037497462301, 60.014998868639, 59.992500274977, 59.970001681315, 59.947503087652, 59.925004493989, 59.902505900326, 59.880007306663, 59.857508712999, 59.835010119336, 59.812511525672, 59.790012932007, 59.767514338343, 59.745015744678, 59.722517151013, 59.700018557348, 59.677519963683, 59.655021370017, 59.632522776351, 59.610024182685, 59.587525589019, 59.565026995352, 59.542528401686, 59.520029808019, 59.497531214351, 59.475032620684, 59.452534027016, 59.430035433348, 59.407536839680, 59.385038246012, 59.362539652343, 59.340041058674, 59.317542465005, 59.295043871336, 59.272545277667, 59.250046683997, 59.227548090327, 59.205049496657, 59.182550902986, 59.160052309316, 59.137553715645, 59.115055121974, 59.092556528303, 59.070057934631, 59.047559340960, 59.025060747288, 59.002562153616, 58.980063559943, 58.957564966271, 58.935066372598, 58.912567778925, 58.890069185252, 58.867570591578, 58.845071997905, 58.822573404231, 58.800074810557, 58.777576216883, 58.755077623208, 58.732579029533, 58.710080435859, 58.687581842183, 58.665083248508, 58.642584654833, 58.620086061157, 58.597587467481, 58.575088873805, 58.552590280128, 58.530091686452, 58.507593092775, 58.485094499098, 58.462595905421, 58.440097311744, 58.417598718066, 58.395100124388, 58.372601530710, 58.350102937032, 58.327604343353, 58.305105749675, 58.282607155996, 58.260108562317, 58.237609968638, 58.215111374958, 58.192612781279, 58.170114187599, 58.147615593919, 58.125117000239, 58.102618406558, 58.080119812878, 58.057621219197, 58.035122625516, 58.012624031835, 57.990125438153, 57.967626844472, 57.945128250790, 57.922629657108, 57.900131063426, 57.877632469743, 57.855133876061, 57.832635282378, 57.810136688695, 57.787638095012, 57.765139501328, 57.742640907645, 57.720142313961, 57.697643720277, 57.675145126593, 57.652646532909, 57.630147939224, 57.607649345540, 57.585150751855, 57.562652158170, 57.540153564484, 57.517654970799, 57.495156377113, 57.472657783427, 57.450159189741, 57.427660596055, 57.405162002369, 57.382663408682, 57.360164814995, 57.337666221309, 57.315167627621, 57.292669033934, 57.270170440247, 57.247671846559, 57.225173252871, 57.202674659183, 57.180176065495, 57.157677471806, 57.135178878118, 57.112680284429, 57.090181690740, 57.067683097051, 57.045184503362, 57.022685909672, 57.000187315982, 56.977688722293, 56.955190128603, 56.932691534912, 56.910192941222, 56.887694347531, 56.865195753841, 56.842697160150, 56.820198566459, 56.797699972767, 56.775201379076, 56.752702785384, 56.730204191692, 56.707705598001, 56.685207004308, 56.662708410616, 56.640209816924, 56.617711223231, 56.595212629538, 56.572714035845, 56.550215442152, 56.527716848458, 56.505218254765, 56.482719661071, 56.460221067377, 56.437722473683, 56.415223879989, 56.392725286295, 56.370226692600, 56.347728098905, 56.325229505211, 56.302730911515, 56.280232317820, 56.257733724125, 56.235235130429, 56.212736536734, 56.190237943038, 56.167739349342, 56.145240755645, 56.122742161949, 56.100243568252, 56.077744974556, 56.055246380859, 56.032747787162, 56.010249193465, 55.987750599767, 55.965252006070, 55.942753412372, 55.920254818674, 55.897756224976, 55.875257631278, 55.852759037579, 55.830260443881, 55.807761850182, 55.785263256483, 55.762764662784, 55.740266069085, 55.717767475386, 55.695268881686, 55.672770287987, 55.650271694287, 55.627773100587, 55.605274506887, 55.582775913187, 55.560277319486, 55.537778725786, 55.515280132085, 55.492781538384, 55.470282944683, 55.447784350982, 55.425285757280, 55.402787163579, 55.380288569877, 55.357789976175, 55.335291382473, 55.312792788771, 55.290294195069, 55.267795601366, 55.245297007664, 55.222798413961, 55.200299820258, 55.177801226555, 55.155302632852, 55.132804039148, 55.110305445445, 55.087806851741, 55.065308258037, 55.042809664333, 55.020311070629, 54.997812476925, 54.975313883221, 54.952815289516, 54.930316695811, 54.907818102107, 54.885319508401, 54.862820914696, 54.840322320991, 54.817823727286, 54.795325133580, 54.772826539874, 54.750327946168, 54.727829352462, 54.705330758756, 54.682832165050, 54.660333571343, 54.637834977637, 54.615336383930, 54.592837790223, 54.570339196516, 54.547840602809, 54.525342009101, 54.502843415394, 54.480344821686, 54.457846227978, 54.435347634270, 54.412849040562, 54.390350446854, 54.367851853146, 54.345353259437, 54.322854665729, 54.300356072020, 54.277857478311, 54.255358884602, 54.232860290893, 54.210361697183, 54.187863103474, 54.165364509764, 54.142865916054, 54.120367322344, 54.097868728634, 54.075370134924, 54.052871541214, 54.030372947503, 54.007874353793, 53.985375760082, 53.962877166371, 53.940378572660, 53.917879978949, 53.895381385238, 53.872882791526, 53.850384197815, 53.827885604103, 53.805387010391, 53.782888416679, 53.760389822967, 53.737891229255, 53.715392635543, 53.692894041830, 53.670395448118, 53.647896854405, 53.625398260692, 53.602899666979, 53.580401073266, 53.557902479552, 53.535403885839, 53.512905292125, 53.490406698412, 53.467908104698, 53.445409510984, 53.422910917270, 53.400412323556, 53.377913729841, 53.355415136127, 53.332916542412, 53.310417948697, 53.287919354983, 53.265420761268, 53.242922167552, 53.220423573837, 53.197924980122, 53.175426386406, 53.152927792691, 53.130429198975, 53.107930605259, 53.085432011543, 53.062933417827, 53.040434824110, 53.017936230394, 52.995437636678, 52.972939042961, 52.950440449244, 52.927941855527, 52.905443261810, 52.882944668093, 52.860446074376, 52.837947480658, 52.815448886941, 52.792950293223, 52.770451699505, 52.747953105787, 52.725454512069, 52.702955918351, 52.680457324633, 52.657958730914, 52.635460137196, 52.612961543477, 52.590462949759, 52.567964356040, 52.545465762321, 52.522967168601, 52.500468574882, 52.477969981163, 52.455471387443, 52.432972793724, 52.410474200004, 52.387975606284, 52.365477012564, 52.342978418844, 52.320479825124, 52.297981231403, 52.275482637683, 52.252984043962, 52.230485450242, 52.207986856521, 52.185488262800, 52.162989669079, 52.140491075358, 52.117992481636, 52.095493887915, 52.072995294193, 52.050496700472, 52.027998106750, 52.005499513028, 51.983000919306, 51.960502325584, 51.938003731862, 51.915505138139, 51.893006544417, 51.870507950694, 51.848009356972, 51.825510763249, 51.803012169526, 51.780513575803, 51.758014982080, 51.735516388357, 51.713017794633, 51.690519200910, 51.668020607186, 51.645522013462, 51.623023419738, 51.600524826015, 51.578026232290, 51.555527638566, 51.533029044842, 51.510530451118, 51.488031857393, 51.465533263668, 51.443034669944, 51.420536076219, 51.398037482494, 51.375538888769, 51.353040295044, 51.330541701318, 51.308043107593, 51.285544513868, 51.263045920142, 51.240547326416, 51.218048732690, 51.195550138964, 51.173051545238, 51.150552951512, 51.128054357786, 51.105555764059, 51.083057170333, 51.060558576606, 51.038059982880, 51.015561389153, 50.993062795426, 50.970564201699, 50.948065607972, 50.925567014245, 50.903068420517, 50.880569826790, 50.858071233062, 50.835572639334, 50.813074045607, 50.790575451879, 50.768076858151, 50.745578264423, 50.723079670695, 50.700581076966, 50.678082483238, 50.655583889509, 50.633085295781, 50.610586702052, 50.588088108323, 50.565589514594, 50.543090920865, 50.520592327136, 50.498093733407, 50.475595139677, 50.453096545948, 50.430597952218, 50.408099358489, 50.385600764759, 50.363102171029, 50.340603577299, 50.318104983569, 50.295606389839, 50.273107796109, 50.250609202378, 50.228110608648, 50.205612014917, 50.183113421187, 50.160614827456, 50.138116233725, 50.115617639994, 50.093119046263, 50.070620452532, 50.048121858800, 50.025623265069, 50.003124671337, 49.980626077606, 49.958127483874, 49.935628890142, 49.913130296411, 49.890631702679, 49.868133108946, 49.845634515214, 49.823135921482, 49.800637327750, 49.778138734017, 49.755640140285, 49.733141546552, 49.710642952819, 49.688144359086, 49.665645765353, 49.643147171620, 49.620648577887, 49.598149984154, 49.575651390421, 49.553152796687, 49.530654202954, 49.508155609220, 49.485657015486, 49.463158421752, 49.440659828018, 49.418161234284, 49.395662640550, 49.373164046816, 49.350665453082, 49.328166859347, 49.305668265613, 49.283169671878, 49.260671078144, 49.238172484409, 49.215673890674, 49.193175296939, 49.170676703204, 49.148178109469, 49.125679515734, 49.103180921998, 49.080682328263, 49.058183734527, 49.035685140792, 49.013186547056, 48.990687953320, 48.968189359584, 48.945690765848, 48.923192172112, 48.900693578376, 48.878194984640, 48.855696390904, 48.833197797167, 48.810699203431, 48.788200609694, 48.765702015957, 48.743203422220, 48.720704828484, 48.698206234747, 48.675707641010, 48.653209047272, 48.630710453535, 48.608211859798, 48.585713266060, 48.563214672323, 48.540716078585, 48.518217484848, 48.495718891110, 48.473220297372, 48.450721703634, 48.428223109896, 48.405724516158, 48.383225922419, 48.360727328681, 48.338228734943, 48.315730141204, 48.293231547466, 48.270732953727, 48.248234359988, 48.225735766249, 48.203237172510, 48.180738578771, 48.158239985032, 48.135741391293, 48.113242797554, 48.090744203815, 48.068245610075, 48.045747016336, 48.023248422596, 48.000749828856, 47.978251235116, 47.955752641377, 47.933254047637, 47.910755453897, 47.888256860156, 47.865758266416, 47.843259672676, 47.820761078935, 47.798262485195, 47.775763891454, 47.753265297714, 47.730766703973, 47.708268110232, 47.685769516491, 47.663270922750, 47.640772329009, 47.618273735268, 47.595775141527, 47.573276547786, 47.550777954044, 47.528279360303, 47.505780766561, 47.483282172820, 47.460783579078, 47.438284985336, 47.415786391594, 47.393287797852, 47.370789204110, 47.348290610368, 47.325792016626, 47.303293422883, 47.280794829141, 47.258296235399, 47.235797641656, 47.213299047913, 47.190800454171, 47.168301860428, 47.145803266685, 47.123304672942, 47.100806079199, 47.078307485456, 47.055808891713, 47.033310297970, 47.010811704226, 46.988313110483, 46.965814516739, 46.943315922996, 46.920817329252, 46.898318735508, 46.875820141765, 46.853321548021, 46.830822954277, 46.808324360533, 46.785825766788, 46.763327173044, 46.740828579300, 46.718329985556, 46.695831391811, 46.673332798067, 46.650834204322, 46.628335610577, 46.605837016833, 46.583338423088, 46.560839829343, 46.538341235598, 46.515842641853, 46.493344048108, 46.470845454362, 46.448346860617, 46.425848266872, 46.403349673126, 46.380851079381, 46.358352485635, 46.335853891890, 46.313355298144, 46.290856704398, 46.268358110652, 46.245859516906, 46.223360923160, 46.200862329414, 46.178363735668, 46.155865141921, 46.133366548175, 46.110867954429, 46.088369360682, 46.065870766936, 46.043372173189, 46.020873579442, 45.998374985695, 45.975876391948, 45.953377798202, 45.930879204454, 45.908380610707, 45.885882016960, 45.863383423213, 45.840884829466, 45.818386235718, 45.795887641971, 45.773389048223, 45.750890454476, 45.728391860728, 45.705893266980, 45.683394673232, 45.660896079485, 45.638397485737, 45.615898891988, 45.593400298240, 45.570901704492, 45.548403110744, 45.525904516996, 45.503405923247, 45.480907329499, 45.458408735750, 45.435910142002, 45.413411548253, 45.390912954504, 45.368414360755, 45.345915767006, 45.323417173257, 45.300918579508, 45.278419985759, 45.255921392010, 45.233422798261, 45.210924204511, 45.188425610762, 45.165927017013, 45.143428423263, 45.120929829513, 45.098431235764, 45.075932642014, 45.053434048264, 45.030935454514, 45.008436860764, 44.985938267014, 44.963439673264, 44.940941079514, 44.918442485764, 44.895943892014, 44.873445298263, 44.850946704513, 44.828448110762, 44.805949517012, 44.783450923261, 44.760952329510, 44.738453735760, 44.715955142009, 44.693456548258, 44.670957954507, 44.648459360756, 44.625960767005, 44.603462173254, 44.580963579502, 44.558464985751, 44.535966392000, 44.513467798248, 44.490969204497, 44.468470610745, 44.445972016993, 44.423473423242, 44.400974829490, 44.378476235738, 44.355977641986, 44.333479048234, 44.310980454482, 44.288481860730, 44.265983266978, 44.243484673226, 44.220986079473, 44.198487485721, 44.175988891969, 44.153490298216, 44.130991704464, 44.108493110711, 44.085994516958, 44.063495923205, 44.040997329453, 44.018498735700, 43.996000141947, 43.973501548194, 43.951002954441, 43.928504360688, 43.906005766934, 43.883507173181, 43.861008579428, 43.838509985674, 43.816011391921, 43.793512798167, 43.771014204414, 43.748515610660, 43.726017016906, 43.703518423153, 43.681019829399, 43.658521235645, 43.636022641891, 43.613524048137, 43.591025454383, 43.568526860629, 43.546028266874, 43.523529673120, 43.501031079366, 43.478532485611, 43.456033891857, 43.433535298102, 43.411036704348, 43.388538110593, 43.366039516838, 43.343540923084, 43.321042329329, 43.298543735574, 43.276045141819, 43.253546548064, 43.231047954309, 43.208549360554, 43.186050766798, 43.163552173043, 43.141053579288, 43.118554985532, 43.096056391777, 43.073557798021, 43.051059204266, 43.028560610510, 43.006062016754, 42.983563422999, 42.961064829243, 42.938566235487, 42.916067641731, 42.893569047975, 42.871070454219, 42.848571860463, 42.826073266707, 42.803574672951, 42.781076079194, 42.758577485438, 42.736078891681, 42.713580297925, 42.691081704168, 42.668583110412, 42.646084516655, 42.623585922899, 42.601087329142, 42.578588735385, 42.556090141628, 42.533591547871, 42.511092954114, 42.488594360357, 42.466095766600, 42.443597172843, 42.421098579086, 42.398599985328, 42.376101391571, 42.353602797814, 42.331104204056, 42.308605610299, 42.286107016541, 42.263608422783, 42.241109829026, 42.218611235268, 42.196112641510, 42.173614047752, 42.151115453994, 42.128616860236, 42.106118266478, 42.083619672720, 42.061121078962, 42.038622485204, 42.016123891446, 41.993625297687, 41.971126703929, 41.948628110170, 41.926129516412, 41.903630922653, 41.881132328895, 41.858633735136, 41.836135141378, 41.813636547619, 41.791137953860, 41.768639360101, 41.746140766342, 41.723642172583, 41.701143578824, 41.678644985065, 41.656146391306, 41.633647797547, 41.611149203787, 41.588650610028, 41.566152016269, 41.543653422509, 41.521154828750, 41.498656234990, 41.476157641231, 41.453659047471, 41.431160453711, 41.408661859952, 41.386163266192, 41.363664672432, 41.341166078672, 41.318667484912, 41.296168891152, 41.273670297392, 41.251171703632, 41.228673109872, 41.206174516111, 41.183675922351, 41.161177328591, 41.138678734830, 41.116180141070, 41.093681547309, 41.071182953549, 41.048684359788, 41.026185766028, 41.003687172267, 40.981188578506, 40.958689984745, 40.936191390984, 40.913692797224, 40.891194203463, 40.868695609702, 40.846197015940, 40.823698422179, 40.801199828418, 40.778701234657, 40.756202640896, 40.733704047134, 40.711205453373, 40.688706859611, 40.666208265850, 40.643709672088, 40.621211078327, 40.598712484565, 40.576213890803, 40.553715297042, 40.531216703280, 40.508718109518, 40.486219515756, 40.463720921994, 40.441222328232, 40.418723734470, 40.396225140708, 40.373726546946, 40.351227953184, 40.328729359421, 40.306230765659, 40.283732171897, 40.261233578134, 40.238734984372, 40.216236390609, 40.193737796847, 40.171239203084, 40.148740609322, 40.126242015559, 40.103743421796, 40.081244828033, 40.058746234270, 40.036247640508, 40.013749046745, 39.991250452982, 39.968751859218, 39.946253265455, 39.923754671692, 39.901256077929, 39.878757484166, 39.856258890402, 39.833760296639, 39.811261702876, 39.788763109112, 39.766264515349, 39.743765921585, 39.721267327822, 39.698768734058, 39.676270140294, 39.653771546531, 39.631272952767, 39.608774359003, 39.586275765239, 39.563777171475, 39.541278577711, 39.518779983947, 39.496281390183, 39.473782796419, 39.451284202655, 39.428785608891, 39.406287015126, 39.383788421362, 39.361289827598, 39.338791233833, 39.316292640069, 39.293794046304, 39.271295452540, 39.248796858775, 39.226298265010, 39.203799671246, 39.181301077481, 39.158802483716, 39.136303889951, 39.113805296187, 39.091306702422, 39.068808108657, 39.046309514892, 39.023810921127, 39.001312327361, 38.978813733596, 38.956315139831, 38.933816546066, 38.911317952301, 38.888819358535, 38.866320764770, 38.843822171004, 38.821323577239, 38.798824983473, 38.776326389708, 38.753827795942, 38.731329202177, 38.708830608411, 38.686332014645, 38.663833420879, 38.641334827113, 38.618836233348, 38.596337639582, 38.573839045816, 38.551340452050, 38.528841858284, 38.506343264517, 38.483844670751, 38.461346076985, 38.438847483219, 38.416348889453, 38.393850295686, 38.371351701920, 38.348853108153, 38.326354514387, 38.303855920620, 38.281357326854, 38.258858733087, 38.236360139321, 38.213861545554, 38.191362951787, 38.168864358020, 38.146365764254, 38.123867170487, 38.101368576720, 38.078869982953, 38.056371389186, 38.033872795419, 38.011374201652, 37.988875607885, 37.966377014117, 37.943878420350, 37.921379826583, 37.898881232816, 37.876382639048, 37.853884045281, 37.831385451513, 37.808886857746, 37.786388263978, 37.763889670211, 37.741391076443, 37.718892482676, 37.696393888908, 37.673895295140, 37.651396701372, 37.628898107605, 37.606399513837, 37.583900920069, 37.561402326301, 37.538903732533, 37.516405138765, 37.493906544997, 37.471407951229, 37.448909357461, 37.426410763692, 37.403912169924, 37.381413576156, 37.358914982388, 37.336416388619, 37.313917794851, 37.291419201082, 37.268920607314, 37.246422013545, 37.223923419777, 37.201424826008, 37.178926232240, 37.156427638471, 37.133929044702, 37.111430450933, 37.088931857164, 37.066433263396, 37.043934669627, 37.021436075858, 36.998937482089, 36.976438888320, 36.953940294551, 36.931441700782, 36.908943107012, 36.886444513243, 36.863945919474, 36.841447325705, 36.818948731936, 36.796450138166, 36.773951544397, 36.751452950627, 36.728954356858, 36.706455763088, 36.683957169319, 36.661458575549, 36.638959981780, 36.616461388010, 36.593962794240, 36.571464200471, 36.548965606701, 36.526467012931, 36.503968419161, 36.481469825391, 36.458971231621, 36.436472637851, 36.413974044081, 36.391475450311, 36.368976856541, 36.346478262771, 36.323979669001, 36.301481075230, 36.278982481460, 36.256483887690, 36.233985293920, 36.211486700149, 36.188988106379, 36.166489512608, 36.143990918838, 36.121492325067, 36.098993731297, 36.076495137526, 36.053996543756, 36.031497949985, 36.008999356214, 35.986500762443, 35.964002168673, 35.941503574902, 35.919004981131, 35.896506387360, 35.874007793589, 35.851509199818, 35.829010606047, 35.806512012276, 35.784013418505, 35.761514824734, 35.739016230963, 35.716517637191, 35.694019043420, 35.671520449649, 35.649021855877, 35.626523262106, 35.604024668335, 35.581526074563, 35.559027480792, 35.536528887020, 35.514030293249, 35.491531699477, 35.469033105705, 35.446534511934, 35.424035918162, 35.401537324390, 35.379038730619, 35.356540136847, 35.334041543075, 35.311542949303, 35.289044355531, 35.266545761759, 35.244047167987, 35.221548574215, 35.199049980443, 35.176551386671, 35.154052792899, 35.131554199127, 35.109055605354, 35.086557011582, 35.064058417810, 35.041559824038, 35.019061230265, 34.996562636493, 34.974064042720, 34.951565448948, 34.929066855175, 34.906568261403, 34.884069667630, 34.861571073858, 34.839072480085, 34.816573886312, 34.794075292540, 34.771576698767, 34.749078104994, 34.726579511221, 34.704080917448, 34.681582323676, 34.659083729903, 34.636585136130, 34.614086542357, 34.591587948584, 34.569089354811, 34.546590761037, 34.524092167264, 34.501593573491, 34.479094979718, 34.456596385945, 34.434097792171, 34.411599198398, 34.389100604625, 34.366602010851, 34.344103417078, 34.321604823304, 34.299106229531, 34.276607635757, 34.254109041984, 34.231610448210, 34.209111854437, 34.186613260663, 34.164114666889, 34.141616073116, 34.119117479342, 34.096618885568, 34.074120291794, 34.051621698020, 34.029123104246, 34.006624510473, 33.984125916699, 33.961627322925, 33.939128729151, 33.916630135376, 33.894131541602, 33.871632947828, 33.849134354054, 33.826635760280, 33.804137166506, 33.781638572731, 33.759139978957, 33.736641385183, 33.714142791408, 33.691644197634, 33.669145603860, 33.646647010085, 33.624148416311, 33.601649822536, 33.579151228761, 33.556652634987, 33.534154041212, 33.511655447438, 33.489156853663, 33.466658259888, 33.444159666113, 33.421661072339, 33.399162478564, 33.376663884789, 33.354165291014, 33.331666697239, 33.309168103464, 33.286669509689, 33.264170915914, 33.241672322139, 33.219173728364, 33.196675134589, 33.174176540814, 33.151677947038, 33.129179353263, 33.106680759488, 33.084182165713, 33.061683571937, 33.039184978162, 33.016686384387, 32.994187790611, 32.971689196836, 32.949190603060, 32.926692009285, 32.904193415509, 32.881694821734, 32.859196227958, 32.836697634183, 32.814199040407, 32.791700446631, 32.769201852855, 32.746703259080, 32.724204665304, 32.701706071528, 32.679207477752, 32.656708883976, 32.634210290200, 32.611711696424, 32.589213102648, 32.566714508872, 32.544215915096, 32.521717321320, 32.499218727544, 32.476720133768, 32.454221539992, 32.431722946216, 32.409224352440, 32.386725758663, 32.364227164887, 32.341728571111, 32.319229977334, 32.296731383558, 32.274232789782, 32.251734196005, 32.229235602229, 32.206737008452, 32.184238414676, 32.161739820899, 32.139241227122, 32.116742633346, 32.094244039569, 32.071745445793, 32.049246852016, 32.026748258239, 32.004249664462, 31.981751070685, 31.959252476909, 31.936753883132, 31.914255289355, 31.891756695578, 31.869258101801, 31.846759508024, 31.824260914247, 31.801762320470, 31.779263726693, 31.756765132916, 31.734266539139, 31.711767945362, 31.689269351584, 31.666770757807, 31.644272164030, 31.621773570253, 31.599274976475, 31.576776382698, 31.554277788921, 31.531779195143, 31.509280601366, 31.486782007588, 31.464283413811, 31.441784820033, 31.419286226256, 31.396787632478, 31.374289038701, 31.351790444923, 31.329291851145, 31.306793257368, 31.284294663590, 31.261796069812, 31.239297476034, 31.216798882257, 31.194300288479, 31.171801694701, 31.149303100923, 31.126804507145, 31.104305913367, 31.081807319589, 31.059308725811, 31.036810132033, 31.014311538255, 30.991812944477, 30.969314350699, 30.946815756921, 30.924317163143, 30.901818569365, 30.879319975586, 30.856821381808, 30.834322788030, 30.811824194251, 30.789325600473, 30.766827006695, 30.744328412916, 30.721829819138, 30.699331225360, 30.676832631581, 30.654334037803, 30.631835444024, 30.609336850245, 30.586838256467, 30.564339662688, 30.541841068910, 30.519342475131, 30.496843881352, 30.474345287574, 30.451846693795, 30.429348100016, 30.406849506237, 30.384350912458, 30.361852318680, 30.339353724901, 30.316855131122, 30.294356537343, 30.271857943564, 30.249359349785, 30.226860756006, 30.204362162227, 30.181863568448, 30.159364974669, 30.136866380889, 30.114367787110, 30.091869193331, 30.069370599552, 30.046872005773, 30.024373411993, 30.001874818214, 29.979376224435, 29.956877630656, 29.934379036876, 29.911880443097, 29.889381849317, 29.866883255538, 29.844384661758, 29.821886067979, 29.799387474199, 29.776888880420, 29.754390286640, 29.731891692861, 29.709393099081, 29.686894505301, 29.664395911522, 29.641897317742, 29.619398723962, 29.596900130182, 29.574401536403, 29.551902942623, 29.529404348843, 29.506905755063, 29.484407161283, 29.461908567503, 29.439409973723, 29.416911379943, 29.394412786163, 29.371914192383, 29.349415598603, 29.326917004823, 29.304418411043, 29.281919817263, 29.259421223483, 29.236922629703, 29.214424035923, 29.191925442142, 29.169426848362, 29.146928254582, 29.124429660802, 29.101931067021, 29.079432473241, 29.056933879461, 29.034435285680, 29.011936691900, 28.989438098119, 28.966939504339, 28.944440910558, 28.921942316778, 28.899443722997, 28.876945129217, 28.854446535436, 28.831947941655, 28.809449347875, 28.786950754094, 28.764452160313, 28.741953566533, 28.719454972752, 28.696956378971, 28.674457785190, 28.651959191410, 28.629460597629, 28.606962003848, 28.584463410067, 28.561964816286, 28.539466222505, 28.516967628724, 28.494469034943, 28.471970441162, 28.449471847381, 28.426973253600, 28.404474659819, 28.381976066038, 28.359477472257, 28.336978878476, 28.314480284694, 28.291981690913, 28.269483097132, 28.246984503351, 28.224485909569, 28.201987315788, 28.179488722007, 28.156990128225, 28.134491534444, 28.111992940663, 28.089494346881, 28.066995753100, 28.044497159318, 28.021998565537, 27.999499971755, 27.977001377974, 27.954502784192, 27.932004190411, 27.909505596629, 27.887007002847, 27.864508409066, 27.842009815284, 27.819511221502, 27.797012627721, 27.774514033939, 27.752015440157, 27.729516846375, 27.707018252594, 27.684519658812, 27.662021065030, 27.639522471248, 27.617023877466, 27.594525283684, 27.572026689902, 27.549528096120, 27.527029502338, 27.504530908556, 27.482032314774, 27.459533720992, 27.437035127210, 27.414536533428, 27.392037939646, 27.369539345863, 27.347040752081, 27.324542158299, 27.302043564517, 27.279544970735, 27.257046376952, 27.234547783170, 27.212049189388, 27.189550595605, 27.167052001823, 27.144553408041, 27.122054814258, 27.099556220476, 27.077057626693, 27.054559032911, 27.032060439128, 27.009561845346, 26.987063251563, 26.964564657781, 26.942066063998, 26.919567470216, 26.897068876433, 26.874570282650, 26.852071688868, 26.829573095085, 26.807074501302, 26.784575907519, 26.762077313737, 26.739578719954, 26.717080126171, 26.694581532388, 26.672082938605, 26.649584344823, 26.627085751040, 26.604587157257, 26.582088563474, 26.559589969691, 26.537091375908, 26.514592782125, 26.492094188342, 26.469595594559, 26.447097000776, 26.424598406993, 26.402099813210, 26.379601219426, 26.357102625643, 26.334604031860, 26.312105438077, 26.289606844294, 26.267108250510, 26.244609656727, 26.222111062944, 26.199612469161, 26.177113875377, 26.154615281594, 26.132116687811, 26.109618094027, 26.087119500244, 26.064620906460, 26.042122312677, 26.019623718893, 25.997125125110, 25.974626531326, 25.952127937543, 25.929629343759, 25.907130749976, 25.884632156192, 25.862133562409, 25.839634968625, 25.817136374841, 25.794637781058, 25.772139187274, 25.749640593490, 25.727141999706, 25.704643405923, 25.682144812139, 25.659646218355, 25.637147624571, 25.614649030787, 25.592150437004, 25.569651843220, 25.547153249436, 25.524654655652, 25.502156061868, 25.479657468084, 25.457158874300, 25.434660280516, 25.412161686732, 25.389663092948, 25.367164499164, 25.344665905380, 25.322167311596, 25.299668717812, 25.277170124027, 25.254671530243, 25.232172936459, 25.209674342675, 25.187175748891, 25.164677155106, 25.142178561322, 25.119679967538, 25.097181373754, 25.074682779969, 25.052184186185, 25.029685592401, 25.007186998616, 24.984688404832, 24.962189811047, 24.939691217263, 24.917192623479, 24.894694029694, 24.872195435910, 24.849696842125, 24.827198248340, 24.804699654556, 24.782201060771, 24.759702466987, 24.737203873202, 24.714705279418, 24.692206685633, 24.669708091848, 24.647209498064, 24.624710904279, 24.602212310494, 24.579713716709, 24.557215122925, 24.534716529140, 24.512217935355, 24.489719341570, 24.467220747785, 24.444722154000, 24.422223560216, 24.399724966431, 24.377226372646, 24.354727778861, 24.332229185076, 24.309730591291, 24.287231997506, 24.264733403721, 24.242234809936, 24.219736216151, 24.197237622366, 24.174739028581, 24.152240434796, 24.129741841010, 24.107243247225, 24.084744653440, 24.062246059655, 24.039747465870, 24.017248872085, 23.994750278299, 23.972251684514, 23.949753090729, 23.927254496944, 23.904755903158, 23.882257309373, 23.859758715588, 23.837260121802, 23.814761528017, 23.792262934231, 23.769764340446, 23.747265746661, 23.724767152875, 23.702268559090, 23.679769965304, 23.657271371519, 23.634772777733, 23.612274183948, 23.589775590162, 23.567276996376, 23.544778402591, 23.522279808805, 23.499781215020, 23.477282621234, 23.454784027448, 23.432285433663, 23.409786839877, 23.387288246091, 23.364789652305, 23.342291058520, 23.319792464734, 23.297293870948, 23.274795277162, 23.252296683376, 23.229798089591, 23.207299495805, 23.184800902019, 23.162302308233, 23.139803714447, 23.117305120661, 23.094806526875, 23.072307933089, 23.049809339303, 23.027310745517, 23.004812151731, 22.982313557945, 22.959814964159, 22.937316370373, 22.914817776587, 22.892319182801, 22.869820589015, 22.847321995229, 22.824823401442, 22.802324807656, 22.779826213870, 22.757327620084, 22.734829026298, 22.712330432511, 22.689831838725, 22.667333244939, 22.644834651153, 22.622336057366, 22.599837463580, 22.577338869794, 22.554840276007, 22.532341682221, 22.509843088434, 22.487344494648, 22.464845900862, 22.442347307075, 22.419848713289, 22.397350119502, 22.374851525716, 22.352352931929, 22.329854338143, 22.307355744356, 22.284857150570, 22.262358556783, 22.239859962996, 22.217361369210, 22.194862775423, 22.172364181637, 22.149865587850, 22.127366994063, 22.104868400277, 22.082369806490, 22.059871212703, 22.037372618916, 22.014874025130, 21.992375431343, 21.969876837556, 21.947378243769, 21.924879649982, 21.902381056196, 21.879882462409, 21.857383868622, 21.834885274835, 21.812386681048, 21.789888087261, 21.767389493474, 21.744890899687, 21.722392305900, 21.699893712113, 21.677395118326, 21.654896524539, 21.632397930752, 21.609899336965, 21.587400743178, 21.564902149391, 21.542403555604, 21.519904961817, 21.497406368030, 21.474907774243, 21.452409180455, 21.429910586668, 21.407411992881, 21.384913399094, 21.362414805307, 21.339916211519, 21.317417617732, 21.294919023945, 21.272420430158, 21.249921836370, 21.227423242583, 21.204924648796, 21.182426055008, 21.159927461221, 21.137428867434, 21.114930273646, 21.092431679859, 21.069933086071, 21.047434492284, 21.024935898497, 21.002437304709, 20.979938710922, 20.957440117134, 20.934941523347, 20.912442929559, 20.889944335772, 20.867445741984, 20.844947148196, 20.822448554409, 20.799949960621, 20.777451366834, 20.754952773046, 20.732454179258, 20.709955585471, 20.687456991683, 20.664958397895, 20.642459804108, 20.619961210320, 20.597462616532, 20.574964022744, 20.552465428957, 20.529966835169, 20.507468241381, 20.484969647593, 20.462471053805, 20.439972460018, 20.417473866230, 20.394975272442, 20.372476678654, 20.349978084866, 20.327479491078, 20.304980897290, 20.282482303502, 20.259983709714, 20.237485115926, 20.214986522138, 20.192487928350, 20.169989334562, 20.147490740774, 20.124992146986, 20.102493553198, 20.079994959410, 20.057496365622, 20.034997771834, 20.012499178046, 19.990000584258, 19.967501990470, 19.945003396681, 19.922504802893, 19.900006209105, 19.877507615317, 19.855009021529, 19.832510427740, 19.810011833952, 19.787513240164, 19.765014646376, 19.742516052587, 19.720017458799, 19.697518865011, 19.675020271223, 19.652521677434, 19.630023083646, 19.607524489857, 19.585025896069, 19.562527302281, 19.540028708492, 19.517530114704, 19.495031520915, 19.472532927127, 19.450034333339, 19.427535739550, 19.405037145762, 19.382538551973, 19.360039958185, 19.337541364396, 19.315042770607, 19.292544176819, 19.270045583030, 19.247546989242, 19.225048395453, 19.202549801665, 19.180051207876, 19.157552614087, 19.135054020299, 19.112555426510, 19.090056832721, 19.067558238933, 19.045059645144, 19.022561051355, 19.000062457566, 18.977563863778, 18.955065269989, 18.932566676200, 18.910068082411, 18.887569488623, 18.865070894834, 18.842572301045, 18.820073707256, 18.797575113467, 18.775076519678, 18.752577925889, 18.730079332101, 18.707580738312, 18.685082144523, 18.662583550734, 18.640084956945, 18.617586363156, 18.595087769367, 18.572589175578, 18.550090581789, 18.527591988000, 18.505093394211, 18.482594800422, 18.460096206633, 18.437597612844, 18.415099019055, 18.392600425266, 18.370101831477, 18.347603237687, 18.325104643898, 18.302606050109, 18.280107456320, 18.257608862531, 18.235110268742, 18.212611674953, 18.190113081163, 18.167614487374, 18.145115893585, 18.122617299796, 18.100118706006, 18.077620112217, 18.055121518428, 18.032622924639, 18.010124330849, 17.987625737060, 17.965127143271, 17.942628549481, 17.920129955692, 17.897631361903, 17.875132768113, 17.852634174324, 17.830135580535, 17.807636986745, 17.785138392956, 17.762639799166, 17.740141205377, 17.717642611587, 17.695144017798, 17.672645424008, 17.650146830219, 17.627648236429, 17.605149642640, 17.582651048850, 17.560152455061, 17.537653861271, 17.515155267482, 17.492656673692, 17.470158079903, 17.447659486113, 17.425160892323, 17.402662298534, 17.380163704744, 17.357665110954, 17.335166517165, 17.312667923375, 17.290169329585, 17.267670735796, 17.245172142006, 17.222673548216, 17.200174954427, 17.177676360637, 17.155177766847, 17.132679173057, 17.110180579267, 17.087681985478, 17.065183391688, 17.042684797898, 17.020186204108, 16.997687610318, 16.975189016529, 16.952690422739, 16.930191828949, 16.907693235159, 16.885194641369, 16.862696047579, 16.840197453789, 16.817698859999, 16.795200266209, 16.772701672419, 16.750203078629, 16.727704484839, 16.705205891049, 16.682707297259, 16.660208703469, 16.637710109679, 16.615211515889, 16.592712922099, 16.570214328309, 16.547715734519, 16.525217140729, 16.502718546939, 16.480219953149, 16.457721359359, 16.435222765569, 16.412724171779, 16.390225577989, 16.367726984198, 16.345228390408, 16.322729796618, 16.300231202828, 16.277732609038, 16.255234015247, 16.232735421457, 16.210236827667, 16.187738233877, 16.165239640086, 16.142741046296, 16.120242452506, 16.097743858716, 16.075245264925, 16.052746671135, 16.030248077345, 16.007749483554, 15.985250889764, 15.962752295974, 15.940253702183, 15.917755108393, 15.895256514603, 15.872757920812, 15.850259327022, 15.827760733231, 15.805262139441, 15.782763545651, 15.760264951860, 15.737766358070, 15.715267764279, 15.692769170489, 15.670270576698, 15.647771982908, 15.625273389117, 15.602774795327, 15.580276201536, 15.557777607746, 15.535279013955, 15.512780420165, 15.490281826374, 15.467783232583, 15.445284638793, 15.422786045002, 15.400287451212, 15.377788857421, 15.355290263630, 15.332791669840, 15.310293076049, 15.287794482258, 15.265295888468, 15.242797294677, 15.220298700886, 15.197800107096, 15.175301513305, 15.152802919514, 15.130304325724, 15.107805731933, 15.085307138142, 15.062808544351, 15.040309950560, 15.017811356770, 14.995312762979, 14.972814169188, 14.950315575397, 14.927816981606, 14.905318387816, 14.882819794025, 14.860321200234, 14.837822606443, 14.815324012652, 14.792825418861, 14.770326825070, 14.747828231280, 14.725329637489, 14.702831043698, 14.680332449907, 14.657833856116, 14.635335262325, 14.612836668534, 14.590338074743, 14.567839480952, 14.545340887161, 14.522842293370, 14.500343699579, 14.477845105788, 14.455346511997, 14.432847918206, 14.410349324415, 14.387850730624, 14.365352136833, 14.342853543042, 14.320354949251, 14.297856355460, 14.275357761668, 14.252859167877, 14.230360574086, 14.207861980295, 14.185363386504, 14.162864792713, 14.140366198922, 14.117867605131, 14.095369011339, 14.072870417548, 14.050371823757, 14.027873229966, 14.005374636175, 13.982876042383, 13.960377448592, 13.937878854801, 13.915380261010, 13.892881667218, 13.870383073427, 13.847884479636, 13.825385885845, 13.802887292053, 13.780388698262, 13.757890104471, 13.735391510679, 13.712892916888, 13.690394323097, 13.667895729305, 13.645397135514, 13.622898541723, 13.600399947931, 13.577901354140, 13.555402760348, 13.532904166557, 13.510405572766, 13.487906978974, 13.465408385183, 13.442909791391, 13.420411197600, 13.397912603809, 13.375414010017, 13.352915416226, 13.330416822434, 13.307918228643, 13.285419634851, 13.262921041060, 13.240422447268, 13.217923853477, 13.195425259685, 13.172926665894, 13.150428072102, 13.127929478310, 13.105430884519, 13.082932290727, 13.060433696936, 13.037935103144, 13.015436509353, 12.992937915561, 12.970439321769, 12.947940727978, 12.925442134186, 12.902943540394, 12.880444946603, 12.857946352811, 12.835447759019, 12.812949165228, 12.790450571436, 12.767951977644, 12.745453383853, 12.722954790061, 12.700456196269, 12.677957602478, 12.655459008686, 12.632960414894, 12.610461821102, 12.587963227311, 12.565464633519, 12.542966039727, 12.520467445935, 12.497968852144, 12.475470258352, 12.452971664560, 12.430473070768, 12.407974476976, 12.385475883184, 12.362977289393, 12.340478695601, 12.317980101809, 12.295481508017, 12.272982914225, 12.250484320433, 12.227985726641, 12.205487132850, 12.182988539058, 12.160489945266, 12.137991351474, 12.115492757682, 12.092994163890, 12.070495570098, 12.047996976306, 12.025498382514, 12.002999788722, 11.980501194930, 11.958002601138, 11.935504007346, 11.913005413554, 11.890506819762, 11.868008225970, 11.845509632178, 11.823011038386, 11.800512444594, 11.778013850802, 11.755515257010, 11.733016663218, 11.710518069426, 11.688019475634, 11.665520881842, 11.643022288050, 11.620523694258, 11.598025100466, 11.575526506674, 11.553027912882, 11.530529319089, 11.508030725297, 11.485532131505, 11.463033537713, 11.440534943921, 11.418036350129, 11.395537756337, 11.373039162544, 11.350540568752, 11.328041974960, 11.305543381168, 11.283044787376, 11.260546193583, 11.238047599791, 11.215549005999, 11.193050412207, 11.170551818415, 11.148053224622, 11.125554630830, 11.103056037038, 11.080557443246, 11.058058849453, 11.035560255661, 11.013061661869, 10.990563068076, 10.968064474284, 10.945565880492, 10.923067286700, 10.900568692907, 10.878070099115, 10.855571505323, 10.833072911530, 10.810574317738, 10.788075723946, 10.765577130153, 10.743078536361, 10.720579942568, 10.698081348776, 10.675582754984, 10.653084161191, 10.630585567399, 10.608086973607, 10.585588379814, 10.563089786022, 10.540591192229, 10.518092598437, 10.495594004644, 10.473095410852, 10.450596817059, 10.428098223267, 10.405599629475, 10.383101035682, 10.360602441890, 10.338103848097, 10.315605254305, 10.293106660512, 10.270608066720, 10.248109472927, 10.225610879135, 10.203112285342, 10.180613691550, 10.158115097757, 10.135616503965, 10.113117910172, 10.090619316379, 10.068120722587, 10.045622128794, 10.023123535002, 10.000624941209, 9.978126347417, 9.955627753624, 9.933129159831, 9.910630566039, 9.888131972246, 9.865633378454, 9.843134784661, 9.820636190868, 9.798137597076, 9.775639003283, 9.753140409490, 9.730641815698, 9.708143221905, 9.685644628112, 9.663146034320, 9.640647440527, 9.618148846734, 9.595650252942, 9.573151659149, 9.550653065356, 9.528154471564, 9.505655877771, 9.483157283978, 9.460658690185, 9.438160096393, 9.415661502600, 9.393162908807, 9.370664315014, 9.348165721222, 9.325667127429, 9.303168533636, 9.280669939843, 9.258171346051, 9.235672752258, 9.213174158465, 9.190675564672, 9.168176970879, 9.145678377087, 9.123179783294, 9.100681189501, 9.078182595708, 9.055684001915, 9.033185408123, 9.010686814330, 8.988188220537, 8.965689626744, 8.943191032951, 8.920692439158, 8.898193845365, 8.875695251573, 8.853196657780, 8.830698063987, 8.808199470194, 8.785700876401, 8.763202282608, 8.740703688815, 8.718205095022, 8.695706501229, 8.673207907436, 8.650709313643, 8.628210719851, 8.605712126058, 8.583213532265, 8.560714938472, 8.538216344679, 8.515717750886, 8.493219157093, 8.470720563300, 8.448221969507, 8.425723375714, 8.403224781921, 8.380726188128, 8.358227594335, 8.335729000542, 8.313230406749, 8.290731812956, 8.268233219163, 8.245734625370, 8.223236031577, 8.200737437784, 8.178238843991, 8.155740250198, 8.133241656405, 8.110743062612, 8.088244468819, 8.065745875025, 8.043247281232, 8.020748687439, 7.998250093646, 7.975751499853, 7.953252906060, 7.930754312267, 7.908255718474, 7.885757124681, 7.863258530888, 7.840759937095, 7.818261343301, 7.795762749508, 7.773264155715, 7.750765561922, 7.728266968129, 7.705768374336, 7.683269780543, 7.660771186750, 7.638272592956, 7.615773999163, 7.593275405370, 7.570776811577, 7.548278217784, 7.525779623991, 7.503281030197, 7.480782436404, 7.458283842611, 7.435785248818, 7.413286655025, 7.390788061231, 7.368289467438, 7.345790873645, 7.323292279852, 7.300793686058, 7.278295092265, 7.255796498472, 7.233297904679, 7.210799310886, 7.188300717092, 7.165802123299, 7.143303529506, 7.120804935713, 7.098306341919, 7.075807748126, 7.053309154333, 7.030810560539, 7.008311966746, 6.985813372953, 6.963314779160, 6.940816185366, 6.918317591573, 6.895818997780, 6.873320403986, 6.850821810193, 6.828323216400, 6.805824622606, 6.783326028813, 6.760827435020, 6.738328841226, 6.715830247433, 6.693331653640, 6.670833059846, 6.648334466053, 6.625835872260, 6.603337278466, 6.580838684673, 6.558340090880, 6.535841497086, 6.513342903293, 6.490844309500, 6.468345715706, 6.445847121913, 6.423348528119, 6.400849934326, 6.378351340533, 6.355852746739, 6.333354152946, 6.310855559152, 6.288356965359, 6.265858371566, 6.243359777772, 6.220861183979, 6.198362590185, 6.175863996392, 6.153365402598, 6.130866808805, 6.108368215011, 6.085869621218, 6.063371027425, 6.040872433631, 6.018373839838, 5.995875246044, 5.973376652251, 5.950878058457, 5.928379464664, 5.905880870870, 5.883382277077, 5.860883683283, 5.838385089490, 5.815886495696, 5.793387901903, 5.770889308109, 5.748390714316, 5.725892120522, 5.703393526729, 5.680894932935, 5.658396339142, 5.635897745348, 5.613399151555, 5.590900557761, 5.568401963968, 5.545903370174, 5.523404776381, 5.500906182587, 5.478407588794, 5.455908995000, 5.433410401207, 5.410911807413, 5.388413213619, 5.365914619826, 5.343416026032, 5.320917432239, 5.298418838445, 5.275920244652, 5.253421650858, 5.230923057064, 5.208424463271, 5.185925869477, 5.163427275684, 5.140928681890, 5.118430088097, 5.095931494303, 5.073432900509, 5.050934306716, 5.028435712922, 5.005937119128, 4.983438525335, 4.960939931541, 4.938441337748, 4.915942743954, 4.893444150160, 4.870945556367, 4.848446962573, 4.825948368780, 4.803449774986, 4.780951181192, 4.758452587399, 4.735953993605, 4.713455399811, 4.690956806018, 4.668458212224, 4.645959618430, 4.623461024637, 4.600962430843, 4.578463837049, 4.555965243256, 4.533466649462, 4.510968055668, 4.488469461875, 4.465970868081, 4.443472274287, 4.420973680494, 4.398475086700, 4.375976492906, 4.353477899113, 4.330979305319, 4.308480711525, 4.285982117731, 4.263483523938, 4.240984930144, 4.218486336350, 4.195987742557, 4.173489148763, 4.150990554969, 4.128491961175, 4.105993367382, 4.083494773588, 4.060996179794, 4.038497586001, 4.015998992207, 3.993500398413, 3.971001804619, 3.948503210826, 3.926004617032, 3.903506023238, 3.881007429444, 3.858508835651, 3.836010241857, 3.813511648063, 3.791013054269, 3.768514460476, 3.746015866682, 3.723517272888, 3.701018679094, 3.678520085301, 3.656021491507, 3.633522897713, 3.611024303919, 3.588525710126, 3.566027116332, 3.543528522538, 3.521029928744, 3.498531334950, 3.476032741157, 3.453534147363, 3.431035553569, 3.408536959775, 3.386038365981, 3.363539772188, 3.341041178394, 3.318542584600, 3.296043990806, 3.273545397012, 3.251046803219, 3.228548209425, 3.206049615631, 3.183551021837, 3.161052428043, 3.138553834250, 3.116055240456, 3.093556646662, 3.071058052868, 3.048559459074, 3.026060865281, 3.003562271487, 2.981063677693, 2.958565083899, 2.936066490105, 2.913567896311, 2.891069302518, 2.868570708724, 2.846072114930, 2.823573521136, 2.801074927342, 2.778576333548, 2.756077739754, 2.733579145961, 2.711080552167, 2.688581958373, 2.666083364579, 2.643584770785, 2.621086176991, 2.598587583198, 2.576088989404, 2.553590395610, 2.531091801816, 2.508593208022, 2.486094614228, 2.463596020434, 2.441097426640, 2.418598832847, 2.396100239053, 2.373601645259, 2.351103051465, 2.328604457671, 2.306105863877, 2.283607270083, 2.261108676289, 2.238610082496, 2.216111488702, 2.193612894908, 2.171114301114, 2.148615707320, 2.126117113526, 2.103618519732, 2.081119925938, 2.058621332145, 2.036122738351, 2.013624144557, 1.991125550763, 1.968626956969, 1.946128363175, 1.923629769381, 1.901131175587, 1.878632581793, 1.856133987999, 1.833635394206, 1.811136800412, 1.788638206618, 1.766139612824, 1.743641019030, 1.721142425236, 1.698643831442, 1.676145237648, 1.653646643854, 1.631148050060, 1.608649456266, 1.586150862473, 1.563652268679, 1.541153674885, 1.518655081091, 1.496156487297, 1.473657893503, 1.451159299709, 1.428660705915, 1.406162112121, 1.383663518327, 1.361164924533, 1.338666330739, 1.316167736946, 1.293669143152, 1.271170549358, 1.248671955564, 1.226173361770, 1.203674767976, 1.181176174182, 1.158677580388, 1.136178986594, 1.113680392800, 1.091181799006, 1.068683205212, 1.046184611418, 1.023686017624, 1.001187423830, 0.978688830037, 0.956190236243, 0.933691642449, 0.911193048655, 0.888694454861, 0.866195861067, 0.843697267273, 0.821198673479, 0.798700079685, 0.776201485891, 0.753702892097, 0.731204298303, 0.708705704509, 0.686207110715, 0.663708516921, 0.641209923127, 0.618711329334, 0.596212735540, 0.573714141746, 0.551215547952, 0.528716954158, 0.506218360364, 0.483719766570, 0.461221172776, 0.438722578982, 0.416223985188, 0.393725391394, 0.371226797600, 0.348728203806, 0.326229610012, 0.303731016218, 0.281232422424, 0.258733828630, 0.236235234836, 0.213736641043, 0.191238047249, 0.168739453455, 0.146240859661, 0.123742265867, 0.101243672073, 0.078745078279, 0.056246484485, 0.033747890691, 0.011249296897)) } // namespace gaussian } // namespace spacing } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/spacing/gaussian/N64.cc0000664000175000017500000000416015160212070023262 0ustar alastairalastair/* * (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. */ // TL127 #include "atlas/grid/detail/spacing/gaussian/N.h" namespace atlas { namespace grid { namespace spacing { namespace gaussian { DEFINE_GAUSSIAN_LATITUDES(64, LIST(88.927735352296, 87.538705213027, 86.141472101528, 84.742385590714, 83.342596044070, 81.942466299173, 80.542146434617, 79.141709648622, 77.741195865514, 76.340628702372, 74.940023019649, 73.539388633767, 72.138732289162, 70.738058772518, 69.337371574961, 67.936673302579, 66.535965940176, 65.135251026035, 63.734529770843, 62.333803140532, 60.933071915207, 59.532336731827, 58.131598115644, 56.730856503714, 55.330112262703, 53.929365702556, 52.528617087100, 51.127866642353, 49.727114563110, 48.326361018188, 46.925606154665, 45.524850101302, 44.124092971356, 42.723334864877, 41.322575870623, 39.921816067646, 38.521055526624, 37.120294310979, 35.719532477824, 34.318770078771, 32.918007160614, 31.517243765923, 30.116479933546, 28.715715699055, 27.314951095120, 25.914186151847, 24.513420897063, 23.112655356578, 21.711889554404, 20.311123512960, 18.910357253245, 17.509590794999, 16.108824156841, 14.708057356405, 13.307290410446, 11.906523334954, 10.505756145244, 9.104988856049, 7.704221481600, 6.303454035708, 4.902686531827, 3.501918983131, 2.101151402579, 0.700383802973)) } // namespace gaussian } // namespace spacing } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/spacing/gaussian/N800.cc0000664000175000017500000003600015160212070023336 0ustar alastairalastair/* * (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. */ // TL1599 #include "atlas/grid/detail/spacing/gaussian/N.h" namespace atlas { namespace grid { namespace spacing { namespace gaussian { DEFINE_GAUSSIAN_LATITUDES( 800, LIST(89.913910432567, 89.802388520364, 89.690208010997, 89.577878696439, 89.465492936558, 89.353079862649, 89.240651524094, 89.128213797733, 89.015769888926, 88.903321693762, 88.790870405278, 88.678416811542, 88.565961453973, 88.453504716809, 88.341046880271, 88.228588153518, 88.116128695829, 88.003668630629, 87.891208055042, 87.778747046539, 87.666285667668, 87.553823969486, 87.441361994075, 87.328899776437, 87.216437345920, 87.103974727314, 86.991511941706, 86.879049007143, 86.766585939163, 86.654122751216, 86.541659455003, 86.429196060750, 86.316732577436, 86.204269012976, 86.091805374374, 85.979341667850, 85.866877898947, 85.754414072622, 85.641950193319, 85.529486265037, 85.417022291380, 85.304558275609, 85.192094220678, 85.079630129273, 84.967166003838, 84.854701846604, 84.742237659612, 84.629773444731, 84.517309203677, 84.404844938029, 84.292380649239, 84.179916338650, 84.067452007501, 83.954987656940, 83.842523288029, 83.730058901757, 83.617594499041, 83.505130080736, 83.392665647637, 83.280201200487, 83.167736739980, 83.055272266765, 82.942807781449, 82.830343284601, 82.717878776757, 82.605414258418, 82.492949730055, 82.380485192112, 82.268020645007, 82.155556089135, 82.043091524867, 81.930626952554, 81.818162372528, 81.705697785102, 81.593233190575, 81.480768589225, 81.368303981322, 81.255839367117, 81.143374746850, 81.030910120750, 80.918445489032, 80.805980851905, 80.693516209562, 80.581051562192, 80.468586909973, 80.356122253073, 80.243657591655, 80.131192925873, 80.018728255875, 79.906263581802, 79.793798903788, 79.681334221962, 79.568869536448, 79.456404847364, 79.343940154822, 79.231475458931, 79.119010759796, 79.006546057514, 78.894081352183, 78.781616643893, 78.669151932734, 78.556687218788, 78.444222502139, 78.331757782864, 78.219293061038, 78.106828336733, 77.994363610020, 77.881898880965, 77.769434149634, 77.656969416087, 77.544504680386, 77.432039942588, 77.319575202748, 77.207110460921, 77.094645717159, 76.982180971512, 76.869716224029, 76.757251474755, 76.644786723737, 76.532321971017, 76.419857216639, 76.307392460644, 76.194927703070, 76.082462943956, 75.969998183339, 75.857533421255, 75.745068657739, 75.632603892824, 75.520139126543, 75.407674358928, 75.295209590008, 75.182744819815, 75.070280048376, 74.957815275720, 74.845350501873, 74.732885726863, 74.620420950714, 74.507956173452, 74.395491395100, 74.283026615682, 74.170561835220, 74.058097053737, 73.945632271255, 73.833167487793, 73.720702703373, 73.608237918014, 73.495773131735, 73.383308344555, 73.270843556493, 73.158378767565, 73.045913977789, 72.933449187182, 72.820984395761, 72.708519603540, 72.596054810536, 72.483590016764, 72.371125222239, 72.258660426974, 72.146195630983, 72.033730834282, 71.921266036881, 71.808801238796, 71.696336440038, 71.583871640619, 71.471406840552, 71.358942039849, 71.246477238520, 71.134012436578, 71.021547634032, 70.909082830894, 70.796618027174, 70.684153222883, 70.571688418029, 70.459223612624, 70.346758806675, 70.234294000194, 70.121829193188, 70.009364385667, 69.896899577639, 69.784434769113, 69.671969960097, 69.559505150599, 69.447040340627, 69.334575530189, 69.222110719292, 69.109645907944, 68.997181096152, 68.884716283923, 68.772251471264, 68.659786658182, 68.547321844684, 68.434857030775, 68.322392216464, 68.209927401755, 68.097462586655, 67.984997771169, 67.872532955305, 67.760068139067, 67.647603322461, 67.535138505493, 67.422673688169, 67.310208870492, 67.197744052469, 67.085279234106, 66.972814415406, 66.860349596374, 66.747884777016, 66.635419957337, 66.522955137340, 66.410490317031, 66.298025496414, 66.185560675493, 66.073095854272, 65.960631032757, 65.848166210950, 65.735701388856, 65.623236566480, 65.510771743824, 65.398306920893, 65.285842097690, 65.173377274220, 65.060912450485, 64.948447626490, 64.835982802238, 64.723517977732, 64.611053152976, 64.498588327972, 64.386123502726, 64.273658677238, 64.161193851514, 64.048729025555, 63.936264199365, 63.823799372947, 63.711334546304, 63.598869719438, 63.486404892353, 63.373940065051, 63.261475237535, 63.149010409808, 63.036545581872, 62.924080753730, 62.811615925385, 62.699151096839, 62.586686268095, 62.474221439154, 62.361756610020, 62.249291780695, 62.136826951181, 62.024362121480, 61.911897291595, 61.799432461528, 61.686967631281, 61.574502800856, 61.462037970255, 61.349573139481, 61.237108308536, 61.124643477421, 61.012178646138, 60.899713814690, 60.787248983078, 60.674784151304, 60.562319319371, 60.449854487279, 60.337389655031, 60.224924822629, 60.112459990074, 59.999995157368, 59.887530324513, 59.775065491510, 59.662600658361, 59.550135825068, 59.437670991632, 59.325206158055, 59.212741324339, 59.100276490485, 58.987811656494, 58.875346822368, 58.762881988108, 58.650417153717, 58.537952319194, 58.425487484543, 58.313022649763, 58.200557814858, 58.088092979827, 57.975628144672, 57.863163309394, 57.750698473996, 57.638233638477, 57.525768802840, 57.413303967086, 57.300839131215, 57.188374295229, 57.075909459130, 56.963444622917, 56.850979786594, 56.738514950160, 56.626050113617, 56.513585276965, 56.401120440207, 56.288655603343, 56.176190766374, 56.063725929301, 55.951261092126, 55.838796254849, 55.726331417471, 55.613866579993, 55.501401742417, 55.388936904743, 55.276472066972, 55.164007229106, 55.051542391144, 54.939077553089, 54.826612714941, 54.714147876700, 54.601683038368, 54.489218199946, 54.376753361435, 54.264288522834, 54.151823684146, 54.039358845371, 53.926894006510, 53.814429167564, 53.701964328533, 53.589499489418, 53.477034650221, 53.364569810941, 53.252104971580, 53.139640132139, 53.027175292617, 52.914710453017, 52.802245613338, 52.689780773581, 52.577315933747, 52.464851093838, 52.352386253852, 52.239921413792, 52.127456573658, 52.014991733450, 51.902526893169, 51.790062052816, 51.677597212392, 51.565132371896, 51.452667531331, 51.340202690695, 51.227737849991, 51.115273009218, 51.002808168378, 50.890343327470, 50.777878486496, 50.665413645456, 50.552948804350, 50.440483963179, 50.328019121945, 50.215554280646, 50.103089439284, 49.990624597860, 49.878159756373, 49.765694914825, 49.653230073216, 49.540765231546, 49.428300389817, 49.315835548028, 49.203370706180, 49.090905864273, 48.978441022308, 48.865976180286, 48.753511338207, 48.641046496072, 48.528581653880, 48.416116811633, 48.303651969331, 48.191187126974, 48.078722284563, 47.966257442098, 47.853792599579, 47.741327757008, 47.628862914385, 47.516398071709, 47.403933228982, 47.291468386204, 47.179003543375, 47.066538700496, 46.954073857566, 46.841609014588, 46.729144171560, 46.616679328483, 46.504214485358, 46.391749642185, 46.279284798965, 46.166819955698, 46.054355112383, 45.941890269023, 45.829425425616, 45.716960582163, 45.604495738666, 45.492030895123, 45.379566051536, 45.267101207904, 45.154636364228, 45.042171520509, 44.929706676747, 44.817241832942, 44.704776989094, 44.592312145205, 44.479847301273, 44.367382457299, 44.254917613285, 44.142452769229, 44.029987925133, 43.917523080997, 43.805058236821, 43.692593392605, 43.580128548349, 43.467663704055, 43.355198859722, 43.242734015350, 43.130269170940, 43.017804326492, 42.905339482007, 42.792874637484, 42.680409792924, 42.567944948328, 42.455480103695, 42.343015259025, 42.230550414320, 42.118085569579, 42.005620724803, 41.893155879992, 41.780691035146, 41.668226190265, 41.555761345350, 41.443296500400, 41.330831655417, 41.218366810400, 41.105901965350, 40.993437120267, 40.880972275151, 40.768507430002, 40.656042584821, 40.543577739608, 40.431112894362, 40.318648049085, 40.206183203777, 40.093718358438, 39.981253513067, 39.868788667666, 39.756323822234, 39.643858976772, 39.531394131279, 39.418929285757, 39.306464440205, 39.193999594624, 39.081534749013, 38.969069903374, 38.856605057705, 38.744140212008, 38.631675366283, 38.519210520529, 38.406745674747, 38.294280828938, 38.181815983101, 38.069351137236, 37.956886291344, 37.844421445426, 37.731956599480, 37.619491753508, 37.507026907509, 37.394562061484, 37.282097215433, 37.169632369356, 37.057167523253, 36.944702677124, 36.832237830971, 36.719772984792, 36.607308138588, 36.494843292359, 36.382378446106, 36.269913599828, 36.157448753526, 36.044983907199, 35.932519060849, 35.820054214475, 35.707589368077, 35.595124521656, 35.482659675211, 35.370194828743, 35.257729982253, 35.145265135739, 35.032800289203, 34.920335442644, 34.807870596063, 34.695405749459, 34.582940902834, 34.470476056186, 34.358011209517, 34.245546362826, 34.133081516114, 34.020616669381, 33.908151822626, 33.795686975850, 33.683222129054, 33.570757282236, 33.458292435398, 33.345827588540, 33.233362741661, 33.120897894762, 33.008433047843, 32.895968200904, 32.783503353946, 32.671038506967, 32.558573659970, 32.446108812953, 32.333643965916, 32.221179118861, 32.108714271786, 31.996249424693, 31.883784577581, 31.771319730451, 31.658854883301, 31.546390036134, 31.433925188949, 31.321460341745, 31.208995494523, 31.096530647284, 30.984065800027, 30.871600952752, 30.759136105459, 30.646671258150, 30.534206410823, 30.421741563479, 30.309276716118, 30.196811868740, 30.084347021345, 29.971882173934, 29.859417326506, 29.746952479061, 29.634487631601, 29.522022784124, 29.409557936631, 29.297093089122, 29.184628241597, 29.072163394056, 28.959698546500, 28.847233698928, 28.734768851340, 28.622304003737, 28.509839156119, 28.397374308486, 28.284909460838, 28.172444613174, 28.059979765496, 27.947514917803, 27.835050070096, 27.722585222374, 27.610120374637, 27.497655526887, 27.385190679122, 27.272725831342, 27.160260983549, 27.047796135742, 26.935331287921, 26.822866440086, 26.710401592237, 26.597936744375, 26.485471896499, 26.373007048610, 26.260542200708, 26.148077352792, 26.035612504863, 25.923147656922, 25.810682808967, 25.698217960999, 25.585753113019, 25.473288265026, 25.360823417021, 25.248358569002, 25.135893720972, 25.023428872929, 24.910964024874, 24.798499176807, 24.686034328727, 24.573569480636, 24.461104632533, 24.348639784418, 24.236174936291, 24.123710088152, 24.011245240002, 23.898780391841, 23.786315543668, 23.673850695483, 23.561385847288, 23.448920999081, 23.336456150863, 23.223991302634, 23.111526454394, 22.999061606143, 22.886596757882, 22.774131909610, 22.661667061327, 22.549202213033, 22.436737364729, 22.324272516415, 22.211807668090, 22.099342819755, 21.986877971410, 21.874413123055, 21.761948274690, 21.649483426314, 21.537018577929, 21.424553729534, 21.312088881129, 21.199624032715, 21.087159184291, 20.974694335857, 20.862229487414, 20.749764638962, 20.637299790500, 20.524834942029, 20.412370093549, 20.299905245059, 20.187440396561, 20.074975548053, 19.962510699537, 19.850045851012, 19.737581002478, 19.625116153935, 19.512651305384, 19.400186456824, 19.287721608255, 19.175256759678, 19.062791911093, 18.950327062499, 18.837862213897, 18.725397365287, 18.612932516668, 18.500467668042, 18.388002819407, 18.275537970765, 18.163073122115, 18.050608273457, 17.938143424791, 17.825678576117, 17.713213727436, 17.600748878747, 17.488284030051, 17.375819181347, 17.263354332636, 17.150889483917, 17.038424635192, 16.925959786458, 16.813494937718, 16.701030088971, 16.588565240217, 16.476100391455, 16.363635542687, 16.251170693912, 16.138705845130, 16.026240996341, 15.913776147546, 15.801311298744, 15.688846449935, 15.576381601120, 15.463916752298, 15.351451903470, 15.238987054636, 15.126522205795, 15.014057356948, 14.901592508095, 14.789127659235, 14.676662810370, 14.564197961499, 14.451733112621, 14.339268263738, 14.226803414849, 14.114338565954, 14.001873717053, 13.889408868147, 13.776944019234, 13.664479170317, 13.552014321393, 13.439549472465, 13.327084623530, 13.214619774591, 13.102154925646, 12.989690076696, 12.877225227740, 12.764760378780, 12.652295529814, 12.539830680843, 12.427365831867, 12.314900982886, 12.202436133900, 12.089971284910, 11.977506435914, 11.865041586914, 11.752576737909, 11.640111888899, 11.527647039885, 11.415182190866, 11.302717341843, 11.190252492815, 11.077787643783, 10.965322794746, 10.852857945705, 10.740393096660, 10.627928247610, 10.515463398556, 10.402998549498, 10.290533700437, 10.178068851371, 10.065604002301, 9.953139153227, 9.840674304149, 9.728209455067, 9.615744605982, 9.503279756892, 9.390814907800, 9.278350058703, 9.165885209603, 9.053420360499, 8.940955511392, 8.828490662281, 8.716025813167, 8.603560964049, 8.491096114928, 8.378631265804, 8.266166416677, 8.153701567546, 8.041236718412, 7.928771869275, 7.816307020135, 7.703842170992, 7.591377321846, 7.478912472698, 7.366447623546, 7.253982774391, 7.141517925234, 7.029053076074, 6.916588226911, 6.804123377746, 6.691658528578, 6.579193679407, 6.466728830234, 6.354263981058, 6.241799131880, 6.129334282700, 6.016869433517, 5.904404584332, 5.791939735145, 5.679474885955, 5.567010036763, 5.454545187570, 5.342080338374, 5.229615489176, 5.117150639976, 5.004685790774, 4.892220941570, 4.779756092365, 4.667291243157, 4.554826393948, 4.442361544737, 4.329896695525, 4.217431846311, 4.104966997095, 3.992502147877, 3.880037298658, 3.767572449438, 3.655107600216, 3.542642750993, 3.430177901769, 3.317713052543, 3.205248203316, 3.092783354087, 2.980318504858, 2.867853655627, 2.755388806396, 2.642923957163, 2.530459107929, 2.417994258695, 2.305529409459, 2.193064560223, 2.080599710986, 1.968134861748, 1.855670012509, 1.743205163269, 1.630740314029, 1.518275464789, 1.405810615547, 1.293345766305, 1.180880917063, 1.068416067820, 0.955951218577, 0.843486369334, 0.731021520090, 0.618556670846, 0.506091821601, 0.393626972357, 0.281162123112, 0.168697273867, 0.056232424622)) } // namespace gaussian } // namespace spacing } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/spacing/gaussian/N1280.cc0000664000175000017500000005716315160212070023436 0ustar alastairalastair/* * (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. */ // TL2559 #include "atlas/grid/detail/spacing/gaussian/N.h" namespace atlas { namespace grid { namespace spacing { namespace gaussian { DEFINE_GAUSSIAN_LATITUDES( 1280, LIST(89.946187715666, 89.876478353332, 89.806357319542, 89.736143271610, 89.665893941216, 89.595627537554, 89.525351592371, 89.455069779123, 89.384784101392, 89.314495744374, 89.244205453805, 89.173913722284, 89.103620888239, 89.033327191846, 88.963032808263, 88.892737868231, 88.822442471310, 88.752146694651, 88.681850598962, 88.611554232668, 88.541257634869, 88.470960837475, 88.400663866794, 88.330366744703, 88.260069489546, 88.189772116821, 88.119474639706, 88.049177069484, 87.978879415867, 87.908581687262, 87.838283890982, 87.767986033420, 87.697688120188, 87.627390156234, 87.557092145936, 87.486794093181, 87.416496001435, 87.346197873796, 87.275899713042, 87.205601521672, 87.135303301940, 87.065005055883, 86.994706785348, 86.924408492014, 86.854110177409, 86.783811842927, 86.713513489844, 86.643215119329, 86.572916732453, 86.502618330204, 86.432319913490, 86.362021483149, 86.291723039957, 86.221424584631, 86.151126117835, 86.080827640187, 86.010529152260, 85.940230654589, 85.869932147670, 85.799633631968, 85.729335107917, 85.659036575923, 85.588738036364, 85.518439489598, 85.448140935957, 85.377842375757, 85.307543809290, 85.237245236836, 85.166946658654, 85.096648074992, 85.026349486082, 84.956050892143, 84.885752293383, 84.815453689997, 84.745155082172, 84.674856470083, 84.604557853897, 84.534259233771, 84.463960609857, 84.393661982296, 84.323363351224, 84.253064716770, 84.182766079057, 84.112467438200, 84.042168794312, 83.971870147499, 83.901571497861, 83.831272845495, 83.760974190494, 83.690675532945, 83.620376872933, 83.550078210538, 83.479779545838, 83.409480878906, 83.339182209812, 83.268883538625, 83.198584865410, 83.128286190228, 83.057987513139, 82.987688834201, 82.917390153469, 82.847091470996, 82.776792786832, 82.706494101027, 82.636195413627, 82.565896724678, 82.495598034223, 82.425299342304, 82.355000648962, 82.284701954235, 82.214403258161, 82.144104560776, 82.073805862115, 82.003507162212, 81.933208461099, 81.862909758807, 81.792611055367, 81.722312350809, 81.652013645159, 81.581714938446, 81.511416230696, 81.441117521935, 81.370818812187, 81.300520101476, 81.230221389825, 81.159922677258, 81.089623963795, 81.019325249457, 80.949026534265, 80.878727818239, 80.808429101398, 80.738130383760, 80.667831665344, 80.597532946166, 80.527234226244, 80.456935505594, 80.386636784233, 80.316338062175, 80.246039339436, 80.175740616030, 80.105441891972, 80.035143167276, 79.964844441954, 79.894545716020, 79.824246989487, 79.753948262366, 79.683649534670, 79.613350806411, 79.543052077600, 79.472753348248, 79.402454618366, 79.332155887964, 79.261857157052, 79.191558425641, 79.121259693740, 79.050960961358, 78.980662228505, 78.910363495190, 78.840064761421, 78.769766027208, 78.699467292557, 78.629168557478, 78.558869821978, 78.488571086065, 78.418272349746, 78.347973613030, 78.277674875922, 78.207376138430, 78.137077400561, 78.066778662322, 77.996479923719, 77.926181184758, 77.855882445445, 77.785583705787, 77.715284965790, 77.644986225459, 77.574687484800, 77.504388743819, 77.434090002520, 77.363791260910, 77.293492518993, 77.223193776775, 77.152895034260, 77.082596291454, 77.012297548360, 76.941998804985, 76.871700061331, 76.801401317404, 76.731102573208, 76.660803828747, 76.590505084026, 76.520206339048, 76.449907593818, 76.379608848339, 76.309310102615, 76.239011356650, 76.168712610448, 76.098413864012, 76.028115117346, 75.957816370454, 75.887517623337, 75.817218876001, 75.746920128448, 75.676621380681, 75.606322632704, 75.536023884520, 75.465725136131, 75.395426387540, 75.325127638752, 75.254828889767, 75.184530140590, 75.114231391222, 75.043932641667, 74.973633891927, 74.903335142004, 74.833036391902, 74.762737641623, 74.692438891169, 74.622140140542, 74.551841389746, 74.481542638781, 74.411243887652, 74.340945136359, 74.270646384904, 74.200347633291, 74.130048881522, 74.059750129597, 73.989451377520, 73.919152625292, 73.848853872916, 73.778555120392, 73.708256367724, 73.637957614913, 73.567658861960, 73.497360108869, 73.427061355639, 73.356762602274, 73.286463848775, 73.216165095143, 73.145866341381, 73.075567587489, 73.005268833470, 72.934970079325, 72.864671325055, 72.794372570663, 72.724073816149, 72.653775061515, 72.583476306763, 72.513177551893, 72.442878796909, 72.372580041809, 72.302281286597, 72.231982531274, 72.161683775840, 72.091385020297, 72.021086264647, 71.950787508890, 71.880488753029, 71.810189997063, 71.739891240994, 71.669592484824, 71.599293728554, 71.528994972184, 71.458696215717, 71.388397459152, 71.318098702491, 71.247799945736, 71.177501188887, 71.107202431945, 71.036903674912, 70.966604917788, 70.896306160574, 70.826007403271, 70.755708645881, 70.685409888405, 70.615111130842, 70.544812373195, 70.474513615463, 70.404214857649, 70.333916099752, 70.263617341774, 70.193318583716, 70.123019825578, 70.052721067362, 69.982422309068, 69.912123550696, 69.841824792249, 69.771526033726, 69.701227275128, 69.630928516457, 69.560629757712, 69.490330998895, 69.420032240006, 69.349733481047, 69.279434722017, 69.209135962918, 69.138837203750, 69.068538444514, 68.998239685210, 68.927940925840, 68.857642166404, 68.787343406903, 68.717044647337, 68.646745887706, 68.576447128012, 68.506148368256, 68.435849608437, 68.365550848557, 68.295252088615, 68.224953328613, 68.154654568552, 68.084355808431, 68.014057048251, 67.943758288014, 67.873459527718, 67.803160767366, 67.732862006957, 67.662563246492, 67.592264485972, 67.521965725397, 67.451666964768, 67.381368204085, 67.311069443348, 67.240770682558, 67.170471921717, 67.100173160823, 67.029874399877, 66.959575638881, 66.889276877835, 66.818978116738, 66.748679355592, 66.678380594396, 66.608081833152, 66.537783071860, 66.467484310520, 66.397185549132, 66.326886787698, 66.256588026217, 66.186289264690, 66.115990503117, 66.045691741499, 65.975392979836, 65.905094218128, 65.834795456377, 65.764496694581, 65.694197932743, 65.623899170861, 65.553600408936, 65.483301646970, 65.413002884961, 65.342704122911, 65.272405360820, 65.202106598688, 65.131807836516, 65.061509074303, 64.991210312051, 64.920911549759, 64.850612787428, 64.780314025058, 64.710015262650, 64.639716500204, 64.569417737720, 64.499118975198, 64.428820212639, 64.358521450043, 64.288222687411, 64.217923924742, 64.147625162038, 64.077326399297, 64.007027636522, 63.936728873711, 63.866430110865, 63.796131347985, 63.725832585070, 63.655533822122, 63.585235059139, 63.514936296124, 63.444637533075, 63.374338769993, 63.304040006879, 63.233741243732, 63.163442480553, 63.093143717342, 63.022844954099, 62.952546190825, 62.882247427520, 62.811948664184, 62.741649900817, 62.671351137420, 62.601052373993, 62.530753610535, 62.460454847048, 62.390156083532, 62.319857319986, 62.249558556411, 62.179259792807, 62.108961029175, 62.038662265514, 61.968363501825, 61.898064738108, 61.827765974364, 61.757467210592, 61.687168446792, 61.616869682965, 61.546570919112, 61.476272155231, 61.405973391324, 61.335674627391, 61.265375863432, 61.195077099446, 61.124778335435, 61.054479571399, 60.984180807337, 60.913882043249, 60.843583279137, 60.773284515000, 60.702985750838, 60.632686986652, 60.562388222441, 60.492089458207, 60.421790693948, 60.351491929665, 60.281193165359, 60.210894401030, 60.140595636677, 60.070296872301, 59.999998107902, 59.929699343481, 59.859400579037, 59.789101814570, 59.718803050081, 59.648504285570, 59.578205521036, 59.507906756481, 59.437607991905, 59.367309227306, 59.297010462687, 59.226711698046, 59.156412933384, 59.086114168701, 59.015815403997, 58.945516639273, 58.875217874528, 58.804919109762, 58.734620344977, 58.664321580171, 58.594022815345, 58.523724050500, 58.453425285635, 58.383126520750, 58.312827755846, 58.242528990922, 58.172230225979, 58.101931461018, 58.031632696037, 57.961333931038, 57.891035166019, 57.820736400983, 57.750437635927, 57.680138870854, 57.609840105762, 57.539541340653, 57.469242575525, 57.398943810380, 57.328645045216, 57.258346280036, 57.188047514837, 57.117748749622, 57.047449984389, 56.977151219139, 56.906852453871, 56.836553688587, 56.766254923287, 56.695956157969, 56.625657392635, 56.555358627284, 56.485059861917, 56.414761096534, 56.344462331134, 56.274163565718, 56.203864800287, 56.133566034839, 56.063267269376, 55.992968503897, 55.922669738403, 55.852370972893, 55.782072207367, 55.711773441826, 55.641474676271, 55.571175910699, 55.500877145113, 55.430578379513, 55.360279613897, 55.289980848266, 55.219682082621, 55.149383316961, 55.079084551287, 55.008785785599, 54.938487019896, 54.868188254179, 54.797889488448, 54.727590722702, 54.657291956943, 54.586993191170, 54.516694425384, 54.446395659583, 54.376096893769, 54.305798127941, 54.235499362100, 54.165200596246, 54.094901830378, 54.024603064497, 53.954304298603, 53.884005532696, 53.813706766776, 53.743408000843, 53.673109234897, 53.602810468939, 53.532511702968, 53.462212936984, 53.391914170988, 53.321615404979, 53.251316638958, 53.181017872924, 53.110719106879, 53.040420340821, 52.970121574751, 52.899822808669, 52.829524042575, 52.759225276469, 52.688926510352, 52.618627744222, 52.548328978081, 52.478030211928, 52.407731445764, 52.337432679589, 52.267133913402, 52.196835147203, 52.126536380993, 52.056237614772, 51.985938848540, 51.915640082297, 51.845341316043, 51.775042549778, 51.704743783502, 51.634445017215, 51.564146250917, 51.493847484609, 51.423548718289, 51.353249951960, 51.282951185619, 51.212652419269, 51.142353652907, 51.072054886536, 51.001756120154, 50.931457353762, 50.861158587360, 50.790859820947, 50.720561054525, 50.650262288092, 50.579963521649, 50.509664755197, 50.439365988735, 50.369067222262, 50.298768455780, 50.228469689289, 50.158170922787, 50.087872156277, 50.017573389756, 49.947274623226, 49.876975856687, 49.806677090138, 49.736378323580, 49.666079557012, 49.595780790436, 49.525482023850, 49.455183257255, 49.384884490651, 49.314585724037, 49.244286957415, 49.173988190784, 49.103689424144, 49.033390657495, 48.963091890837, 48.892793124171, 48.822494357496, 48.752195590812, 48.681896824119, 48.611598057418, 48.541299290709, 48.471000523990, 48.400701757264, 48.330402990529, 48.260104223786, 48.189805457034, 48.119506690274, 48.049207923506, 47.978909156730, 47.908610389945, 47.838311623152, 47.768012856352, 47.697714089543, 47.627415322726, 47.557116555902, 47.486817789069, 47.416519022229, 47.346220255381, 47.275921488525, 47.205622721661, 47.135323954790, 47.065025187911, 46.994726421024, 46.924427654130, 46.854128887228, 46.783830120319, 46.713531353402, 46.643232586478, 46.572933819546, 46.502635052608, 46.432336285661, 46.362037518708, 46.291738751747, 46.221439984779, 46.151141217804, 46.080842450822, 46.010543683832, 45.940244916836, 45.869946149832, 45.799647382822, 45.729348615805, 45.659049848780, 45.588751081749, 45.518452314711, 45.448153547666, 45.377854780614, 45.307556013556, 45.237257246491, 45.166958479419, 45.096659712340, 45.026360945255, 44.956062178164, 44.885763411065, 44.815464643961, 44.745165876849, 44.674867109732, 44.604568342607, 44.534269575477, 44.463970808340, 44.393672041197, 44.323373274047, 44.253074506891, 44.182775739729, 44.112476972561, 44.042178205386, 43.971879438205, 43.901580671019, 43.831281903826, 43.760983136627, 43.690684369422, 43.620385602211, 43.550086834994, 43.479788067771, 43.409489300542, 43.339190533307, 43.268891766067, 43.198592998820, 43.128294231568, 43.057995464310, 42.987696697046, 42.917397929776, 42.847099162501, 42.776800395220, 42.706501627934, 42.636202860641, 42.565904093344, 42.495605326040, 42.425306558731, 42.355007791417, 42.284709024097, 42.214410256772, 42.144111489441, 42.073812722104, 42.003513954763, 41.933215187416, 41.862916420064, 41.792617652706, 41.722318885343, 41.652020117975, 41.581721350601, 41.511422583223, 41.441123815839, 41.370825048450, 41.300526281056, 41.230227513656, 41.159928746252, 41.089629978843, 41.019331211428, 40.949032444009, 40.878733676584, 40.808434909155, 40.738136141720, 40.667837374281, 40.597538606836, 40.527239839387, 40.456941071933, 40.386642304474, 40.316343537011, 40.246044769542, 40.175746002069, 40.105447234591, 40.035148467108, 39.964849699620, 39.894550932128, 39.824252164631, 39.753953397130, 39.683654629624, 39.613355862113, 39.543057094598, 39.472758327078, 39.402459559553, 39.332160792024, 39.261862024491, 39.191563256953, 39.121264489410, 39.050965721863, 38.980666954312, 38.910368186756, 38.840069419196, 38.769770651632, 38.699471884063, 38.629173116490, 38.558874348913, 38.488575581331, 38.418276813745, 38.347978046155, 38.277679278560, 38.207380510961, 38.137081743359, 38.066782975752, 37.996484208140, 37.926185440525, 37.855886672906, 37.785587905282, 37.715289137654, 37.644990370023, 37.574691602387, 37.504392834747, 37.434094067103, 37.363795299455, 37.293496531804, 37.223197764148, 37.152898996488, 37.082600228825, 37.012301461157, 36.942002693486, 36.871703925811, 36.801405158132, 36.731106390449, 36.660807622762, 36.590508855071, 36.520210087377, 36.449911319679, 36.379612551977, 36.309313784271, 36.239015016562, 36.168716248849, 36.098417481132, 36.028118713412, 35.957819945688, 35.887521177960, 35.817222410229, 35.746923642494, 35.676624874755, 35.606326107013, 35.536027339267, 35.465728571518, 35.395429803765, 35.325131036009, 35.254832268249, 35.184533500486, 35.114234732719, 35.043935964949, 34.973637197175, 34.903338429398, 34.833039661618, 34.762740893834, 34.692442126047, 34.622143358256, 34.551844590462, 34.481545822665, 34.411247054864, 34.340948287060, 34.270649519253, 34.200350751443, 34.130051983629, 34.059753215812, 33.989454447991, 33.919155680168, 33.848856912341, 33.778558144511, 33.708259376678, 33.637960608842, 33.567661841002, 33.497363073160, 33.427064305314, 33.356765537465, 33.286466769613, 33.216168001758, 33.145869233900, 33.075570466039, 33.005271698175, 32.934972930308, 32.864674162437, 32.794375394564, 32.724076626688, 32.653777858809, 32.583479090926, 32.513180323041, 32.442881555153, 32.372582787262, 32.302284019368, 32.231985251471, 32.161686483571, 32.091387715668, 32.021088947763, 31.950790179854, 31.880491411943, 31.810192644029, 31.739893876112, 31.669595108192, 31.599296340270, 31.528997572344, 31.458698804416, 31.388400036485, 31.318101268552, 31.247802500615, 31.177503732676, 31.107204964734, 31.036906196790, 30.966607428843, 30.896308660893, 30.826009892940, 30.755711124985, 30.685412357027, 30.615113589066, 30.544814821103, 30.474516053137, 30.404217285169, 30.333918517198, 30.263619749224, 30.193320981248, 30.123022213270, 30.052723445288, 29.982424677304, 29.912125909318, 29.841827141329, 29.771528373338, 29.701229605344, 29.630930837348, 29.560632069349, 29.490333301348, 29.420034533344, 29.349735765338, 29.279436997329, 29.209138229318, 29.138839461305, 29.068540693289, 28.998241925270, 28.927943157250, 28.857644389227, 28.787345621201, 28.717046853174, 28.646748085144, 28.576449317111, 28.506150549077, 28.435851781039, 28.365553013000, 28.295254244959, 28.224955476915, 28.154656708868, 28.084357940820, 28.014059172769, 27.943760404716, 27.873461636661, 27.803162868604, 27.732864100544, 27.662565332482, 27.592266564418, 27.521967796352, 27.451669028284, 27.381370260213, 27.311071492140, 27.240772724065, 27.170473955988, 27.100175187909, 27.029876419828, 26.959577651744, 26.889278883659, 26.818980115571, 26.748681347482, 26.678382579390, 26.608083811296, 26.537785043200, 26.467486275102, 26.397187507002, 26.326888738900, 26.256589970796, 26.186291202690, 26.115992434582, 26.045693666472, 25.975394898360, 25.905096130246, 25.834797362130, 25.764498594012, 25.694199825892, 25.623901057770, 25.553602289646, 25.483303521520, 25.413004753393, 25.342705985263, 25.272407217131, 25.202108448998, 25.131809680863, 25.061510912726, 24.991212144586, 24.920913376446, 24.850614608303, 24.780315840158, 24.710017072012, 24.639718303863, 24.569419535713, 24.499120767561, 24.428821999407, 24.358523231252, 24.288224463094, 24.217925694935, 24.147626926774, 24.077328158612, 24.007029390447, 23.936730622281, 23.866431854113, 23.796133085943, 23.725834317772, 23.655535549599, 23.585236781424, 23.514938013247, 23.444639245069, 23.374340476889, 23.304041708707, 23.233742940524, 23.163444172339, 23.093145404152, 23.022846635964, 22.952547867774, 22.882249099582, 22.811950331389, 22.741651563194, 22.671352794997, 22.601054026799, 22.530755258600, 22.460456490398, 22.390157722195, 22.319858953991, 22.249560185785, 22.179261417577, 22.108962649368, 22.038663881157, 21.968365112945, 21.898066344731, 21.827767576515, 21.757468808298, 21.687170040080, 21.616871271860, 21.546572503638, 21.476273735415, 21.405974967191, 21.335676198965, 21.265377430738, 21.195078662509, 21.124779894278, 21.054481126046, 20.984182357813, 20.913883589578, 20.843584821342, 20.773286053104, 20.702987284865, 20.632688516625, 20.562389748383, 20.492090980140, 20.421792211895, 20.351493443649, 20.281194675401, 20.210895907153, 20.140597138902, 20.070298370651, 19.999999602398, 19.929700834143, 19.859402065888, 19.789103297631, 19.718804529372, 19.648505761113, 19.578206992852, 19.507908224589, 19.437609456326, 19.367310688061, 19.297011919794, 19.226713151527, 19.156414383258, 19.086115614988, 19.015816846717, 18.945518078444, 18.875219310170, 18.804920541895, 18.734621773618, 18.664323005341, 18.594024237062, 18.523725468782, 18.453426700500, 18.383127932218, 18.312829163934, 18.242530395649, 18.172231627363, 18.101932859075, 18.031634090787, 17.961335322497, 17.891036554206, 17.820737785914, 17.750439017621, 17.680140249326, 17.609841481031, 17.539542712734, 17.469243944436, 17.398945176137, 17.328646407837, 17.258347639536, 17.188048871233, 17.117750102930, 17.047451334625, 16.977152566319, 16.906853798012, 16.836555029705, 16.766256261396, 16.695957493085, 16.625658724774, 16.555359956462, 16.485061188149, 16.414762419834, 16.344463651519, 16.274164883202, 16.203866114885, 16.133567346566, 16.063268578247, 15.992969809926, 15.922671041605, 15.852372273282, 15.782073504958, 15.711774736634, 15.641475968308, 15.571177199981, 15.500878431654, 15.430579663325, 15.360280894996, 15.289982126665, 15.219683358334, 15.149384590001, 15.079085821668, 15.008787053333, 14.938488284998, 14.868189516662, 14.797890748324, 14.727591979986, 14.657293211647, 14.586994443307, 14.516695674966, 14.446396906625, 14.376098138282, 14.305799369938, 14.235500601594, 14.165201833248, 14.094903064902, 14.024604296555, 13.954305528207, 13.884006759858, 13.813707991508, 13.743409223158, 13.673110454806, 13.602811686454, 13.532512918101, 13.462214149747, 13.391915381392, 13.321616613036, 13.251317844680, 13.181019076323, 13.110720307964, 13.040421539606, 12.970122771246, 12.899824002885, 12.829525234524, 12.759226466162, 12.688927697799, 12.618628929435, 12.548330161071, 12.478031392706, 12.407732624340, 12.337433855973, 12.267135087606, 12.196836319237, 12.126537550868, 12.056238782499, 11.985940014128, 11.915641245757, 11.845342477385, 11.775043709013, 11.704744940639, 11.634446172265, 11.564147403891, 11.493848635515, 11.423549867139, 11.353251098762, 11.282952330385, 11.212653562006, 11.142354793628, 11.072056025248, 11.001757256868, 10.931458488487, 10.861159720105, 10.790860951723, 10.720562183340, 10.650263414957, 10.579964646573, 10.509665878188, 10.439367109803, 10.369068341417, 10.298769573030, 10.228470804643, 10.158172036255, 10.087873267866, 10.017574499477, 9.947275731087, 9.876976962697, 9.806678194306, 9.736379425915, 9.666080657523, 9.595781889130, 9.525483120737, 9.455184352343, 9.384885583949, 9.314586815554, 9.244288047159, 9.173989278763, 9.103690510366, 9.033391741969, 8.963092973571, 8.892794205173, 8.822495436775, 8.752196668376, 8.681897899976, 8.611599131576, 8.541300363175, 8.471001594774, 8.400702826372, 8.330404057970, 8.260105289567, 8.189806521164, 8.119507752760, 8.049208984356, 7.978910215951, 7.908611447546, 7.838312679141, 7.768013910735, 7.697715142328, 7.627416373921, 7.557117605514, 7.486818837106, 7.416520068698, 7.346221300289, 7.275922531880, 7.205623763470, 7.135324995060, 7.065026226650, 6.994727458239, 6.924428689828, 6.854129921416, 6.783831153004, 6.713532384592, 6.643233616179, 6.572934847765, 6.502636079352, 6.432337310938, 6.362038542524, 6.291739774109, 6.221441005694, 6.151142237278, 6.080843468862, 6.010544700446, 5.940245932030, 5.869947163613, 5.799648395196, 5.729349626778, 5.659050858360, 5.588752089942, 5.518453321523, 5.448154553105, 5.377855784685, 5.307557016266, 5.237258247846, 5.166959479426, 5.096660711006, 5.026361942585, 4.956063174164, 4.885764405743, 4.815465637321, 4.745166868899, 4.674868100477, 4.604569332055, 4.534270563632, 4.463971795209, 4.393673026786, 4.323374258363, 4.253075489939, 4.182776721515, 4.112477953091, 4.042179184666, 3.971880416242, 3.901581647817, 3.831282879392, 3.760984110967, 3.690685342541, 3.620386574115, 3.550087805689, 3.479789037263, 3.409490268837, 3.339191500410, 3.268892731984, 3.198593963557, 3.128295195130, 3.057996426702, 2.987697658275, 2.917398889847, 2.847100121419, 2.776801352991, 2.706502584563, 2.636203816135, 2.565905047706, 2.495606279278, 2.425307510849, 2.355008742420, 2.284709973991, 2.214411205562, 2.144112437133, 2.073813668703, 2.003514900274, 1.933216131844, 1.862917363415, 1.792618594985, 1.722319826555, 1.652021058125, 1.581722289695, 1.511423521264, 1.441124752834, 1.370825984404, 1.300527215973, 1.230228447543, 1.159929679112, 1.089630910681, 1.019332142250, 0.949033373820, 0.878734605389, 0.808435836958, 0.738137068527, 0.667838300096, 0.597539531665, 0.527240763234, 0.456941994803, 0.386643226372, 0.316344457940, 0.246045689509, 0.175746921078, 0.105448152647, 0.035149384216)) } // namespace gaussian } // namespace spacing } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/spacing/SpacingFactory.h0000664000175000017500000000307615160212070023664 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include "atlas/util/Config.h" #include "atlas/util/Factory.h" namespace atlas { namespace grid { namespace spacing { //---------------------------------------------------------------------------------------------------------------------- class Spacing; class SpacingFactory : public util::Factory { public: static std::string className() { return "SpacingFactory"; } static const Spacing* build(const std::string&); static const Spacing* build(const std::string&, const eckit::Parametrisation&); using Factory::Factory; private: virtual const Spacing* make(const eckit::Parametrisation&) = 0; }; //---------------------------------------------------------------------------------------------------------------------- template class SpacingBuilder : public SpacingFactory { private: virtual const Spacing* make(const eckit::Parametrisation& param) { return new T(param); } public: using SpacingFactory::SpacingFactory; }; //---------------------------------------------------------------------------------------------------------------------- } // namespace spacing } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/spacing/Spacing.h0000664000175000017500000000322015160212070022323 0ustar alastairalastair/* * (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 #include "atlas/util/Object.h" namespace eckit { class Parametrisation; } namespace atlas { namespace util { class Config; } } // namespace atlas namespace atlas { namespace grid { namespace spacing { class Spacing : public util::Object { public: using const_iterator = std::vector::const_iterator; using Interval = std::array; using Spec = atlas::util::Config; public: static const Spacing* create(const eckit::Parametrisation& params); virtual std::string type() const = 0; double operator[](size_t i) const { return x_[i]; } size_t size() const { return x_.size(); } const double* data() const { return x_.data(); } const_iterator begin() const { return x_.begin(); } const_iterator end() const { return x_.end(); } double front() const { return x_.front(); } double back() const { return x_.back(); } Interval interval() const { return {{min_, max_}}; } double min() const { return min_; } double max() const { return max_; } virtual Spec spec() const = 0; protected: std::vector x_; double min_; double max_; }; } // namespace spacing } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/spacing/CustomSpacing.cc0000664000175000017500000000355215160212070023664 0ustar alastairalastair/* * (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/grid/detail/spacing/CustomSpacing.h" #include #include "eckit/config/Parametrisation.h" #include "atlas/grid/detail/spacing/SpacingFactory.h" #include "atlas/runtime/Exception.h" namespace atlas { namespace grid { namespace spacing { CustomSpacing::CustomSpacing(long N, const double values[], const Interval& interval) { x_.assign(values, values + N); min_ = std::min(interval[0], interval[1]); max_ = std::max(interval[0], interval[1]); } CustomSpacing::CustomSpacing(const eckit::Parametrisation& params) { params.get("values", x_); size_t N; if (params.get("N", N)) { ATLAS_ASSERT(x_.size() == N); } N = x_.size(); std::vector interval; if (params.get("interval", interval)) { min_ = std::min(interval[0], interval[1]); max_ = std::max(interval[0], interval[1]); } else { min_ = x_.front(); max_ = x_.front(); for (size_t j = 1; j < N; ++j) { min_ = std::min(min_, x_[j]); max_ = std::max(max_, x_[j]); } } } CustomSpacing::Spec CustomSpacing::spec() const { Spec spacing_specs; spacing_specs.set("type", static_type()); spacing_specs.set("values", x_); spacing_specs.set("interval", std::vector{min(), max()}); return spacing_specs; } namespace { static SpacingBuilder __builder(CustomSpacing::static_type()); } } // namespace spacing } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/spacing/SpacingFactory.cc0000664000175000017500000000256215160212070024021 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include #include "atlas/grid/detail/spacing/SpacingFactory.h" namespace atlas { namespace grid { namespace spacing { //---------------------------------------------------------------------------------------------------------------------- void force_link() { static struct Link { Link() = default; } link; [](const Link&) {}(link); // disable unused warnings } //---------------------------------------------------------------------------------------------------------------------- const Spacing* SpacingFactory::build(const std::string& builder) { return build(builder, util::NoConfig()); } const Spacing* SpacingFactory::build(const std::string& builder, const eckit::Parametrisation& param) { force_link(); auto factory = get(builder); return factory->make(param); } //---------------------------------------------------------------------------------------------------------------------- } // namespace spacing } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/spacing/Spacing.cc0000664000175000017500000000166515160212070022474 0ustar alastairalastair/* * (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 "Spacing.h" #include "eckit/config/Parametrisation.h" #include "atlas/grid/detail/spacing/SpacingFactory.h" #include "atlas/runtime/Exception.h" namespace atlas { namespace grid { namespace spacing { const Spacing* Spacing::create(const eckit::Parametrisation& params) { std::string spacingType; if (not params.get("type", spacingType)) { throw_Exception("type missing in configuration", Here()); } return SpacingFactory::build(spacingType, params); } } // namespace spacing } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/grid/0000775000175000017500000000000015160212070020072 5ustar alastairalastairatlas-0.46.0/src/atlas/grid/detail/grid/Unstructured.cc0000664000175000017500000002035115160212070023111 0ustar alastairalastair/* * (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/grid/detail/grid/Unstructured.h" #include #include #include #include #include "eckit/types/FloatCompare.h" #include "eckit/utils/Hash.h" #include "atlas/array/ArrayView.h" #include "atlas/grid/Iterator.h" #include "atlas/grid/SpecRegistry.h" #include "atlas/grid/detail/grid/GridBuilder.h" #include "atlas/grid/detail/grid/GridFactory.h" #include "atlas/option.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Log.h" #include "atlas/util/CoordinateEnums.h" #include "atlas/util/NormaliseLongitude.h" namespace atlas { namespace grid { namespace detail { namespace grid { namespace { static GridFactoryBuilder __register_Unstructured(Unstructured::static_type()); } namespace { class Normalise { public: Normalise(const RectangularDomain& domain): degrees_(domain.units() == "degrees"), normalise_(domain.xmin(), domain.xmax()) {} double operator()(double x) const { if (degrees_) { x = normalise_(x); } return x; } private: const bool degrees_; util::NormaliseLongitude normalise_; }; } // namespace Unstructured::Unstructured(const Grid& grid, Domain domain): Grid() { domain_ = domain; points_.reset(new std::vector); points_->reserve(grid.size()); if (not domain_) { domain_ = GlobalDomain(); } atlas::grid::IteratorXY it(grid.xy_begin()); PointXY p; if (RectangularDomain(domain_)) { auto normalise = Normalise(RectangularDomain(domain_)); while (it.next(p)) { p.x() = normalise(p.x()); if (domain_.contains(p)) { points_->emplace_back(p); } } } else if (ZonalBandDomain(domain_)) { while (it.next(p)) { if (domain_.contains(p)) { points_->emplace_back(p); } } } else { while (it.next(p)) { points_->emplace_back(p); } } points_->shrink_to_fit(); } Unstructured::Unstructured(const util::Config& config): Grid() { util::Config config_domain; if (not config.get("domain", config_domain)) { config_domain.set("type", "global"); } domain_ = Domain(config_domain); std::vector xy; if (config.get("xy", xy)) { const size_t N = xy.size() / 2; points_.reset(new std::vector); points_->reserve(N); for (size_t n = 0; n < N; ++n) { points_->emplace_back(PointXY{xy[2 * n], xy[2 * n + 1]}); } } else { std::vector x; std::vector y; if (not config.get("x", x)) { throw_Exception("x missing from configuration"); } if (not config.get("y", y)) { throw_Exception("y missing from configuration"); } ATLAS_ASSERT(x.size() == y.size()); points_.reset(new std::vector); points_->reserve(x.size()); for (size_t n = 0; n < x.size(); ++n) { points_->emplace_back(PointXY{x[n], y[n]}); } } } Unstructured::Unstructured(std::vector* pts): Grid(), points_(pts) { domain_ = GlobalDomain(); } Unstructured::Unstructured(std::vector&& pts): Grid(), points_(new std::vector(std::move(pts))) { domain_ = GlobalDomain(); } Unstructured::Unstructured(const std::vector& pts): Grid(), points_(new std::vector(pts)) { domain_ = GlobalDomain(); } Unstructured::Unstructured(std::initializer_list initializer_list): Grid(), points_(new std::vector(initializer_list)) { domain_ = GlobalDomain(); } Unstructured::Unstructured(const std::string& uid, size_t N, const double x[], const double y[], size_t xstride, size_t ystride): Grid(), points_(new std::vector(N)) { util::Config config_domain; config_domain.set("type", "global"); domain_ = Domain(config_domain); std::vector& p = *points_; const idx_t npts = static_cast(p.size()); for (idx_t n = 0; n < npts; ++n) { p[n].assign(x[n*xstride], y[n*ystride]); } if (!uid.empty()) { if (atlas::grid::SpecRegistry::has(uid)) { cached_spec_.reset(new Grid::Spec{atlas::grid::SpecRegistry::get(uid)}); cached_spec_->get("uid",uid_); cached_spec_->get("name",shortName_); } if (uid_.empty()) { uid_ = uid; } } } Unstructured::Unstructured(size_t N, const double xy[]): Unstructured(N,xy,xy+1,2,2) {} Unstructured::~Unstructured() = default; Grid::uid_t Unstructured::name() const { if (shortName_.empty()) { std::ostringstream s; s << "unstructured." << Grid::hash().substr(0, 7); shortName_ = s.str(); } return shortName_; } void Unstructured::hash(eckit::Hash& h) const { ATLAS_ASSERT(points_ != nullptr); const std::vector& pts = *points_; h.add(&pts[0], sizeof(PointXY) * pts.size()); for (idx_t i = 0, N = static_cast(pts.size()); i < N; i++) { const PointXY& p = pts[i]; h << p.x() << p.y(); } projection().hash(h); } size_t Unstructured::footprint() const { return sizeof(PointXY) * points_->size(); } RectangularLonLatDomain Unstructured::lonlatBoundingBox() const { return projection_ ? projection_.lonlatBoundingBox(domain_) : domain_; } idx_t Unstructured::size() const { ATLAS_ASSERT(points_ != nullptr); return static_cast(points_->size()); } Grid::Spec Unstructured::spec() const { if (cached_spec_) { return *cached_spec_; } cached_spec_.reset(new Grid::Spec); cached_spec_->set("type", static_type()); cached_spec_->set("domain", domain().spec()); cached_spec_->set("projection", projection().spec()); auto it = xy_begin(); std::vector coords(2 * size()); idx_t c(0); PointXY xy; while (it->next(xy)) { coords[c++] = xy.x(); coords[c++] = xy.y(); } cached_spec_->set("xy", coords); return *cached_spec_; } Grid::Config Unstructured::meshgenerator() const { return Config("type", "delaunay"); } Grid::Config Unstructured::partitioner() const { return Config("type", "equal_regions"); } void Unstructured::print(std::ostream& os) const { os << "Unstructured(Npts:" << size() << ")"; } namespace { // anonymous static class unstructured : public GridBuilder { using Implementation = atlas::Grid::Implementation; using Config = Grid::Config; public: unstructured(): GridBuilder("unstructured") {} void print(std::ostream& os) const override { os << std::left << std::setw(20) << " " << "Unstructured grid"; } const Implementation* create(const std::string& /* name */, const Config&) const override { throw_NotImplemented("Cannot create unstructured grid from name", Here()); } const Implementation* create(const Config& config) const override { return new Unstructured(config); } } unstructured_; } // anonymous namespace extern "C" { const Unstructured* atlas__grid__Unstructured__points(const double xy[], int shapef[], int stridesf[]) { size_t nb_points = shapef[1]; ATLAS_ASSERT(shapef[0] == 2); size_t stride_n = stridesf[1]; size_t stride_v = stridesf[0]; std::vector points; points.reserve(nb_points); 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)); } const Unstructured* atlas__grid__Unstructured__config(util::Config* conf) { ATLAS_ASSERT(conf != nullptr); const Unstructured* grid = dynamic_cast(Grid::create(*conf)); ATLAS_ASSERT(grid != nullptr); return grid; } } } // namespace grid } // namespace detail } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/grid/StencilComputerInterface.h0000664000175000017500000000252315160212070025206 0ustar alastairalastair/* * (C) Copyright 2023 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include "atlas/grid/StencilComputer.h" #include "atlas/grid/StructuredGrid.h" extern "C" { atlas::grid::ComputeNorth* atlas__grid__ComputeNorth__new(const atlas::StructuredGrid::Implementation* grid, int halo); void atlas__grid__ComputeNorth__delete(atlas::grid::ComputeNorth* This); atlas::idx_t atlas__grid__ComputeNorth__execute_real32(const atlas::grid::ComputeNorth* This, float y); atlas::idx_t atlas__grid__ComputeNorth__execute_real64(const atlas::grid::ComputeNorth* This, double y); atlas::grid::ComputeWest* atlas__grid__ComputeWest__new(const atlas::StructuredGrid::Implementation* grid, int halo); void atlas__grid__ComputeWest__delete(atlas::grid::ComputeWest* This); atlas::idx_t atlas__grid__ComputeWest__execute_real32(const atlas::grid::ComputeWest* This, float x, atlas::idx_t j); atlas::idx_t atlas__grid__ComputeWest__execute_real64(const atlas::grid::ComputeWest* This, double x, atlas::idx_t j); } atlas-0.46.0/src/atlas/grid/detail/grid/GridFactory.cc0000664000175000017500000000277015160212070022624 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include #include "atlas/grid/detail/grid/CubedSphere.h" #include "atlas/grid/detail/grid/GridFactory.h" #include "atlas/grid/detail/grid/Structured.h" #include "atlas/grid/detail/grid/Unstructured.h" namespace atlas { namespace grid { //---------------------------------------------------------------------------------------------------------------------- namespace { void force_link() { static struct Link { Link() { GridFactoryBuilder(); GridFactoryBuilder(); GridFactoryBuilder(); } } link; } } // namespace //---------------------------------------------------------------------------------------------------------------------- const GridImpl* GridFactory::build(const std::string& builder, const util::Config& config) { force_link(); auto factory = get(builder); return factory->make(config); } //---------------------------------------------------------------------------------------------------------------------- } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/grid/Structured.cc0000664000175000017500000007330515160212070022555 0ustar alastairalastair/* * (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 "Structured.h" #include #include #include #include #include "eckit/types/FloatCompare.h" #include "eckit/utils/Hash.h" #include "atlas/domain/Domain.h" #include "atlas/grid/StructuredGrid.h" #include "atlas/grid/detail/grid/GridBuilder.h" #include "atlas/grid/detail/grid/GridFactory.h" #include "atlas/grid/detail/spacing/CustomSpacing.h" #include "atlas/grid/detail/spacing/LinearSpacing.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/NormaliseLongitude.h" #include "atlas/util/Point.h" #include "atlas/util/UnitSphere.h" namespace atlas { namespace grid { namespace detail { namespace grid { std::string Structured::static_type() { return "structured"; } std::string Structured::name() const { return name_; } Structured::Structured(XSpace xspace, YSpace yspace, Projection p, Domain domain): Structured(Structured::static_type(), xspace, yspace, p, domain) {} Structured::Structured(const std::string& name, XSpace xspace, YSpace yspace, Projection projection, Domain domain): Grid(), name_(name), xspace_(xspace), yspace_(yspace) { // Copy members projection_ = projection ? projection : Projection(); y_.assign(yspace_.begin(), yspace_.end()); idx_t ny{static_cast(y_.size())}; if (xspace_.ny() == 1 && yspace_.size() > 1) { nx_.resize(ny, xspace_.nx()[0]); dx_.resize(ny, xspace_.dx()[0]); xmin_.resize(ny, xspace_.xmin()[0]); xmax_.resize(ny, xspace_.xmax()[0]); } else { nx_ = xspace_.nx(); dx_ = xspace_.dx(); xmin_ = xspace_.xmin(); xmax_ = xspace_.xmax(); } ATLAS_ASSERT(static_cast(nx_.size()) == ny); // Further setup nxmin_ = nxmax_ = nx_.front(); for (idx_t j = 1; j < ny; ++j) { nxmin_ = std::min(nx_[j], nxmin_); nxmax_ = std::max(nx_[j], nxmax_); } size_t npts_size_t = 0; for (auto& nx : nx_) { npts_size_t += size_t(nx); } npts_ = std::accumulate(nx_.begin(), nx_.end(), idx_t{0}); if (size_t(npts_) != npts_size_t) { ATLAS_THROW_EXCEPTION( "Cannot represent grid.size() with idx_t of " << ATLAS_BITS_LOCAL << " bits.\n" "Recompile atlas with idx_t 64bits, or wait for pending development where grid index to become gidx_t"); } crop(domain); ny = nx_.size(); computeTruePeriodicity(); jglooff_.resize(ny + 1); jglooff_[0] = 0; for (idx_t j = 1; j < ny + 1; j++) { jglooff_[j] = jglooff_[j - 1] + nx_[j - 1]; } } Domain Structured::computeDomain() const { if (periodic()) { return ZonalBandDomain({yspace().min(), yspace().max()}, xspace().min()); } return RectangularDomain({xspace().min(), xspace().max()}, {yspace().min(), yspace().max()}, projection_.units()); } Structured::~Structured() = default; Structured::XSpace::XSpace(): impl_(nullptr) {} template Structured::XSpace::XSpace(const std::array& interval, const NVector& N, bool endpoint): impl_(new Implementation(interval, N, endpoint)) {} template Structured::XSpace::XSpace(const std::array& interval, const std::vector& N, bool endpoint); template Structured::XSpace::XSpace(const std::array& interval, const std::vector& N, bool endpoint); Structured::XSpace::XSpace(const std::array& interval, std::initializer_list&& N, bool endpoint): XSpace(interval, std::vector{N}, endpoint) {} Structured::XSpace::XSpace(const Spacing& spacing, idx_t ny): impl_(new Implementation(spacing, ny)) {} Structured::XSpace::XSpace(const std::vector& spacings): impl_(new Implementation(spacings)) {} Structured::XSpace::XSpace(const std::vector& spacings): impl_(new Implementation(spacings)) {} Structured::XSpace::XSpace(const Config& config): impl_(new Implementation(config)) {} Structured::XSpace::XSpace(const std::vector& config): impl_(new Implementation(config)) {} Grid::Spec Structured::XSpace::spec() const { return impl_->spec(); } Structured::XSpace::Implementation::Implementation(const Config& config) { Config config_xspace(config); std::string xspace_type; config_xspace.get("type", xspace_type); ATLAS_ASSERT(xspace_type == "linear"); std::vector v_N; std::vector v_start; std::vector v_end; std::vector v_length; config_xspace.get("N[]", v_N); config_xspace.get("start[]", v_start); config_xspace.get("end[]", v_end); config_xspace.get("length[]", v_length); idx_t ny = std::max(v_N.size(), std::max(v_start.size(), std::max(v_end.size(), std::max(v_length.size(), 1ul)))); reserve(ny); if (not v_N.empty()) { ATLAS_ASSERT(static_cast(v_N.size()) == ny); } if (not v_start.empty()) { ATLAS_ASSERT(static_cast(v_start.size()) == ny); } if (not v_end.empty()) { ATLAS_ASSERT(static_cast(v_end.size()) == ny); } if (not v_length.empty()) { ATLAS_ASSERT(static_cast(v_length.size()) == ny); } nxmin_ = std::numeric_limits::max(); nxmax_ = 0; min_ = std::numeric_limits::max(); max_ = -std::numeric_limits::max(); for (idx_t j = 0; j < ny; ++j) { if (not v_N.empty()) { config_xspace.set("N", v_N[j]); } if (not v_start.empty()) { config_xspace.set("start", v_start[j]); } if (not v_end.empty()) { config_xspace.set("end", v_end[j]); } if (not v_length.empty()) { config_xspace.set("length", v_length[j]); } spacing::LinearSpacing::Params xspace(config_xspace); xmin_.push_back(xspace.start); xmax_.push_back(xspace.end); nx_.push_back(xspace.N); dx_.push_back(xspace.step); nxmin_ = std::min(nxmin_, nx_[j]); nxmax_ = std::max(nxmax_, nx_[j]); min_ = std::min(min_, xspace.start); max_ = std::max(max_, xspace.end); } } Structured::XSpace::Implementation::Implementation(const std::vector& config_list) { reserve(config_list.size()); nxmin_ = std::numeric_limits::max(); nxmax_ = 0; min_ = std::numeric_limits::max(); max_ = -std::numeric_limits::max(); std::string xspace_type; for (idx_t j = 0; j < ny(); ++j) { config_list[j].get("type", xspace_type); ATLAS_ASSERT(xspace_type == "linear"); spacing::LinearSpacing::Params xspace(config_list[j]); xmin_.push_back(xspace.start); xmax_.push_back(xspace.end); nx_.push_back(xspace.N); dx_.push_back(xspace.step); nxmin_ = std::min(nxmin_, nx_[j]); nxmax_ = std::max(nxmax_, nx_[j]); min_ = std::min(min_, xspace.start); max_ = std::max(max_, xspace.end); } } Structured::XSpace::Implementation::Implementation(const std::vector& spacings) { reserve(spacings.size()); nxmin_ = std::numeric_limits::max(); nxmax_ = 0; min_ = std::numeric_limits::max(); max_ = -std::numeric_limits::max(); for (idx_t j = 0; j < ny(); ++j) { const auto& xspace = spacings[j]; xmin_.push_back(xspace.start); xmax_.push_back(xspace.end); nx_.push_back(xspace.N); dx_.push_back(xspace.step); nxmin_ = std::min(nxmin_, nx_[j]); nxmax_ = std::max(nxmax_, nx_[j]); min_ = std::min(min_, xspace.start); max_ = std::max(max_, xspace.end); } } void Structured::XSpace::Implementation::Implementation::reserve(idx_t ny) { ny_ = ny; nx_.reserve(ny); xmin_.reserve(ny); xmax_.reserve(ny); dx_.reserve(ny); } template Structured::XSpace::Implementation::Implementation(const std::array& interval, const NVector& N, bool endpoint): ny_(N.size()), nx_(N.begin(), N.end()), xmin_(ny_, interval[0]), xmax_(ny_, interval[1]), dx_(ny_) { nxmin_ = std::numeric_limits::max(); nxmax_ = 0; min_ = std::numeric_limits::max(); max_ = -std::numeric_limits::max(); double length = interval[1] - interval[0]; for (idx_t j = 0; j < ny_; ++j) { nxmin_ = std::min(nxmin_, nx_[j]); nxmax_ = std::max(nxmax_, nx_[j]); dx_[j] = endpoint ? length / double(nx_[j] - 1) : length / double(nx_[j]); min_ = std::min(min_, xmin_[j]); max_ = std::max(max_, xmax_[j]); } } template Structured::XSpace::Implementation::Implementation(const std::array& interval, const std::vector& N, bool endpoint); template Structured::XSpace::Implementation::Implementation(const std::array& interval, const std::vector& N, bool endpoint); Structured::XSpace::Implementation::Implementation(const std::array& interval, std::initializer_list&& N, bool endpoint): Implementation(interval, std::vector{N}, endpoint) {} Structured::XSpace::Implementation::Implementation(const Spacing& spacing, idx_t ny): ny_(ny), nx_(ny_, spacing.size()), xmin_(ny_, spacing.min()), xmax_(ny_, spacing.max()), dx_(ny_) { const spacing::LinearSpacing& linspace = dynamic_cast(*spacing.get()); dx_[0] = linspace.step(); for (idx_t j = 1; j < ny_; ++j) { dx_[j] = dx_[0]; } nxmax_ = nx_[0]; nxmin_ = nx_[0]; min_ = spacing.min(); max_ = spacing.max(); } Structured::XSpace::Implementation::Implementation(const std::vector& spacings): ny_(spacings.size()), nxmin_(std::numeric_limits::max()), nxmax_(0), nx_(ny_), xmin_(ny_), xmax_(ny_), dx_(ny_), min_(std::numeric_limits::max()), max_(-std::numeric_limits::max()) { for (idx_t j = 0; j < ny_; ++j) { const spacing::LinearSpacing& linspace = dynamic_cast(*spacings[j].get()); nx_[j] = linspace.size(); xmin_[j] = linspace.min(); xmax_[j] = linspace.max(); dx_[j] = linspace.step(); nxmin_ = std::min(nxmin_, nx_[j]); nxmax_ = std::max(nxmax_, nx_[j]); min_ = std::min(min_, xmin_[j]); max_ = std::max(max_, xmax_[j]); } } std::string Structured::XSpace::Implementation::type() const { return "linear"; } Grid::Spec Structured::XSpace::Implementation::spec() const { Grid::Spec spec; bool same_xmin = true; bool same_xmax = true; bool same_nx = true; double xmin = xmin_[0]; double xmax = xmax_[0]; idx_t nx = nx_[0]; double dx = dx_[0]; ATLAS_ASSERT(static_cast(xmin_.size()) == ny_); ATLAS_ASSERT(static_cast(xmax_.size()) == ny_); ATLAS_ASSERT(static_cast(nx_.size()) == ny_); for (idx_t j = 1; j < ny_; ++j) { same_xmin = same_xmin && (eckit::types::is_approximately_equal(xmin_[j], xmin)); same_xmax = same_xmax && (eckit::types::is_approximately_equal(xmax_[j], xmax)); same_nx = same_nx && (nx_[j] == nx); } bool endpoint = std::abs((xmax - xmin) - (nx - 1) * dx) < 1.e-10; spec.set("type", "linear"); if (same_xmin) { spec.set("start", xmin); } else { spec.set("start[]", xmin_); } if (same_xmax) { spec.set("end", xmax); } else { spec.set("end[]", xmax_); } if (same_nx) { spec.set("N", nx); } else { spec.set("N[]", nx_); } spec.set("endpoint", endpoint); return spec; } namespace { class Normalise { public: Normalise(const RectangularDomain& domain): degrees_(domain.units() == "degrees"), normalise_(domain.xmin()) {} double operator()(double x) const { if (degrees_) { x = normalise_(x); } return x; } private: const bool degrees_; util::NormaliseLongitude normalise_; }; } // namespace void Structured::crop(const Domain& dom) { using eckit::types::is_approximately_equal; if (!dom) { domain_ = RectangularDomain({xspace_.min(), xspace_.max()}, {yspace_.min(), yspace_.max()}, projection_.units()); return; } ATLAS_ASSERT(dom.units() == projection().units()); auto rect_domain = RectangularDomain(dom); if (!rect_domain) { std::stringstream errmsg; errmsg << "Cannot crop the grid with domain " << dom; throw_Exception(errmsg.str(), Here()); } const double cropped_ymin = rect_domain.ymin(); const double cropped_ymax = rect_domain.ymax(); // Cropping in Y idx_t jmin = ny(); idx_t jmax = 0; for (idx_t j = 0; j < ny(); ++j) { if (rect_domain.contains_y(y(j))) { jmin = std::min(j, jmin); jmax = std::max(j, jmax); } } ATLAS_ASSERT(jmax >= jmin); idx_t cropped_ny = jmax - jmin + 1; ATLAS_ASSERT(cropped_ny <= ny()); std::vector cropped_y(y_.begin() + jmin, y_.begin() + jmin + cropped_ny); std::vector cropped_dx(dx_.begin() + jmin, dx_.begin() + jmin + cropped_ny); std::vector cropped_xmin(cropped_ny, std::numeric_limits::max()); std::vector cropped_xmax(cropped_ny, -std::numeric_limits::max()); std::vector cropped_nx(cropped_ny); Normalise normalise(rect_domain); auto index_of_smallest_normalised_x = [&](idx_t j) { // Compute point of normalisation with bisection method idx_t imin = 0; idx_t imax = nx(j) - 1; auto normalised_x = [&](idx_t i) { return normalise(x(i, j)); }; double xmin = normalised_x(imin); double xmax = normalised_x(imax); idx_t max_iter = nx(j); idx_t iter = 0; while (xmax < xmin && imax != imin + 1) { idx_t imid = (imin + imax) / 2; if (normalised_x(imid) > xmin) { imin = imid; xmin = normalised_x(imin); } else { imax = imid; xmax = normalised_x(imax); } if (iter == max_iter) { ATLAS_THROW_EXCEPTION("Could not converge"); } ++iter; } return (xmin < xmax ? imin : imax); }; bool is_regular = [&]() { if (reduced()) { return false; } long _nx = nx(jmin); double _dx = _nx > 0 ? dx(jmin) : std::numeric_limits::max(); double _xmin = _nx > 0 ? xmin(jmin) : std::numeric_limits::max(); for (idx_t j = jmin; j <= jmax; ++j) { if (nx(j) != _nx) { return false; } if (dx(j) != _dx) { return false; } if (xmin(j) != _xmin) { return false; } } return true; }(); // Cropping in X bool endpoint = true; { if (rect_domain.zonal_band()) { for (idx_t j = jmin, jcropped = 0; j <= jmax; ++j, ++jcropped) { if (is_regular && jcropped > 0) { cropped_xmin[jcropped] = cropped_xmin[0]; cropped_xmax[jcropped] = cropped_xmax[0]; cropped_nx[jcropped] = cropped_nx[0]; continue; } idx_t i_xmin = index_of_smallest_normalised_x(j); idx_t i_xmax = i_xmin == 0 ? nx(j) - 1 : i_xmin - 1; cropped_xmin[jcropped] = normalise(x(i_xmin, j)); cropped_xmax[jcropped] = normalise(x(i_xmax, j)); cropped_nx[jcropped] = nx(j); if (is_approximately_equal(cropped_xmax[jcropped] + cropped_dx[jcropped], cropped_xmin[jcropped] + 360., 1.e-10)) { cropped_xmax[jcropped] = cropped_xmin[jcropped] + 360.; endpoint = false; } } } else { for (idx_t j = jmin, jcropped = 0; j <= jmax; ++j, ++jcropped) { if (is_regular && jcropped > 0) { cropped_xmin[jcropped] = cropped_xmin[0]; cropped_xmax[jcropped] = cropped_xmax[0]; cropped_nx[jcropped] = cropped_nx[0]; continue; } idx_t i_xmin = index_of_smallest_normalised_x(j); for (idx_t n = 0; n < nx(j); ++n) { idx_t i = i_xmin + n; if (i >= nx(j)) { i -= nx(j); } double xmin = normalise(x(i, j)); if (rect_domain.contains_x(xmin)) { i_xmin = i; cropped_xmin[jcropped] = xmin; break; } } double xmin = normalise(x(i_xmin, j)); idx_t i_xmax = i_xmin; idx_t max_iter = nx(j); idx_t n = 1; ATLAS_ASSERT(rect_domain.contains_x(xmin)); while (true) { idx_t i_xmax_next = i_xmax + 1; if (i_xmax_next == nx(j)) { i_xmax_next -= nx(j); } if (i_xmax_next == i_xmin) { break; } double xmax_next = normalise(x(i_xmax_next, j)); if (not rect_domain.contains_x(xmax_next)) { break; } if (n == max_iter) { ATLAS_THROW_EXCEPTION("Could not converge"); } i_xmax++; n++; } cropped_xmin[jcropped] = normalise(x(i_xmin, j)); cropped_xmax[jcropped] = normalise(x(i_xmax, j)); cropped_nx[jcropped] = std::max(1, n); } } } // Complete structures idx_t cropped_nxmin, cropped_nxmax; cropped_nxmin = cropped_nxmax = cropped_nx.front(); for (idx_t j = 1; j < cropped_ny; ++j) { cropped_nxmin = std::min(cropped_nx[j], cropped_nxmin); cropped_nxmax = std::max(cropped_nx[j], cropped_nxmax); if (is_regular) { break; } } idx_t cropped_npts = std::accumulate(cropped_nx.begin(), cropped_nx.end(), idx_t{0}); XSpace cropped_xspace; if (is_regular) { cropped_xspace = XSpace(LinearSpacing{cropped_xmin[0], cropped_xmax[0], cropped_nx[0], endpoint}, cropped_ny); } else { std::vector cropped_xspacing_params(cropped_ny); for (idx_t j = 0; j < cropped_ny; ++j) { cropped_xspacing_params[j] = {cropped_xmin[j], cropped_xmax[j], cropped_nx[j], endpoint}; } cropped_xspace = XSpace(cropped_xspacing_params); } for (idx_t j = 0; j < cropped_ny; ++j) { ATLAS_ASSERT(eckit::types::is_approximately_equal(cropped_xspace.xmin()[j], cropped_xmin[j])); if (cropped_nx[j] > 1) { ATLAS_ASSERT(eckit::types::is_approximately_equal(cropped_xspace.dx()[j], cropped_dx[j], 1.e-10)); } ATLAS_ASSERT(cropped_xspace.nx()[j] == cropped_nx[j]); ATLAS_ASSERT(cropped_xspace.nxmin() == cropped_nxmin); ATLAS_ASSERT(cropped_xspace.nxmax() == cropped_nxmax); } // Modify grid if (ny() != cropped_ny) { // keep specialised spacing (Gaussian, Linear, ...) unless cropping happens yspace_ = new spacing::CustomSpacing(cropped_ny, cropped_y.data(), {cropped_ymin, cropped_ymax}); } xspace_ = cropped_xspace; xmin_ = cropped_xmin; xmax_ = cropped_xmax; dx_ = cropped_dx; nx_ = cropped_nx; nxmin_ = cropped_nxmin; nxmax_ = cropped_nxmax; npts_ = cropped_npts; y_ = cropped_y; domain_ = dom; } void Structured::computeTruePeriodicity() { ATLAS_ASSERT(domain_); if (projection_.strictlyRegional()) { periodic_x_ = false; return; } if( domain_.global() ) { periodic_x_ = true; return; } if (ZonalBandDomain(domain_)) { periodic_x_ = true; return; } idx_t j = ny() / 2; const PointLonLat Pllmin = projection().lonlat(PointXY(xmin_[j], y_[j])); const PointLonLat Pllmax = projection().lonlat(PointXY(xmax_[j], y_[j])); Point3 Pxmin; util::UnitSphere::convertSphericalToCartesian(Pllmin, Pxmin); Point3 Pxmax; util::UnitSphere::convertSphericalToCartesian(Pllmax, Pxmax); periodic_x_ = points_equal(Pxmin, Pxmax); } void Structured::print(std::ostream& os) const { os << "Structured(Name:" << name() << ")"; } std::string Structured::type() const { return static_type(); } Grid::Config Structured::meshgenerator() const { return Config("type", "structured"); } Grid::Config Structured::partitioner() const { Config config; if (reduced()) { config.set("type", "equal_regions"); } else { config.set("type", "checkerboard"); } return config; } void Structured::hash(eckit::Hash& h) const { double multiplier = projection().units() == "meters" ? 1e2 : 1e8; auto add_double = [&](const double& x) { h.add(std::round(x * multiplier)); }; auto add_double_vector = [&](const std::vector& xvec) { for (auto& x : xvec) { add_double(x); } }; auto add_long = [&](const idx_t& x) { h.add(long(x)); }; auto add_long_vector = [&](const std::vector& xvec) { for (auto& x : xvec) { add_long(x); } }; add_double_vector(y()); add_long_vector(nx()); add_double_vector(xmin_); add_double_vector(dx_); // also add projection information projection().hash(h); // also add domain information, even though already encoded in grid. domain().hash(h); } RectangularLonLatDomain Structured::lonlatBoundingBox() const { return projection_ ? projection_.lonlatBoundingBox(computeDomain()) : domain(); } Grid::Spec Structured::spec() const { Grid::Spec grid_spec; if (name() == "structured") { grid_spec.set("type", type()); grid_spec.set("xspace", xspace().spec()); grid_spec.set("yspace", yspace().spec()); } else { grid_spec.set("name", name()); } grid_spec.set("domain", domain().spec()); grid_spec.set("projection", projection().spec()); return grid_spec; } // -------------------------------------------------------------------- #if 1 namespace { // anonymous static class structured : public GridBuilder { using Implementation = atlas::Grid::Implementation; using Config = Grid::Config; using XSpace = StructuredGrid::XSpace; public: structured(): GridBuilder("structured") {} void print(std::ostream& os) const override { os << std::left << std::setw(20) << " " << "Structured grid"; } const Implementation* create(const std::string& /* name */, const Config&) const override { throw_NotImplemented("Cannot create structured grid from name", Here()); } const Implementation* create(const Config& config) const override { Projection projection; Spacing yspace; Domain domain; Config config_proj; if (config.get("projection", config_proj)) { projection = Projection(config_proj); } Config config_domain; if (config.get("domain", config_domain)) { domain = Domain(config_domain); } Config config_yspace; if (not config.get("yspace", config_yspace)) { throw_Exception("yspace missing in configuration", Here()); } yspace = Spacing(config_yspace); XSpace xspace; Config config_xspace; std::vector config_xspace_list; if (config.get("xspace[]", config_xspace_list)) { xspace = XSpace(config_xspace_list); } else if (config.get("xspace", config_xspace)) { xspace = XSpace(config_xspace); } else { throw_Exception("xspace missing in configuration", Here()); } return new StructuredGrid::grid_t(xspace, yspace, projection, domain); } } structured_; } // anonymous namespace #endif // -------------------------------------------------------------------- extern "C" { idx_t atlas__grid__Structured__ny(Structured* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_StructuredGrid"); return This->ny(); } idx_t atlas__grid__Structured__nx(Structured* This, idx_t jlat) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_StructuredGrid"); return This->nx(jlat); } gidx_t atlas__grid__Structured__index(Structured* This, idx_t i, idx_t j) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_StructuredGrid"); return This->index(i, j); } void atlas__grid__Structured__index2ij(Structured* This, gidx_t gidx, idx_t& i, idx_t& j) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_StructuredGrid"); This->index2ij(gidx, i, j); } void atlas__grid__Structured__nx_array(Structured* This, const idx_t*& nx_array, idx_t& size) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_StructuredGrid"); nx_array = This->nx().data(); size = idx_t(This->nx().size()); } idx_t atlas__grid__Structured__nxmax(Structured* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_StructuredGrid"); return This->nxmax(); } idx_t atlas__grid__Structured__nxmin(Structured* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_StructuredGrid"); return This->nxmin(); } double atlas__grid__Structured__y(Structured* This, idx_t j) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_StructuredGrid"); return This->y(j); } double atlas__grid__Structured__x(Structured* This, idx_t i, idx_t j) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_StructuredGrid"); return This->x(i, j); } void atlas__grid__Structured__xy(Structured* This, idx_t i, idx_t j, double crd[]) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_StructuredGrid"); This->xy(i, j, crd); } void atlas__grid__Structured__lonlat(Structured* This, idx_t i, idx_t j, double crd[]) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_StructuredGrid"); This->lonlat(i, j, crd); } void atlas__grid__Structured__y_array(Structured* This, const double*& y_array, idx_t& size) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_StructuredGrid"); y_array = This->y().data(); size = idx_t(This->y().size()); } int atlas__grid__Structured__reduced(Structured* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_StructuredGrid"); return This->reduced(); } const Structured* atlas__grid__Structured(const char* identifier) { const Structured* grid = dynamic_cast(Grid::create(std::string(identifier))); ATLAS_ASSERT(grid != nullptr); return grid; } const Structured* atlas__grid__Structured__config(util::Config* conf) { ATLAS_ASSERT(conf != nullptr); const Structured* grid = dynamic_cast(Grid::create(*conf)); ATLAS_ASSERT(grid != nullptr); return grid; } void atlas__grid__Structured__delete(Structured* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_StructuredGrid"); delete This; } const Structured* atlas__grid__regular__RegularGaussian(long N) { std::string gridname = "F"+std::to_string(N); return atlas__grid__Structured(gridname.c_str()); } const Structured* atlas__grid__regular__RegularLonLat(long nlon, long nlat) { std::string gridname = "L"+std::to_string(nlon)+"x"+std::to_string(nlat); return atlas__grid__Structured(gridname.c_str()); } const Structured* atlas__grid__regular__ShiftedLonLat(long nlon, long nlat) { std::string gridname = "S"+std::to_string(nlon)+"x"+std::to_string(nlat); return atlas__grid__Structured(gridname.c_str()); } const Structured* atlas__grid__regular__ShiftedLon(long nlon, long nlat) { std::string gridname = "Slon"+std::to_string(nlon)+"x"+std::to_string(nlat); return atlas__grid__Structured(gridname.c_str()); } const Structured* atlas__grid__regular__ShiftedLat(long nlon, long nlat) { std::string gridname = "Slat"+std::to_string(nlon)+"x"+std::to_string(nlat); return atlas__grid__Structured(gridname.c_str()); } idx_t atlas__grid__Gaussian__N(Structured* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_StructuredGrid"); GaussianGrid gaussian(This); ATLAS_ASSERT(gaussian, "This grid is not a Gaussian grid"); return gaussian.N(); } } namespace { GridFactoryBuilder __register_Structured(Structured::static_type()); } } // namespace grid } // namespace detail } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/grid/Unstructured.h0000664000175000017500000002027415160212070022757 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ /// @file Unstructured.h /// @author Willem Deconinck /// @author Tiago Quintino /// @author Pedro Maciel /// @date January 2015 #pragma once #include #include #include #include "atlas/grid/detail/grid/Grid.h" #include "atlas/mdspan.h" #include "atlas/runtime/Exception.h" #include "atlas/util/Point.h" namespace atlas { namespace grid { namespace detail { namespace grid { class Unstructured : public Grid { private: struct ComputePointXY { void update_value(idx_t n) {} void compute_value(idx_t n, PointXY& point) { grid_.xy(n, point.data()); } const PointXY& get_reference(idx_t n) const { return grid_.xy(n); } ComputePointXY(const Unstructured& grid): grid_(grid) {} const Unstructured& grid_; }; struct ComputePointLonLat { void update_value(idx_t n) { if (n < size_) { grid_.lonlat(n, point_.data()); } } void compute_value(idx_t n, PointLonLat& point) { grid_.lonlat(n, point.data()); } const PointLonLat& get_reference(idx_t n) const { return point_; } ComputePointLonLat(const Unstructured& grid): grid_(grid), size_(grid_.size()) {} const Unstructured& grid_; idx_t size_; PointLonLat point_; }; template class UnstructuredIterator : public Base { public: UnstructuredIterator(const Unstructured& grid, bool begin = true): grid_(grid), size_(static_cast(grid_.points_->size())), n_(begin ? 0 : size_), point_computer_{grid_} { point_computer_.update_value(n_); } virtual bool next(typename Base::value_type& point) { if (n_ < size_) { point_computer_.compute_value(n_, point); ++n_; return true; } else { return false; } } virtual typename Base::reference operator*() const { return point_computer_.get_reference(n_); } virtual const Base& operator++() { ++n_; point_computer_.update_value(n_); return *this; } virtual const Base& operator+=(typename Base::difference_type distance) { n_ += distance; point_computer_.update_value(n_); return *this; } virtual bool operator==(const Base& other) const { return n_ == static_cast(other).n_; } virtual bool operator!=(const Base& other) const { return n_ != static_cast(other).n_; } virtual typename Base::difference_type distance(const Base& other) const { const auto& _other = static_cast(other); return _other.n_ - n_; } virtual std::unique_ptr clone() const { auto result = new UnstructuredIterator(grid_, false); result->n_ = n_; result->point_computer_.update_value(n_); return std::unique_ptr(result); } protected: const Unstructured& grid_; idx_t size_; idx_t n_; ComputePoint point_computer_; }; public: using IteratorXY = UnstructuredIterator; using IteratorLonLat = UnstructuredIterator; public: // methods static std::string static_type() { return "unstructured"; } virtual std::string name() const override; virtual std::string type() const override { return static_type(); } /// Constructor converting any Grid with domain to an unstructured grid Unstructured(const Grid&, Domain); /// Constructor taking a list of parameters Unstructured(const Config&); /// Constructor taking a list of points (takes ownership) Unstructured(std::vector* pts); /// Constructor taking a list of points (takes ownership) Unstructured(std::vector&& pts); /// Constructor taking a list of points (makes copy) Unstructured(const std::vector& pts); /// Constructor taking a uid and list of points (makes copy) Unstructured(const std::string& uid, size_t N, const double x[], const double y[], size_t xstride = 1, size_t ystride = 1); /// Constructor taking a list of points (makes copy) Unstructured(size_t N, const double x[], const double y[], size_t xstride = 1, size_t ystride = 1) : Unstructured("", N, x, y, xstride, ystride) {} /// Constructor taking a list of points (makes copy) Unstructured(size_t N, const double xy[]); /// Constructor taking a mdspan (makes copy) /// First dimension is number of points, second dimension is coordinate. First X (lon), then Y (lat) template Unstructured(mdspan xy): Unstructured(xy.extent(0), &xy[std::array{0,0}], &xy[std::array{0,1}], xy.stride(1), xy.stride(1)) {} /// Constructor taking a mdspan (makes copy) /// First dimension is number of points, second dimension is coordinate. First X (lon), then Y (lat) template Unstructured(const std::string& uid, mdspan xy): Unstructured(uid, xy.extent(0), &xy[std::array{0,0}], &xy[std::array{0,1}], xy.stride(1), xy.stride(1)) {} /// Constructor from initializer list Unstructured(std::initializer_list); virtual ~Unstructured() override; virtual idx_t size() const override; virtual Spec spec() const override; virtual uid_t uid() const override { if (! uid_.empty()) { return uid_; } return Grid::uid(); } const PointXY& xy(idx_t n) const { return (*points_)[n]; } PointLonLat lonlat(idx_t n) const { return projection_.lonlat((*points_)[n]); } void xy(idx_t n, double crd[]) const { PointXY& p = (*points_)[n]; crd[0] = p[0]; crd[1] = p[1]; } void lonlat(idx_t n, double crd[]) const { xy(n, crd); projection_.xy2lonlat(crd); } virtual std::unique_ptr xy_begin() const override { return std::make_unique(*this); } virtual std::unique_ptr xy_end() const override { return std::make_unique(*this, false); } virtual std::unique_ptr lonlat_begin() const override { return std::make_unique(*this); } virtual std::unique_ptr lonlat_end() const override { return std::make_unique(*this, false); } Config meshgenerator() const override; Config partitioner() const override; size_t footprint() const override; private: // methods virtual void print(std::ostream&) const override; /// Hash of the lonlat array virtual void hash(eckit::Hash&) const override; /// @return parallel/meridian limits containing the grid virtual RectangularLonLatDomain lonlatBoundingBox() const override; protected: /// Storage of coordinate points std::unique_ptr> points_; /// Cache for the shortName mutable std::string shortName_; /// Cache for the spec since may be quite heavy to compute mutable std::unique_ptr cached_spec_; uid_t uid_; }; extern "C" { const Unstructured* atlas__grid__Unstructured__points(const double lonlat[], int shapef[], int stridesf[]); const Unstructured* atlas__grid__Unstructured__config(util::Config* conf); } } // namespace grid } // namespace detail } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/grid/Gaussian.cc0000664000175000017500000002141115160212070022152 0ustar alastairalastair/* * (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 "Gaussian.h" #include #include #include #include #include "eckit/utils/Translator.h" #include "atlas/grid/detail/grid/GridBuilder.h" #include "atlas/grid/detail/pl/classic_gaussian/PointsPerLatitude.h" namespace atlas { namespace grid { namespace { // anonymous static eckit::Translator to_int; static Projection projection(const Grid::Config& grid) { Grid::Config config; if (grid.get("projection", config)) { return Projection(config); } return Projection(); } static Domain domain(const Grid::Config& grid) { Grid::Config config; if (grid.get("domain", config)) { return Domain(config); } return Domain(); } static Spacing yspace(const Grid::Config& grid) { long N; grid.get("N", N); Grid::Config config; config.set("type", "gaussian"); config.set("start", 90.0); config.set("end", -90.0); config.set("N", 2 * N); return Spacing(config); } template static StructuredGrid::XSpace xspace(const vector_t& nx) { return StructuredGrid::XSpace({0., 360.}, nx, false); } //--------------------------------------------------------------------------------------------------------------------- static class classic_gaussian : public GridBuilder { public: classic_gaussian(): GridBuilder("classic_gaussian", {"^[Nn]([0-9]+)$"}, {"N"}) {} void print(std::ostream& os) const override { os << std::left << std::setw(20) << "N" << "Classic Gaussian grid"; } const Grid::Implementation* create(const std::string& name, const Grid::Config& config) const override { int id; std::vector matches; if (match(name, matches, id)) { util::Config grid(config); int N = to_int(matches[0]); grid.set("type", type()); grid.set("N", N); return create(grid); } return nullptr; } const Grid::Implementation* create(const Grid::Config& config) const override { long N; config.get("N", N); std::vector nx(2 * N); detail::pl::classic_gaussian::points_per_latitude_npole_spole(N, nx.data()); return new StructuredGrid::grid_t("N" + std::to_string(N), xspace(nx), yspace(config), projection(config), domain(config)); } void force_link() {} } classic_gaussian_; //--------------------------------------------------------------------------------------------------------------------- static class octahedral_gaussian : GridBuilder { public: octahedral_gaussian(): GridBuilder("octahedral_gaussian", {"^[Oo]([0-9]+)$"}, {"O"}) {} void print(std::ostream& os) const override { os << std::left << std::setw(20) << "O" << "Octahedral Gaussian grid"; } const Grid::Implementation* create(const std::string& name, const Grid::Config& config) const override { int id; std::vector matches; if (match(name, matches, id)) { util::Config grid(config); int N = to_int(matches[0]); grid.set("type", type()); grid.set("N", N); return create(grid); } return nullptr; } const Grid::Implementation* create(const Grid::Config& config) const override { long N; config.get("N", N); long start = 20; config.get("nx[0]", start); std::vector nx(2 * N); for (long j = 0; j < N; ++j) { nx[j] = start + 4 * j; nx[2 * N - 1 - j] = nx[j]; } return new StructuredGrid::grid_t("O" + std::to_string(N), xspace(nx), yspace(config), projection(config), domain(config)); } void force_link() {} } octahedral_gaussian_; //--------------------------------------------------------------------------------------------------------------------- static class regular_gaussian : GridBuilder { public: regular_gaussian(): GridBuilder("regular_gaussian", {"^[Ff]([0-9]+)$"}, {"F"}) {} void print(std::ostream& os) const override { os << std::left << std::setw(20) << "F" << "Regular Gaussian grid"; } const Grid::Implementation* create(const std::string& name, const Grid::Config& config) const override { int id; std::vector matches; if (match(name, matches, id)) { util::Config grid(config); int N = to_int(matches[0]); grid.set("type", type()); grid.set("N", N); return create(grid); } return nullptr; } const Grid::Implementation* create(const Grid::Config& config) const override { long N; config.get("N", N); std::vector nx(2 * N, 4 * N); return new StructuredGrid::grid_t("F" + std::to_string(N), xspace(nx), yspace(config), projection(config), domain(config)); } void force_link() {} } regular_gaussian_; //--------------------------------------------------------------------------------------------------------------------- } // anonymous namespace namespace detail { namespace grid { void force_link_Gaussian() { regular_gaussian_.force_link(); classic_gaussian_.force_link(); octahedral_gaussian_.force_link(); } StructuredGrid::grid_t* reduced_gaussian(const std::vector& nx) { Grid::Config yspace; yspace.set("type", "gaussian"); yspace.set("start", 90.0); yspace.set("end", -90.0); yspace.set("N", nx.size()); return new StructuredGrid::grid_t(xspace(nx), Spacing(yspace), Projection(), Domain()); } StructuredGrid::grid_t* reduced_gaussian(const std::vector& nx, const Domain& domain) { Grid::Config yspace; yspace.set("type", "gaussian"); yspace.set("start", 90.0); yspace.set("end", -90.0); yspace.set("N", nx.size()); return new StructuredGrid::grid_t(xspace(nx), Spacing(yspace), Projection(), domain); } StructuredGrid::grid_t* reduced_gaussian(const std::vector& nx, const Projection& projection) { Grid::Config yspace; yspace.set("type", "gaussian"); yspace.set("start", 90.0); yspace.set("end", -90.0); yspace.set("N", nx.size()); return new StructuredGrid::grid_t(xspace(nx), Spacing(yspace), projection, Domain()); } StructuredGrid::grid_t* reduced_gaussian(const std::vector& nx) { Grid::Config yspace; yspace.set("type", "gaussian"); yspace.set("start", 90.0); yspace.set("end", -90.0); yspace.set("N", nx.size()); return new StructuredGrid::grid_t(xspace(nx), Spacing(yspace), Projection(), Domain()); } StructuredGrid::grid_t* reduced_gaussian(const std::vector& nx, const Domain& domain) { Grid::Config yspace; yspace.set("type", "gaussian"); yspace.set("start", 90.0); yspace.set("end", -90.0); yspace.set("N", nx.size()); return new StructuredGrid::grid_t(xspace(nx), Spacing(yspace), Projection(), domain); } StructuredGrid::grid_t* reduced_gaussian(const std::vector& nx, const Projection& projection) { Grid::Config yspace; yspace.set("type", "gaussian"); yspace.set("start", 90.0); yspace.set("end", -90.0); yspace.set("N", nx.size()); return new StructuredGrid::grid_t(xspace(nx), Spacing(yspace), projection, Domain()); } template inline std::vector idx_vector(Int nx, idx_t ny) { std::vector _nx(ny); for (idx_t j = 0; j < ny; ++j) { _nx[j] = nx[j]; } return _nx; } extern "C" { StructuredGrid::grid_t* atlas__grid__reduced__ReducedGaussian_int(int nx[], long ny) { return reduced_gaussian(idx_vector(nx, ny)); } StructuredGrid::grid_t* atlas__grid__reduced__ReducedGaussian_long(long nx[], long ny) { return reduced_gaussian(idx_vector(nx, ny)); } StructuredGrid::grid_t* atlas__grid__reduced__ReducedGaussian_int_projection( int nx[], long ny, const Projection::Implementation* projection) { return reduced_gaussian(idx_vector(nx, ny), Projection(projection)); } StructuredGrid::grid_t* atlas__grid__reduced__ReducedGaussian_long_projection( long nx[], long ny, const Projection::Implementation* projection) { return reduced_gaussian(idx_vector(nx, ny), Projection(projection)); } } } // namespace grid } // namespace detail } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/grid/Gaussian.h0000664000175000017500000000211715160212070022016 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include #include "atlas/grid/StructuredGrid.h" namespace atlas { namespace grid { namespace detail { namespace grid { StructuredGrid::grid_t* reduced_gaussian(const std::vector& nx); StructuredGrid::grid_t* reduced_gaussian(const std::vector& nx, const Projection&); StructuredGrid::grid_t* reduced_gaussian(const std::vector& nx, const Domain&); StructuredGrid::grid_t* reduced_gaussian(const std::vector& nx); StructuredGrid::grid_t* reduced_gaussian(const std::vector& nx, const Projection&); StructuredGrid::grid_t* reduced_gaussian(const std::vector& nx, const Domain&); } // namespace grid } // namespace detail } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/grid/Grid.cc0000664000175000017500000001330315160212070021266 0ustar alastairalastair/* * (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 "Grid.h" #include #include "eckit/utils/MD5.h" #include "atlas/domain/detail/Domain.h" #include "atlas/grid.h" #include "atlas/grid/detail/grid/CubedSphere.h" #include "atlas/grid/detail/grid/GridBuilder.h" #include "atlas/grid/detail/grid/Structured.h" #include "atlas/grid/detail/grid/Unstructured.h" #include "atlas/mesh/Mesh.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Log.h" namespace atlas { namespace grid { namespace detail { namespace grid { static void checkSizeOfPoint() { // compile time check support C++11 static_assert(sizeof(PointXY) == 2 * sizeof(double), "Grid requires size of Point to be 2*double"); // runtime check ATLAS_ASSERT(sizeof(PointXY) == 2 * sizeof(double)); } const Grid* Grid::create(const Config& config) { std::string name; if (config.get("name", name)) { return create(name, config); } std::string type; if (config.get("type", type)) { const GridBuilder::Registry& registry = GridBuilder::typeRegistry(); if (registry.find(type) != registry.end()) { const GridBuilder& gc = *registry.at(type); return gc.create(config); } } if (name.size()) { Log::info() << "name provided: " << name << std::endl; } if (type.size()) { Log::info() << "type provided: " << type << std::endl; } if (name.empty() && type.empty()) { throw_Exception("no name or type in configuration", Here()); } else { throw_Exception("name or type in configuration don't exist", Here()); } } const Grid* Grid::create(const std::string& name) { return create(name, util::NoConfig()); } const Grid* Grid::create(const std::string& name, const Grid::Config& config) { for (const auto& [key, builder]: GridBuilder::nameRegistry()) { const Grid* grid = builder->create(name, config); if (grid) { return grid; } } // Throw exception std::ostringstream log; log << "Could not construct Grid from the name \"" << name << "\"\n"; log << "Accepted names are: \n"; for (const auto& [key, grid_builder]: GridBuilder::typeRegistry()) { for( auto& grid_name: grid_builder->names()) { log << " - " << grid_name << "\n"; } } throw_Exception(log.str()); } const Grid* Grid::create(const Grid& grid, const Domain& domain) { if (grid.type() == "cubedsphere") { const CubedSphere& cs = dynamic_cast(grid); return new CubedSphere(cs.name(), cs.N(), cs.projection(), cs.stagger()); } else if (grid.type() == "structured") { const Structured& g = dynamic_cast(grid); return new Structured(g.name(), g.xspace(), g.yspace(), g.projection(), domain); } else { return new Unstructured(grid, domain); } } Grid::Grid() { checkSizeOfPoint(); } Grid::~Grid() { while (grid_observers_.size()) { GridObserver* o = grid_observers_.back(); o->onGridDestruction(*this); o->unregisterGrid(*this); // will also delete observer from mesh } } Grid::uid_t Grid::uid() const { if (uid_.empty()) { uid_ = hash(); } return uid_; } std::string Grid::hash() const { if (hash_.empty()) { eckit::MD5 md5; hash(md5); hash_ = md5.digest(); } return hash_; } void Grid::attachObserver(GridObserver& observer) const { if (std::find(grid_observers_.begin(), grid_observers_.end(), &observer) == grid_observers_.end()) { grid_observers_.push_back(&observer); } } void Grid::detachObserver(GridObserver& observer) const { grid_observers_.erase(std::remove(grid_observers_.begin(), grid_observers_.end(), &observer), grid_observers_.end()); } Grid::Config Grid::meshgenerator() const { ATLAS_NOTIMPLEMENTED; } Grid::Config Grid::partitioner() const { ATLAS_NOTIMPLEMENTED; } idx_t atlas__grid__Grid__size(Grid* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Grid"); return This->size(); } Grid::Spec* atlas__grid__Grid__spec(Grid* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Grid"); return new Grid::Spec(This->spec()); } void atlas__grid__Grid__uid(const Grid* This, char*& uid, int& size) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Grid"); eckit::MD5 md5; This->hash(md5); std::string s = This->uid(); size = static_cast(s.size()); uid = new char[size + 1]; std::strncpy(uid, s.c_str(), size + 1); } void atlas__grid__Grid__name(const Grid* This, char*& name, int& size) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Grid"); std::string s = This->name(); size = static_cast(s.size()); name = new char[size + 1]; std::strncpy(name, s.c_str(), size + 1); } Grid::Domain::Implementation* atlas__grid__Grid__lonlat_bounding_box(const Grid* This) { Grid::Domain::Implementation* lonlatboundingbox; { auto handle = This->lonlatBoundingBox(); lonlatboundingbox = handle.get(); lonlatboundingbox->attach(); } lonlatboundingbox->detach(); return lonlatboundingbox; } } // namespace grid } // namespace detail } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/grid/RegionalVariableResolution.h0000664000175000017500000000033615160212070025537 0ustar alastairalastair/** * (C) Crown copyright 2021, Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ #include "atlas/grid.h" atlas-0.46.0/src/atlas/grid/detail/grid/CubedSphere2.h0000664000175000017500000001357315160212070022527 0ustar alastairalastair#pragma once #include "atlas/grid/detail/grid/Grid.h" #include "atlas/runtime/Exception.h" #include "atlas/util/Config.h" #include "atlas/util/Point.h" namespace atlas { namespace grid { namespace detail { namespace grid { class CubedSphere2 : public Grid { private: // Get the lonlat and return as PointLonLat object struct ComputePointXY { ComputePointXY(const CubedSphere2& grid): grid_(grid) {} void operator()(idx_t n, PointXY& point) { grid_.xy(n, point); } const CubedSphere2& grid_; }; // Get the lonlat and return as PointLonLat object struct ComputePointLonLat { ComputePointLonLat(const CubedSphere2& grid): grid_(grid) {} void operator()(idx_t n, PointLonLat& point) { grid_.lonlat(n, point); } const CubedSphere2& grid_; }; template class CubedSphere2Iterator : public Base { public: CubedSphere2Iterator(const CubedSphere2& grid, bool begin = true): grid_(grid), size_(grid.size()), n_(begin ? 0 : size_), compute_point{grid_} { if (n_ < size_) { compute_point(n_, point_); } } // Return the point and move iterator to the next location bool next(typename Base::value_type& point) { if (n_ < size_) { compute_point(n_, point); ++n_; return true; } return false; } // * operator const typename Base::reference operator*() const {return point_; } // ++ operator, move to next element and return point const Base& operator++() { ++n_; if (n_ < size_) { compute_point(n_, point_); } return *this; } // += operator, move some distance through the iterator and return point const Base& operator+=(typename Base::difference_type distance) { n_ += distance; if (n_ >= 0 && n_ < size_) { compute_point(n_, point_); } return *this; } // == operator, check two iterators are on the same index bool operator==(const Base& other) const { return n_ == static_cast(other).n_; } // != operator, check two iterators are not on the same index bool operator!=(const Base& other) const { return n_ != static_cast(other).n_; } // Return the number of points between two iterators typename Base::difference_type distance(const Base& other) const { return static_cast(other).n_ - n_; } // Clone the iterator in its current position std::unique_ptr clone() const { auto result = new CubedSphere2Iterator(grid_, false); result->n_ = n_; result->compute_point(n_, result->point_); return std::unique_ptr(result); } const CubedSphere2& grid_; idx_t size_; idx_t n_; typename Base::value_type point_; ComputePoint compute_point; }; public: using IteratorXY = CubedSphere2Iterator; using IteratorLonLat = CubedSphere2Iterator; using Spec = atlas::util::Config; CubedSphere2(idx_t resolution); CubedSphere2(idx_t resolution, Projection projection); std::string name() const override; std::string type() const override; static std::string static_type(); idx_t N() const {return N_;} void hash(eckit::Hash&) const override; RectangularLonLatDomain lonlatBoundingBox() const override; idx_t size() const override; Spec spec() const override; virtual std::unique_ptr xy_begin() const override { return std::make_unique(*this); } virtual std::unique_ptr xy_end() const override { return std::make_unique(*this, false); } virtual std::unique_ptr lonlat_begin() const override { return std::make_unique(*this); } virtual std::unique_ptr lonlat_end() const override { return std::make_unique(*this, false); } void xy(idx_t n, Point2& point) const; Point2 xy(idx_t n) const; void lonlat(idx_t n, Point2& point) const; Point2 lonlat(idx_t n) const; protected: void print(std::ostream&) const override; private: using CSIndices = std::array; CSIndices get_cs_indices(gidx_t n) const; PointXY ij_to_tangent_coord(idx_t i, idx_t j) const; PointXYZ tangent_to_xyz_coord(const PointXY& tan_coord, idx_t tile) const; protected: // (N_ * N_) = number of cells on a tile idx_t N_; // Number of tiles static constexpr idx_t nTiles_ = 6; private: using Matrix = std::array, 3>; /* // currently unused std::array lfric_rotations_ = {{ {{ {0, 1, 0}, {0, 0, -1}, {1, 0, 0} }}, {{ {-1, 0, 0}, {0, 0, -1}, {0, 1, 0} }}, {{ {0, -1, 0}, {0, 0, -1}, {-1, 0, 0} }}, {{ {1, 0, 0}, {0, 0, -1}, {0, -1, 0} }}, {{ {-1, 0, 0}, {0, 1, 0}, {0, 0, 1} }}, {{ {-1, 0, 0}, {0, -1, 0}, {0, 0, -1} }} }}; */ std::array lfric_rotations_transposed_ = {{ {{ {0, 0, 1}, {1, 0, 0}, {0, -1, 0} }}, {{ {-1, 0, 0}, {0, 0, 1}, {0, -1, 0} }}, {{ {0, 0, -1}, {-1, 0, 0}, {0, -1, 0} }}, {{ {1, 0, 0}, {0, 0, -1}, {0, -1, 0} }}, {{ {-1, 0, 0}, {0, 1, 0}, {0, 0, 1} }}, {{ {-1, 0, 0}, {0, -1, 0}, {0, 0, -1} }} }}; }; } // namespace grid } // namespace detail } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/grid/GridBuilder.cc0000664000175000017500000001675215160212070022610 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ // file deepcode ignore CppMemoryLeak: static pointers for global registry are OK and will be cleaned up at end #include "GridBuilder.h" #include #include #include "eckit/thread/AutoLock.h" #include "eckit/thread/Mutex.h" #include "eckit/utils/Translator.h" #include "atlas/grid/detail/grid/GridFactory.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Log.h" #include "atlas/util/Config.h" namespace atlas { namespace grid { namespace { size_t regex_count_parens(const std::string& string) { size_t out = 0; bool last_was_backslash = 0; for (const char* step = string.c_str(); *step != '\0'; step++) { if (*step == '\\' && !last_was_backslash) { last_was_backslash = true; continue; } if (*step == ')' && !last_was_backslash) { out++; } last_was_backslash = false; } return out; } int regex_match_impl(const std::string& string, const std::string& regex, std::vector& substr, bool use_substr, bool use_case) { regex_t re; size_t matchcount = 0; if (use_substr) { matchcount = regex_count_parens(regex); } std::vector result(matchcount + 1); int compiled_ok = !regcomp(&re, regex.c_str(), REG_EXTENDED + (use_case ? 0 : REG_ICASE) + (use_substr ? 0 : REG_NOSUB)); if (!compiled_ok) { Log::error() << "This regular expression didn't compile: \"" << regex << "\"" << std::endl; } ATLAS_ASSERT(compiled_ok); int found = !regexec(&re, string.c_str(), matchcount + 1, result.data(), 0); if (found && use_substr) { substr.resize(matchcount); // match zero is the whole string; ignore it. for (size_t i = 0; i < matchcount; i++) { if (result[i + 1].rm_eo > 0) { // GNU peculiarity: match-to-empty marked with -1. size_t length_of_match = result[i + 1].rm_eo - result[i + 1].rm_so; substr[i] = std::string(&string[result[i + 1].rm_so], length_of_match); } } } regfree(&re); return found; } class Regex { public: Regex(const std::string& regex, bool use_case = true): regex_(regex), use_case_(use_case) {} /* // unused bool match( const std::string& string ) const { std::vector substr; return regex_match_impl( string, regex_, substr, false, use_case_ ); } */ bool match(const std::string& string, std::vector& substr) const { return regex_match_impl(string, regex_, substr, true, use_case_); } /* // unused operator std::string() const { return regex_; } */ private: std::string regex_; bool use_case_; }; 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; static void init() { local_mutex = new eckit::Mutex(); named_grids = new GridBuilder::Registry(); typed_grids = new GridBuilder::Registry(); } } // anonymous namespace //--------------------------------------------------------------------------------------------------------------------- namespace detail { namespace grid { void force_link_CubedSphere(); void force_link_CubedSphere2(); void force_link_Gaussian(); void force_link_LonLat(); void force_link_Regional(); void force_link_Regional_var_resolution(); } // namespace grid } // namespace detail const GridBuilder::Registry& GridBuilder::nameRegistry() { detail::grid::force_link_CubedSphere(); detail::grid::force_link_CubedSphere2(); detail::grid::force_link_Gaussian(); detail::grid::force_link_LonLat(); detail::grid::force_link_Regional(); detail::grid::force_link_Regional_var_resolution(); return *named_grids; } const GridBuilder::Registry& GridBuilder::typeRegistry() { return *typed_grids; } GridBuilder::GridBuilder(const std::string& type): names_(), type_(type) { pthread_once(&once, init); eckit::AutoLock lock(local_mutex); ATLAS_ASSERT(typed_grids->find(type_) == typed_grids->end()); (*typed_grids)[type] = this; } GridBuilder::GridBuilder(const std::string& type, const std::vector& regexes, const std::vector& names): names_(regexes), pretty_names_(names), type_(type) { pthread_once(&once, init); eckit::AutoLock lock(local_mutex); for (const std::string& name : names_) { ATLAS_ASSERT(named_grids->find(name) == named_grids->end()); (*named_grids)[name] = this; } ATLAS_ASSERT(typed_grids->find(type_) == typed_grids->end()); (*typed_grids)[type] = this; } GridBuilder::~GridBuilder() { pthread_once(&once, init); eckit::AutoLock lock(local_mutex); for (const std::string& name : names_) { ATLAS_ASSERT(named_grids->find(name) != named_grids->end()); (*named_grids).erase(name); } if (not type_.empty()) { ATLAS_ASSERT(typed_grids->find(type_) != typed_grids->end()); (*typed_grids).erase(type_); } } void GridBuilder::registerNamedGrid(const std::string& name) { pthread_once(&once, init); eckit::AutoLock lock(local_mutex); std::string regex = "^"+name+"$"; ATLAS_ASSERT(named_grids->find(regex) == named_grids->end()); (*named_grids)[regex] = this; names_.emplace_back(regex); pretty_names_.emplace_back(name); } const Grid::Implementation* GridBuilder::create(const Grid::Config& config) const { //eckit::Factory& fact = eckit::Factory::instance(); std::string name; if (config.get("name", name)) { // ignore any further configuration return create(name); } std::string type; if (config.get("type", type) && GridFactory::has(type)) { return GridFactory::build(type, config); } if (name.size()) { Log::error() << "name provided: " << name << std::endl; } if (type.size()) { Log::error() << "type provided: " << type << std::endl; } if (name.empty() && type.empty()) { throw_Exception("no name or type in configuration", Here()); } else { throw_Exception("name or type in configuration don't exist", Here()); } } bool GridBuilder::match(const std::string& string, std::vector& matches, int& id) const { id = 0; for (const std::string& name : names_) { if (Regex(name).match(string, matches)) { return true; } ++id; } return false; } const std::string& GridBuilder::type() const { return type_; } const std::vector& GridBuilder::names() const { return pretty_names_; } const std::vector& GridBuilder::regexes() const { return names_; } std::ostream& operator<<(std::ostream& os, const GridBuilder& g) { g.print(os); return os; } //--------------------------------------------------------------------------------------------------------------------- } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/grid/CubedSphere.h0000664000175000017500000003754015160212070022445 0ustar alastairalastair/* * (C) Copyright 2020 UCAR * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ #pragma once #include #include #include #include #include #include #include "eckit/types/Types.h" #include "atlas/grid/Spacing.h" #include "atlas/grid/Tiles.h" #include "atlas/grid/detail/grid/Grid.h" #include "atlas/library/config.h" #include "atlas/projection/detail/CubedSphereProjectionBase.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Log.h" #include "atlas/util/CoordinateEnums.h" #include "atlas/util/Object.h" #include "atlas/util/ObjectHandle.h" #include "atlas/util/Point.h" namespace atlas { namespace grid { namespace detail { namespace grid { /** * @brief CubedSphere Grid * * This class is a base class for all grids that can be described as * a cubed sphere. * * For more detail on this implementation see atlas/grid/CubedSphereGrid.h */ using atlas::projection::detail::CubedSphereProjectionBase; class CubedSphere : public Grid { private: // Get the position in the xy plane and return as PointXY object struct ComputePointXY { ComputePointXY(const CubedSphere& grid): grid_(grid) {} void operator()(idx_t i, idx_t j, idx_t t, PointXY& point) { grid_.xy(i, j, t, point.data()); } const CubedSphere& grid_; }; // Get the lonlat and return as PointLonLat object struct ComputePointLonLat { ComputePointLonLat(const CubedSphere& grid): grid_(grid) {} void operator()(idx_t i, idx_t j, idx_t t, PointLonLat& point) { grid_.lonlat(i, j, t, point.data()); } const CubedSphere& grid_; }; class PointTIJ : public std::array { public: using std::array::array; idx_t t() const { return data()[0]; } idx_t i() const { return data()[1]; } idx_t j() const { return data()[2]; } idx_t& t() { return data()[0]; } idx_t& i() { return data()[1]; } idx_t& j() { return data()[2]; } }; struct ComputePointTIJ { ComputePointTIJ(const CubedSphere& grid): grid_(grid) {} void operator()(idx_t i, idx_t j, idx_t t, PointTIJ& point) { point.t() = t; point.i() = i; point.j() = j; } const CubedSphere& grid_; }; // ----------------------------------------------------------------------------------------------- template class CubedSphereIterator : public Base { public: // Create an iterator and return the first or last point. If begin is true it starts at the // beginning of the iterator, otherwise at the end. Class is templated and point can be xy or // lonlat. CubedSphereIterator(const CubedSphere& grid, bool begin = true): grid_(grid), i_(begin ? 0 : grid_.N()), j_(begin ? 0 : grid_.N()), t_(begin ? 0 : 5), size_(grid_.size()), n_(begin ? 0 : size_), compute_point{grid_} { // Check that point lies in grid and if so return the xy/lonlat if (grid_.inGrid(i_, j_, t_)) { compute_point(i_, j_, t_, point_); } } // Return the point and move iterator to the next location virtual bool next(typename Base::value_type& point) { if (n_ != size_) { compute_point(i_, j_, t_, point); std::unique_ptr ijt = grid_.nextElement(i_, j_, t_); i_ = ijt[0]; j_ = ijt[1]; t_ = ijt[2]; ++n_; return true; } return false; } // * operator virtual const typename Base::reference operator*() const { return point_; } // ++ operator, move to next element in grid iterator and return point virtual const Base& operator++() { std::unique_ptr ijt = grid_.nextElement(i_, j_, t_); i_ = ijt[0]; j_ = ijt[1]; t_ = ijt[2]; ++n_; if (n_ != size_) { compute_point(i_, j_, t_, point_); } return *this; } // += operator, move some distance d through the iterator and return point virtual const Base& operator+=(typename Base::difference_type distance) { idx_t d = distance; // Following loop could be optimised to not iterate through every point, // but rather jump through a tile at a time if possible. // Then OpenMP algorithms can be made much quicker. for (int n = 0; n < d; n++) { std::unique_ptr ijt = grid_.nextElement(i_, j_, t_); i_ = ijt[0]; j_ = ijt[1]; t_ = ijt[2]; } n_ += d; if (n_ != size_) { compute_point(i_, j_, t_, point_); } return *this; } // Given two positions in the grid iterator return the distance, which for the cubed-sphere // grid is just the number of grid points between the two points. virtual typename Base::difference_type distance(const Base& other) const { const auto& _other = static_cast(other); typename Base::difference_type d = 0; idx_t i = i_; idx_t j = j_; idx_t t = t_; bool found = false; for (int n = 0; n < grid_.size(); n++) { if (i == _other.i_ && j == _other.j_ && t == _other.t_) { found = true; break; } std::unique_ptr ijt = grid_.nextElement(i, j, t); i = ijt[0]; j = ijt[1]; t = ijt[2]; ++d; } ATLAS_ASSERT(!found, "CubedSphereIterator.distance: cycled entire grid without finding other"); return d; } // == operator for checking two positions in the iterator are equal virtual bool operator==(const Base& other) const { return i_ == static_cast(other).i_ && j_ == static_cast(other).j_ && t_ == static_cast(other).t_; } // != operator for checking that two positions in the iterator are not equal virtual bool operator!=(const Base& other) const { return i_ != static_cast(other).i_ || j_ != static_cast(other).j_ || t_ != static_cast(other).t_; } // Clone the grid iterator virtual std::unique_ptr clone() const { auto result = new CubedSphereIterator(grid_, false); result->i_ = i_; result->j_ = j_; result->t_ = t_; result->point_ = point_; result->size_ = size_; result->n_ = n_; return std::unique_ptr(result); } const CubedSphere& grid_; idx_t i_; idx_t j_; idx_t t_; idx_t size_; idx_t n_; typename Base::value_type point_; ComputePoint compute_point; }; // ----------------------------------------------------------------------------------------------- public: // Iterators for returning xy or lonlat using IteratorXY = CubedSphereIterator; using IteratorLonLat = CubedSphereIterator; class IteratorTIJ_Base : public IteratorT {}; using IteratorTIJ = CubedSphereIterator; static std::string static_type(); // Constructors CubedSphere(const std::string&, int, Projection, const std::string& stagger); CubedSphere(int, Projection, const std::string& stagger); CubedSphere(const CubedSphere&); // Destructor virtual ~CubedSphere() override; // Return total grid size virtual idx_t size() const override { return accumulate(npts_.begin(), npts_.end(), 0); } // Return information about the grid virtual Spec spec() const override; virtual std::string name() const override; virtual std::string type() const override; // Return N_, where (N_ * N_) is the number of cells on a tile inline idx_t N() const { return N_; } // Access to the tile class inline atlas::grid::CubedSphereTiles tiles() const { return tiles_; } // Tile specific access to x and y locations // ----------------------------------------- inline double xsPlusIndex(idx_t idx, idx_t t) const { return static_cast(xs_[t]) + static_cast(idx); } inline double xsrMinusIndex(idx_t idx, idx_t t) const { return static_cast(xsr_[t]) - static_cast(idx); } inline double ysPlusIndex(idx_t idx, idx_t t) const { return static_cast(ys_[t]) + static_cast(idx); } inline double ysrMinusIndex(idx_t idx, idx_t t) const { return static_cast(ysr_[t]) - static_cast(idx); } // Lambdas for access to appropriate functions for tile // ---------------------------------------------------- std::vector> xtile; std::vector> ytile; // Functions for returning xy // -------------------------- inline void xyt(idx_t i, idx_t j, idx_t t, double crd[]) const { crd[0] = xtile.at(t)(i, j, t); crd[1] = ytile.at(t)(i, j, t); crd[2] = static_cast(t); } PointXY xyt(idx_t i, idx_t j, idx_t t) const { return PointXY(xtile.at(t)(i, j, t), ytile.at(t)(i, j, t)); } inline void xy(idx_t i, idx_t j, idx_t t, double xy[]) const { double crd[3]; this->xyt(i, j, t, crd); this->xyt2xy(crd, xy); } PointXY xy(idx_t i, idx_t j, idx_t t) const { double crd[2]; this->xy(i, j, t, crd); return PointXY(crd[0], crd[1]); } // Functions for returning lonlat, either as array or PointLonLat // -------------------------------------------------------------- void lonlat(idx_t i, idx_t j, idx_t t, double lonlat[]) const { this->xy(i, j, t, lonlat); // outputing xy in lonlat projection_.xy2lonlat(lonlat); // converting xy to lonlat } PointLonLat lonlat(idx_t i, idx_t j, idx_t t) const { double lonlat[2]; this->lonlat(i, j, t, lonlat); return PointLonLat(lonlat[LON], lonlat[LAT]); } // Check whether i, j, t is in grid // -------------------------------- inline bool inGrid(idx_t i, idx_t j, idx_t t) const { constexpr idx_t tmax = 5; if (t >= 0 && t <= tmax) { if (j >= jmin_[t] && j <= jmax_[t]) { if (i >= imin_[t][j] && i <= imax_[t][j]) { return true; } } } return false; } // Check on whether the final element // ---------------------------------- bool finalElement(idx_t i, idx_t j, idx_t t) const { constexpr idx_t tmax = 5; if (t == tmax) { idx_t jmax = jmax_[tmax]; if (j == jmax) { if (i == imax_[tmax][jmax]) { return true; } } } return false; } // Move to next grid element in an iterator // ---------------------------------------- // Note that i is the fastest index, followed by j, followed by t std::unique_ptr nextElement(const idx_t i, const idx_t j, const idx_t t) const { auto ijt = std::make_unique(3); ijt[0] = i; ijt[1] = j; ijt[2] = t; if (i < imax_[t][j]) { ijt[0] = i + 1; ijt[1] = j; ijt[2] = t; return ijt; } if (i == imax_[t][j]) { if (j < jmax_[t]) { // move to next column ijt[0] = 0; ijt[1] = j + 1; ijt[2] = t; return ijt; } if (j == jmax_[t]) { if (t < nTiles_ - 1) { // move to next tile ijt[0] = 0; ijt[1] = 0; ijt[2] = t + 1; return ijt; } if (t == nTiles_ - 1) { // We are at the final point so we go to // to a point that defines the "end()" of the // iterator i.e. it is not a point on the grid // For now it is set at (N_, N_, nTiles -1) ijt[0] = N_; ijt[1] = N_; ijt[2] = nTiles_ - 1; return ijt; } } } return ijt; } // Iterator start/end positions // ---------------------------- virtual std::unique_ptr xy_begin() const override { return std::make_unique(*this); } virtual std::unique_ptr xy_end() const override { return std::make_unique(*this, false); } virtual std::unique_ptr lonlat_begin() const override { return std::make_unique(*this); } virtual std::unique_ptr lonlat_end() const override { return std::make_unique(*this, false); } virtual std::unique_ptr tij_begin() const { return std::make_unique(*this); } virtual std::unique_ptr tij_end() const { return std::make_unique(*this, false); } // Default configurations Config meshgenerator() const override; Config partitioner() const override; const std::string& stagger() const { return stagger_; } protected: virtual void print(std::ostream&) const override; virtual void hash(eckit::Hash&) const override; virtual RectangularLonLatDomain lonlatBoundingBox() const override; Domain computeDomain() const; private: void xy2xyt(const double xy[], double xyt[]) const; // note: unused! void xyt2xy(const double xyt[], double xy[]) const; protected: // (N_ * N_) = number of cells on a tile idx_t N_; // Number of tiles static const idx_t nTiles_ = 6; // Start points in x,y direction double xs_[nTiles_]; double ys_[nTiles_]; double xsr_[nTiles_]; // x order reversed double ysr_[nTiles_]; // y order reversed (for FV3 panels 4, 5, 6) // Number of unique points on each tile std::vector npts_; std::string tileType_; std::array jmin_; std::array jmax_; std::vector> imin_; std::vector> imax_; std::string stagger_; private: std::string name_ = {"cubedsphere"}; CubedSphereProjectionBase* cs_projection_; // store pointer to dynamic_cast for convenience atlas::grid::CubedSphereTiles tiles_; std::array, 2> tiles_offsets_xy2ab_; std::array, 2> tiles_offsets_ab2xy_; }; } // namespace grid } // namespace detail } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/grid/GridFactory.h0000664000175000017500000000304315160212070022460 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include "atlas/util/Config.h" #include "atlas/util/Factory.h" namespace atlas { namespace grid { namespace detail { namespace grid { class Grid; } } // namespace detail using GridImpl = detail::grid::Grid; //---------------------------------------------------------------------------------------------------------------------- class GridFactory : public util::Factory { public: static std::string className() { return "GridFactory"; } static const GridImpl* build(const std::string&, const util::Config&); using Factory::Factory; private: virtual const GridImpl* make(const util::Config&) = 0; }; //---------------------------------------------------------------------------------------------------------------------- template class GridFactoryBuilder : public GridFactory { private: virtual const GridImpl* make(const util::Config& config) override { return T::create(config); } public: using GridFactory::GridFactory; }; //---------------------------------------------------------------------------------------------------------------------- } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/grid/Healpix.cc0000664000175000017500000001002715160212070021773 0ustar alastairalastair/* * (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 "Healpix.h" #include #include #include #include #include #include "atlas/grid/Spacing.h" #include "atlas/grid/StructuredGrid.h" #include "atlas/grid/detail/grid/GridBuilder.h" #include "atlas/grid/detail/grid/Structured.h" #include "atlas/grid/detail/spacing/CustomSpacing.h" #include "atlas/util/Constants.h" namespace atlas { namespace grid { namespace { // anonymous //--------------------------------------------------------------------------------------------------------------------- static class HealpixGridBuilder : GridBuilder { public: HealpixGridBuilder(): GridBuilder("healpix", {"^[Hh]([1-9][0-9]*)$"}, {"H"}) {} void print(std::ostream& os) const override { os << std::left << std::setw(20) << "H" << "Healpix grid with x points for each of 12 tiles"; } const Grid::Implementation* create(const std::string& name, const Grid::Config& config) const override { int id; std::vector matches; if (match(name, matches, id)) { int N = std::stoi(matches[0]); std::string ordering = config.getString("ordering", "ring"); return new detail::grid::Healpix(N, ordering); } return nullptr; } const Grid::Implementation* create(const Grid::Config& config) const override { long N = config.getLong("N", 0); std::string ordering = config.getString("ordering", "ring"); return new detail::grid::Healpix(N, ordering); } } healpix_builder_; //--------------------------------------------------------------------------------------------------------------------- } // namespace namespace detail { namespace grid { Healpix::XSpace healpix_xspace(long N) { std::vector vp(4 * N - 1); // Polar caps for (int r = 1; r < N; r++) { double start = 45./r; vp[r - 1] = LinearSpacing(start, start + 360., 4 * r, false); vp[4 * N - r - 1] = vp[r - 1]; } // Equatorial belt const double start = 45. / N; for (int r = N; r < 2 * N; r++) { double r_start = start * (2. - (r - N + 1) % 2); vp[r - 1] = LinearSpacing(r_start, r_start + 360., 4 * N, false); vp[4 * N - r - 1] = vp[r - 1]; } // Equator double r_start = start * (1 - (N % 2 ? 1 : 0)); vp[2 * N - 1] = LinearSpacing(r_start, r_start + 360., 4 * N, false); return vp; } Healpix::YSpace healpix_yspace(long N) { constexpr double rad2deg = util::Constants::radiansToDegrees(); std::vector y(4 * N - 1); // Polar caps for (int r = 1; r < N; r++) { y[r - 1] = 90. - rad2deg * std::acos(1. - r * r / (3. * N * N)); y[4 * N - 1 - r] = -y[r - 1]; } // Equatorial belt for (int r = N; r < 2 * N; r++) { y[r - 1] = 90. - rad2deg * std::acos((4. * N - 2. * r) / (3. * N)); y[4 * N - 1 - r] = -y[r - 1]; } // Equator y[2 * N - 1] = 0.; return new spacing::CustomSpacing(y.size(), y.data()); } Healpix::Healpix(long N, const std::string& ordering): Structured("H" + std::to_string(N), healpix_xspace(N), healpix_yspace(N), Projection(), GlobalDomain()) { if (ordering != "ring") { ATLAS_THROW_EXCEPTION("atlas Healpix Grid is only supported with ring ordering"); } } Healpix::Config Healpix::meshgenerator() const { return util::Config("type", "healpix"); } Healpix::Config Healpix::partitioner() const { return util::Config("type", "equal_regions"); } } // namespace grid } // namespace detail } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/grid/Regional.cc0000664000175000017500000003067015160212070022147 0ustar alastairalastair/* * (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 "Regional.h" #include "eckit/types/FloatCompare.h" #include "atlas/grid/StructuredGrid.h" #include "atlas/grid/detail/grid/GridBuilder.h" #include "atlas/projection/detail/ProjectionImpl.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Log.h" #include "atlas/util/NormaliseLongitude.h" using atlas::grid::LinearSpacing; using XSpace = atlas::StructuredGrid::XSpace; using YSpace = atlas::StructuredGrid::YSpace; namespace atlas { namespace grid { namespace { // anonymous static Domain domain(const Grid::Config& grid) { Grid::Config config; if (grid.get("domain", config)) { return Domain(config); } return Domain(); } static Projection projection(const Grid::Config& grid) { // Get the projection from the Grid::Config. Grid::Config proj_config; if (grid.get("projection", proj_config)) { return Projection{proj_config}; } return Projection(); } struct ConfigParser { struct Parsed { Parsed() = default; Parsed(std::initializer_list interval): min(*interval.begin()), max(*(interval.begin() + 1)) {} double min; double max; long N; double step; bool endpoint = {true}; }; bool valid = {false}; Parsed x; Parsed y; double step(double min, double max, long N, bool endpoint = true) { double l = max - min; if (endpoint && N > 1) { return l / double(N - 1); } else { return l / double(N); } } static bool parse(const Projection&, const Grid::Config&, Parsed& x, Parsed& y); template static bool parse(const Projection&, const Grid::Config&, Parsed& x, Parsed& y); }; struct Parse_llc_step : ConfigParser { Parse_llc_step(const Projection& p, const Grid::Config& config) { std::vector centre_lonlat; valid = config.get("nx", x.N) && config.get("ny", y.N) && config.get("dx", x.step) && config.get("dy", y.step) && config.get("lonlat(centre)", centre_lonlat); if (not valid) { return; } double centre[] = {centre_lonlat[0], centre_lonlat[1]}; p.lonlat2xy(centre); double lx = x.step * double(x.N - 1); double ly = y.step * double(y.N - 1); x.min = centre[0] - 0.5 * lx; x.max = centre[0] + 0.5 * lx; y.min = centre[1] - 0.5 * ly; y.max = centre[1] + 0.5 * ly; } }; struct Parse_bounds_xy : ConfigParser { Parse_bounds_xy(const Projection& /*p*/, const Grid::Config& config) { valid = config.get("nx", x.N) && config.get("ny", y.N) && config.get("xmin", x.min) && config.get("xmax", x.max) && config.get("ymin", y.min) && config.get("ymax", y.max); if (not valid) { return; } x.step = step(x.min, x.max, x.N); y.step = step(y.min, y.max, y.N); } }; struct Parse_bounds_lonlat : ConfigParser { Parse_bounds_lonlat(const Projection& p, const Grid::Config& config) { valid = config.get("nx", x.N) && config.get("ny", y.N) && config.get("north", y.max) // unrotated! && config.get("south", y.min) // unrotated! && config.get("east", x.max) // unrotated! && config.get("west", x.min); // unrotated! // This version only works with a "lonlat" or "rotated_lonlat" projection!!! if (valid) { bool valid_projection = not p or p.type() == "rotated_lonlat"; if (not valid_projection) { std::stringstream errmsg; errmsg << "This configuration requires that the projection is " "\"lonlat\" or \"rotated_lonlat\". Received: " << p.type(); errmsg << "\n" "p.bool() = " << bool(p); throw_Exception(errmsg.str(), Here()); } } if (not valid) { return; } x.step = step(x.min, x.max, x.N); y.step = step(y.min, y.max, y.N); } }; struct Parse_ll00_ll11 : ConfigParser { Parse_ll00_ll11(const Projection& p, const Grid::Config& config) { std::vector sw; std::vector ne; valid = config.get("nx", x.N) && config.get("ny", y.N) && config.get("lonlat(xmin,ymin)", sw) // includes rotation && config.get("lonlat(xmax,ymax)", ne); // includes rotation if (not valid) { return; } p.lonlat2xy(sw.data()); p.lonlat2xy(ne.data()); x.min = sw[0]; x.max = ne[0]; y.min = sw[1]; y.max = ne[1]; x.step = step(x.min, x.max, x.N); y.step = step(y.min, y.max, y.N); } }; struct Parse_xy01_step : ConfigParser { Parse_xy01_step(const Projection&, const Grid::Config& config) { valid = config.get("nx", x.N) && config.get("ny", y.N) && config.get("dx", x.step) && config.get("dy", y.step) && config.get("xmin", x.min) && config.get("ymax", y.max); // includes rotation if (not valid) { return; } x.max = x.min + x.step * (x.N - 1); y.min = y.max - y.step * (y.N - 1); } }; struct Parse_ll00_step : ConfigParser { Parse_ll00_step(const Projection& p, const Grid::Config& config) { std::vector sw; valid = config.get("nx", x.N) && config.get("ny", y.N) && config.get("dx", x.step) && config.get("dy", y.step) && config.get("lonlat(xmin,ymin)", sw); // includes rotation if (not valid) { return; } p.lonlat2xy(sw.data()); x.min = sw[0]; y.min = sw[1]; x.max = x.min + x.step * (x.N - 1); y.max = y.min + y.step * (y.N - 1); } }; struct Parse_xy00_step : ConfigParser { Parse_xy00_step(const Projection&, const Grid::Config& config) { valid = config.get("nx", x.N) && config.get("ny", y.N) && config.get("dx", x.step) && config.get("dy", y.step) && config.get("xmin", x.min) && config.get("ymin", y.min); if (not valid) { return; } x.max = x.min + x.step * (x.N - 1); y.max = y.min + y.step * (y.N - 1); } }; struct Parse_ll01_step : ConfigParser { // This resembles GRIB input for "lambert_azimutal_equal_area" Parse_ll01_step(const Projection& p, const Grid::Config& config) { std::vector nw; valid = config.get("nx", x.N) && config.get("ny", y.N) && config.get("dx", x.step) && config.get("dy", y.step) && config.get("lonlat(xmin,ymax)", nw); // includes rotation if (not valid) { return; } p.lonlat2xy(nw.data()); x.min = nw[0]; y.max = nw[1]; x.max = x.min + x.step * (x.N - 1); y.min = y.max - y.step * (y.N - 1); } }; template bool ConfigParser::parse(const Projection& projection, const Grid::Config& config, Parsed& x, Parsed& y) { Parser p(projection, config); if (p.valid) { x = p.x; y = p.y; return true; // success } return false; // failure } bool ConfigParser::parse(const Projection& projection, const Grid::Config& config, Parsed& x, Parsed& y) { // bounding box using 4 variables (any projection allowed) if (ConfigParser::parse(projection, config, x, y)) { return true; } // centre of domain and increments (any projection allowed) if (ConfigParser::parse(projection, config, x, y)) { return true; } // top-left of domain and increments (any projection allowed) if (ConfigParser::parse(projection, config, x, y)) { return true; } if (ConfigParser::parse(projection, config, x, y)) { return true; } // bottom-left of domain and increments (any projection allowed) if (ConfigParser::parse(projection, config, x, y)) { return true; } if (ConfigParser::parse(projection, config, x, y)) { return true; } // bounding box using two points defined in lonlat (any projection allowed) if (ConfigParser::parse(projection, config, x, y)) { return true; } //-------- From here on, projection must be (rotated) lonlat --------// // bounding box using 4 variables (south west north east) if (ConfigParser::parse(projection, config, x, y)) { return true; } return false; } static class regional : public GridBuilder { public: regional(): GridBuilder("regional") {} void print(std::ostream&) const override { // os << std::left << std::setw(20) << "O" << "Octahedral Gaussian // grid"; } const Grid::Implementation* create(const std::string& /*name*/, const Grid::Config& /*config*/) const override { throw_NotImplemented("There are no named regional grids implemented.", Here()); return nullptr; } const Grid::Implementation* create(const Grid::Config& config) const override { // read projection subconfiguration Projection proj = projection(config); // Read grid configuration ConfigParser::Parsed x, y; if (not ConfigParser::parse(proj, config, x, y)) { throw_Exception("Could not parse configuration for RegularRegional grid", Here()); } YSpace yspace = config.getInt("y_numbering", +1) < 0 ? LinearSpacing(y.max, y.min, y.N, y.endpoint) : LinearSpacing(y.min, y.max, y.N, y.endpoint); bool with_endpoint = true; XSpace xspace({x.min, x.max}, std::vector(y.N, x.N), with_endpoint); return new StructuredGrid::grid_t(xspace, yspace, proj, domain(config)); } void force_link() {} } regional_; static class zonal_band : public GridBuilder { public: zonal_band(): GridBuilder("zonal_band") {} void print(std::ostream&) const override { // os << std::left << std::setw(20) << "O" << "Octahedral Gaussian // grid"; } const Grid::Implementation* create(const std::string& /*name*/, const Grid::Config&) const override { throw_NotImplemented("There are no named zonal_band grids implemented.", Here()); return nullptr; } const Grid::Implementation* create(const Grid::Config& config) const override { // read projection subconfiguration Projection projection; { util::Config config_proj; if (config.get("projection", config_proj)) { projection = Projection(config_proj); } } ATLAS_ASSERT(projection.units() == "degrees"); // Read grid configuration ConfigParser::Parsed y; long nx; if (not config.get("nx", nx)) { throw_Exception("Parameter 'nx' missing in configuration", Here()); } if (not config.get("ny", y.N)) { throw_Exception("Parameter 'ny' missing in configuration", Here()); } if (not(config.get("ymin", y.min) or config.get("south", y.min))) { y.min = -90.; } if (not(config.get("ymax", y.max) or config.get("north", y.max))) { y.max = 90.; } YSpace yspace = config.getInt("y_numbering", -1) < 0 ? LinearSpacing(y.max, y.min, y.N, true) : LinearSpacing(y.min, y.max, y.N, true); XSpace xspace({0., 360.}, std::vector(y.N, nx), false); return new StructuredGrid::grid_t(xspace, yspace, projection, domain(config)); } void force_link() {} } zonal_band_; } // namespace namespace detail { namespace grid { void force_link_Regional() { regional_.force_link(); zonal_band_.force_link(); } } // namespace grid } // namespace detail } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/grid/Grid.h0000664000175000017500000001362115160212070021133 0ustar alastairalastair/* * (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 #include #include #include "atlas/domain/Domain.h" #include "atlas/library/config.h" #include "atlas/projection/Projection.h" #include "atlas/util/Object.h" namespace eckit { class Hash; } namespace atlas { class PointXY; class PointLonLat; namespace util { class Config; }; } // namespace atlas namespace atlas { namespace grid { namespace detail { namespace grid { class GridObserver; class Grid : public util::Object { public: // types using Projection = atlas::Projection; using Domain = atlas::Domain; using Config = atlas::util::Config; using Spec = atlas::util::Config; using uid_t = std::string; using hash_t = std::string; template class IteratorT { public: using difference_type = size_t; using iterator_category = std::random_access_iterator_tag; using value_type = Point; using pointer = const Point*; using reference = const Point&; public: virtual bool next(value_type&) = 0; virtual reference operator*() const = 0; virtual const Derived& operator++() = 0; virtual const Derived& operator+=(difference_type) = 0; virtual bool operator==(const Derived&) const = 0; virtual bool operator!=(const Derived&) const = 0; virtual difference_type distance(const Derived&) const = 0; virtual std::unique_ptr clone() const = 0; virtual ~IteratorT() {} }; class IteratorXY : public IteratorT {}; class IteratorLonLat : public IteratorT {}; public: // methods static const Grid* create(const Config&); static const Grid* create(const std::string& name); static const Grid* create(const std::string& name, const Config&); static const Grid* create(const Grid&, const Domain&); /// ctor (default) Grid(); /// dtor virtual ~Grid(); /// Human readable name (may not be unique) virtual std::string name() const = 0; virtual std::string type() const = 0; /// Unique grid id /// Computed from the hash. Can be used to compare 2 grids. virtual uid_t uid() const; /// Adds to the hash the information that makes this Grid unique virtual void hash(eckit::Hash&) const = 0; /// @returns the hash of the information that makes this Grid unique std::string hash() const; /// @return area represented by the grid const Domain& domain() const { return domain_; } /// @return parallel/meridian limits containing the grid virtual RectangularLonLatDomain lonlatBoundingBox() const = 0; virtual size_t footprint() const { return 0; } /// @return projection (mapping between geographic coordinates and grid /// coordinates) const Projection& projection() const { return projection_; } /// @return number of grid points /// @note This methods should have constant access time, if necessary derived // classes should compute it at construction virtual idx_t size() const = 0; virtual Spec spec() const = 0; virtual std::unique_ptr xy_begin() const = 0; virtual std::unique_ptr xy_end() const = 0; virtual std::unique_ptr lonlat_begin() const = 0; virtual std::unique_ptr lonlat_end() const = 0; void attachObserver(GridObserver&) const; void detachObserver(GridObserver&) const; virtual Config meshgenerator() const; virtual Config partitioner() const; protected: // methods /// Fill provided me virtual void print(std::ostream&) const = 0; private: // methods friend std::ostream& operator<<(std::ostream& s, const Grid& p) { p.print(s); return s; } private: // members /// Cache the unique ID mutable uid_t uid_; /// Cache the hash mutable hash_t hash_; protected: // members Projection projection_; Domain domain_; mutable std::vector grid_observers_; }; class GridObserver { private: std::vector registered_grids_; public: void registerGrid(const Grid& grid) { if (std::find(registered_grids_.begin(), registered_grids_.end(), &grid) == registered_grids_.end()) { registered_grids_.push_back(&grid); grid.attachObserver(*this); } } void unregisterGrid(const Grid& grid) { auto found = std::find(registered_grids_.begin(), registered_grids_.end(), &grid); if (found != registered_grids_.end()) { registered_grids_.erase(found); grid.detachObserver(*this); } } virtual ~GridObserver() { for (auto grid : registered_grids_) { grid->detachObserver(*this); } } virtual void onGridDestruction(Grid&) = 0; }; //---------------------------------------------------------------------------------------------------------------------- extern "C" { void atlas__grid__Grid__name(const Grid* This, char*& uid, int& size); idx_t atlas__grid__Grid__size(Grid* This); Grid::Spec* atlas__grid__Grid__spec(Grid* This); void atlas__grid__Grid__uid(const Grid* This, char*& uid, int& size); Grid::Domain::Implementation* atlas__grid__Grid__lonlat_bounding_box(const Grid* This); } } // namespace grid } // namespace detail } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/grid/Healpix.h0000664000175000017500000000171115160212070021635 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include "atlas/grid/detail/grid/Structured.h" namespace atlas { namespace grid { namespace detail { namespace grid { class Healpix : public Structured { public: using Structured::Structured; Healpix(long N, const std::string& ordering = "ring"); Config meshgenerator() const override; Config partitioner() const override; static std::string static_type() { return "healpix"; } virtual std::string type() const override { return static_type(); } }; } // namespace grid } // namespace detail } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/grid/GridBuilder.h0000664000175000017500000000335215160212070022442 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ /// @author Willem Deconinck #pragma once #include #include #include #include "atlas/grid/Grid.h" #include "atlas/util/Config.h" namespace atlas { namespace grid { class GridBuilder { public: using Registry = std::map; static const Registry& nameRegistry(); static const Registry& typeRegistry(); public: GridBuilder(const std::string& names); GridBuilder(const std::string& type, const std::vector& regexes, const std::vector& names); virtual ~GridBuilder(); virtual const Grid::Implementation* create(const Grid::Config&) const; virtual const Grid::Implementation* create(const std::string&, const Grid::Config& = Grid::Config()) const = 0; virtual const std::string& type() const; const std::vector& regexes() const; const std::vector& names() const; void registerNamedGrid(const std::string& name); protected: bool match(const std::string& string, std::vector& matches, int& id) const; private: friend std::ostream& operator<<(std::ostream& os, const GridBuilder& g); virtual void print(std::ostream& os) const = 0; std::vector names_; std::vector pretty_names_; std::string type_; }; } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/grid/RegionalVariableResolution.cc0000664000175000017500000001074315160212070025700 0ustar alastairalastair/** * (C) Crown copyright 2021, Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ #include "RegionalVariableResolution.h" #include "eckit/types/FloatCompare.h" #include "atlas/grid/StructuredGrid.h" #include "atlas/grid/detail/grid/GridBuilder.h" #include "atlas/projection/detail/ProjectionImpl.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Log.h" #include "atlas/util/NormaliseLongitude.h" using atlas::grid::LinearSpacing; using XSpace = atlas::StructuredGrid::XSpace; using YSpace = atlas::StructuredGrid::YSpace; namespace atlas { namespace grid { namespace { // anonymous static class regional_var_resolution : public GridBuilder { public: regional_var_resolution(): GridBuilder("regional_variable_resolution") {} void print(std::ostream&) const override { // os << std::left << std::setw(20) << "O" << "Octahedral Gaussian // grid"; } const Grid::Implementation* create(const std::string& /*name*/, const Grid::Config&) const override { throw_NotImplemented("There are no named regional_var_resolution grids implemented.", Here()); return nullptr; } //create return pointer, data type to return const Grid::Implementation* create(const Grid::Config& config) const override { // read projection subconfiguration double inner_xmin = 0; double inner_xmax = 0; double inner_ymin = 0; double inner_ymax = 0; double outer_xmin = 0; double outer_xmax = 0; double outer_ymin = 0; double outer_ymax = 0; double delta_inner = 0.; Projection projection; { util::Config config_all, config_proj, config_inner, config_outer; util::Config config_pr, configwx, configwy; config_all.set("type", "variable_resolution"); if (config.get("projection", config_proj)) { config_all.set(config_proj); if (config_proj.getString("type") == "lonlat") { config_all.set("type", "variable_resolution"); } else if (config_proj.getString("type") == "rotated_lonlat") { config_all.set("type", "rotated_variable_resolution"); } else { throw_Exception("Only \"lonlat\" or \"rotated_lonlat\" projection is supported"); } } config.get("inner", config_inner); config.get("outer", config_outer); // merge of multiple configs use | config.get("progression", config_pr); config_all.set("progression", config_pr); config_all.set("inner", config_inner); config_all.set("outer", config_outer); projection = Projection(config_all); } //< Add different parts in the grid something like //< int inner_xmin = config.getInt("inner.xmin"); //compute number of points in the regular grid util::Config config_grid; config.get("inner.xmin", inner_xmin); config.get("inner.xend", inner_xmax); config.get("inner.ymin", inner_ymin); config.get("inner.yend", inner_ymax); config.get("outer.xmin", outer_xmin); config.get("outer.xend", outer_xmax); config.get("outer.ymin", outer_ymin); config.get("outer.yend", outer_ymax); config.get("inner.dx", delta_inner); constexpr float epstest = std::numeric_limits::epsilon(); int nx_reg = ((outer_xmax - outer_xmin + epstest) / delta_inner) + 1; int ny_reg = ((outer_ymax - outer_ymin + epstest) / delta_inner) + 1; YSpace yspace = LinearSpacing{outer_ymin, outer_ymax, ny_reg}; XSpace xspace = LinearSpacing{outer_xmin, outer_xmax, nx_reg}; //< RegularGrid is a type of structuredGrid auto domain_ = RectangularDomain{{outer_xmin, outer_xmax}, {outer_ymin, outer_ymax}}; //< allocate memory to make class, create an object using new "constructor" return new StructuredGrid::grid_t(xspace, yspace, projection, domain_); } void force_link() {} } regional_var_resolution_; } // namespace namespace detail { namespace grid { void force_link_Regional_var_resolution() { regional_var_resolution_.force_link(); } } // namespace grid } // namespace detail } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/grid/LonLat.h0000664000175000017500000000064015160212070021434 0ustar alastairalastair/* * (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/grid.h" atlas-0.46.0/src/atlas/grid/detail/grid/Structured.h0000664000175000017500000003602215160212070022412 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include #include "atlas/grid/Spacing.h" #include "atlas/grid/detail/grid/Grid.h" #include "atlas/grid/detail/spacing/LinearSpacing.h" #include "atlas/library/config.h" #include "atlas/runtime/Exception.h" #include "atlas/util/Object.h" #include "atlas/util/ObjectHandle.h" #include "atlas/util/Point.h" namespace atlas { namespace grid { namespace detail { namespace grid { /** * @brief Structured Grid * * This class is a base class for all grids that can be described by * constant latitudes with a uniform distribution of points per latitude * in zonal direction. * This means any full grid and reduced grid, both regular, gaussian or other * such distribution can be represented with this class */ class Structured : public Grid { private: struct ComputePointXY { ComputePointXY(const Structured& grid): grid_(grid), ny_(grid_.ny()) {} void operator()(idx_t i, idx_t j, PointXY& point) { if (j < ny_) { // likely grid_.xy(i, j, point.data()); } } const Structured& grid_; idx_t ny_; }; struct ComputePointLonLat { ComputePointLonLat(const Structured& grid): grid_(grid), ny_(grid_.ny()) {} void operator()(idx_t i, idx_t j, PointLonLat& point) { if (j < ny_) { // likely grid_.lonlat(i, j, point.data()); } } const Structured& grid_; idx_t ny_; }; template class StructuredIterator : public Base { public: StructuredIterator(const Structured& grid, bool begin = true): grid_(grid), ny_(grid_.ny()), i_(0), j_(begin ? 0 : grid_.ny()), compute_point{grid_} { if (j_ != ny_ && grid_.size()) { compute_point(i_, j_, point_); } } virtual bool next(typename Base::value_type& point) { if (j_ < ny_ && i_ < grid_.nx(j_)) { compute_point(i_++, j_, point); if (i_ == grid_.nx(j_)) { j_++; i_ = 0; } return true; } return false; } virtual const typename Base::reference operator*() const { return point_; } virtual const Base& operator++() { ++i_; if (i_ == grid_.nx(j_)) { ++j_; i_ = 0; } compute_point(i_, j_, point_); return *this; } virtual const Base& operator+=(typename Base::difference_type distance) { idx_t d = distance; while (j_ != ny_ && d >= (grid_.nx(j_) - i_)) { d -= (grid_.nx(j_) - i_); ++j_; i_ = 0; } i_ += d; compute_point(i_, j_, point_); return *this; } virtual typename Base::difference_type distance(const Base& other) const { const auto& _other = static_cast(other); typename Base::difference_type d = 0; idx_t j = j_; idx_t i = i_; while (j < _other.j_) { d += grid_.nx(j) - i; ++j; i = 0; } d += _other.i_; return d; } virtual bool operator==(const Base& other) const { return j_ == static_cast(other).j_ && i_ == static_cast(other).i_; } virtual bool operator!=(const Base& other) const { return i_ != static_cast(other).i_ || j_ != static_cast(other).j_; } virtual std::unique_ptr clone() const { auto result = new StructuredIterator(grid_, false); result->i_ = i_; result->j_ = j_; result->point_ = point_; return std::unique_ptr(result); } public: const Structured& grid_; idx_t ny_; idx_t i_; idx_t j_; typename Base::value_type point_; ComputePoint compute_point; }; public: using IteratorXY = StructuredIterator; using IteratorLonLat = StructuredIterator; public: class XSpace { class Implementation : public util::Object { public: // Constructor NVector can be either std::vector or std::vector template Implementation(const std::array& interval, const NVector& N, bool endpoint = true); Implementation(const std::array& interval, std::initializer_list&& N, bool endpoint = true); Implementation(const Spacing&, idx_t ny = 1); Implementation(const std::vector&); Implementation(const Config&); Implementation(const std::vector&); Implementation(const std::vector&); idx_t ny() const { return ny_; } // Minimum number of points across parallels (constant y) idx_t nxmin() const { return nxmin_; } // Maximum number of points across parallels (constant y) idx_t nxmax() const { return nxmax_; } /// Number of points per latitude const std::vector& nx() const { return nx_; } /// Value of minimum longitude per latitude [default=0] const std::vector& xmin() const { return xmin_; } /// Value of maximum longitude per latitude [default=0] const std::vector& xmax() const { return xmax_; } /// Value of longitude increment const std::vector& dx() const { return dx_; } /// Value of minimum x over entire grid double min() const { return min_; } /// Value of maximum x over entire grid double max() const { return max_; } Spec spec() const; std::string type() const; private: void reserve(idx_t ny); private: idx_t ny_; idx_t nxmin_; idx_t nxmax_; std::vector nx_; std::vector xmin_; std::vector xmax_; std::vector dx_; double min_; double max_; }; public: XSpace(); XSpace(const Spacing&, idx_t ny = 1); XSpace(const std::vector&); XSpace(const std::vector&); // Constructor NVector can be either std::vector or std::vector or initializer list template XSpace(const std::array& interval, const NVector& N, bool endpoint = true); XSpace(const std::array& interval, std::initializer_list&& N, bool endpoint = true); XSpace(const Config&); XSpace(const std::vector&); idx_t ny() const { return impl_->ny(); } // Minimum number of points across parallels (constant y) idx_t nxmin() const { return impl_->nxmin(); } // Maximum number of points across parallels (constant y) idx_t nxmax() const { return impl_->nxmax(); } /// Number of points per latitude const std::vector& nx() const { return impl_->nx(); } /// Value of minimum longitude per latitude [default=0] const std::vector& xmin() const { return impl_->xmin(); } /// Value of maximum longitude per latitude [default=0] const std::vector& xmax() const { return impl_->xmax(); } /// Value of longitude increment const std::vector& dx() const { return impl_->dx(); } /// Value of minimum x over entire grid double min() const { return impl_->min(); } /// Value of maximum x over entire grid double max() const { return impl_->max(); } Spec spec() const; std::string type() const { return impl_->type(); } private: util::ObjectHandle impl_; }; using YSpace = Spacing; public: static std::string static_type(); public: Structured(const std::string&, XSpace, YSpace, Projection, Domain); Structured(XSpace, YSpace, Projection, Domain); Structured(const Structured&, Domain); virtual ~Structured() override; virtual idx_t size() const override { return npts_; } virtual Spec spec() const override; /** * Human readable name * Either the name is the one given at construction as a canonical named grid, * or the name "structured" */ virtual std::string name() const override; virtual std::string type() const override; inline idx_t ny() const { return static_cast(y_.size()); } inline idx_t nx(idx_t j) const { return static_cast(nx_[j]); } inline idx_t nxmax() const { return nxmax_; } inline idx_t nxmin() const { return nxmin_; } inline const std::vector& nx() const { return nx_; } inline const std::vector& y() const { return y_; } inline double dx(idx_t j) const { return dx_[j]; } inline double xmin(idx_t j) const { return xmin_[j]; } inline double x(idx_t i, idx_t j) const { return xmin_[j] + static_cast(i) * dx_[j]; } inline double y(idx_t j) const { return y_[j]; } inline void xy(idx_t i, idx_t j, double crd[]) const { crd[0] = x(i, j); crd[1] = y(j); } PointXY xy(idx_t i, idx_t j) const { return PointXY(x(i, j), y(j)); } PointLonLat lonlat(idx_t i, idx_t j) const { return projection_.lonlat(xy(i, j)); } void lonlat(idx_t i, idx_t j, double crd[]) const { xy(i, j, crd); projection_.xy2lonlat(crd); } inline bool reduced() const { return nxmax() != nxmin(); } bool periodic() const { return periodic_x_; } const XSpace& xspace() const { return xspace_; } const YSpace& yspace() const { return yspace_; } virtual std::unique_ptr xy_begin() const override { return std::make_unique(*this); } virtual std::unique_ptr xy_end() const override { return std::make_unique(*this, false); } virtual std::unique_ptr lonlat_begin() const override { return std::make_unique(*this); } virtual std::unique_ptr lonlat_end() const override { return std::make_unique(*this, false); } gidx_t index(idx_t i, idx_t j) const { return jglooff_[j] + i; } void index2ij(gidx_t gidx, idx_t& i, idx_t& j) const { if ((gidx < 0) || (gidx >= jglooff_.back())) { throw_Exception("Structured::index2ij: gidx out of bounds", Here()); } idx_t ja = 0, jb = jglooff_.size(); while (jb - ja > 1) { idx_t jm = (ja + jb) / 2; if (gidx < jglooff_[jm]) { jb = jm; } else { ja = jm; } } i = gidx - jglooff_[ja]; j = ja; } Config meshgenerator() const override; Config partitioner() const override; protected: // methods virtual void print(std::ostream&) const override; virtual void hash(eckit::Hash&) const override; virtual RectangularLonLatDomain lonlatBoundingBox() const override; void computeTruePeriodicity(); Domain computeDomain() const; void crop(const Domain&); protected: // Minimum number of points across parallels (constant y) idx_t nxmin_; // Maximum number of points across parallels (constant y) idx_t nxmax_; /// Total number of unique points in the grid idx_t npts_; /// Latitude values std::vector y_; /// Number of points per latitude std::vector nx_; /// Value of minimum longitude per latitude [default=0] std::vector xmin_; /// Value of maximum longitude per latitude [default=0] std::vector xmax_; /// Value of longitude increment std::vector dx_; /// Periodicity in x-direction bool periodic_x_; /// Per-row offset std::vector jglooff_; private: std::string name_ = {"structured"}; XSpace xspace_; YSpace yspace_; mutable std::string type_; }; extern "C" { void atlas__grid__Structured__delete(Structured* This); const Structured* atlas__grid__Structured(const char* identifier); const Structured* atlas__grid__Structured__config(util::Config* conf); const Structured* atlas__grid__regular__RegularGaussian(long N); Structured* atlas__grid__reduced__ReducedGaussian_int(int nx[], long ny); Structured* atlas__grid__reduced__ReducedGaussian_long(long nx[], long ny); Structured* atlas__grid__reduced__ReducedGaussian_int_projection(int nx[], long ny, const Projection::Implementation* projection); Structured* atlas__grid__reduced__ReducedGaussian_long_projection(long nx[], long ny, const Projection::Implementation* projection); const Structured* atlas__grid__regular__RegularLonLat(long nx, long ny); const Structured* atlas__grid__regular__ShiftedLonLat(long nx, long ny); const Structured* atlas__grid__regular__ShiftedLon(long nx, long ny); const Structured* atlas__grid__regular__ShiftedLat(long nx, long ny); void atlas__grid__Structured__nx_array(Structured* This, const idx_t*& nx, idx_t& size); idx_t atlas__grid__Structured__nx(Structured* This, idx_t j); idx_t atlas__grid__Structured__ny(Structured* This); gidx_t atlas__grid__Structured__index(Structured* This, idx_t i, idx_t j); void atlas__grid__Structured__index2ij(Structured* This, gidx_t gidx, idx_t& i, idx_t& j); idx_t atlas__grid__Structured__nxmin(Structured* This); idx_t atlas__grid__Structured__nxmax(Structured* This); double atlas__grid__Structured__y(Structured* This, idx_t j); double atlas__grid__Structured__x(Structured* This, idx_t i, idx_t j); void atlas__grid__Structured__xy(Structured* This, idx_t i, idx_t j, double crd[]); void atlas__grid__Structured__lonlat(Structured* This, idx_t i, idx_t j, double crd[]); void atlas__grid__Structured__y_array(Structured* This, const double*& lats, idx_t& size); int atlas__grid__Structured__reduced(Structured* This); idx_t atlas__grid__Gaussian__N(Structured* This); } } // namespace grid } // namespace detail } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/grid/StencilComputerInterface.cc0000664000175000017500000000327515160212070025351 0ustar alastairalastair/* * (C) Copyright 2023 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 "StencilComputerInterface.h" extern "C" { atlas::grid::ComputeNorth* atlas__grid__ComputeNorth__new(const atlas::StructuredGrid::Implementation* grid, int halo) { return new atlas::grid::ComputeNorth{atlas::StructuredGrid{grid}, halo}; } void atlas__grid__ComputeNorth__delete(atlas::grid::ComputeNorth* This) { delete This; } atlas::idx_t atlas__grid__ComputeNorth__execute_real32(const atlas::grid::ComputeNorth* This, float y) { return This->operator()(y); } atlas::idx_t atlas__grid__ComputeNorth__execute_real64(const atlas::grid::ComputeNorth* This, double y) { return This->operator()(y); } atlas::grid::ComputeWest* atlas__grid__ComputeWest__new(const atlas::StructuredGrid::Implementation* grid, int halo) { return new atlas::grid::ComputeWest{atlas::StructuredGrid{grid}, halo}; } void atlas__grid__ComputeWest__delete(atlas::grid::ComputeWest* This) { delete This; } atlas::idx_t atlas__grid__ComputeWest__execute_real32(const atlas::grid::ComputeWest* This, float x, atlas::idx_t j) { return This->operator()(x, j); } atlas::idx_t atlas__grid__ComputeWest__execute_real64(const atlas::grid::ComputeWest* This, double x, atlas::idx_t j) { return This->operator()(x, j); } } atlas-0.46.0/src/atlas/grid/detail/grid/CubedSphere2.cc0000664000175000017500000001335315160212070022661 0ustar alastairalastair#include "atlas/grid/detail/grid/CubedSphere2.h" #include #include #include "atlas/grid/CubedSphereGrid2.h" #include "atlas/grid/detail/grid/GridBuilder.h" #include "atlas/grid/detail/grid/GridFactory.h" #include "atlas/runtime/Exception.h" #include "eckit/geometry/Sphere.h" #include "eckit/utils/Hash.h" #include "eckit/utils/Translator.h" namespace atlas { namespace grid { namespace detail { namespace grid { static eckit::Translator to_int; // Public methods CubedSphere2::CubedSphere2(idx_t resolution) : N_(resolution) {} CubedSphere2::CubedSphere2(idx_t resolution, Projection projection) : Grid(), N_(resolution) { // Copy members util::Config defaultProjConfig; defaultProjConfig.set("type", "lonlat"); projection_ = projection ? projection : Projection(defaultProjConfig); // Domain domain_ = GlobalDomain(); } std::string CubedSphere2::name() const { return "CS-LFR-" + std::to_string(N_) + "-2"; } std::string CubedSphere2::type() const { return static_type(); } std::string CubedSphere2::static_type() { return "cubedsphere2"; } // Provide a unique identification hash for the grid and the projection. void CubedSphere2::hash(eckit::Hash& h) const { h.add(name()); h.add(int(N_)); // also add projection information projection().hash(h); // also add domain information, even though already encoded in grid. domain().hash(h); } // Return the bounding box for the grid, global RectangularLonLatDomain CubedSphere2::lonlatBoundingBox() const { return GlobalDomain(); } // Return the total number of points idx_t CubedSphere2::size() const { return N_ * N_ * nTiles_; } // Return the specification for the grid. Grid::Spec CubedSphere2::spec() const { Grid::Spec grid_spec; grid_spec.set("name", name()); grid_spec.set("type", type()); grid_spec.set("projection", projection().spec()); grid_spec.set("domain", domain()); return grid_spec; } // Get the xy for a given index void CubedSphere2::xy(idx_t n, Point2& point) const { auto [t, i, j] = get_cs_indices(n); PointXY tangent_xy = ij_to_tangent_coord(i, j); PointXYZ xyz = tangent_to_xyz_coord(tangent_xy, t); eckit::geometry::Sphere::convertCartesianToSpherical(1., xyz, point); } // Get the xy for a given index Point2 CubedSphere2::xy(idx_t n) const { Point2 point; xy(n, point); return point; } // Get the lonlat for a given index void CubedSphere2::lonlat(idx_t n, Point2& point) const { xy(n, point); projection_.xy2lonlat(point); } // Get the lonlat for a given index Point2 CubedSphere2::lonlat(idx_t n) const { Point2 point; lonlat(n, point); return point; } // Protected methods // Print the name of the Grid void CubedSphere2::print(std::ostream& os) const { os << "CubedSphere2(Name:" << name() << ")"; } // Private methods // Get t, i, and j for a given index CubedSphere2::CSIndices CubedSphere2::get_cs_indices(gidx_t n) const { ATLAS_ASSERT(n <= size()); const idx_t tile_size = N() * N(); const idx_t t = n / tile_size; const idx_t ij = n % tile_size; const idx_t j = ij / N(); const idx_t i = ij % N(); return {t, i, j}; } // Get the point on the tangent plane for a given ij index PointXY CubedSphere2::ij_to_tangent_coord(idx_t i, idx_t j) const { const auto get_curvilinear_coord = [&](idx_t idx) { return M_PI_2 * (-0.5 + (0.5 + static_cast(idx)) / static_cast(N())); }; return {std::tan(get_curvilinear_coord(i)), std::tan(get_curvilinear_coord(j))}; } // Transform a point on the tangent plane to a point on a cube PointXYZ CubedSphere2::tangent_to_xyz_coord(const PointXY& tan_coord, idx_t tile) const { PointXYZ xyz; const Matrix& transform = lfric_rotations_transposed_[tile]; xyz[0] = transform[0][0] * tan_coord[0] + transform[0][1] * tan_coord[1] + transform[0][2]; xyz[1] = transform[1][0] * tan_coord[0] + transform[1][1] * tan_coord[1] + transform[1][2]; xyz[2] = transform[2][0] * tan_coord[0] + transform[2][1] * tan_coord[1] + transform[2][2]; return PointXYZ::normalize(xyz); } namespace { GridFactoryBuilder __register_CubedSphere2(CubedSphere2::static_type()); } static class cubedsphere2_lfric : public GridBuilder { public: cubedsphere2_lfric(): GridBuilder("cubedsphere2_lfric", {"^[Cc][Ss][_-][Ll][Ff][Rr][-_]([1-9][0-9]*)[_-][2]$"}, {"CS-LFR--2"}) {} void print(std::ostream& os) const override { os << std::left << std::setw(20) << "CS-LFR--2" << "Cubed sphere for LFRic"; } // Factory constructor const atlas::Grid::Implementation* create(const std::string& name, const Grid::Config& config) const override { int id; std::vector matches; if (match(name, matches, id)) { util::Config gridconf(config); int N = to_int(matches[0]); gridconf.set("type", type()); gridconf.set("N", N); return create(gridconf); } return nullptr; } // Factory constructor const atlas::Grid::Implementation* create(const Grid::Config& config) const override { int N = 0; if (not config.get("N", N)) { throw_AssertionFailed("Could not find \"N\" in configuration of cubed sphere grid 2", Here()); } std::string name = "CS-LFR-" + std::to_string(N) + "-2"; util::Config projconf; projconf.set("type", "lonlat"); return new CubedSphereGrid2::grid_t(N, Projection(projconf)); } void force_link() {} } cubedsphere2_lfric_; void force_link_CubedSphere2() { cubedsphere2_lfric_.force_link(); } } // namespace grid } // namespace detail } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/grid/Regional.h0000664000175000017500000000064015160212070022003 0ustar alastairalastair/* * (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/grid.h" atlas-0.46.0/src/atlas/grid/detail/grid/LonLat.cc0000664000175000017500000002226115160212070021575 0ustar alastairalastair/* * (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 "LonLat.h" #include "eckit/utils/Translator.h" #include "atlas/grid/StructuredGrid.h" #include "atlas/grid/detail/grid/GridBuilder.h" #include "atlas/runtime/Exception.h" namespace atlas { namespace grid { namespace { // anonymous static eckit::Translator to_int; static Domain domain(const Grid::Config& grid) { Grid::Config config; if (grid.get("domain", config)) { return Domain(config); } return Domain(); } struct Shift { enum Bits { NONE = 0, LAT = (1 << 1), LON = (1 << 2) }; Shift(int bits = NONE): bits_(bits) {} Shift(bool shift_lon, bool shift_lat): bits_((shift_lon ? LON : NONE) | (shift_lat ? LAT : NONE)) {} bool operator()(int bits) const { return (bits_ & bits) == bits; } const int bits_; }; using XSpace = StructuredGrid::XSpace; StructuredGrid::grid_t* create_lonlat(long nlon, long nlat, Shift shift, const Grid::Config& config = Grid::Config()) { bool shifted_x = shift(Shift::LON); bool shifted_y = shift(Shift::LAT); double start_x = (shifted_x ? 0.5 : 0.0) * 360.0 / double(nlon); std::array interval_x = {start_x, start_x + 360.}; bool no_endpoint = false; XSpace xspace(interval_x, std::vector(nlat, nlon), no_endpoint); // spacing is uniform in y // If shifted_y, the whole interval is shifted by -dy/2, and last latitude // would be -90-dy/2 (below -90!!!), if endpoint=true. // Instead, we set endpoint=false so that last latitude is -90+dy/2 instead. Spacing yspace([&] { Grid::Config config_spacing; config_spacing.set("type", "linear"); config_spacing.set("start", 90.0 - (shifted_y ? 90.0 / double(nlat) : 0.0)); config_spacing.set("end", -90.0 - (shifted_y ? 90.0 / double(nlat) : 0.0)); config_spacing.set("endpoint", shifted_y ? false : true); config_spacing.set("N", nlat); return config_spacing; }()); Projection projection; Grid::Config config_projection; if (config.get("projection", config_projection)) { projection = Projection(config_projection); } std::string name; if (shifted_x and shifted_y) { name = "S"; } else if (shifted_x and not shifted_y) { name = "Slon"; } else if (not shifted_x and shifted_y) { name = "Slat"; } else { name = "L"; } name += std::to_string(nlon) + "x" + std::to_string(nlat); return new StructuredGrid::grid_t(name, xspace, yspace, projection, domain(config)); } StructuredGrid::grid_t* create_lonlat(const Grid::Config& config, Shift shift) { bool shifted_y = shift(Shift::LAT); long N, nx, ny; // dimensions if (config.get("N", N)) { nx = 4 * N; ny = shifted_y ? 2 * N : 2 * N + 1; } else if (config.get("nx", nx) && config.get("ny", ny)) { } else { throw_Exception("Configuration requires either N, or (nx,ny)", Here()); } return create_lonlat(nx, ny, shift, config); } //--------------------------------------------------------------------------------------------------------------------- static class regular_lonlat : public GridBuilder { public: regular_lonlat(): GridBuilder("regular_lonlat", {"^[Ll]([1-9][0-9]*)x([1-9][0-9]*)$", "^[Ll]([1-9][0-9]*)$"}, {"Lx", "L"}) {} void print(std::ostream& os) const override { os << std::left << std::setw(20) << "Lx / L" << "Regular longitude-latitude grid"; } const Grid::Implementation* create(const std::string& name, const Grid::Config& config) const override { int id; std::vector matches; if (match(name, matches, id)) { util::Config grid(config); grid.set("type", type()); if (id == 0) { grid.set("nx", to_int(matches[0])); grid.set("ny", to_int(matches[1])); return create(grid); } if (id == 1) { grid.set("N", to_int(matches[0])); return create(grid); } } return nullptr; } const Grid::Implementation* create(const Grid::Config& config) const override { return create_lonlat(config, Shift(false, false)); } void force_link() {} } regular_lonlat_; //--------------------------------------------------------------------------------------------------------------------- static class shifted_lonlat : public GridBuilder { public: shifted_lonlat(): GridBuilder("shifted_lonlat", {"^[Ss]([1-9][0-9]*)x([1-9][0-9]*)$", "^[Ss]([1-9][0-9]*)$"}, {"Sx", "S"}) {} void print(std::ostream& os) const override { os << std::left << std::setw(20) << "Sx / S" << "Shifted longitude-latitude grid"; } const Grid::Implementation* create(const std::string& name, const Grid::Config& config) const override { int id; std::vector matches; if (match(name, matches, id)) { util::Config grid(config); grid.set("type", type()); if (id == 0) { grid.set("nx", to_int(matches[0])); grid.set("ny", to_int(matches[1])); return create(grid); } if (id == 1) { grid.set("N", to_int(matches[0])); return create(grid); } } return nullptr; } const Grid::Implementation* create(const Grid::Config& config) const override { return create_lonlat(config, Shift(true, true)); } void force_link() {} } shifted_lonlat_; //--------------------------------------------------------------------------------------------------------------------- static class shifted_lon : public GridBuilder { public: shifted_lon(): GridBuilder("shifted_lon", {"^[Ss][Ll][Oo][Nn]([1-9][0-9]*)x([1-9][0-9]*)$", "^[Ss][Ll][Oo][Nn]([1-9][0-9]*)$"}, {"Slonx", "Slon"}) {} void print(std::ostream& os) const override { os << std::left << std::setw(20) << "Slonx / Slon" << "Shifted longitude grid"; } const Grid::Implementation* create(const std::string& name, const Grid::Config& config) const override { int id; std::vector matches; if (match(name, matches, id)) { util::Config grid(config); grid.set("type", type()); if (id == 0) { grid.set("nx", to_int(matches[0])); grid.set("ny", to_int(matches[1])); return create(grid); } if (id == 1) { grid.set("N", to_int(matches[0])); return create(grid); } } return nullptr; } const Grid::Implementation* create(const Grid::Config& config) const override { return create_lonlat(config, Shift(true, false)); } void force_link() {} } shifted_lon_; //--------------------------------------------------------------------------------------------------------------------- static class shifted_lat : public GridBuilder { public: shifted_lat(): GridBuilder("shifted_lat", {"^[Ss][Ll][Aa][Tt]([1-9][0-9]*)x([1-9][0-9]*)$", "^[Ss][Ll][Aa][Tt]([1-9][0-9]*)$"}, {"Slatx", "Slat"}) {} void print(std::ostream& os) const override { os << std::left << std::setw(20) << "Slatx / Slat" << "Shifted latitude grid"; } const Grid::Implementation* create(const std::string& name, const Grid::Config& config) const override { int id; std::vector matches; if (match(name, matches, id)) { util::Config grid(config); grid.set("type", type()); if (id == 0) { grid.set("nx", to_int(matches[0])); grid.set("ny", to_int(matches[1])); return create(grid); } if (id == 1) { grid.set("N", to_int(matches[0])); return create(grid); } } return nullptr; } const Grid::Implementation* create(const Grid::Config& config) const override { return create_lonlat(config, Shift(false, true)); } void force_link() {} } shifted_lat_; //--------------------------------------------------------------------------------------------------------------------- } // anonymous namespace namespace detail { namespace grid { void force_link_LonLat() { regular_lonlat_.force_link(); shifted_lonlat_.force_link(); shifted_lon_.force_link(); shifted_lat_.force_link(); } } // namespace grid } // namespace detail } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/detail/grid/CubedSphere.cc0000664000175000017500000005134115160212070022576 0ustar alastairalastair/* * (C) Copyright 2020 UCAR * * 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. */ #include "CubedSphere.h" #include #include #include #include #include "eckit/types/FloatCompare.h" #include "eckit/utils/Hash.h" #include "eckit/utils/Translator.h" #include "atlas/domain/Domain.h" #include "atlas/grid/CubedSphereGrid.h" #include "atlas/grid/Tiles.h" #include "atlas/grid/detail/grid/GridBuilder.h" #include "atlas/grid/detail/grid/GridFactory.h" #include "atlas/grid/detail/spacing/CustomSpacing.h" #include "atlas/grid/detail/spacing/LinearSpacing.h" #include "atlas/projection/detail/CubedSphereProjectionBase.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Log.h" #include "atlas/util/NormaliseLongitude.h" #include "atlas/util/Point.h" #include "atlas/util/UnitSphere.h" namespace atlas { namespace grid { namespace detail { namespace grid { static eckit::Translator to_int; std::string CubedSphere::static_type() { return "cubedsphere"; } std::string CubedSphere::name() const { return name_; } using atlas::projection::detail::CubedSphereProjectionBase; CubedSphere::CubedSphere(int N, Projection p, const std::string& s): CubedSphere(CubedSphere::static_type(), N, p, s) {} CubedSphere::CubedSphere(const std::string& name, int N, Projection projection, const std::string& stagger): Grid(), N_(N), stagger_(stagger), name_(name) { if (stagger_ != "C" && stagger_ != "L") { ATLAS_THROW_EXCEPTION("Unrecognized stagger \"" << stagger_ << "\" for grid " << name); } // Number of tiles hardwired to 6 at the moment. Regional may need 1 // Copy members util::Config defaultProjConfig; defaultProjConfig.set("type", "cubedsphere_equiangular"); projection_ = projection ? projection : Projection(defaultProjConfig); // Domain domain_ = computeDomain(); // x and y are the position in a 2D plane for the unfolded cubed-sphere grid, shown in the // comments in grid/CubedSphereGrid.h. In order to locate the position in this xy array the start // position for each face (tile) of the cube is needed. xs represents the x start position and ys // the y start position. Tile 3, 4 and 5 are rotated and ysr provides the start point for y after // these rotations. using atlas::projection::detail::CubedSphereProjectionBase; cs_projection_ = dynamic_cast(projection_.get()); if (not cs_projection_) { ATLAS_THROW_EXCEPTION("Provided projection " << projection_.type() << " is incompatible with the CubedSphere grid type"); } tiles_ = cs_projection_->getCubedSphereTiles(); tiles_offsets_xy2ab_ = tiles_.xy2abOffsets(); tiles_offsets_ab2xy_ = tiles_.ab2xyOffsets(); double staggerSize = (stagger_ == "C") ? 0.5 : 0.0; for (std::size_t i = 0; i < nTiles_; ++i) { // default assumes all panels start in bottom left corner or center xs_[i] = tiles_offsets_xy2ab_[LON][i] * N + staggerSize; xsr_[i] = tiles_offsets_xy2ab_[LON][i] * N + staggerSize; ys_[i] = tiles_offsets_xy2ab_[LAT][i] * N + staggerSize; ysr_[i] = tiles_offsets_xy2ab_[LAT][i] * N + staggerSize; // default assumes number of points on a panel is N * N npts_.push_back(N * N); } // default assumes jmax_ value of N - 1 on all tiles jmin_ = std::array{0, 0, 0, 0, 0, 0}; jmax_ = std::array{N - 1, N - 1, N - 1, N - 1, N - 1, N - 1}; if (tiles_.type() == "cubedsphere_fv3") { // panel 3,4,5 are reversed in that they start in top left corner for (std::size_t i = 3; i < nTiles_; ++i) { if (stagger_ == "C") { ysr_[i] += N - 1; } else { ys_[i] += 1; ysr_[i] += N; } } // Exceptions to N * N grid points on certain tiles if (stagger_ == "L") { npts_[0] += 1; // An extra nodal point lies on tile 1 npts_[1] += 1; // An extra nodal point lies on tile 2 } xtile = {[this](int i, int j, int t) { return this->xsPlusIndex(i, t); }, [this](int i, int j, int t) { return this->xsPlusIndex(i, t); }, [this](int i, int j, int t) { return this->xsPlusIndex(i, t); }, [this](int i, int j, int t) { return this->xsPlusIndex(j, t); }, [this](int i, int j, int t) { return this->xsPlusIndex(j, t); }, [this](int i, int j, int t) { return this->xsPlusIndex(j, t); }}; ytile = {[this](int i, int j, int t) { return this->ysPlusIndex(j, t); }, [this](int i, int j, int t) { return this->ysPlusIndex(j, t); }, [this](int i, int j, int t) { return this->ysPlusIndex(j, t); }, [this](int i, int j, int t) { return this->ysrMinusIndex(i, t); }, [this](int i, int j, int t) { return this->ysrMinusIndex(i, t); }, [this](int i, int j, int t) { return this->ysrMinusIndex(i, t); }}; // Exceptions to jmax_ value of N-1 on certain tiles if (stagger_ == "L") { jmax_[0] = N; // Due to extra nodal point on panel 1 } for (idx_t t = 0; t < nTiles_; ++t) { std::size_t rowlength = 1 + jmax_[t] - jmin_[t]; std::vector imaxTile(rowlength, N - 1); std::vector iminTile(rowlength, 0); if (stagger_ == "L") { // extra points if (t == 0) { imaxTile[N] = 0; } if (t == 1) { imaxTile[0] = N; } } imax_.push_back(imaxTile); imin_.push_back(iminTile); } } else if (tiles_.type() == "cubedsphere_lfric") { // panel 2, 3 starts in lower right corner initially going upwards xs_[2] += 1; xsr_[2] += N - 1; xs_[3] += 1; xsr_[3] += N - 1; // panel 5 starts in upper left corner going downwards if (stagger_ == "L") { xs_[5] += 1; ys_[5] += 1; } ysr_[5] += N - 1; // Exceptions to N * N grid points on certain tiles if (stagger_ == "L") { npts_[4] = (N + 1) * (N + 1); // nodal top panel includes all edges npts_[5] = (N - 1) * (N - 1); // nodal bottom panel excludes all edges } xtile = {[this](int i, int j, int t) { return this->xsPlusIndex(i, t); }, [this](int i, int j, int t) { return this->xsPlusIndex(i, t); }, [this](int i, int j, int t) { return this->xsrMinusIndex(j, t); }, [this](int i, int j, int t) { return this->xsrMinusIndex(j, t); }, [this](int i, int j, int t) { return this->xsPlusIndex(i, t); }, [this](int i, int j, int t) { return this->xsPlusIndex(j, t); }}; ytile = {[this](int i, int j, int t) { return this->ysPlusIndex(j, t); }, [this](int i, int j, int t) { return this->ysPlusIndex(j, t); }, [this](int i, int j, int t) { return this->ysPlusIndex(i, t); }, [this](int i, int j, int t) { return this->ysPlusIndex(i, t); }, [this](int i, int j, int t) { return this->ysPlusIndex(j, t); }, [this](int i, int j, int t) { return this->ysrMinusIndex(i, t); }}; // Exceptions to jmax_ value of N-1 on certain tiles if (stagger_ == "L") { jmax_[4] = N; jmax_[5] = N - 2; } for (std::size_t t = 0; t < nTiles_; ++t) { std::size_t rowlength = 1 + jmax_[t] - jmin_[t]; std::vector imaxTile(rowlength, N - 1); std::vector iminTile(rowlength, 0); if (stagger_ == "L") { if (t == 4) { std::fill_n(imaxTile.begin(), rowlength, N); } if (t == 5) { std::fill_n(imaxTile.begin(), rowlength, N - 2); } } imax_.push_back(imaxTile); imin_.push_back(iminTile); } } } // Provide the domain for the cubed-sphere grid, which is global. Domain CubedSphere::computeDomain() const { return GlobalDomain(); } // Destructor CubedSphere::~CubedSphere() = default; // Print the name of the Grid void CubedSphere::print(std::ostream& os) const { os << "CubedSphere(Name:" << name() << ")"; } // Return the type of this Grid std::string CubedSphere::type() const { return static_type(); } // Provide a unique identification hash for the grid and the projection. void CubedSphere::hash(eckit::Hash& h) const { h.add("CubedSphere"); h.add(int(N_)); // also add projection information projection().hash(h); // also add domain information, even though already encoded in grid. domain().hash(h); } // Return the bounding box for the grid, global RectangularLonLatDomain CubedSphere::lonlatBoundingBox() const { return projection_ ? projection_.lonlatBoundingBox(computeDomain()) : domain(); } // Return the specification for the grid. Grid::Spec CubedSphere::spec() const { Grid::Spec grid_spec; if (name() == "cubedsphere") { grid_spec.set("type", type()); } else { grid_spec.set("name", name()); } grid_spec.set("projection", projection().spec()); return grid_spec; } // Convert from xy space into resolution dependent xyt space. // Note: unused void CubedSphere::xy2xyt(const double xy[], double xyt[]) const { // xy is in degrees while xyt is in radians // (alpha, beta) and tiles. double normalisedX = xy[XX] / 90.; double normalisedY = (xy[YY] + 135.) / 90.; double NDouble = static_cast(N_); std::array yOffset{NDouble, NDouble, 2. * NDouble, NDouble, NDouble, 0}; xyt[0] = (normalisedX - std::floor(normalisedX)) * static_cast(N_) + xs_[static_cast(xyt[2])]; xyt[1] = (normalisedY - std::floor(normalisedY)) * static_cast(N_) + yOffset[static_cast(xyt[2])]; xyt[2] = tiles_.indexFromXY(xy); throw std::runtime_error("error xy2xyt"); } // Convert from xyt space into continuous xy space. void CubedSphere::xyt2xy(const double xyt[], double xy[]) const { // xy is in degrees // while xyt is in number of grid points // (alpha, beta) and tiles. double N = static_cast(N_); std::size_t t = static_cast(xyt[2]); double normalisedX = (xyt[0] - tiles_offsets_xy2ab_[XX][t] * N) / N; double normalisedY = (xyt[1] - tiles_offsets_xy2ab_[YY][t] * N) / N; xy[XX] = normalisedX * 90. + tiles_offsets_ab2xy_[LON][t]; xy[YY] = normalisedY * 90. + tiles_offsets_ab2xy_[LAT][t]; } // ------------------------------------------ namespace { GridFactoryBuilder __register_CubedSphere(CubedSphere::static_type()); } // ------------------------------------------------------------------------------------------------- static class cubedsphere_lfric : public GridBuilder { public: cubedsphere_lfric(): GridBuilder("cubedsphere_lfric", {"^[Cc][Ss][_-][Ll][Ff][Rr][-_](([CL])[-_])?([1-9][0-9]*)$"}, {"CS-LFR-", "CS-LFR-{C,L}-"}) {} void print(std::ostream& os) const override { os << std::left << std::setw(20) << "CS-LFR-" << "Cubed sphere for LFRic"; } // Factory constructor const atlas::Grid::Implementation* create(const std::string& name, const Grid::Config& config) const override { int id; std::vector matches; if (match(name, matches, id)) { util::Config gridconf(config); int N = to_int(matches[2]); std::string stagger = matches[1].empty() ? "C" : matches[1]; gridconf.set("type", type()); gridconf.set("N", N); gridconf.set("stagger", stagger); return create(gridconf); } return nullptr; } // Factory constructor const atlas::Grid::Implementation* create(const Grid::Config& config) const override { int N = 0; if (not config.get("N", N)) { throw_AssertionFailed("Could not find \"N\" in configuration of cubed sphere grid", Here()); } std::string name; std::string stagger; if (not config.get("stagger", stagger)) { stagger = "C"; // Default to centred } if (stagger == "C") { name = "CS-LFR-" + std::to_string(N); } else { name = "CS-LFR-" + stagger + "-" + std::to_string(N); } util::Config projconf; projconf.set("type", "cubedsphere_equiangular"); projconf.set("tile.type", "cubedsphere_lfric"); // Shift projection by a longitude if (config.has("ShiftLon")) { double shiftLon = 0.0; config.get("ShiftLon", shiftLon); projconf.set("ShiftLon", shiftLon); } // Apply a Schmidt transform if (config.has("DoSchmidt")) { bool doSchmidt = false; config.get("DoSchmidt", doSchmidt); if (doSchmidt) { double stretchFac; double targetLon; double targetLat; config.get("StretchFac", stretchFac); config.get("TargetLon", targetLon); config.get("TargetLat", targetLat); projconf.set("DoSchmidt", doSchmidt); projconf.set("StretchFac", stretchFac); projconf.set("TargetLon", targetLon); projconf.set("TargetLat", targetLat); } } return new CubedSphereGrid::grid_t(name, N, Projection(projconf), stagger); } void force_link() {} } cubedsphere_lfric_; // Specialization based on type of projection // ------------------------------------------ static class cubedsphere_equiangular : public GridBuilder { public: cubedsphere_equiangular(): GridBuilder("cubedsphere_equiangular", {"^[Cc][Ss][_-][Ee][Aa][-_](([CL])[-_])?([1-9][0-9]*)$"}, {"CS-EA-", "CS-EA-{C,L}-"}) {} void print(std::ostream& os) const override { os << std::left << std::setw(20) << "CS-EA--" << "Cubed sphere for equiangular"; } // Factory constructor const atlas::Grid::Implementation* create(const std::string& name, const Grid::Config& config) const override { int id; std::vector matches; if (match(name, matches, id)) { util::Config gridconf(config); int N = to_int(matches[2]); std::string stagger = matches[1].empty() ? "C" : matches[1]; gridconf.set("type", type()); gridconf.set("N", N); gridconf.set("stagger", stagger); return create(gridconf); } return nullptr; } // Factory constructor const atlas::Grid::Implementation* create(const Grid::Config& config) const override { int N = 0; if (not config.get("N", N)) { throw_AssertionFailed("Could not find \"N\" in configuration of cubed sphere grid", Here()); } std::string stagger; if (not config.get("stagger", stagger)) { stagger = "C"; // Default to centred } std::string name; if (stagger == "C") { name = "CS-LFR-" + std::to_string(N); } else { name = "CS-LFR-" + stagger + "-" + std::to_string(N); } util::Config projconf; projconf.set("type", "cubedsphere_equiangular"); projconf.set("tile.type", "cubedsphere_fv3"); // Shift projection by a longitude if (config.has("ShiftLon")) { double shiftLon = 0.0; config.get("ShiftLon", shiftLon); projconf.set("ShiftLon", shiftLon); } // Apply a Schmidt transform if (config.has("DoSchmidt")) { bool doSchmidt = false; config.get("DoSchmidt", doSchmidt); if (doSchmidt) { double stretchFac; double targetLon; double targetLat; config.get("StretchFac", stretchFac); config.get("TargetLon", targetLon); config.get("TargetLat", targetLat); projconf.set("DoSchmidt", doSchmidt); projconf.set("StretchFac", stretchFac); projconf.set("TargetLon", targetLon); projconf.set("TargetLat", targetLat); } } return new CubedSphereGrid::grid_t(name, N, Projection(projconf), stagger); } void force_link() {} } cubedsphere_equiangular_; // ------------------------------------------------------------------------------------------------- static class cubedsphere_equidistant : public GridBuilder { public: cubedsphere_equidistant(): GridBuilder("cubedsphere_equidistant", {"^[Cc][Ss][_-][Ee][Dd][-_](([CL])[-_])?([1-9][0-9]*)$"}, {"CS-ED-", "CS-ED-{C,L}-"}) {} void print(std::ostream& os) const override { os << std::left << std::setw(20) << "CS-ED-" << "Cubed sphere, equidistant"; } const atlas::Grid::Implementation* create(const std::string& name, const Grid::Config& config) const override { int id; std::vector matches; if (match(name, matches, id)) { util::Config gridconf(config); int N = to_int(matches[2]); std::string stagger = matches[1].empty() ? "C" : matches[1]; gridconf.set("type", type()); gridconf.set("N", N); gridconf.set("stagger", stagger); return create(gridconf); } return nullptr; } const atlas::Grid::Implementation* create(const Grid::Config& config) const override { int N = 0; if (not config.get("N", N)) { throw_AssertionFailed("Could not find \"N\" in configuration of cubed sphere grid", Here()); } std::string stagger; if (not config.get("stagger", stagger)) { stagger = "C"; // Default to centred } std::string name; if (stagger == "C") { name = "CS-ED-" + std::to_string(N); } else { name = "CS-ED-" + stagger + "-" + std::to_string(N); } util::Config projconf; projconf.set("type", "cubedsphere_equidistant"); projconf.set("tile.type", "cubedsphere_fv3"); // Shift projection by a longitude if (config.has("ShiftLon")) { double shiftLon = 0.0; config.get("ShiftLon", shiftLon); projconf.set("ShiftLon", shiftLon); } // Apply a Schmidt transform if (config.has("DoSchmidt")) { bool doSchmidt = false; config.get("DoSchmidt", doSchmidt); if (doSchmidt) { double stretchFac; double targetLon; double targetLat; config.get("StretchFac", stretchFac); config.get("TargetLon", targetLon); config.get("TargetLat", targetLat); projconf.set("DoSchmidt", doSchmidt); projconf.set("StretchFac", stretchFac); projconf.set("TargetLon", targetLon); projconf.set("TargetLat", targetLat); } } return new CubedSphereGrid::grid_t(name, N, Projection(projconf), stagger); } void force_link() {} } cubedsphere_equidistant_; // ------------------------------------------------------------------------------------------------- void force_link_CubedSphere() { cubedsphere_lfric_.force_link(); cubedsphere_equiangular_.force_link(); cubedsphere_equidistant_.force_link(); } Grid::Config CubedSphere::meshgenerator() const { if (stagger_ == "L") { return Config("type", "nodal-cubedsphere"); } return Config("type", "cubedsphere"); } Grid::Config CubedSphere::partitioner() const { Grid::Config config; if (stagger_ == "L") { // TODO: implement better one specific for cubed sphere that // works for nodal grid config.set("type", "equal_regions"); config.set("coordinates", "lonlat"); // do not use the grid.xy() coordinates for partitioning return config; } config.set("coordinates", "lonlat"); config.set("type", "cubedsphere"); return config; } // ------------------------------------------------------------------------------------------------- } // namespace grid } // namespace detail } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/StructuredPartitionPolygon.cc0000664000175000017500000004173115160212070023626 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ // file deepcode ignore MissingOpenCheckOnFile: False positive #include "atlas/grid/StructuredPartitionPolygon.h" #include #include #include #include #include "atlas/array/MakeView.h" #include "atlas/functionspace/StructuredColumns.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/runtime/Trace.h" #include "atlas/util/CoordinateEnums.h" namespace atlas { namespace grid { void compute(const functionspace::FunctionSpaceImpl& _fs, idx_t _halo, std::vector& points, std::vector& bb) { if (not dynamic_cast(&_fs)) { throw_Exception("Could not cast functionspace to StructuredColumns", Here()); } const auto& fs = dynamic_cast(_fs); const auto grid = fs.grid(); const auto dom = RectangularDomain(grid.domain()); if (_halo > 0 && _halo != fs.halo()) { throw_Exception("halo must zero or matching the one of the StructuredColumns", Here()); } const int y_numbering = (grid.y().front() < grid.y().back()) ? +1 : -1; bool jfirst_at_pole = y_numbering * 90. + grid.y(0) == 0.; bool jlast_at_pole = y_numbering * 90. - grid.y(grid.ny() - 1) == 0; auto compute_j = [&](const idx_t j) { if (j < 0) { return -j - 1 + jfirst_at_pole; } else if (j >= grid.ny()) { return 2 * grid.ny() - j - 1 - jlast_at_pole; } return j; }; auto compute_y = [&](const idx_t j) { return fs.compute_xy(0,j).y(); }; auto compute_x = [&](const idx_t i, const idx_t j) { return fs.compute_xy(i,j).x(); }; auto equal = [](const double& a, const double& b) { return std::abs(a - b) < 1.e-12; }; auto last_edge_horizontal = [&]() { if (points.size() < 2) { return false; } size_t size = points.size(); return equal(points.at(size - 1)[YY], points.at(size - 2)[YY]); }; auto last_edge_vertical = [&]() { if (points.size() < 2) { return false; } size_t size = points.size(); return equal(points.at(size - 1)[XX], points.at(size - 2)[XX]); }; PointXY p; idx_t c{0}; auto add_point = [&](const Point2& point) { points.emplace_back(point); // Log::info() << "add point (" << points.size() - 1 << ") " << point << std::endl; }; auto add_vertical_edge = [&](const Point2& point) { if (last_edge_vertical()) { points.back()[YY] = point[YY]; // Log::info() << "mod point (" << points.size() - 1 << ") " << point << std::endl; } else { add_point(point); c++; } }; auto add_horizontal_edge = [&](const Point2& point) { if (last_edge_horizontal()) { points.back()[XX] = point[XX]; // Log::info() << "mod point (" << points.size() - 1 << ") " << point << std::endl; } else { add_point(point); c++; } }; double ymax, ymin, xmax, xmin; if (fs.j_begin() >= fs.j_end()) { return; } // Top // Top left point { idx_t j = _halo ? fs.j_begin_halo() : fs.j_begin(); idx_t i = _halo ? fs.i_begin_halo(j) : fs.i_begin(j); if (j == 0 && _halo == 0) { p[YY] = y_numbering < 0 ? dom.ymax() : dom.ymin(); } else { p[YY] = 0.5 * (compute_y(j - 1) + compute_y(j)); } if (i == 0 && _halo == 0) { p[XX] = dom.xmin(); } else { p[XX] = 0.5 * (compute_x(i - 1, j) + compute_x(i, j)); } add_point(p); } // Top right point { idx_t j = _halo ? fs.j_begin_halo() : fs.j_begin(); idx_t i = _halo ? fs.i_end_halo(j) - 1 : fs.i_end(j) - 1; if (j == 0 && _halo == 0) { p[YY] = y_numbering < 0 ? dom.ymax() : dom.ymin(); } else { p[YY] = 0.5 * (compute_y(j - 1) + compute_y(j)); } if (i == grid.nx(compute_j(j)) - 1 && _halo == 0) { p[XX] = dom.xmax(); } else { p[XX] = 0.5 * (compute_x(i, j) + compute_x(i + 1, j)); } add_horizontal_edge(p); ymax = p[YY]; xmax = p[XX]; } // Right side { idx_t j_begin = _halo ? fs.j_begin_halo() : fs.j_begin(); idx_t j_end = _halo ? fs.j_end_halo() : fs.j_end(); for (idx_t j = j_begin; j < j_end - 1 ; ++j) { p[YY] = compute_y(j); idx_t i = _halo ? fs.i_end_halo(j) - 1 : fs.i_end(j) - 1; if (i == grid.nx(compute_j(j)) - 1 && _halo == 0) { p[XX] = dom.xmax(); } else { p[XX] = 0.5 * (compute_x(i, j) + compute_x(i + 1, j)); } if (p == points.back()) { continue; } if (not equal(p[XX], points.back()[XX])) { // Make a corner plus horizontal edge PointXY ptmp = p; // vertical edge p[XX] = points.back()[XX]; p[YY] = 0.5 * (points.back()[YY] + p[YY]); add_vertical_edge(p); p = ptmp; // horizontal edge ptmp = p; p[YY] = points.back()[YY]; add_horizontal_edge(p); p = ptmp; } add_vertical_edge(p); xmax = std::min(xmax, p[XX]); } } // Bottom // Bottom right point(s) { idx_t j = _halo ? fs.j_end_halo() - 1 : fs.j_end() - 1; idx_t i = _halo ? fs.i_end_halo(j) - 1 : fs.i_end(j) - 1; if (j == grid.ny() - 1 && _halo == 0) { p[YY] = y_numbering < 0 ? dom.ymin() : dom.ymax(); } else { p[YY] = 0.5 * (compute_y(j) + compute_y(j + 1)); } if (i == grid.nx(compute_j(j)) - 1 && _halo == 0) { p[XX] = dom.xmax(); } else { p[XX] = 0.5 * (compute_x(i, j) + compute_x(i + 1, j)); } ymin = p[YY]; PointXY pmin = p; if (not equal(p[XX], points.back()[XX])) { PointXY ptmp; ptmp = p; p[XX] = points.back()[XX]; p[YY] = 0.5 * (points.back()[YY] + compute_y(j)); add_vertical_edge(p); pmin = p; xmax = std::min(xmax, p[XX]); p = ptmp; ptmp = p; p[YY] = points.back()[YY]; add_horizontal_edge(p); p = ptmp; } if (xmax - grid.xspace().dx()[compute_j(j)] < compute_x(i, j)) { xmax = std::min(xmax, 0.5 * (compute_x(i + 1, j) + compute_x(i, j))); } else { ymin = pmin[YY]; } add_vertical_edge(p); } // Bottom left point { idx_t j = _halo ? fs.j_end_halo() - 1 : fs.j_end() - 1; idx_t i = _halo ? fs.i_begin_halo(j) : fs.i_begin(j); if (j == grid.ny() - 1 && _halo == 0) { p[YY] = y_numbering < 0 ? dom.ymin() : dom.ymax(); } else { p[YY] = 0.5 * (compute_y(j) + compute_y(j + 1)); } if (i == 0 && _halo == 0) { p[XX] = dom.xmin(); } else { p[XX] = 0.5 * (compute_x(i - 1, j) + compute_x(i, j)); } add_horizontal_edge(p); xmin = p[XX]; } // Left side { idx_t j_end = _halo ? fs.j_end_halo() : fs.j_end(); idx_t j_begin = _halo ? fs.j_begin_halo() : fs.j_begin(); for (idx_t j = j_end - 1; j >= j_begin; --j) { p[YY] = compute_y(j); idx_t i = _halo ? fs.i_begin_halo(j) : fs.i_begin(j); if (i == 0 && _halo == 0) { p[XX] = dom.xmin(); } else { p[XX] = 0.5 * (compute_x(i - 1, j) + compute_x(i, j)); } if (j > j_begin) { xmin = std::max(xmin, p[XX]); } if (j == j_begin && xmin < points[0][XX]) { idx_t jtop = j_begin; idx_t itop = _halo ? fs.i_begin_halo(jtop) : fs.i_begin(jtop); if (xmin + grid.xspace().dx()[compute_j(jtop)] > compute_x(itop, jtop)) { xmin = std::max(xmin, 0.5 * (compute_x(itop - 1, jtop) + compute_x(itop, jtop))); } else { ymax = 0.5 * (p[YY] + compute_y(jtop+1)); } } if (p == points.back()) { continue; } if (not equal(p[XX], points.back()[XX])) { PointXY ptmp; ptmp = p; // vertical edge p[XX] = points.back()[XX]; p[YY] = 0.5 * (points.back()[YY] + compute_y(j)); add_vertical_edge(p); p = ptmp; // horizontal edge ptmp = p; p[YY] = points.back()[YY]; add_horizontal_edge(p); p = ptmp; } add_vertical_edge(p); } } // Connect to top if (last_edge_vertical()) { points.pop_back(); c--; } add_point(points.front()); bb = std::vector{{xmin, ymax}, {xmin, ymin}, {xmax, ymin}, {xmax, ymax}, {xmin, ymax}}; } StructuredPartitionPolygon::StructuredPartitionPolygon(const functionspace::FunctionSpaceImpl& fs, idx_t halo): fs_(fs), halo_(halo) { ATLAS_TRACE("StructuredPartitionPolygon"); compute(fs, halo, points_, inner_bounding_box_); auto min = Point2(std::numeric_limits::max(), std::numeric_limits::max()); auto max = Point2(std::numeric_limits::lowest(), std::numeric_limits::lowest()); for (int i = 0; i < static_cast(inner_bounding_box_.size()) - 1; ++i) { min = Point2::componentsMin(min, inner_bounding_box_[i]); max = Point2::componentsMax(max, inner_bounding_box_[i]); } inscribed_domain_ = {{min[XX], max[XX]}, {min[YY], max[YY]}}; } size_t StructuredPartitionPolygon::footprint() const { size_t size = sizeof(*this); size += capacity() * sizeof(idx_t); return size; } void StructuredPartitionPolygon::outputPythonScript(const eckit::PathName& filepath, const eckit::Configuration& config) const { ATLAS_TRACE("Output PartitionPolygon"); const auto& comm = atlas::mpi::comm(fs_.mpi_comm()); int mpi_rank = int(comm.rank()); int mpi_size = int(comm.size()); auto xy = array::make_view(dynamic_cast(fs_).xy()); const std::vector& points = config.getBool("inner_bounding_box", false) ? inner_bounding_box_ : points_; double xmin = std::numeric_limits::max(); double xmax = -std::numeric_limits::max(); for (size_t i = 0; i < points.size(); ++i) { xmin = std::min(xmin, points[i][XX]); xmax = std::max(xmax, points[i][XX]); } comm.allReduceInPlace(xmin, eckit::mpi::min()); comm.allReduceInPlace(xmax, eckit::mpi::max()); idx_t count = dynamic_cast(fs_).sizeOwned(); idx_t count_all = count; comm.allReduceInPlace(count_all, eckit::mpi::sum()); bool plot_nodes = config.getBool("nodes", false); for (int r = 0; r < mpi_size; ++r) { // clang-format off if ( mpi_rank == r ) { std::ofstream f( filepath.asString().c_str(), mpi_rank == 0 ? std::ios::trunc : std::ios::app ); ATLAS_ASSERT( f.is_open() ); if ( mpi_rank == 0 ) { f << "\n" "import sys" "\n" "\n" "# Configuration option to plot nodes" "\n" "plot_nodes = False" "\n" "for argv in sys.argv[1:] :" "\n" " if argv == \"--nodes\" :" "\n" " plot_nodes = " + std::string( plot_nodes ? "True" : "False" ) + "\n" "\n" "import matplotlib.pyplot as plt" "\n" "from matplotlib.path import Path" "\n" "import matplotlib.patches as patches" "\n" "\n" "from itertools import cycle" "\n" "import matplotlib.cm as cm" "\n" "import numpy as np" "\n" "colours = cycle([cm.Paired(i) for i in np.linspace(0,1,12,endpoint=True)])" "\n" "\n" "fig = plt.figure()" "\n" "ax = fig.add_subplot(111,aspect='equal')" "\n"; } if (points.size() > 0) { f << "\n" "verts_" << r << " = ["; for ( size_t i=0; i mypolygon; mypolygon.reserve(poly.size() * 2); for (auto& p : poly.xy()) { mypolygon.push_back(p[XX]); mypolygon.push_back(p[YY]); } eckit::mpi::Buffer recv_polygons(mpi_size); comm.allGatherv(mypolygon.begin(), mypolygon.end(), recv_polygons); for (idx_t p = 0; p < mpi_size; ++p) { PointsXY recv_points; recv_points.reserve(recv_polygons.counts[p]); for (idx_t j = 0; j < recv_polygons.counts[p] / 2; ++j) { PointXY pxy(*(recv_polygons.begin() + recv_polygons.displs[p] + 2 * j + XX), *(recv_polygons.begin() + recv_polygons.displs[p] + 2 * j + YY)); recv_points.push_back(pxy); } polygons_.emplace_back(new util::ExplicitPartitionPolygon(std::move(recv_points))); } } // void StructuredPartitionPolygon::print( std::ostream& out ) const { // out << "polygon:{" // << "halo:" << halo_ << ",size:" << polygon_.size() << ",nodes:" << static_cast( polygon_ ) // << "}"; // } // ---------------------------------------------------------------------------- } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/CubedSphereGrid.h0000664000175000017500000003343615160212070021064 0ustar alastairalastair/* * (C) Copyright 2020 UCAR * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ #pragma once #include #include "atlas/grid/Grid.h" #include "atlas/grid/Tiles.h" #include "atlas/grid/detail/grid/CubedSphere.h" namespace atlas { //--------------------------------------------------------------------------------------------------------------------- class CubedSphereGrid; /* Grid | +----------+----------+-------------------------------+ | | | StructuredGrid UnstructuredGrid CubedSphere | +--------------------+-----------------------+ | | | ReducedGrid GaussianGrid RegularGrid | | | | | +--------+--------+ +--------+--------+ +-----+ | | | ReducedGaussianGrid RegularGaussianGrid RegularLonLatGrid */ //--------------------------------------------------------------------------------------------------------------------- /*! @class CubedSphereGrid * @brief Specialization of Grid, where the grid is a cubed sphere * @details * @copydetails Grid * @dotfile classatlas_1_1CubedSphereGrid__inherit__graph.dot */ /* Description of indices ---------------------- This class and the implementations in detail/grid/CubedSphere.h are for the structured cubed-sphere grid. In the following there are indices in the grid, denoted idx_t i, idx_t j, idx_t t and positions in the grid, denoted double x, double y and t. For the cubed-sphere each face of the tile is represented by rank 2 arrays with index in that array denoted by i and j. The face is denoted by t. For a regular like grid, translating between i, j and x, y could be done from a starting point and the grid spacing. But this is not possible for the cubed-sphere grid, where the spacing is not necessarily regular and the array not rank 2 for the entire grid, i.e. is made more complicated by the tile index. x and y represent the position on a plane with the tiles arranged in an 'unfolded manner', shown below. Tile t is carried around also as part of the array xy in order to do the projection properly. Cubed-sphere Panel Arrangement: y ^ ....... | | : | | 2 : | | : | --->--- | *....... ....... ------- ------- | | :| :| :| : | | 0 :| 1 :v 3 :v 4 : | | :| :| :| : | --->--- --->--* ....... ....... | ------- | | : | v 5 : | | : | ....... -----------------------------------------------------------> x Key Solid lines: left and bottom edges of panel Dotted lines: right and top edges of panel >, <, v: direction of increasing index in first dimension *: location of two extra points (ngrid = 6 * NCube * NCube + 2) ---------------------------------------------------------------------------------------------------- NCube = 3 example ( 6 * NCube * NCube + 2 = 56 grid points ) Index of each point within the grid (i,j,t): () denotes actual points in the grids <> denotes points that are duplicates (not part of the grid) but shown for completeness of the grid <0,3,0>-<0,2,4>-<0,1,4>-<0,0,4> | | | | (0,1,2) (1,2,2) (2,2,2) <0,2,3> | | | | (0,1,2) (1,1,2) (2,1,2) <0,1,3> | | | | (0,3,0)-<0,1,2>-<0,1,2>-(0,0,2)-(1,0,2)-(2,0,2)-(0,0,3)-(0,1,3)-(0,2,3)-(0,0,4)-(0,1,4)-(0,2,4)-<0,3,0> | | | | | | | | | | (0,2,0) (1,2,0) (2,2,0) (0,1,1) (1,2,1) (2,2,1) (1,0,3) (1,1,3) (1,2,3) (1,0,4) (1,1,4) (1,2,4) <0,1,0> | | | | | | | | | | (0,1,0) (1,1,0) (2,1,0) (0,1,1) (1,1,1) (2,1,1) (2,0,3) (2,1,3) (2,2,3) (2,0,4) (2,1,4) (2,2,4) <0,1,0> | | | | | | | | | | (0,0,0)-(1,0,0)-(2,0,0)-(0,0,1)-(1,0,1)-(2,0,1)-(3,0,1)-<2,0,5>-<1,0,5>-(0,0,5)-(0,1,5)-(0,2,5)-<0,0,0> | | | | (1,0,5) (1,1,5) (1,2,5) <1,0,0> | | | | (2,0,5) (2,1,5) (2,2,5) <2,0,0> | | | | <3,0,1>-<2,0,1>-<1,0,1>-<0,0,1> Position of each point within the grid (x,y,t): () denotes actual points in the grids + denotes duplicate points For the xyt, t is only required in order to pass to the projection and thus know how to rotate the projection of tile 0 to obtain the other tiles +-------+-------+-------+ | | | | (3,8,2) (4,8,2) (5,8,2) + | | | | (3,7,2) (4,7,2) (5,7,2) + | | | | (0,6,0)----+-------+----(3,6,2)-(4,6,2)-(5,6,2)-(6,6,3)-(7,6,3)-(8,6,3)-(9,6,4)-(10,6,4)-(11,6,4)----+ | | | | | | | | | | (0,5,0) (1,5,0) (2,5,0) (3,5,1) (4,5,1) (5,5,1) (6,5,3) (7,5,3) (8,5,3) (9,5,4) (10,5,4) (11,5,4) + | | | | | | | | | | (0,4,0) (1,4,0) (2,4,0) (3,4,1) (4,4,1) (5,4,1) (6,4,3) (7,4,3) (8,4,3) (9,4,4) (10,4,4) (11,4,4) + | | | | | | | | | | (0,3,0)-(1,3,0)-(2,3,0)-(3,3,1)-(4,3,1)-(5,3,1)-(6,3,1)----+-------+----(9,3,5)-(10,3,5)-(11,3,5)----+ | | | | (9,2,5) (10,2,5) (11,2,5) + | | | | (9,1,5) (10,1,5) (11,1,5) + | | | | +--------+-------+--------+ Position in the grid iterator/mesh: First element of each tile: {0, 10, 20, 29, 38, 47} +---+---+---+ | | 26 27 28 + | | 23 24 25 + | | 9---+---+--20--21--22--29--32--35---38--41--44---+ | | | | | 6 7 8 17 18 19 30 33 36 39 42 45 + | | | | | 3 4 5 14 15 16 31 34 37 40 43 46 + | | | | | 0---1---2--10--11--12--13---+---+---47--50--53---+ | | 48 51 54 + | | 49 52 55 + | | +---+---+---+ ---------------------------------------------------------------------------------------------------- */ namespace temporary { class IteratorTIJ { using implementation_t = grid::detail::grid::CubedSphere::IteratorTIJ; public: using difference_type = implementation_t::difference_type; using iterator_category = implementation_t::iterator_category; using value_type = implementation_t::value_type; using pointer = implementation_t::pointer; using reference = implementation_t::reference; public: IteratorTIJ(std::unique_ptr iterator): iterator_(std::move(iterator)) {} bool next(value_type& xy) { return iterator_->next(xy); } reference operator*() const { return iterator_->operator*(); } const IteratorTIJ& operator++() { iterator_->operator++(); return *this; } const IteratorTIJ& operator+=(difference_type distance) { iterator_->operator+=(distance); return *this; } friend difference_type operator-(const IteratorTIJ& last, const IteratorTIJ& first) { return first.iterator_->distance(*last.iterator_); } bool operator==(const IteratorTIJ& other) const { return iterator_->operator==(*other.iterator_); } bool operator!=(const IteratorTIJ& other) const { return iterator_->operator!=(*other.iterator_); } private: difference_type distance(const IteratorTIJ& other) const { return iterator_->distance(*other.iterator_); } private: std::unique_ptr iterator_; }; class IterateTIJ { public: using iterator = IteratorTIJ; using const_iterator = iterator; using Grid = grid::detail::grid::CubedSphere; public: IterateTIJ(const Grid& grid): grid_(grid) {} iterator begin() const { return grid_.tij_begin(); } iterator end() const { return grid_.tij_end(); } private: const Grid& grid_; }; } // namespace temporary class CubedSphereGrid : public Grid { public: using grid_t = grid::detail::grid::CubedSphere; using CubedSphereProjection = projection::detail::CubedSphereProjectionBase; using CubedSphereTiles = grid::CubedSphereTiles; public: CubedSphereGrid(); CubedSphereGrid(const Grid&); CubedSphereGrid(const Grid::Implementation*); CubedSphereGrid(const std::string& name); CubedSphereGrid(const Config&); CubedSphereGrid(const int&, const Projection& = Projection()); operator bool() const { return valid(); } bool valid() const { return grid_; } using Grid::xy; void xyt(idx_t i, idx_t j, idx_t t, double xyt[]) const { grid_->xyt(i, j, t, xyt); } PointXY xyt(idx_t i, idx_t j, idx_t t) const { return grid_->xyt(i, j, t); } // Given indexes in the array (i, j, t) return position array xyt void xy(idx_t i, idx_t j, idx_t t, double xy[]) const { grid_->xy(i, j, t, xy); } PointXY xy(idx_t i, idx_t j, idx_t t) const { return grid_->xy(i, j, t); } using Grid::lonlat; // Given indexes in the array (i, j) return lat/lon array (via the projection) void lonlat(idx_t i, idx_t j, idx_t t, double lonlat[]) const { grid_->lonlat(i, j, t, lonlat); } // Given indexes in the array (i, j, t) return lat/lon as a PointLonLat object PointLonLat lonlat(idx_t i, idx_t j, idx_t t) const { return grid_->lonlat(i, j, t); } // Return the size of the cubed sphere grid, where N is the number of grid boxes along the edge of a tile inline int N() const { return grid_->N(); } /// @brief return tiles object. inline CubedSphereTiles tiles() const { return grid_->tiles(); } /// @brief return cubed sphere projection object. inline const CubedSphereProjection& cubedSphereProjection() const { return dynamic_cast(*projection().get()); }; temporary::IterateTIJ tij() const { return temporary::IterateTIJ(*grid_); } const std::string& stagger() const { return grid_->stagger(); } private: const grid_t* grid_; }; //--------------------------------------------------------------------------------------------------------------------- } // namespace atlas atlas-0.46.0/src/atlas/grid/StructuredGrid.cc0000664000175000017500000000655315160212070021175 0ustar alastairalastair/* * (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/grid/Grid.h" #include #include #include #include "eckit/config/Parametrisation.h" #include "atlas/domain/Domain.h" #include "atlas/grid/Grid.h" #include "atlas/grid/Spacing.h" #include "atlas/grid/StructuredGrid.h" #include "atlas/grid/detail/grid/Gaussian.h" #include "atlas/projection/Projection.h" #include "atlas/util/Config.h" namespace atlas { inline const StructuredGrid::grid_t* structured_grid(const Grid::Implementation* grid) { return dynamic_cast(grid); } StructuredGrid::StructuredGrid(): Grid(), grid_(nullptr) {} StructuredGrid::StructuredGrid(const Grid& grid): Grid(grid), grid_(structured_grid(get())) {} StructuredGrid::StructuredGrid(const Grid::Implementation* grid): Grid(grid), grid_(structured_grid(get())) {} StructuredGrid::StructuredGrid(const std::string& grid, const Domain& domain): Grid(grid, domain), grid_(structured_grid(get())) {} StructuredGrid::StructuredGrid(const std::string& grid, const Projection& projection, const Domain& domain): Grid(grid, projection, domain), grid_(structured_grid(get())) {} StructuredGrid::StructuredGrid(const Config& grid): Grid(grid), grid_(structured_grid(get())) {} StructuredGrid::StructuredGrid(const XSpace& xspace, const YSpace& yspace, const Projection& projection, const Domain& domain): Grid(new StructuredGrid::grid_t(xspace, yspace, projection, domain)), grid_(structured_grid(get())) {} StructuredGrid::StructuredGrid(const Grid& grid, const Grid::Domain& domain): Grid(grid, domain), grid_(structured_grid(get())) {} ReducedGaussianGrid::ReducedGaussianGrid(const std::vector& nx, const Domain& domain): ReducedGaussianGrid::grid_t(grid::detail::grid::reduced_gaussian(nx, domain)) {} ReducedGaussianGrid::ReducedGaussianGrid(const std::vector& nx, const Domain& domain): ReducedGaussianGrid::grid_t(grid::detail::grid::reduced_gaussian(nx, domain)) {} ReducedGaussianGrid::ReducedGaussianGrid(const std::vector& nx, const Projection& projection): ReducedGaussianGrid::grid_t(grid::detail::grid::reduced_gaussian(nx, projection)) {} ReducedGaussianGrid::ReducedGaussianGrid(const std::vector& nx, const Projection& projection): ReducedGaussianGrid::grid_t(grid::detail::grid::reduced_gaussian(nx, projection)) {} ReducedGaussianGrid::ReducedGaussianGrid(const std::initializer_list& nx): ReducedGaussianGrid(std::vector(nx)) {} RegularGaussianGrid::RegularGaussianGrid(int N, const Grid::Domain& domain): RegularGaussianGrid::grid_t("F" + std::to_string(N), domain) {} inline const HealpixGrid::grid_t* healpix_grid(const Grid::Implementation* grid) { return dynamic_cast(grid); } HealpixGrid::HealpixGrid(const Grid& grid): StructuredGrid(grid), grid_(healpix_grid(get())) {} HealpixGrid::HealpixGrid(int N, const std::string& ordering): HealpixGrid(Grid(new HealpixGrid::grid_t(N, ordering))) {} } // namespace atlas atlas-0.46.0/src/atlas/grid/Stencil.h0000664000175000017500000000510615160212070017457 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include "atlas/library/config.h" namespace atlas { namespace grid { //--------------------------------------------------------------------------------------------------------------------- template class HorizontalStencil { friend class ComputeHorizontalStencil; std::array i_begin_; idx_t j_begin_; public: idx_t i(idx_t offset_i, idx_t offset_j) const { return i_begin_[offset_j] + offset_i; } idx_t j(idx_t offset) const { return j_begin_ + offset; } constexpr idx_t width() const { return StencilWidth; } }; //----------------------------------------------------------------------------- template class VerticalStencil { friend class ComputeVerticalStencil; idx_t k_begin_; idx_t k_interval_; // within the stencil, a point falls in a certain interval. // e.g. for a cubic stencil: // + |-----|-----|-----| --> k_interval_ = -1 // |--+--|-----|-----| --> k_interval_ = 0 // |-----|--+--|-----| --> k_interval_ = 1 (the centred case) // |-----|-----|--+--| --> k_interval_ = 2 // |-----|-----|-----| + --> k_interval_ = 3 public: idx_t k(idx_t offset) const { return k_begin_ + offset; } constexpr idx_t width() const { return StencilWidth; } idx_t k_interval() const { return k_interval_; } }; //----------------------------------------------------------------------------- template class Stencil3D { friend class ComputeHorizontalStencil; friend class ComputeVerticalStencil; std::array i_begin_; idx_t j_begin_; idx_t k_begin_; idx_t k_interval_; public: idx_t i(idx_t offset_i, idx_t offset_j) const { return i_begin_[offset_j] + offset_i; } idx_t j(idx_t offset) const { return j_begin_ + offset; } idx_t k(idx_t offset) const { return k_begin_ + offset; } constexpr idx_t width() const { return StencilWidth; } idx_t k_interval() const { return k_interval_; } }; //--------------------------------------------------------------------------------------------------------------------- } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/Vertical.h0000664000175000017500000000553015160212070017630 0ustar alastairalastair/* * (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" namespace atlas { //--------------------------------------------------------------------------------------------------------------------- class Vertical { public: template // expect "vector_t::size()" and "vector_t::operator[]" Vertical(idx_t levels, const vector_t& z, const util::Config& config = util::NoConfig()); template // expect "vector_t::size()" and "vector_t::operator[]" Vertical(idx_t levels, const vector_t& z, const Interval& interval, const util::Config& config = util::NoConfig()); Vertical(const util::Config& config = util::NoConfig()); public: idx_t k_begin() const { return k_begin_; } idx_t k_end() const { return k_end_; } idx_t size() const { return size_; } template double operator()(const Int k) const { return z_[k]; } template double operator[](const Int k) const { return z_[k]; } double min() const { return min_; } double max() const { return max_; } double front() const { return z_.front(); } double back() const { return z_.back(); } /// @brief Output information of field friend std::ostream& operator<<(std::ostream& os, const Vertical& v); private: idx_t size_; idx_t k_begin_; idx_t k_end_; std::vector z_; double min_; double max_; }; //--------------------------------------------------------------------------------------------------------------------- template Vertical::Vertical(idx_t levels, const vector_t& z, const Interval& interval, const util::Config& config): Vertical(levels, z, config) { min_ = interval[0]; max_ = interval[1]; } //--------------------------------------------------------------------------------------------------------------------- template Vertical::Vertical(idx_t levels, const vector_t& z, const util::Config&): size_{levels}, k_begin_{0}, k_end_{size_} { assert(size_ == static_cast(z.size())); z_.resize(size_); for (idx_t k = 0; k < size_; ++k) { z_[k] = z[k]; } min_ = (size_ ? z[0] : 0.); max_ = (size_ ? z[size_ - 1] : 1.); } //--------------------------------------------------------------------------------------------------------------------- } // namespace atlas atlas-0.46.0/src/atlas/grid/Partitioner.cc0000664000175000017500000001410615160212070020514 0ustar alastairalastair/* * (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/grid/Partitioner.h" #include "atlas/functionspace/FunctionSpace.h" #include "atlas/grid/Distribution.h" #include "atlas/grid/Grid.h" #include "atlas/grid/detail/distribution/DistributionImpl.h" #include "atlas/grid/detail/partitioner/Partitioner.h" #include "atlas/mesh/Mesh.h" #include "atlas/option.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Trace.h" #include "atlas/util/Config.h" namespace atlas { namespace grid { using Factory = detail::partitioner::PartitionerFactory; bool Partitioner::exists(const std::string& type) { return Factory::has(type); } Partitioner::Partitioner(const std::string& type): Handle(Factory::build(type)) {} Partitioner::Partitioner(const std::string& type, const idx_t nb_partitions): Handle(Factory::build(type, nb_partitions)) {} namespace { detail::partitioner::Partitioner* partitioner_from_config(const std::string& type, const Partitioner::Config& config) { if (detail::partitioner::Partitioner::extract_nb_partitions(config) == 1) { return Factory::build("serial",config); } return Factory::build(type, config); } detail::partitioner::Partitioner* partitioner_from_config(const Partitioner::Config& config) { std::string type; if (not config.get("type", type)) { throw_Exception("'type' missing in configuration for Partitioner", Here()); } return partitioner_from_config(type, config); } } // namespace Partitioner::Partitioner(const std::string& type, const Config& config): Handle(partitioner_from_config(type, config)) {} Partitioner::Partitioner(const Config& config): Handle(partitioner_from_config(config)) {} void Partitioner::partition(const Grid& grid, int part[]) const { ATLAS_TRACE("Partitioner::partition [type=" + get()->type() + "]"); get()->partition(grid, part); } Distribution Partitioner::partition(const Grid& grid) const { return get()->partition(grid); } idx_t Partitioner::nb_partitions() const { return get()->nb_partitions(); } std::string Partitioner::type() const { return get()->type(); } std::string Partitioner::mpi_comm() const { return get()->mpi_comm(); } MatchingPartitioner::MatchingPartitioner(): Partitioner() {} grid::detail::partitioner::Partitioner* matching_mesh_partititioner(const Mesh& mesh, const Partitioner::Config& config) { std::string type("lonlat-polygon"); config.get("type", type); return grid::detail::partitioner::MatchingPartitionerFactory::build(type, mesh, config); } MatchingPartitioner::MatchingPartitioner(const Mesh& mesh): MatchingPartitioner(mesh, util::NoConfig()) {} MatchingPartitioner::MatchingPartitioner(const Mesh& mesh, const Config& config): Partitioner(matching_mesh_partititioner(mesh, config)) {} grid::detail::partitioner::Partitioner* matching_functionspace_partititioner(const FunctionSpace& functionspace, const Partitioner::Config& config) { std::string type("lonlat-polygon"); config.get("type", type); return grid::detail::partitioner::MatchingPartitionerFactory::build(type, functionspace, config); } MatchingPartitioner::MatchingPartitioner(const FunctionSpace& functionspace): MatchingPartitioner(functionspace, util::NoConfig()) {} MatchingPartitioner::MatchingPartitioner(const FunctionSpace& functionspace, const Config& config): Partitioner(matching_functionspace_partititioner(functionspace, config)) {} extern "C" { detail::partitioner::Partitioner* atlas__grid__Partitioner__new(const Partitioner::Config* config) { detail::partitioner::Partitioner* p; { Partitioner partitioner(*config); p = const_cast(partitioner.get()); p->attach(); } p->detach(); return p; } detail::partitioner::Partitioner* atlas__grid__Partitioner__new_type(const char* type) { detail::partitioner::Partitioner* p; { Partitioner partitioner{option::type(type)}; p = const_cast(partitioner.get()); p->attach(); } p->detach(); return p; } detail::partitioner::Partitioner* atlas__grid__MatchingMeshPartitioner__new(const Mesh::Implementation* mesh, const Partitioner::Config* config) { detail::partitioner::Partitioner* p; { MatchingPartitioner partitioner(Mesh(mesh), *config); p = const_cast(partitioner.get()); p->attach(); } p->detach(); return p; } detail::partitioner::Partitioner* atlas__grid__MatchingFunctionSpacePartitioner__new( const FunctionSpace::Implementation* functionspace, const Partitioner::Config* config) { detail::partitioner::Partitioner* p; { MatchingPartitioner partitioner(FunctionSpace(functionspace), *config); p = const_cast(partitioner.get()); p->attach(); } p->detach(); return p; } Distribution::Implementation* atlas__grid__Partitioner__partition(const Partitioner::Implementation* This, const Grid::Implementation* grid) { Distribution::Implementation* d; { Distribution distribution = This->partition(Grid(grid)); d = const_cast(distribution.get()); d->attach(); } d->detach(); return d; } void atlas__grid__Partitioner__delete(detail::partitioner::Partitioner* This) { delete This; } } // extern "C" } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/Tiles.h0000664000175000017500000000637215160212070017144 0ustar alastairalastair/* * (C) Crown Copyright 2021 Met Office. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include #include #include "atlas/library/config.h" #include "atlas/util/ObjectHandle.h" //--------------------------------------------------------------------------------------------------------------------- // Forward declarations namespace eckit { class Parametrisation; class Hash; } // namespace eckit //--------------------------------------------------------------------------------------------------------------------- namespace atlas { class PointXY; class PointLonLat; namespace util { class Config; } // namespace util namespace projection { class Jacobian; } namespace grid { #ifndef DOXYGEN_SHOULD_SKIP_THIS namespace detail { class CubedSphereTiles; } // namespace detail #endif //--------------------------------------------------------------------------------------------------------------------- class CubedSphereTiles : DOXYGEN_HIDE(public util::ObjectHandle) { public: using Spec = util::Config; public: using Handle::Handle; CubedSphereTiles() = default; CubedSphereTiles(const eckit::Parametrisation&); CubedSphereTiles(const std::string&); /// Type of the cubed-sphere tiles: std::string type() const; // These are offsets needed for transforming // from xy space to the "archetypal base" tile. std::array, 2> xy2abOffsets() const; std::array, 2> ab2xyOffsets() const; void rotate(idx_t t, double xyz[]) const; void unrotate(idx_t t, double xyz[]) const; // tile index from xy space idx_t indexFromXY(const double xy[]) const; idx_t indexFromXY(const PointXY& xy) const; // tile index from longitude and latitude space idx_t indexFromLonLat(const double lonlat[]) const; idx_t indexFromLonLat(const PointLonLat& lonlat) const; // enforceXYdomain reinforces the tile shape in xy space; // if values move a miniscule amount outside the domain, it will be brought back in. void enforceXYdomain(double xy[]) const; idx_t size() const; // this provides periodicity to each of the tiles by extending each tile over edges // in a cross-like fashion. Periodicity of this form does not allow // a "diagonal" extension over corners of the cube. atlas::PointXY tileCubePeriodicity(const atlas::PointXY& xyExtended, const atlas::idx_t tile) const; /// @brief Return the position of the tile centre in xy space. const PointXY& tileCentre(size_t t) const; /// @brief Return the Jacobian of xy with respect to the curvilinear /// coordinates of the tile. const projection::Jacobian& tileJacobian(size_t t) const; private: /// Output to stream void print(std::ostream&) const; friend std::ostream& operator<<(std::ostream& s, const CubedSphereTiles& cst); }; } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/StencilComputer.cc0000664000175000017500000001025515160212070021335 0ustar alastairalastair/* * (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/grid/StencilComputer.h" #include "atlas/grid/StructuredGrid.h" #include "atlas/runtime/Exception.h" namespace atlas { namespace grid { ComputeLower::ComputeLower(const Vertical& z): nlev_{z.size()} { z_.resize(nlev_); double dz = std::numeric_limits::max(); constexpr double tol = 1.e-12; ATLAS_ASSERT(dz > 0); for (idx_t jlev = 0; jlev < nlev_; ++jlev) { if (jlev + 1 < nlev_) { dz = std::min(dz, z[jlev + 1] - z[jlev]); } z_[jlev] = z[jlev] - tol; } nlevaux_ = static_cast(std::round(2. * (z.max() - z.min()) / dz + 0.5) + 1); rlevaux_ = double(nlevaux_); nvaux_.resize(nlevaux_ + 1); double dzaux = (z.max() - z.min()) / rlevaux_; idx_t iref = 0; for (idx_t jlevaux = 0; jlevaux <= nlevaux_; ++jlevaux) { if (iref + 1 < nlev_ && jlevaux * dzaux >= z[iref + 1]) { ++iref; } nvaux_[jlevaux] = iref; } } ComputeNorth::ComputeNorth(const StructuredGrid& grid, idx_t halo) { ATLAS_ASSERT(grid); if (not grid.domain().global()) { throw_NotImplemented("Only implemented for global grids", Here()); } halo_ = halo; ny_ = grid.ny(); y_.resize(ny_ + 2 * halo_); ATLAS_ASSERT(halo_ < ny_); idx_t north_pole_included = 90. - std::abs(grid.y().front()) < tol(); idx_t south_pole_included = 90. - std::abs(grid.y().back()) < tol(); for (idx_t j = -halo_; j < 0; ++j) { idx_t jj = -j - 1 + north_pole_included; y_[halo_ + j] = 180. - grid.y(jj) + tol(); } for (idx_t j = 0; j < ny_; ++j) { y_[halo_ + j] = grid.y(j) + tol(); } for (idx_t j = ny_; j < ny_ + halo_; ++j) { idx_t jj = 2 * ny_ - j - 1 - south_pole_included; y_[halo_ + j] = -180. - grid.y(jj) + tol(); } dy_ = std::abs(grid.y(1) - grid.y(0)); } ComputeWest::ComputeWest(const StructuredGrid& grid, idx_t halo) { ATLAS_ASSERT(grid); if (not grid.domain().global()) { throw_NotImplemented("Only implemented for global grids", Here()); } halo_ = halo; idx_t north_pole_included = 90. - std::abs(grid.y().front()) < tol(); idx_t south_pole_included = 90. - std::abs(grid.y().back()) < tol(); ny_ = grid.ny(); dx.resize(ny_ + 2 * halo_); xref.resize(ny_ + 2 * halo_); for (idx_t j = -halo_; j < 0; ++j) { idx_t jj = -j - 1 + north_pole_included; dx[halo_ + j] = grid.x(1, jj) - grid.x(0, jj); xref[halo_ + j] = grid.x(0, jj) - tol(); } for (idx_t j = 0; j < ny_; ++j) { dx[halo_ + j] = std::abs(grid.x(1, j) - grid.x(0, j)); xref[halo_ + j] = grid.x(0, j) - tol(); } for (idx_t j = ny_; j < ny_ + halo_; ++j) { idx_t jj = 2 * ny_ - j - 1 - south_pole_included; dx[halo_ + j] = std::abs(grid.x(1, jj) - grid.x(0, jj)); xref[halo_ + j] = grid.x(0, jj) - tol(); } } ComputeHorizontalStencil::ComputeHorizontalStencil(const StructuredGrid& grid, idx_t stencil_width): halo_((stencil_width + 1) / 2), compute_north_(grid, halo_), compute_west_(grid, halo_), stencil_width_(stencil_width) { stencil_begin_ = stencil_width_ - idx_t(double(stencil_width_) / 2. + 1.); } ComputeVerticalStencil::ComputeVerticalStencil(const Vertical& vertical, idx_t stencil_width): compute_lower_(vertical), stencil_width_(stencil_width) { stencil_begin_ = stencil_width_ - idx_t(double(stencil_width_) / 2. + 1.); clip_begin_ = 0; clip_end_ = vertical.size(); vertical_min_ = vertical[clip_begin_]; vertical_max_ = vertical[clip_end_ - 1]; } //--------------------------------------------------------------------------------------------------------------------- } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/Spacing.cc0000664000175000017500000000402715160212070017601 0ustar alastairalastair/* * (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/grid/Spacing.h" #include "atlas/grid/detail/spacing/GaussianSpacing.h" #include "atlas/grid/detail/spacing/LinearSpacing.h" #include "atlas/grid/detail/spacing/Spacing.h" #include "atlas/util/Config.h" namespace atlas { namespace grid { Spacing::Spacing(const eckit::Parametrisation& p): Handle(atlas::grid::spacing::Spacing::create(p)) {} size_t Spacing::size() const { return get()->size(); } double Spacing::operator[](size_t i) const { return get()->operator[](i); } Spacing::const_iterator Spacing::begin() const { return get()->begin(); } Spacing::const_iterator Spacing::end() const { return get()->end(); } double Spacing::front() const { return get()->front(); } double Spacing::back() const { return get()->back(); } Spacing::Interval Spacing::interval() const { return get()->interval(); } double Spacing::min() const { return get()->min(); } double Spacing::max() const { return get()->max(); } std::string Spacing::type() const { return get()->type(); } Spacing::Spec Spacing::spec() const { return get()->spec(); } LinearSpacing::LinearSpacing(double start, double stop, long N, bool endpoint): Spacing(new atlas::grid::spacing::LinearSpacing(start, stop, N, endpoint)) {} LinearSpacing::LinearSpacing(const Interval& interval, long N, bool endpoint): Spacing(new atlas::grid::spacing::LinearSpacing(interval, N, endpoint)) {} double LinearSpacing::step() const { return dynamic_cast(get())->step(); } GaussianSpacing::GaussianSpacing(long N): Spacing(new atlas::grid::spacing::GaussianSpacing(N)) {} } // namespace grid } // namespace atlas atlas-0.46.0/src/atlas/grid/StructuredGrid.h0000664000175000017500000002354315160212070021035 0ustar alastairalastair/* * (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 #include "atlas/grid/Grid.h" #include "atlas/grid/detail/grid/Healpix.h" #include "atlas/grid/detail/grid/Structured.h" namespace atlas { //--------------------------------------------------------------------------------------------------------------------- class StructuredGrid; class RegularGrid; class GaussianGrid; class ReducedGaussianGrid; class RegularGaussianGrid; class RegularLonLatGrid; class ShiftedLonLatGrid; class HealpixGrid; /* Grid | +----------+----------+ | | StructuredGrid UnstructuredGrid | +--------------------+-----------------------+------------------+ | | | | ReducedGrid GaussianGrid RegularGrid HealpixGrid | | | | | +--------+--------+ +--------+--------+ +-----+ | | | ReducedGaussianGrid RegularGaussianGrid RegularLonLatGrid */ //--------------------------------------------------------------------------------------------------------------------- /*! @class StructuredGrid * @brief Specialization of Grid, where the grid can be represented by rows with uniform distribution * @details * @copydetails Grid * @dotfile classatlas_1_1StructuredGrid__inherit__graph.dot */ class StructuredGrid : public Grid { public: using grid_t = grid::detail::grid::Structured; using XSpace = grid_t::XSpace; using YSpace = grid_t::YSpace; public: StructuredGrid(); StructuredGrid(const Grid&); StructuredGrid(const Grid::Implementation*); StructuredGrid(const std::string& name, const Domain& = Domain()); StructuredGrid(const std::string& name, const Projection&, const Domain& = Domain()); StructuredGrid(const Config&); StructuredGrid(const XSpace&, const YSpace&, const Projection& = Projection(), const Domain& = Domain()); StructuredGrid(const Grid&, const Domain&); operator bool() const { return valid(); } bool valid() const { return grid_; } inline idx_t ny() const { return grid_->ny(); } inline idx_t nx(idx_t j) const { return grid_->nx(j); } inline const std::vector& nx() const { return grid_->nx(); } inline idx_t nxmax() const { return grid_->nxmax(); } inline const std::vector& y() const { return grid_->y(); } /// x coordinate for given grid point {i,j} inline double x(idx_t i, idx_t j) const { return grid_->x(i, j); } /// y coordinate for given grid row {j} inline double y(idx_t j) const { return grid_->y(j); } /// increment in x for a given grid row {j} inline double dx(idx_t j) const { return grid_->dx(j); } /// x coordinate of beginning of a given grid row {j} inline double xmin(idx_t j) const { return grid_->xmin(j); } using Grid::xy; void xy(idx_t i, idx_t j, double xy[]) const { grid_->xy(i, j, xy); } using Grid::lonlat; void lonlat(idx_t i, idx_t j, double lonlat[]) const { grid_->lonlat(i, j, lonlat); } PointXY xy(idx_t i, idx_t j) const { return PointXY(x(i, j), y(j)); } PointLonLat lonlat(idx_t i, idx_t j) const { return grid_->lonlat(i, j); } inline bool reduced() const { return grid_->reduced(); } inline bool regular() const { return not reduced(); } bool periodic() const { return grid_->periodic(); } const XSpace& xspace() const { return grid_->xspace(); } const YSpace& yspace() const { return grid_->yspace(); } gidx_t index(idx_t i, idx_t j) const { return grid_->index(i, j); } void index2ij(gidx_t gidx, idx_t& i, idx_t& j) const { grid_->index2ij(gidx, i, j); } private: const grid_t* grid_; }; //--------------------------------------------------------------------------------------------------------------------- /// @class ReducedGrid /// @brief Specialization of StructuredGrid, where not all rows have the same number of grid points class ReducedGrid : public StructuredGrid { public: using StructuredGrid::StructuredGrid; operator bool() const { return valid(); } bool valid() const { return StructuredGrid::valid() && reduced(); } }; //--------------------------------------------------------------------------------------------------------------------- /// @class RegularGrid /// @brief Specialization of StructuredGrid, where all rows have the same number of grid points class RegularGrid : public StructuredGrid { public: using StructuredGrid::dx; using StructuredGrid::StructuredGrid; using StructuredGrid::x; using StructuredGrid::xy; operator bool() const { return valid(); } bool valid() const { return StructuredGrid::valid() && regular(); } idx_t nx() const { return nxmax(); } inline double dx() const { return dx(0); } inline double x(idx_t i) const { return x(i, 0); } PointXY xy(idx_t i, idx_t j) const { return PointXY(x(i), y(j)); } }; //--------------------------------------------------------------------------------------------------------------------- template class Gaussian : public Grid { public: using Grid::Grid; idx_t N() const { return Grid::ny() / 2; } protected: bool gaussian() const { return Grid::domain().global() && Grid::yspace().type() == "gaussian"; } }; //--------------------------------------------------------------------------------------------------------------------- /// @class GaussianGrid /// @brief Specialization of StructuredGrid, where rows follow a Gaussian distribution class GaussianGrid : public Gaussian { using grid_t = Gaussian; public: using grid_t::grid_t; operator bool() const { return valid(); } bool valid() const { return StructuredGrid::valid() && gaussian(); } }; //--------------------------------------------------------------------------------------------------------------------- /// @class ReducedGaussianGrid /// @brief Specialization of ReducedGrid, where rows follow a Gaussian distribution class ReducedGaussianGrid : public Gaussian { using grid_t = Gaussian; public: using grid_t::grid_t; ReducedGaussianGrid(const std::initializer_list& pl); ReducedGaussianGrid(const std::vector& pl, const Domain& = Domain()); ReducedGaussianGrid(const std::vector& pl, const Domain& = Domain()); ReducedGaussianGrid(const std::vector& pl, const Projection&); ReducedGaussianGrid(const std::vector& pl, const Projection&); operator bool() const { return valid(); } bool valid() const { return ReducedGrid::valid() && gaussian(); } }; //--------------------------------------------------------------------------------------------------------------------- /// @class RegularGaussianGrid /// @brief Specialization of RegularGaussianGrid, where rows follow a Gaussian distribution class RegularGaussianGrid : public Gaussian { using grid_t = Gaussian; public: using grid_t::grid_t; RegularGaussianGrid(int N, const Domain& = Domain()); operator bool() const { return valid(); } bool valid() const { return RegularGrid::valid() && gaussian(); } }; //--------------------------------------------------------------------------------------------------------------------- /// @class RegularLonLatGrid /// @brief Specialization of RegularGrid, assuming a global domain class RegularLonLatGrid : public RegularGrid { public: using RegularGrid::RegularGrid; public: operator bool() const { return valid(); } bool valid() const { return RegularGrid::valid() && global_lonlat(); } inline double lon(idx_t i) const { return x(i); } inline double lat(idx_t j) const { return y(j); } PointLonLat lonlat(idx_t i, idx_t j) const { return xy(i, j); } bool standard() const { return standard_lon() && standard_lat(); } bool shifted() const { return shifted_lon() && shifted_lat(); } bool shiftedLon() const { return shifted_lon() && standard_lat(); } bool shiftedLat() const { return standard_lon() && shifted_lat(); } protected: bool global_lonlat() const { return domain().global() && not projection() && yspace().type() == "linear"; } bool standard_lon() const { return x(0) == 0.; } bool standard_lat() const { return y(0) == 90. && ny() % 2 == 1; } bool shifted_lon() const { return x(0) == 0.5 * 360. / nx(); } bool shifted_lat() const { return y(0) == 90. - 0.5 * 180. / ny() && ny() % 2 == 0; } }; //--------------------------------------------------------------------------------------------------------------------- /// @class HealpixGrid /// @brief Specialization of StructuredGrid, assuming a global domain class HealpixGrid : public StructuredGrid { public: using grid_t = grid::detail::grid::Healpix; public: HealpixGrid(const Grid&); HealpixGrid(int N, const std::string& ordering = "ring"); operator bool() const { return valid(); } bool valid() const { return grid_; } long N() const { return grid_->nxmax() / 4; } private: const grid_t* grid_; }; //--------------------------------------------------------------------------------------------------------------------- } // namespace atlas atlas-0.46.0/src/atlas/grid/CubedSphereGrid2.h0000664000175000017500000000101115160212070021126 0ustar alastairalastair#pragma once #include "atlas/grid/Grid.h" #include "atlas/grid/detail/grid/CubedSphere2.h" namespace atlas { class CubedSphereGrid2 : public atlas::Grid { public: using grid_t = grid::detail::grid::CubedSphere2; public: CubedSphereGrid2(idx_t resolution); CubedSphereGrid2(const Grid& grid); CubedSphereGrid2(idx_t resolution, Projection projection); bool valid() const { return grid_; }; operator bool() const { return valid(); } private: const grid_t* grid_; }; } // namespace atlas atlas-0.46.0/src/atlas/array/0000775000175000017500000000000015160212070016074 5ustar alastairalastairatlas-0.46.0/src/atlas/array/ArrayStrides.h0000664000175000017500000000501615160212070020663 0ustar alastairalastair/* * (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 #include "atlas/library/config.h" //------------------------------------------------------------------------------------------------------ namespace atlas { namespace array { class ArrayStrides : public std::vector { private: using Base = std::vector; public: ArrayStrides() {} ArrayStrides(std::initializer_list list): Base(list) {} ArrayStrides(Base&& base): Base(std::forward(base)) {} }; namespace detail { template inline ArrayStrides make_strides(Int size1) { return ArrayStrides{static_cast(size1)}; } template inline ArrayStrides make_strides(Int1 size1, Int2 size2) { return ArrayStrides{static_cast(size1), static_cast(size2)}; } template inline ArrayStrides make_strides(Int1 size1, Int2 size2, Int3 size3) { return ArrayStrides{static_cast(size1), static_cast(size2), static_cast(size3)}; } template inline ArrayStrides make_strides(Int1 size1, Int2 size2, Int3 size3, Int4 size4) { return ArrayStrides{static_cast(size1), static_cast(size2), static_cast(size3), static_cast(size4)}; } template inline ArrayStrides make_strides(Int1 size1, Int2 size2, Int3 size3, Int4 size4, Int5 size5) { return ArrayStrides{static_cast(size1), static_cast(size2), static_cast(size3), static_cast(size4), static_cast(size5)}; } } // namespace detail inline ArrayStrides make_strides(std::initializer_list sizes) { return ArrayStrides(sizes); } template ArrayStrides make_strides(idx_t... indices) { return detail::make_strides(std::forward(indices)...); } //------------------------------------------------------------------------------------------------------ } // namespace array } // namespace atlas atlas-0.46.0/src/atlas/array/native/0000775000175000017500000000000015160212070017362 5ustar alastairalastairatlas-0.46.0/src/atlas/array/native/NativeIndexView.cc0000664000175000017500000000536115160212070022747 0ustar alastairalastair/* * (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/array/native/NativeIndexView.h" #include "atlas/array/helpers/ArrayAssigner.h" //------------------------------------------------------------------------------------------------------ namespace atlas { namespace array { #undef ENABLE_IF_NON_CONST #define ENABLE_IF_NON_CONST \ template ::value && EnableBool), int>::type*> //------------------------------------------------------------------------------------------------------ template IndexView::IndexView(Value* data, const idx_t shape[Rank]): data_(data) { static_assert(Rank == 1); strides_[0] = 1; shape_[0] = shape[0]; } template IndexView::IndexView(Value* data, const idx_t shape[Rank], const idx_t strides[Rank]): data_(const_cast(data)) { static_assert(Rank == 1); strides_[0] = strides[0]; shape_[0] = shape[0]; } template void IndexView::dump(std::ostream& os) const { os << "size: " << size() << " , values: "; os << "[ "; for (idx_t j = 0; j < size(); ++j) { os << (*this)(j) << " "; } os << "]" << std::endl; } template ENABLE_IF_NON_CONST void IndexView::assign(const std::initializer_list& list) { helpers::array_assigner::apply(*this, list); } //------------------------------------------------------------------------------------------------------ // Explicit template instatiation #define EXPLICIT_TEMPLATE_INSTANTIATION_TYPE_RANK(TYPE, RANK) \ template class IndexView; \ template class IndexView; \ template void IndexView::assign(std::initializer_list const&); #define EXPLICIT_TEMPLATE_INSTANTIATION(RANK) \ EXPLICIT_TEMPLATE_INSTANTIATION_TYPE_RANK(int, RANK) \ EXPLICIT_TEMPLATE_INSTANTIATION_TYPE_RANK(long, RANK) EXPLICIT_TEMPLATE_INSTANTIATION(1) // EXPLICIT_TEMPLATE_INSTANTIATION(2) #undef EXPLICIT_TEMPLATE_INSTANTIATION_TYPE_RANK #undef EXPLICIT_TEMPLATE_INSTANTIATION //------------------------------------------------------------------------------------------------------ } // namespace array } // namespace atlas atlas-0.46.0/src/atlas/array/native/NativeDataStore.h0000664000175000017500000005217615160212070022603 0ustar alastairalastair/* * (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 // std::fill #include // std::numeric_limits::signaling_NaN #include #include #include "pluto/pluto.h" #include "atlas/array/ArrayDataStore.h" #include "atlas/array/ArraySpec.h" #include "atlas/library.h" #include "atlas/library/config.h" #include "atlas/parallel/acc/acc.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Trace.h" #define ATLAS_ACC_DEBUG 0 //------------------------------------------------------------------------------ namespace atlas { namespace array { namespace native { template static constexpr Value invalid_value() { return std::numeric_limits::has_signaling_NaN ? std::numeric_limits::signaling_NaN() : std::numeric_limits::has_quiet_NaN ? std::numeric_limits::quiet_NaN() : std::numeric_limits::has_infinity ? std::numeric_limits::infinity() : std::numeric_limits::max(); } #if ATLAS_INIT_SNAN template void initialise(Value array[], size_t size) { std::fill_n(array, size, invalid_value()); } #else template void initialise(Value[], size_t) {} #endif static bool test_device_is_shared() { // This is currently a bit hacky, to be improved! auto& device_resource = *pluto::device::get_default_resource(); if (device_resource == *pluto::pinned_resource()) { return true; } if (device_resource == *pluto::managed_resource()) { return true; } if (device_resource == *pluto::pinned_pool_resource()) { return true; } if (device_resource == *pluto::managed_pool_resource()) { return true; } // If above shortcuts failed, we can do an introspection on a dummy allocation if (ATLAS_HAVE_GPU && pluto::devices()) { void* device_ptr = device_resource.allocate(8); bool is_shared = pluto::is_managed(device_ptr) || (pluto::is_pinned(device_ptr)); device_resource.deallocate(device_ptr,8); return is_shared; } return false; } static pluto::memory_resource* host_resource(bool shared) { if (shared) { return pluto::device::get_default_resource(); } else { return pluto::host::get_default_resource(); } } static pluto::memory_resource* device_resource(bool shared) { if (shared) { return pluto::null_memory_resource(); } else { return pluto::device::get_default_resource(); } } template class DataStore final : public ArrayDataStore { public: DataStore(size_t size): size_(size), is_shared_data_{test_device_is_shared()}, host_allocator_{host_resource(is_shared_data_)}, device_allocator_{device_resource(is_shared_data_)} { label_ = array::label::get(); allocateHost(); initialise(host_data_, size_); if (ATLAS_HAVE_GPU && pluto::devices()) { device_updated_ = false; } else { device_data_ = host_data_; } } virtual ~DataStore() { deallocateDevice(); deallocateHost(); } void updateDevice() const override { if (ATLAS_HAVE_GPU && pluto::devices()) { if (not device_allocated_) { allocateDevice(); } #if ATLAS_HAVE_TRACE std::unique_ptr trace; if (atlas::Library::instance().traceMemory()) { trace.reset(new atlas::Trace(Here(), "update_device", {"memory","memory.transfer", "memory.transfer.h2d"})); } #endif pluto::copy_host_to_device(label_, device_data_, host_data_, size_); device_updated_ = true; } } void updateHost() const override { if constexpr (ATLAS_HAVE_GPU) { if (device_allocated_) { #if ATLAS_HAVE_TRACE std::unique_ptr trace; if (atlas::Library::instance().traceMemory()) { trace.reset(new atlas::Trace(Here(), "update_host", {"memory","memory.transfer", "memory.transfer.d2h"})); } #endif pluto::copy_device_to_host(label_, host_data_, device_data_, size_); host_updated_ = true; } } } bool valid() const override { return true; } void syncHostDevice() const override { if (host_updated_ and device_updated_) { return; // nothing to do } if (not (host_updated_ or device_updated_)) { throw_AssertionFailed("syncHostDevice() could not figure out which of host or device is up to date. " "Probably it was forgotten to use setDeviceNeedsUpdate(true) or setDeviceNeedsUpdate(true)", Here()); } if (not device_updated_) { updateDevice(); } else if (not host_updated_) { updateHost(); } } void syncHost() const override { if (not host_updated_) { ATLAS_ASSERT(device_updated_,"Unexpected state when calling syncHost() because (deviceNeedsUpdate() and hostNeedsUpdate())"); updateHost(); } } void syncDevice() const override { if (not device_updated_) { ATLAS_ASSERT(host_updated_,"Unexpected state when calling syncDevice() because (deviceNeedsUpdate() and hostNeedsUpdate())"); if (not host_updated_) { throw_AssertionFailed("Unexpected state when calling syncDevice() because (deviceNeedsUpdate() and hostNeedsUpdate())", Here()); } updateDevice(); } } bool deviceAllocated() const override { return device_allocated_; } void allocateDevice() const override { if (ATLAS_HAVE_GPU && pluto::devices()) { if (device_allocated_) { return; } if (size_) { if(is_shared_data_) { device_data_ = pluto::get_registered_device_pointer(host_data_); } else { #if ATLAS_HAVE_TRACE std::unique_ptr trace; if (atlas::Library::instance().traceMemory()) { trace.reset(new atlas::Trace(Here(), "allocate_device", {"memory","memory.allocator","memory.allocator.device","memory.allocator.device.allocate"})); } #endif device_data_ = device_allocator_.allocate(label_, size_); } ATLAS_ASSERT(pluto::is_device_accessible(device_data_)); device_allocated_ = true; accMap(); } } } void deallocateDevice() const override { if (device_allocated_) { accUnmap(); if (not is_shared_data_) { #if ATLAS_HAVE_TRACE std::unique_ptr trace; if (atlas::Library::instance().traceMemory()) { trace.reset(new atlas::Trace(Here(), "deallocate_device", {"memory","memory.allocator","memory.allocator.device","memory.allocator.device.deallocate"})); } #endif device_allocator_.deallocate(label_, device_data_,size_); } device_data_ = nullptr; device_allocated_ = false; device_updated_ = false; } } bool hostNeedsUpdate() const override { return (not host_updated_); } bool deviceNeedsUpdate() const override { return (not device_updated_); } void setHostNeedsUpdate(bool v) const override { host_updated_ = (not v); } void setDeviceNeedsUpdate(bool v) const override { device_updated_ = (not v); } void reactivateDeviceWriteViews() const override {} void reactivateHostWriteViews() const override {} void* voidDataStore() override { return static_cast(host_data_); } void* voidHostData() override { return static_cast(host_data_); } void* voidDeviceData() override { return static_cast(device_data_); } void accMap() const override { if ((not acc_mapped_ && acc::devices())) { if (is_shared_data_) { // nvidia compiler should not accMap for managed memory if (pluto::is_managed(host_data_) && acc::compiler_id() == acc::CompilerId::nvidia) { return; } } ATLAS_ASSERT(deviceAllocated(),"Could not accMap as device data is not allocated"); ATLAS_ASSERT(!atlas::acc::is_present(host_data_, size_ * sizeof(Value))); if constexpr(ATLAS_ACC_DEBUG) { std::cout << " + acc_map_data(hostptr:"< 0) { #if ATLAS_HAVE_TRACE std::unique_ptr trace; if (atlas::Library::instance().traceMemory()) { trace.reset(new atlas::Trace(Here(), "allocate_host", {"memory","memory.allocator","memory.allocator.host","memory.allocator.host.allocate"})); } #endif host_data_ = host_allocator_.allocate(label_, size_); } else { host_data_ = nullptr; } } void deallocateHost() { if (host_data_) { #if ATLAS_HAVE_TRACE std::unique_ptr trace; if (atlas::Library::instance().traceMemory()) { trace.reset(new atlas::Trace(Here(), "deallocate_host", {"memory","memory.allocator","memory.allocator.host","memory.allocator.host.deallocate"})); } #endif host_allocator_.deallocate(label_, host_data_, size_); host_data_ = nullptr; } } size_t footprint() const { return sizeof(Value) * size_; } size_t size_; Value* host_data_; mutable Value* device_data_{nullptr}; mutable bool host_updated_{true}; mutable bool device_updated_{true}; mutable bool device_allocated_{false}; mutable bool acc_mapped_{false}; bool is_shared_data_{false}; pluto::allocator host_allocator_; mutable pluto::allocator device_allocator_; std::string label_; }; //------------------------------------------------------------------------------ template class WrappedDataStore final : public ArrayDataStore { public: void init_device() { if (ATLAS_HAVE_GPU && pluto::devices()) { device_updated_ = false; } else { device_data_ = host_data_; } } WrappedDataStore(Value* host_data, size_t size): host_data_(host_data), size_(size), device_allocator_{pluto::device::get_default_resource()} { label_ = array::label::get(); init_device(); } WrappedDataStore(Value* host_data, const ArraySpec& spec): host_data_(host_data), size_(spec.size()), device_allocator_{pluto::device::get_default_resource()} { label_ = array::label::get(); init_device(); contiguous_ = spec.contiguous(); if (! contiguous_) { int break_idx = 0; bool break_found = false; size_t expected_stride = 1; size_t shp_mult_rhs = 1; for (int i = spec.rank(); i > 0; i--) { if (expected_stride != spec.strides()[i - 1]) { if (! break_found) { break_idx = i - 1; break_found = true; shp_mult_rhs = expected_stride; expected_stride = spec.strides()[i - 1]; // prepare to search another discontiguity } else { // double striding detected --> can not do memcpy anymore memcpy_2d_ = false; } } expected_stride *= spec.shape()[i - 1]; } memcpy_h2d_pitch_ = shp_mult_rhs; memcpy_d2h_pitch_ = spec.strides()[break_idx]; memcpy_width_ = shp_mult_rhs; if (spec.strides()[spec.rank() - 1] > 1) { memcpy_height_ = spec.shape()[0] * spec.device_strides()[0]; } else { size_t shp_mult_lhs = spec.shape()[0]; for (int i = 1; i <= break_idx; i++) { shp_mult_lhs *= spec.shape()[i]; } memcpy_height_ = shp_mult_lhs; } } } virtual ~WrappedDataStore() { deallocateDevice(); } void updateDevice() const override { if (ATLAS_HAVE_GPU && pluto::devices()) { if (not device_allocated_) { allocateDevice(); } #if ATLAS_HAVE_TRACE std::unique_ptr trace; if (atlas::Library::instance().traceMemory()) { trace.reset(new atlas::Trace(Here(), "update_device [host=wrapped]", {"memory","memory.transfer", "memory.transfer.h2d"})); } #endif if (contiguous_) { pluto::copy_host_to_device(label_, device_data_, host_data_, size_); } else { ATLAS_ASSERT(memcpy_2d_, "Atlas fields with more than one discontiguous dimension are not supported"); pluto::copy_host_to_device_2D( label_, device_data_, memcpy_h2d_pitch_, host_data_, memcpy_d2h_pitch_, memcpy_width_, memcpy_height_); } device_updated_ = true; } } void updateHost() const override { if constexpr (ATLAS_HAVE_GPU) { if (device_allocated_) { #if ATLAS_HAVE_TRACE std::unique_ptr trace; if (atlas::Library::instance().traceMemory()) { trace.reset(new atlas::Trace(Here(), "update_host [host=wrapped]", {"memory","memory.transfer", "memory.transfer.d2h"})); } #endif if (contiguous_) { pluto::copy_device_to_host(label_, host_data_, device_data_, size_); } else { ATLAS_ASSERT(memcpy_2d_, "Atlas fields with more than one discontiguous dimension are not supported"); pluto::copy_device_to_host_2D( label_, host_data_, memcpy_d2h_pitch_ , device_data_, memcpy_h2d_pitch_, memcpy_width_, memcpy_height_); } host_updated_ = true; } } } bool valid() const override { return true; } void syncHostDevice() const override { if (host_updated_ and device_updated_) { return; // nothing to do } if (not (host_updated_ or device_updated_)) { throw_AssertionFailed("syncHostDevice() could not figure out which of host or device is up to date. " "Probably it was forgotten to use setDeviceNeedsUpdate(true) or setDeviceNeedsUpdate(true)", Here()); } if (not device_updated_) { updateDevice(); } else if (not host_updated_) { updateHost(); } } void syncHost() const override { if (not host_updated_) { ATLAS_ASSERT(device_updated_,"Unexpected state when calling syncHost() because (deviceNeedsUpdate() and hostNeedsUpdate())"); updateHost(); } } void syncDevice() const override { if (not device_updated_) { ATLAS_ASSERT(host_updated_,"Unexpected state when calling syncDevice() because (deviceNeedsUpdate() and hostNeedsUpdate())"); updateDevice(); } } bool deviceAllocated() const override { return device_allocated_; } void allocateDevice() const override { if (ATLAS_HAVE_GPU && pluto::devices()) { if (device_allocated_) { return; } if (size_) { #if ATLAS_HAVE_TRACE std::unique_ptr trace; if (atlas::Library::instance().traceMemory()) { trace.reset(new atlas::Trace(Here(), "allocate_device [host=wrapped]", {"memory","memory.allocator","memory.allocator.device","memory.allocator.device.allocate"})); } #endif device_data_ = device_allocator_.allocate(label_, size_); device_allocated_ = true; if (contiguous_) { accMap(); } } } } void deallocateDevice() const override { if (device_allocated_) { if (contiguous_) { accUnmap(); } #if ATLAS_HAVE_TRACE std::unique_ptr trace; if (atlas::Library::instance().traceMemory()) { trace.reset(new atlas::Trace(Here(), "deallocate_device [host=wrapped]", {"memory","memory.allocator","memory.allocator.device","memory.allocator.device.deallocate"})); } #endif device_allocator_.deallocate(label_, device_data_, size_); device_data_ = nullptr; device_allocated_ = false; device_updated_ = false; } } bool hostNeedsUpdate() const override { return (not host_updated_); } bool deviceNeedsUpdate() const override { return (not device_updated_); } void setHostNeedsUpdate(bool v) const override { host_updated_ = (not v); } void setDeviceNeedsUpdate(bool v) const override { device_updated_ = (not v); } void reactivateDeviceWriteViews() const override {} void reactivateHostWriteViews() const override {} void* voidDataStore() override { return static_cast(host_data_); } void* voidHostData() override { return static_cast(host_data_); } void* voidDeviceData() override { return static_cast(device_data_); } void accMap() const override { if (not acc_mapped_ && acc::devices()) { ATLAS_ASSERT(deviceAllocated(),"Could not accMap as device data is not allocated"); ATLAS_ASSERT(!atlas::acc::is_present(host_data_, size_ * sizeof(Value))); if constexpr(ATLAS_ACC_DEBUG) { std::cout << " + acc_map_data(hostptr:"< device_allocator_; std::string label_; }; } // namespace native } // namespace array } // namespace atlas atlas-0.46.0/src/atlas/array/native/NativeArrayView.h0000664000175000017500000003555615160212070022631 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ /// @file ArrayView.h /// This file contains the ArrayView class, a class that allows to wrap any /// contiguous raw data into /// a view which is accessible with multiple indices. /// All it needs is the strides for each index, and the shape of each index. /// ATTENTION: The last index is stride 1 /// /// Bounds-checking can be turned ON by defining /// "ATLAS_ARRAYVIEW_BOUNDS_CHECKING" /// before including this header. /// /// Example 1: /// int[] array = { 1, 2, 3, 4, 5, 6, 7, 8, 9}; /// int[2] strides = { 3, 1 }; /// int[2] shape = { 3, 3 }; /// ArrayView matrix( array, shape, strides ); /// for( idx_t i=0; i matrix( array, shape ); /// which is identical for this matrix to previous Example 1 /// /// There is also an easier way to wrap Field and Array classes: /// /// Example 3: /// ArrayView fieldview( Field ); /// ArrayView arrayview( Array ); /// /// @author Willem Deconinck #pragma once #include #include #include #include #include #include #include "atlas/array/ArrayDataStore.h" #include "atlas/array/ArrayViewDefs.h" #include "atlas/array/LocalView.h" #include "atlas/array/Range.h" #include "atlas/array/helpers/ArraySlicer.h" #include "atlas/library/config.h" #include "atlas/mdspan.h" //------------------------------------------------------------------------------------------------------ namespace atlas { namespace array { //------------------------------------------------------------------------------------------------------ /// @brief Multi-dimensional access to a Array object or Field object /// /// An ArrayView enables access to the inner data-memory-storage of the Array. /// It is required to create the view using the make_view helper functions. /// /// ### Example 1: /// /// @code{.cpp} /// auto view = make_view( Array ); /// double sum = 0; /// for( idx_t i=0; i class ArrayView { template using is_non_const_value_type = typename std::is_same::type>; #define ENABLE_IF_NON_CONST \ template ::value && EnableBool), int>::type* = nullptr> #define ENABLE_IF_CONST_WITH_NON_CONST(T) \ template ::value && is_non_const_value_type::value), \ int>::type* = nullptr> public: // -- Type definitions using value_type = Value; using non_const_value_type = typename std::remove_const::type; static constexpr bool is_const = std::is_const::value; static constexpr bool is_non_const = !std::is_const::value; static constexpr int RANK{Rank}; private: using slicer_t = typename helpers::ArraySlicer>; using const_slicer_t = typename helpers::ArraySlicer>; template struct slice_t { using type = typename slicer_t::template Slice::type; }; template struct const_slice_t { using type = typename const_slicer_t::template Slice::type; }; using mdspan_extents_type = dextents; using mdspan_strides_type = std::array; using mdspan_type = mdspan; using const_mdspan_type = mdspan; public: // -- Constructors template >> ArrayView(const ArrayView& other): data_(other.data()), size_(other.size()) { for (int j = 0; j < Rank; ++j) { shape_[j] = other.shape_[j]; strides_[j] = other.strides_[j]; } } template >> ArrayView(ArrayView&& other):data_(other.data()), size_(other.size()) { for (int j = 0; j < Rank; ++j) { shape_[j] = other.shape_[j]; strides_[j] = other.strides_[j]; } } #ifndef DOXYGEN_SHOULD_SKIP_THIS // This constructor should not be used directly, but only through a array::make_view() function. ArrayView(value_type* data, const ArrayShape& shape, const ArrayStrides& strides): data_(data) { size_ = 1; for (int j = 0; j < Rank; ++j) { shape_[j] = shape[j]; strides_[j] = strides[j]; size_ *= size_t(shape_[j]); } } #endif mdspan_type as_mdspan() { return mdspan_type{this->data(), {mdspan_extents(), mdspan_strides()}}; } const_mdspan_type as_mdspan() const { return const_mdspan_type{this->data(), {mdspan_extents(), mdspan_strides()}}; } ENABLE_IF_CONST_WITH_NON_CONST(value_type) operator const ArrayView&() const { return *(const ArrayView*)(this); } // -- Access methods /// @brief Multidimensional index operator: view(i,j,k,...) template > ATLAS_HOST_DEVICE value_type& operator()(Idx... idx) { check_bounds(idx...); return data_[index(idx...)]; } /// @brief Multidimensional index operator: view(i,j,k,...) template > ATLAS_HOST_DEVICE const value_type& operator()(Idx... idx) const { return data_[index(idx...)]; } /// @brief Access to data using square bracket [idx] operator @m_class{m-label m-warning} **Rank==1**. /// /// Note that this function is only present when Rank == 1 #ifndef DOXYGEN_SHOULD_SKIP_THIS template > ATLAS_HOST_DEVICE const value_type& operator[](Idx idx) const { #else // Doxygen API is cleaner! template ATLAS_HOST_DEVICE value_type operator[](Int idx) const { #endif check_bounds(idx); return data_[idx * strides_[0]]; } /// @brief Access to data using square bracket [idx] operator @m_class{m-label m-warning} **Rank==1** /// /// Note that this function is only present when Rank == 1 #ifndef DOXYGEN_SHOULD_SKIP_THIS template > ATLAS_HOST_DEVICE value_type& operator[](Idx idx) { #else // Doxygen API is cleaner! template ATLAS_HOST_DEVICE value_type operator[](Idx idx) { #endif check_bounds(idx); return data_[idx * strides_[0]]; } /// @brief Return number of values in dimension **Dim** (template argument) /// /// Example use: /// @code{.cpp} /// for( idx_t i=0; i(); ++i ) { /// for( idx_t j=0; j(); +j ) { /// ... /// } /// } /// @endcode template ATLAS_HOST_DEVICE idx_t shape() const { return shape_[Dim]; } /// @brief Return stride for values in dimension **Dim** (template argument) template ATLAS_HOST_DEVICE idx_t stride() const { return strides_[Dim]; } /// @brief Return total number of values (accumulated over all dimensions) ATLAS_HOST_DEVICE size_t size() const { return size_; } /// @brief Return the number of dimensions ATLAS_HOST_DEVICE static constexpr idx_t rank() { return Rank; } ATLAS_HOST_DEVICE const idx_t* strides() const { return strides_; } ATLAS_HOST_DEVICE const idx_t* shape() const { return shape_; } /// @brief Return number of values in dimension idx template ATLAS_HOST_DEVICE idx_t shape(Int idx) const { return shape_[idx]; } /// @brief Return stride for values in dimension idx template ATLAS_HOST_DEVICE idx_t stride(Int idx) const { return strides_[idx]; } /// @brief Access to internal data. @m_class{m-label m-danger} **dangerous** ATLAS_HOST_DEVICE value_type const* data() const { return data_; } /// @brief Access to internal data. @m_class{m-label m-danger} **dangerous** ATLAS_HOST_DEVICE value_type* data() { return data_; } ATLAS_HOST_DEVICE bool valid() const { return true; } /// @brief Return true when all values are contiguous in memory. /// /// This means that if there is e.g. padding in the fastest dimension, or if /// the ArrayView represents a slice, the returned value will be false. ATLAS_HOST_DEVICE bool contiguous() const { return (size_ == size_t(shape_[0]) * size_t(strides_[0]) ? true : false); } ENABLE_IF_NON_CONST void assign(const value_type& value); ENABLE_IF_NON_CONST void assign(const std::initializer_list& list); ENABLE_IF_NON_CONST void assign(const ArrayView& other); ENABLE_IF_NON_CONST void assign(const LocalView& other); void dump(std::ostream& os) const; /// @brief Obtain a slice from this view: view.slice( Range, Range, ... ) /// /// The return type of this function is intentionally `auto` and is guaranteed to have /// the same API as ArrayView, but is not necessarily this type. /// /// If the current view has Rank == 2, a Rank == 1 slice can be created in several ways: /// /// @code{.cpp} /// auto slice1 = view.slice( Range(0,2), Range::all() ); /// auto slice2 = view.slice( Range::To(2), Range::all() ); /// @endcode /// /// Sometimes it may be required to extend the rank of the current view to cater for /// certain algorithms requiring an extra rank. /// /// @code{.cpp} /// auto slice3 = view.slice( Range::all(), Range::all(), Range::dummy() ); /// @endcode template ATLAS_HOST_DEVICE auto slice(Args... args) { return slicer_t(*this).apply(args...); } /// @brief Obtain a slice from this view: view.slice( Range, Range, ... ) template ATLAS_HOST_DEVICE auto slice(Args... args) const { return const_slicer_t(*this).apply(args...); } template ATLAS_HOST_DEVICE constexpr idx_t index(Ints... idx) const { return index_part<0>(idx...); } private: // -- Private methods template ATLAS_HOST_DEVICE constexpr idx_t index_part(Int idx, Ints... next_idx) const { return idx * strides_[Dim] + index_part(next_idx...); } template ATLAS_HOST_DEVICE constexpr idx_t index_part(Int last_idx) const { return last_idx * strides_[Dim]; } #if ATLAS_ARRAYVIEW_BOUNDS_CHECKING template ATLAS_HOST_DEVICE void check_bounds(Ints... idx) const { static_assert(sizeof...(idx) == Rank, "Expected number of indices is different from rank of array"); #if ATLAS_HOST_COMPILE return check_bounds_part<0>(idx...); #endif } #else template ATLAS_HOST_DEVICE void check_bounds(Ints... idx) const { static_assert(sizeof...(idx) == Rank, "Expected number of indices is different from rank of array"); } #endif template ATLAS_HOST_DEVICE void check_bounds_force(Ints... idx) const { static_assert(sizeof...(idx) == Rank, "Expected number of indices is different from rank of array"); #if ATLAS_HOST_COMPILE return check_bounds_part<0>(idx...); #endif } template ATLAS_HOST void check_bounds_part(Int idx, Ints... next_idx) const { if (idx_t(idx) >= shape_[Dim]) { throw_OutOfRange("ArrayView", array_dim(), idx, shape_[Dim]); } check_bounds_part(next_idx...); } template ATLAS_HOST void check_bounds_part(Int last_idx) const { if (idx_t(last_idx) >= shape_[Dim]) { throw_OutOfRange("ArrayView", array_dim(), last_idx, shape_[Dim]); } } template ATLAS_HOST_DEVICE mdspan_extents_type _get_mdspan_extents(std::integer_sequence = {}) const { return mdspan_extents_type{(shape_[i])...}; } ATLAS_HOST_DEVICE mdspan_extents_type mdspan_extents() const { return _get_mdspan_extents(std::make_integer_sequence{}); } template ATLAS_HOST_DEVICE mdspan_strides_type _get_mdspan_strides(std::integer_sequence = {}) const { return mdspan_strides_type{(static_cast(strides_[i]))...}; } ATLAS_HOST_DEVICE mdspan_strides_type mdspan_strides() const { return _get_mdspan_strides(std::make_integer_sequence{}); } // -- Private data template friend class ArrayView; value_type* data_; size_t size_; idx_t shape_[Rank]; idx_t strides_[Rank]; }; //------------------------------------------------------------------------------------------------------ #undef ENABLE_IF_NON_CONST #undef ENABLE_IF_CONST_WITH_NON_CONST } // namespace array } // namespace atlas atlas-0.46.0/src/atlas/array/native/NativeArrayView.cc0000664000175000017500000001413115160212070022751 0ustar alastairalastair/* * (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 "atlas/array/ArrayView.h" #include "atlas/array/helpers/ArrayAssigner.h" #include "atlas/array/helpers/ArrayCopier.h" #include "atlas/array/helpers/ArrayWriter.h" //------------------------------------------------------------------------------------------------------ namespace atlas { namespace array { //------------------------------------------------------------------------------------------------------ #undef ENABLE_IF_NON_CONST #define ENABLE_IF_NON_CONST \ template ::value && EnableBool), int>::type*> template ENABLE_IF_NON_CONST void ArrayView::assign(const value_type& value) { helpers::array_assigner::apply(*this, value); } //------------------------------------------------------------------------------------------------------ template ENABLE_IF_NON_CONST void ArrayView::assign(const std::initializer_list& list) { helpers::array_assigner::apply(*this, list); } //------------------------------------------------------------------------------------------------------ template ENABLE_IF_NON_CONST void ArrayView::assign(const ArrayView& other) { helpers::array_copier::apply(other, *this); } //------------------------------------------------------------------------------------------------------ template ENABLE_IF_NON_CONST void ArrayView::assign(const LocalView& other) { helpers::array_copier::apply(other, *this); } //------------------------------------------------------------------------------------------------------ template void ArrayView::dump(std::ostream& os) const { os << "size: " << size() << " , values: "; os << "[ "; helpers::array_writer::apply(*this, os); os << " ]"; } //------------------------------------------------------------------------------------------------------ } // namespace array } // namespace atlas //----------------------------------------------------------------------- // Explicit instantiation namespace atlas { namespace array { #define EXPLICIT_TEMPLATE_INSTANTIATION(Rank) \ template class ArrayView; \ template class ArrayView; \ template class ArrayView; \ template class ArrayView; \ template class ArrayView; \ template class ArrayView; \ template class ArrayView; \ template class ArrayView; \ template class ArrayView; \ template class ArrayView; \ template class ArrayView; \ template class ArrayView; \ template void ArrayView::assign(int const&); \ template void ArrayView::assign(long const&); \ template void ArrayView::assign(float const&); \ template void ArrayView::assign(double const&); \ template void ArrayView::assign(std::initializer_list const&); \ template void ArrayView::assign(std::initializer_list const&); \ template void ArrayView::assign(std::initializer_list const&); \ template void ArrayView::assign(std::initializer_list const&); \ template void ArrayView::assign(ArrayView const&); \ template void ArrayView::assign(ArrayView const&); \ template void ArrayView::assign(ArrayView const&); \ template void ArrayView::assign(ArrayView const&); \ template void ArrayView::assign(LocalView const&); \ template void ArrayView::assign(LocalView const&); \ template void ArrayView::assign(LocalView const&); \ template void ArrayView::assign(LocalView const&); // For each NDims in [1..9] EXPLICIT_TEMPLATE_INSTANTIATION(1) EXPLICIT_TEMPLATE_INSTANTIATION(2) EXPLICIT_TEMPLATE_INSTANTIATION(3) EXPLICIT_TEMPLATE_INSTANTIATION(4) EXPLICIT_TEMPLATE_INSTANTIATION(5) EXPLICIT_TEMPLATE_INSTANTIATION(6) EXPLICIT_TEMPLATE_INSTANTIATION(7) EXPLICIT_TEMPLATE_INSTANTIATION(8) EXPLICIT_TEMPLATE_INSTANTIATION(9) #undef EXPLICIT_TEMPLATE_INSTANTIATION } // namespace array } // namespace atlas atlas-0.46.0/src/atlas/array/native/NativeMakeView.cc0000664000175000017500000001566715160212070022567 0ustar alastairalastair/* * (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 "pluto/pluto.h" #include "atlas/array.h" #include "atlas/array/ArrayView.h" #include "atlas/array/IndexView.h" #include "atlas/library/config.h" #include "atlas/runtime/Exception.h" namespace atlas { namespace array { namespace { template inline static void check_metadata(const Array& array) { if (array.rank() != Rank) { std::stringstream err; err << "Number of dimensions do not match: template argument " << Rank << " expected to be " << array.rank(); throw_Exception(err.str(), Here()); } if (array.datatype() != array::DataType::create()) { std::stringstream err; err << "Data Type does not match: template argument expected to be " << array.datatype().str(); throw_Exception(err.str(), Here()); } } } // namespace //------------------------------------------------------------------------------ template ArrayView make_host_view(Array& array) { return ArrayView(array.host_data(), array.shape(), array.strides()); } template ArrayView make_host_view(const Array& array) { return ArrayView(array.host_data(), array.shape(), array.strides()); } template ArrayView make_device_view(Array& array) { #if ATLAS_HAVE_GPU if (pluto::devices()) { ATLAS_ASSERT(array.deviceAllocated() || pluto::devices(), "make_device_view: Array not allocated on device"); } return ArrayView((array.device_data()), array.shape(), array.device_strides()); #else return make_host_view(array); #endif } template ArrayView make_device_view(const Array& array) { #if ATLAS_HAVE_GPU if (pluto::devices()) { ATLAS_ASSERT(array.deviceAllocated(), "make_device_view: Array not allocated on device"); } return ArrayView(array.device_data(), array.shape(), array.device_strides()); #else return make_host_view(array); #endif } template IndexView make_host_indexview(Array& array) { return IndexView((Value*)(array.storage()), array.shape().data()); } template IndexView make_host_indexview(const Array& array) { return IndexView((const Value*)(array.storage()), array.shape().data()); } template IndexView make_indexview(Array& array) { check_metadata(array); return make_host_indexview(array); } template IndexView make_indexview(const Array& array) { check_metadata(array); return make_host_indexview(array); } template ArrayView make_view(Array& array) { check_metadata(array); return make_host_view(array); } template ArrayView make_view(const Array& array) { check_metadata(array); return make_host_view(array); } // -------------------------------------------------------------------------------------------- } // namespace array } // namespace atlas //----------------------------------------------------------------------- // Explicit instantiation namespace atlas { namespace array { #define EXPLICIT_TEMPLATE_INSTANTIATION_TYPE_RANK(TYPE, RANK) \ template ArrayView make_view(Array&); \ template ArrayView make_view(Array&); \ template ArrayView make_view(const Array&); \ template ArrayView make_view(const Array&); \ \ template ArrayView make_host_view(Array&); \ template ArrayView make_host_view(Array&); \ template ArrayView make_host_view(const Array&); \ template ArrayView make_host_view(const Array&); \ \ template ArrayView make_device_view(Array&); \ template ArrayView make_device_view(Array&); \ template ArrayView make_device_view(const Array&); \ template ArrayView make_device_view(const Array&); #define EXPLICIT_TEMPLATE_INSTATIATION(RANK) \ EXPLICIT_TEMPLATE_INSTANTIATION_TYPE_RANK(int, RANK) \ EXPLICIT_TEMPLATE_INSTANTIATION_TYPE_RANK(long, RANK) \ EXPLICIT_TEMPLATE_INSTANTIATION_TYPE_RANK(float, RANK) \ EXPLICIT_TEMPLATE_INSTANTIATION_TYPE_RANK(double, RANK) \ EXPLICIT_TEMPLATE_INSTANTIATION_TYPE_RANK(unsigned int, RANK) \ EXPLICIT_TEMPLATE_INSTANTIATION_TYPE_RANK(unsigned long, RANK) EXPLICIT_TEMPLATE_INSTATIATION(1) EXPLICIT_TEMPLATE_INSTATIATION(2) EXPLICIT_TEMPLATE_INSTATIATION(3) EXPLICIT_TEMPLATE_INSTATIATION(4) EXPLICIT_TEMPLATE_INSTATIATION(5) EXPLICIT_TEMPLATE_INSTATIATION(6) EXPLICIT_TEMPLATE_INSTATIATION(7) EXPLICIT_TEMPLATE_INSTATIATION(8) EXPLICIT_TEMPLATE_INSTATIATION(9) #undef EXPLICIT_TEMPLATE_INSTATIATION_TYPE_RANK #undef EXPLICIT_TEMPLATE_INSTATIATION #define EXPLICIT_TEMPLATE_INSTANTIATION_INDEXVIEW_TYPE_RANK(TYPE, RANK) \ template IndexView make_indexview(Array&); \ template IndexView make_indexview(Array&); \ template IndexView make_indexview(const Array&); \ template IndexView make_indexview(const Array&); #define EXPLICIT_TEMPLATE_INSTANTIATION_INDEXVIEW(RANK) \ EXPLICIT_TEMPLATE_INSTANTIATION_INDEXVIEW_TYPE_RANK(int, RANK) \ EXPLICIT_TEMPLATE_INSTANTIATION_INDEXVIEW_TYPE_RANK(long, RANK) EXPLICIT_TEMPLATE_INSTANTIATION_INDEXVIEW(1) // EXPLICIT_TEMPLATE_INSTANTIATION_INDEXVIEW(2) #undef EXPLICIT_TEMPLATE_INSTANTIATION_INDEXVIEW #undef EXPLICIT_TEMPLATE_INSTANTIATION_INDEXVIEW } // namespace array } // namespace atlas atlas-0.46.0/src/atlas/array/native/NativeIndexView.h0000664000175000017500000001750215160212070022611 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ /// @file IndexView.h /// This file contains the IndexView class, a class that allows to wrap any /// contiguous raw data into /// a view which is accessible with multiple indices. /// This view is intended to work with Connectivity Tables storing Fortran /// Numbering internally /// All it needs is the strides for each index, and the shape of each index. /// ATTENTION: The last index is stride 1 /// /// Bounds-checking can be turned ON by defining /// "ATLAS_INDEXVIEW_BOUNDS_CHECKING" /// before including this header. /// /// Example: /// int[] array = { 1, 2, 3, 4, 5, 6, 7, 8, 9}; /// int[2] strides = { 3, 1 }; /// int[2] shape = { 3, 3 }; /// IndexView matrix( array, strides, shape ); /// for( size_t i=0; i fieldview( Field ); /// IndexView INDEXVIEW( Array ); #pragma once #include #include #include #include #include "atlas/array/ArrayDataStore.h" #include "atlas/library/config.h" #include "atlas/mdspan.h" //------------------------------------------------------------------------------------------------------ namespace atlas { namespace array { //------------------------------------------------------------------------------------------------------ #if ATLAS_HAVE_FORTRAN #define INDEX_REF Index #define FROM_FORTRAN -1 #else #define INDEX_REF * #define FROM_FORTRAN #endif #define ENABLE_IF_NON_CONST \ template ::value && EnableBool), int>::type* = nullptr> //------------------------------------------------------------------------------------------------------ ///@brief Multidimensional access to Array or Field objects containing index fields /// that are compatible with Fortran indexing /// /// Index fields that are compatible with Fortran are 1-based: a value `1` corresponds /// to the first index in a Fortran array. In C++ the first index would of course correspond to /// the value `0`. To stay compatible, we created this class IndexView that subtracts `1` /// when an index value is accessed, and adds `1` when a value is set. /// /// If Atlas is compiled without the FORTRAN feature (ATLAS_HAVE_FORTRAN==0), then the addition /// and substraction of `1` is compiled out, which may slightly improve performance. template class IndexView { public: using value_type = typename remove_const::type; using accessor_type = index_accessor; using reference = typename accessor_type::reference; private: using mdspan_extents_type = dextents; using mdspan_strides_type = std::array; using mdspan_type = mdspan>; using const_mdspan_type = mdspan>; public: #ifndef DOXYGEN_SHOULD_SKIP_THIS IndexView(Value* data, const idx_t shape[Rank]); IndexView(Value* data, const idx_t shape[Rank], const idx_t strides[Rank]); #endif template && E::rank() == Rank>> IndexView(mdspan& other) : data_(other.data_handle()) { for (int j = 0; j < Rank; ++j) { shape_[j] = other.extent(j); strides_[j] = other.stride(j); } } // -- Access methods /// @brief Multidimensional index operator: view(i,j,k,...) template reference operator()(Idx... idx) { check_bounds(idx...); return accessor_.access(data_, index(idx...)); } template value_type operator()(Idx... idx) const { check_bounds(idx...); return data_[index(idx...)] FROM_FORTRAN; } ENABLE_IF_NON_CONST void assign(const std::initializer_list& list); template idx_t shape(Int idx) const { return shape_[idx]; } mdspan_type as_mdspan() { return mdspan_type{data_, {mdspan_extents(), mdspan_strides()}}; } const_mdspan_type as_mdspan() const { return const_mdspan_type{data_, {mdspan_extents(), mdspan_strides()}}; } 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; template mdspan_extents_type _get_mdspan_extents(std::integer_sequence = {}) const { return mdspan_extents_type{(shape_[i])...}; } mdspan_extents_type mdspan_extents() const { return _get_mdspan_extents(std::make_integer_sequence{}); } template mdspan_strides_type _get_mdspan_strides(std::integer_sequence = {}) const { return mdspan_strides_type{(static_cast(strides_[i]))...}; } mdspan_strides_type mdspan_strides() const { return _get_mdspan_strides(std::make_integer_sequence{}); } private: Value* data_; idx_t strides_[Rank]; idx_t shape_[Rank]; static constexpr accessor_type accessor_{}; }; template class LocalIndexView : public IndexView { using Base = IndexView; public: using Base::Base; }; #undef INDEX_REF #undef FROM_FORTRAN #undef ENABLE_IF_NON_CONST //------------------------------------------------------------------------------------------------------ } // namespace array } // namespace atlas atlas-0.46.0/src/atlas/array/native/NativeArray.cc0000664000175000017500000003166215160212070022126 0ustar alastairalastair/* * (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/array.h" #include "atlas/array/ArrayDataStore.h" #include "atlas/array/MakeView.h" #include "atlas/array/helpers/ArrayInitializer.h" #include "atlas/array/helpers/ArrayWriter.h" #include "atlas/array/native/NativeDataStore.h" #include "atlas/runtime/Exception.h" using namespace atlas::array::helpers; namespace atlas { namespace array { template Array* Array::create(idx_t dim0) { return new ArrayT(dim0); } template Array* Array::create(idx_t dim0, idx_t dim1) { return new ArrayT(dim0, dim1); } template Array* Array::create(idx_t dim0, idx_t dim1, idx_t dim2) { return new ArrayT(dim0, dim1, dim2); } template Array* Array::create(idx_t dim0, idx_t dim1, idx_t dim2, idx_t dim3) { return new ArrayT(dim0, dim1, dim2, dim3); } template Array* Array::create(idx_t dim0, idx_t dim1, idx_t dim2, idx_t dim3, idx_t dim4) { return new ArrayT(dim0, dim1, dim2, dim3, dim4); } template Array* Array::create(const ArrayShape& shape) { return new ArrayT(shape); } template Array* Array::create(const ArrayShape& shape, const ArrayLayout& layout) { return new ArrayT(shape, layout); } template Array* Array::wrap(Value* data, const ArrayShape& shape) { size_t size = 1; for (int i = 0; i < shape.size(); ++i) { size *= shape[i]; } return new ArrayT(new native::WrappedDataStore(data, size), shape); } template Array* Array::wrap(Value* data, const ArraySpec& spec) { return new ArrayT(new native::WrappedDataStore(data, spec), spec); } Array::~Array() = default; Array* Array::create(DataType datatype, const ArrayShape& shape) { switch (datatype.kind()) { case DataType::KIND_REAL64: return new ArrayT(shape); case DataType::KIND_REAL32: return new ArrayT(shape); case DataType::KIND_INT32: return new ArrayT(shape); case DataType::KIND_INT64: return new ArrayT(shape); case DataType::KIND_UINT32: return new ArrayT(shape); case DataType::KIND_UINT64: return new ArrayT(shape); default: { std::stringstream err; err << "data kind " << datatype.kind() << " not recognised."; throw_NotImplemented(err.str(), Here()); } } } Array* Array::create(DataType datatype, ArraySpec&& spec) { switch (datatype.kind()) { case DataType::KIND_REAL64: return new ArrayT(std::move(spec)); case DataType::KIND_REAL32: return new ArrayT(std::move(spec)); case DataType::KIND_INT32: return new ArrayT(std::move(spec)); case DataType::KIND_INT64: return new ArrayT(std::move(spec)); case DataType::KIND_UINT32: return new ArrayT(std::move(spec)); case DataType::KIND_UINT64: return new ArrayT(std::move(spec)); default: { std::stringstream err; err << "data kind " << datatype.kind() << " not recognised."; throw_NotImplemented(err.str(), Here()); } } } Array* Array::create(ArraySpec&& spec) { return create(spec.datatype(), std::move(spec)); } template ArrayT::ArrayT(ArrayDataStore* ds, const ArraySpec& spec) { data_store_ = std::unique_ptr(ds); spec_ = spec; } template ArrayT::ArrayT(idx_t dim0) { spec_ = ArraySpec(make_shape(dim0)); data_store_ = std::make_unique>(spec_.size()); } template ArrayT::ArrayT(idx_t dim0, idx_t dim1) { spec_ = ArraySpec(make_shape(dim0, dim1)); data_store_ = std::make_unique>(spec_.size()); } template ArrayT::ArrayT(idx_t dim0, idx_t dim1, idx_t dim2) { spec_ = ArraySpec(make_shape(dim0, dim1, dim2)); data_store_ = std::make_unique>(spec_.size()); } template ArrayT::ArrayT(idx_t dim0, idx_t dim1, idx_t dim2, idx_t dim3) { spec_ = ArraySpec(make_shape(dim0, dim1, dim2, dim3)); data_store_ = std::make_unique>(spec_.size()); } template ArrayT::ArrayT(idx_t dim0, idx_t dim1, idx_t dim2, idx_t dim3, idx_t dim4) { spec_ = ArraySpec(make_shape(dim0, dim1, dim2, dim3, dim4)); data_store_ = std::make_unique>(spec_.size()); } template ArrayT::ArrayT(const ArrayShape& shape) { ATLAS_ASSERT(shape.size() > 0); size_t size = 1; for (size_t j = 0; j < shape.size(); ++j) { size *= size_t(shape[j]); } data_store_ = std::make_unique>(size); spec_ = ArraySpec(shape); } template ArrayT::ArrayT(const ArrayShape& shape, const ArrayAlignment& alignment) { spec_ = ArraySpec(shape, alignment); data_store_ = std::make_unique>(spec_.allocatedSize()); } template ArrayT::ArrayT(const ArrayShape& shape, const ArrayLayout& layout) { spec_ = ArraySpec(shape); data_store_ = std::make_unique>(spec_.size()); for (size_t j = 0; j < layout.size(); ++j) { ATLAS_ASSERT(spec_.layout()[j] == layout[j]); } } template ArrayT::ArrayT(ArraySpec&& spec): Array(std::move(spec)) { data_store_ = std::make_unique>(spec_.allocatedSize()); } template void ArrayT::resize(const ArrayShape& _shape) { if (rank() != static_cast(_shape.size())) { std::stringstream msg; msg << "Cannot resize existing Array with rank " << rank() << " with a shape of rank " << _shape.size(); throw_Exception(msg.str(), Here()); } Array* resized = Array::create(_shape); array_initializer::apply(*this,*resized); replace(*resized); delete resized; } template void ArrayT::copy(const Array& other, const CopyPolicy&) { array_initializer::apply(other,*this); } template void ArrayT::insert(idx_t idx1, idx_t size1) { ArrayShape nshape = shape(); if (idx1 > nshape[0]) { throw_Exception("Cannot insert into an array at a position beyond its size", Here()); } nshape[0] += size1; Array* resized = Array::create(nshape); array_initializer_partitioned<0>::apply(*this, *resized, idx1, size1); replace(*resized); delete resized; } template void ArrayT::resize(idx_t size1) { resize(make_shape(size1)); } template void ArrayT::resize(idx_t size1, idx_t size2) { resize(make_shape(size1, size2)); } template void ArrayT::resize(idx_t size1, idx_t size2, idx_t size3) { resize(make_shape(size1, size2, size3)); } template void ArrayT::resize(idx_t size1, idx_t size2, idx_t size3, idx_t size4) { resize(make_shape(size1, size2, size3, size4)); } template void ArrayT::resize(idx_t size1, idx_t size2, idx_t size3, idx_t size4, idx_t size5) { resize(make_shape(size1, size2, size3, size4, size5)); } template void ArrayT::dump(std::ostream& out) const { switch (rank()) { case 1: make_host_view(*this).dump(out); break; case 2: make_host_view(*this).dump(out); break; case 3: make_host_view(*this).dump(out); break; case 4: make_host_view(*this).dump(out); break; case 5: make_host_view(*this).dump(out); break; case 6: make_host_view(*this).dump(out); break; case 7: make_host_view(*this).dump(out); break; case 8: make_host_view(*this).dump(out); break; case 9: make_host_view(*this).dump(out); break; default: ATLAS_NOTIMPLEMENTED; } } //------------------------------------------------------------------------------ template size_t ArrayT::footprint() const { size_t size = sizeof(*this); size += bytes(); if (not contiguous()) { ATLAS_NOTIMPLEMENTED; } return size; } template void ArrayT::accMap() const { data_store_->accMap(); } template void ArrayT::accUnmap() const { data_store_->accUnmap(); } template bool ArrayT::accMapped() const { return data_store_->accMapped(); } //------------------------------------------------------------------------------ template Array* Array::create(idx_t); template Array* Array::create(idx_t); template Array* Array::create(idx_t); template Array* Array::create(idx_t); template Array* Array::create(idx_t); template Array* Array::create(idx_t); template Array* Array::create(idx_t, idx_t); template Array* Array::create(idx_t, idx_t); template Array* Array::create(idx_t, idx_t); template Array* Array::create(idx_t, idx_t); template Array* Array::create(idx_t, idx_t); template Array* Array::create(idx_t, idx_t); template Array* Array::create(idx_t, idx_t, idx_t); template Array* Array::create(idx_t, idx_t, idx_t); template Array* Array::create(idx_t, idx_t, idx_t); template Array* Array::create(idx_t, idx_t, idx_t); template Array* Array::create(idx_t, idx_t, idx_t); template Array* Array::create(idx_t, idx_t, idx_t, idx_t); template Array* Array::create(idx_t, idx_t, idx_t, idx_t); template Array* Array::create(idx_t, idx_t, idx_t, idx_t); template Array* Array::create(idx_t, idx_t, idx_t, idx_t); template Array* Array::create(idx_t, idx_t, idx_t, idx_t); template Array* Array::create(idx_t, idx_t, idx_t, idx_t, idx_t); template Array* Array::create(idx_t, idx_t, idx_t, idx_t, idx_t); template Array* Array::create(idx_t, idx_t, idx_t, idx_t, idx_t); template Array* Array::create(idx_t, idx_t, idx_t, idx_t, idx_t); template Array* Array::create(idx_t, idx_t, idx_t, idx_t, idx_t); template Array* Array::create(const ArrayShape&); template Array* Array::create(const ArrayShape&); template Array* Array::create(const ArrayShape&); template Array* Array::create(const ArrayShape&); template Array* Array::create(const ArrayShape&); template Array* Array::create(const ArrayShape&); template Array* Array::create(const ArrayShape&, const ArrayLayout&); template Array* Array::create(const ArrayShape&, const ArrayLayout&); template Array* Array::create(const ArrayShape&, const ArrayLayout&); template Array* Array::create(const ArrayShape&, const ArrayLayout&); template Array* Array::create(const ArrayShape&, const ArrayLayout&); template Array* Array::create(const ArrayShape&, const ArrayLayout&); template Array* Array::wrap(int*, const ArrayShape&); template Array* Array::wrap(long*, const ArrayShape&); template Array* Array::wrap(float*, const ArrayShape&); template Array* Array::wrap(double*, const ArrayShape&); template Array* Array::wrap(long unsigned*, const ArrayShape&); template Array* Array::wrap(unsigned int*, const ArrayShape&); template Array* Array::wrap(int*, const ArraySpec&); template Array* Array::wrap(long*, const ArraySpec&); template Array* Array::wrap(float*, const ArraySpec&); template Array* Array::wrap(double*, const ArraySpec&); template Array* Array::wrap(long unsigned*, const ArraySpec&); template Array* Array::wrap(unsigned int*, const ArraySpec&); template class ArrayT; template class ArrayT; template class ArrayT; template class ArrayT; template class ArrayT; template class ArrayT; } // namespace array } // namespace atlas atlas-0.46.0/src/atlas/array/TableView.cc0000664000175000017500000000537615160212070020300 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "atlas/array/TableView.h" namespace atlas { namespace array { // ---------------------------------------------------------------------------- template TableView::TableView(const Table& table, bool host): host_(host), missing_value_(table.missing_value()), rows_(table.rows()), maxcols_(table.maxcols()), mincols_(table.mincols()), values_(host_ ? make_host_view(*(table.data_[_values_])) : make_device_view(*(table.data_[_values_]))), displs_(host_ ? make_host_view(*(table.data_[_displs_])) : make_device_view(*(table.data_[_displs_]))), counts_(host_ ? make_host_view(*(table.data_[_counts_])) : make_device_view(*(table.data_[_counts_]))), const_access_(this), access_(this) {} // ---------------------------------------------------------------------------- template TableView::TableView(const TableView& other): host_(other.host_), missing_value_(other.missing_value_), rows_(other.rows_), maxcols_(other.maxcols_), mincols_(other.mincols_), values_(other.values_), displs_(other.displs_), counts_(other.counts_), const_access_(this), access_(this) {} // ---------------------------------------------------------------------------- template TableView TableView::operator=(const TableView& other) { host_ = other.host_; missing_value_ = other.missing_value_; rows_ = other.rows_; maxcols_ = other.maxcols_; mincols_ = other.mincols_; values_ = other.values_; displs_ = other.displs_; counts_ = other.counts_; return *this; } // ---------------------------------------------------------------------------- template TableView make_table_view(const Table& table) { return TableView(table); } // ---------------------------------------------------------------------------- template class TableView; template class TableView; template TableView make_table_view(const Table&); template TableView make_table_view(const Table&); // ---------------------------------------------------------------------------- } // namespace array } // namespace atlas atlas-0.46.0/src/atlas/array/Range.h0000664000175000017500000000605115160212070017303 0ustar alastairalastair/* * (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 //------------------------------------------------------------------------------ namespace atlas { namespace array { //------------------------------------------------------------------------------ #ifndef DOXYGEN_SHOULD_SKIP_THIS namespace helpers { //------------------------------------------------------------------------------ class RangeBase {}; //------------------------------------------------------------------------------ class RangeFrom : public RangeBase { public: RangeFrom(int start): start_(start) {} int start() const { return start_; } template int end(const View& view) const { return view.shape(Dim); } template int end(const View& view, int i) const { return view.shape(i); } private: int start_; }; //------------------------------------------------------------------------------ class RangeTo : public RangeBase { public: RangeTo(int end): end_(end) {} int start() const { return 0; } int end() const { return end_; } private: int end_; }; //------------------------------------------------------------------------------ class RangeAll : public RangeBase { public: int start() const { return 0; } template int end(const View& view) const { return view.shape(Dim); } template int end(const View& view, int i) const { return view.shape(i); } friend std::ostream& operator<<(std::ostream& out, const RangeAll& range) { out << ":"; return out; } }; class RangeDummy : public RangeBase {}; //------------------------------------------------------------------------------ } // namespace helpers #endif //------------------------------------------------------------------------------ class Range : public helpers::RangeBase { private: using From = helpers::RangeFrom; using To = helpers::RangeTo; using All = helpers::RangeAll; using Dummy = helpers::RangeDummy; public: static From from(int start) { return From(start); } static To to(int end) { return To(end); } static constexpr All all() { return All(); } static constexpr Dummy dummy() { return Dummy(); } public: template Range(Start start, End end): start_(static_cast(start)), end_(static_cast(end)) {} int start() const { return start_; } int end() const { return end_; } Range(): Range(0, 0) {} private: int start_; int end_; }; //------------------------------------------------------------------------------ } // namespace array } // namespace atlas atlas-0.46.0/src/atlas/array/ArrayViewDefs.h0000664000175000017500000000164015160212070020761 0ustar alastairalastair/* * (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 namespace atlas { namespace array { #ifndef DOXYGEN_SHOULD_SKIP_THIS template struct Dim { static constexpr int cdim = cDim; }; struct LastDim {}; struct FirstDim {}; template struct is_dim_policy : std::false_type {}; template struct is_dim_policy> : std::true_type {}; template <> struct is_dim_policy : std::true_type {}; template <> struct is_dim_policy : std::true_type {}; #endif } // namespace array } // namespace atlas atlas-0.46.0/src/atlas/array/ArrayView.h0000664000175000017500000000324715160212070020164 0ustar alastairalastair/* * (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/library/config.h" #if ATLAS_HAVE_GRIDTOOLS_STORAGE #include "atlas/array/gridtools/GridToolsArrayView.h" #else #include "atlas/array/native/NativeArrayView.h" #endif namespace atlas { namespace array { #define EXPLICIT_TEMPLATE_DECLARATION_TYPE_RANK(TYPE, RANK) \ extern template class ArrayView; \ extern template class ArrayView; #define EXPLICIT_TEMPLATE_DECLARATION(RANK) \ EXPLICIT_TEMPLATE_DECLARATION_TYPE_RANK(int, RANK); \ EXPLICIT_TEMPLATE_DECLARATION_TYPE_RANK(long, RANK); \ EXPLICIT_TEMPLATE_DECLARATION_TYPE_RANK(float, RANK); \ EXPLICIT_TEMPLATE_DECLARATION_TYPE_RANK(double, RANK); // The Fujitsu compiler complains about missing references with the below // so we just skip it in that case #if !defined(__FUJITSU) // For each RANK in [1..9] EXPLICIT_TEMPLATE_DECLARATION(1) EXPLICIT_TEMPLATE_DECLARATION(2) EXPLICIT_TEMPLATE_DECLARATION(3) EXPLICIT_TEMPLATE_DECLARATION(4) EXPLICIT_TEMPLATE_DECLARATION(5) EXPLICIT_TEMPLATE_DECLARATION(6) EXPLICIT_TEMPLATE_DECLARATION(7) EXPLICIT_TEMPLATE_DECLARATION(8) EXPLICIT_TEMPLATE_DECLARATION(9) #endif #undef EXPLICIT_TEMPLATE_DECLARATION_TYPE_RANK #undef EXPLICIT_TEMPLATE_DECLARATION } // namespace array } // namespace atlas atlas-0.46.0/src/atlas/array/DataType.h0000664000175000017500000000067015160212070017763 0ustar alastairalastair/* * (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/util/DataType.h" atlas-0.46.0/src/atlas/array/LocalView.h0000664000175000017500000002521015160212070020132 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ /// @file LocalView.h /// This file contains the LocalView class, a class that allows to wrap any /// contiguous raw data into /// a view which is accessible with multiple indices. #pragma once #include #include #include #include "atlas/array/ArrayDataStore.h" #include "atlas/array/ArrayViewDefs.h" #include "atlas/array/helpers/ArraySlicer.h" #include "atlas/library/config.h" #include "atlas/mdspan.h" //------------------------------------------------------------------------------------------------------ namespace atlas { namespace array { /// @brief Multi-dimensional access existing POD array pointer. /// /// A LocalView is a wrapper around data that enables multidimensional access, and has the exact /// same API as ArrayView. /// /// The data may be strided. /// /// ### Example 1: /// /// @code{.cpp} /// int[] array = { 1, 2, 3, 4, 5, 6, 7, 8, 9}; /// int[2] strides = { 3, 1 }; /// int[2] shape = { 3, 3 }; /// LocalView matrix( array, shape, strides ); /// for( idx_t i=0; i matrix( array, shape ); /// which is identical for this matrix to previous Example 1 /// @endcode template class LocalView { template using is_non_const_value_type = typename std::is_same::type>; #define ENABLE_IF_NON_CONST \ template ::value && EnableBool), int>::type* = nullptr> #define ENABLE_IF_CONST_WITH_NON_CONST(T) \ template ::value && is_non_const_value_type::value), \ int>::type* = nullptr> public: // -- Type definitions using value_type = Value; using return_type = value_type; static constexpr int RANK{Rank}; private: using slicer_t = typename helpers::ArraySlicer>; using const_slicer_t = typename helpers::ArraySlicer>; template struct slice_t { using type = typename slicer_t::template Slice::type; }; template struct const_slice_t { using type = typename const_slicer_t::template Slice::type; }; using mdspan_extents_type = dextents; using mdspan_strides_type = std::array; using mdspan_type = mdspan; using const_mdspan_type = mdspan; public: // -- Constructors template >> LocalView(const LocalView& other): data_(other.data_), size_(other.size_), shape_(other.shape_), strides_(other.strides_) {} template && std::is_integral_v && std::is_integral_v>> LocalView(ValueTp* data, const Int1 shape[], const Int2 strides[]): data_(data) { size_ = 1; for (idx_t j = 0; j < Rank; ++j) { shape_[j] = shape[j]; strides_[j] = strides[j]; size_ *= shape_[j]; } } template && std::is_integral_v>> LocalView(ValueTp* data, const Int shape[]): data_(data) { size_ = 1; for (int j = Rank - 1; j >= 0; --j) { shape_[j] = shape[j]; strides_[j] = size_; size_ *= shape_[j]; } } template >> LocalView(ValueTp* data, const ArrayShape& shape) : LocalView(data,shape.data()) {} template && E::rank() == Rank>> LocalView(mdspan& other) : data_(other.data_handle()), size_(other.size()) { for (int j = 0; j < Rank; ++j) { shape_[j] = other.extent(j); strides_[j] = other.stride(j); } } ENABLE_IF_CONST_WITH_NON_CONST(value_type) operator const LocalView&() const { static_assert(std::is_const::value, "must be const"); static_assert(!std::is_const::value, "must be non-const"); return (const LocalView&)(*this); } const_mdspan_type as_mdspan() const { return const_mdspan_type{this->data(), {mdspan_extents(), mdspan_strides()}}; } mdspan_type as_mdspan() { return mdspan_type{this->data(), {mdspan_extents(), mdspan_strides()}}; } // -- Access methods template > value_type& operator()(Idx... idx) { check_bounds(idx...); return data_[index(idx...)]; } template > const value_type& operator()(Idx... idx) const { check_bounds(idx...); return data_[index(idx...)]; } template > const value_type& operator[](Idx idx) const { check_bounds(idx); return data_[index(idx)]; } template > value_type& operator[](Idx idx) { check_bounds(idx); return data_[index(idx)]; } idx_t size() const { return size_; } template idx_t shape(Int idx) const { return shape_[idx]; } template idx_t stride(Int idx) const { return strides_[idx]; } const idx_t* shape() const { return shape_.data(); } const idx_t* strides() const { return strides_.data(); } value_type const* data() const { return data_; } value_type* data() { return data_; } bool contiguous() const { return (size_ == shape_[0] * strides_[0] ? true : false); } ENABLE_IF_NON_CONST void assign(const value_type& value); void dump(std::ostream& os) const; static constexpr idx_t rank() { return Rank; } template typename slice_t::type slice(Args... args) { return slicer_t(*this).apply(args...); } template typename const_slice_t::type slice(Args... args) const { return const_slicer_t(*this).apply(args...); } friend std::ostream& operator<<(std::ostream& out, const LocalView& x) { x.dump(out); return out; } 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_ARRAYVIEW_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... idx) const { static_assert(sizeof...(idx) == Rank, "Expected number of indices is different from rank of array"); } #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("LocalView", 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("LocalView", array_dim(), last_idx, shape_[Dim]); } } template ATLAS_HOST_DEVICE mdspan_extents_type _get_mdspan_extents(std::integer_sequence = {}) const { return mdspan_extents_type{(shape_[i])...}; } ATLAS_HOST_DEVICE mdspan_extents_type mdspan_extents() const { return _get_mdspan_extents(std::make_integer_sequence{}); } template ATLAS_HOST_DEVICE mdspan_strides_type _get_mdspan_strides(std::integer_sequence = {}) const { return mdspan_strides_type{(static_cast(strides_[i]))...}; } ATLAS_HOST_DEVICE mdspan_strides_type mdspan_strides() const { return _get_mdspan_strides(std::make_integer_sequence{}); } private: // -- Private data template friend class LocalView; value_type* data_; idx_t size_; std::array shape_; std::array strides_; #undef ENABLE_IF_NON_CONST #undef ENABLE_IF_CONST_WITH_NON_CONST }; template using View = LocalView; } // namespace array } // namespace atlas atlas-0.46.0/src/atlas/array/SVector.cc0000664000175000017500000000121515160212070017767 0ustar alastairalastair/* * (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 "SVector.h" namespace atlas { namespace array { //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ } // namespace array } // namespace atlas atlas-0.46.0/src/atlas/array/ArraySpec.h0000664000175000017500000000657515160212070020153 0ustar alastairalastair/* * (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 #include #include "atlas/array/ArrayIdx.h" #include "atlas/array/ArrayLayout.h" #include "atlas/array/ArrayShape.h" #include "atlas/array/ArrayStrides.h" #include "atlas/array/DataType.h" #include "atlas/library/config.h" //------------------------------------------------------------------------------------------------------ namespace atlas { namespace array { class ArraySpec { private: size_t size_; idx_t rank_; size_t allocated_size_; DataType datatype_; ArrayShape shape_; ArrayStrides strides_; ArrayStrides device_strides_; ArrayLayout layout_; ArrayAlignment alignment_; std::vector shapef_; std::vector stridesf_; std::vector device_stridesf_; bool contiguous_; bool default_layout_; public: ArraySpec(); ArraySpec(const ArrayShape&); ArraySpec(const ArrayShape&, const ArrayStrides&); ArraySpec(const ArrayShape&, const ArrayStrides&, const ArrayLayout&); ArraySpec(const ArrayShape&, const ArrayAlignment&); ArraySpec(const ArrayShape&, const ArrayStrides&, const ArrayAlignment&); ArraySpec(const ArrayShape&, const ArrayStrides&, const ArrayLayout&, const ArrayAlignment&); ArraySpec(DataType, const ArrayShape&); ArraySpec(DataType, const ArrayShape&, const ArrayStrides&); ArraySpec(DataType, const ArrayShape&, const ArrayStrides&, const ArrayLayout&); ArraySpec(DataType, const ArrayShape&, const ArrayAlignment&); ArraySpec(DataType, const ArrayShape&, const ArrayStrides&, const ArrayAlignment&); ArraySpec(DataType, const ArrayShape&, const ArrayStrides&, const ArrayLayout&, const ArrayAlignment&); size_t allocatedSize() const { return allocated_size_; } size_t size() const { return size_; } idx_t rank() const { return rank_; } DataType datatype() const { return datatype_; } const ArrayShape& shape() const { return shape_; } const ArrayAlignment& alignment() const { return alignment_; } const ArrayStrides& strides() const { return strides_; } const ArrayStrides& device_strides() const { return device_strides_; } const ArrayLayout& layout() const { return layout_; } const std::vector& shapef() const; const std::vector& stridesf() const; const std::vector& device_stridesf() const; bool contiguous() const { return contiguous_; } bool hasDefaultLayout() const { return default_layout_; } private: void allocate_fortran_specs(); }; // -------------------------------------------------------------------------------------------- class label { public: label(std::string_view s) { previous_ = get(); set(s); } ~label() { set(previous_); } static std::string_view get(); static void set(std::string_view); private: std::string previous_; }; //------------------------------------------------------------------------------------------------------ } // namespace array } // namespace atlas atlas-0.46.0/src/atlas/array/Table.cc0000664000175000017500000003003415160212070017432 0ustar alastairalastair/* * (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 "Table.h" #include #include #include "atlas/array.h" #include "atlas/array/DataType.h" #include "atlas/library/defines.h" #include "atlas/runtime/Exception.h" #if ATLAS_HAVE_FORTRAN #define FORTRAN_BASE 1 #define TO_FORTRAN +1 #else #define FORTRAN_BASE 0 #define TO_FORTRAN #endif namespace atlas { namespace array { // ---------------------------------------------------------------------------- Table::Table(const std::string& name): name_(name), owns_(true), data_{Array::create(0), // values Array::create(1), // displs Array::create(1)}, // counts missing_value_(std::numeric_limits::is_signed ? -1 : std::numeric_limits::max()), rows_(0), maxcols_(0), mincols_(std::numeric_limits::max()), displs_(make_host_view(*(data_[_displs_]))), counts_(make_host_view(*(data_[_counts_]))), values_(make_host_view(*(data_[_values_]))) { displs_(0) = 0; counts_(0) = 0; } // ---------------------------------------------------------------------------- namespace { static size_t get_total_size_counts(size_t rows, size_t counts[]) { size_t total_size = 0; for (size_t j = 0; j < rows; ++j) { total_size += counts[j]; } return total_size; } } // namespace // ---------------------------------------------------------------------------- Table::Table(idx_t values[], size_t rows, size_t displs[], size_t counts[]): name_(), owns_(false), data_{Array::wrap(values, array::ArrayShape{get_total_size_counts(rows, counts)}), Array::wrap(displs, array::ArrayShape{rows}), Array::wrap(counts, array::ArrayShape{rows})}, missing_value_(std::numeric_limits::is_signed ? -1 : std::numeric_limits::max()), rows_(rows), maxcols_(0), mincols_(std::numeric_limits::max()), displs_(make_host_view(*(data_[_displs_]))), counts_(make_host_view(*(data_[_counts_]))), values_(make_host_view(*(data_[_values_]))) { for (size_t j = 0; j < rows; ++j) { maxcols_ = std::max(maxcols_, counts[j]); mincols_ = std::min(mincols_, counts[j]); } } // ---------------------------------------------------------------------------- Table::~Table() { if (owns_) { std::for_each(data_.begin(), data_.end(), [](array::Array* a) { assert(a); delete a; a = 0; }); } } // ---------------------------------------------------------------------------- void Table::clear() { if (owns()) { resize_values(0); resize_counts_and_displs(1); displs_(0) = 0; counts_(0) = 0; } else { std::for_each(data_.begin(), data_.end(), [](array::Array* a) { a = 0; }); // data_ and host_views will be invalid now! } maxcols_ = 0; mincols_ = std::numeric_limits::max(); } // ---------------------------------------------------------------------------- void Table::resize_values(size_t old_size, size_t new_size, bool initialize, const idx_t values[], bool fortran_array) { resize_values(new_size); idx_t add_base = fortran_array ? 0 : FORTRAN_BASE; if (initialize) { for (size_t j = 0, c = old_size; c < new_size; ++c, ++j) { values_(c) = values[j] + add_base; } } else { for (size_t j = old_size; j < new_size; ++j) { values_(j) = missing_value() TO_FORTRAN; } } } // ---------------------------------------------------------------------------- void Table::resize_counts_and_displs(size_t size) { ATLAS_ASSERT(data_[_displs_] != 0); ATLAS_ASSERT(data_[_counts_] != 0); data_[_displs_]->resize(size); data_[_counts_]->resize(size); displs_ = make_host_view(*(data_[_displs_])); counts_ = make_host_view(*(data_[_counts_])); } // ---------------------------------------------------------------------------- void Table::insert_counts_and_displs(size_t position, size_t rows) { ATLAS_ASSERT(data_[_displs_] != 0); ATLAS_ASSERT(data_[_counts_] != 0); data_[_displs_]->insert(position, rows); data_[_counts_]->insert(position, rows); displs_ = make_host_view(*(data_[_displs_])); counts_ = make_host_view(*(data_[_counts_])); } // ---------------------------------------------------------------------------- void Table::resize_values(size_t size) { ATLAS_ASSERT(data_[_values_] != 0); data_[_values_]->resize(size); values_ = make_host_view(*(data_[_values_])); } // ---------------------------------------------------------------------------- void Table::insert_values(size_t position, size_t size) { ATLAS_ASSERT(data_[_values_] != 0); data_[_values_]->insert(position, size); values_ = make_host_view(*(data_[_values_])); } // ---------------------------------------------------------------------------- void Table::add(size_t rows, size_t cols, const idx_t values[], bool fortran_array) { ATLAS_ASSERT(owns_, "HybridConnectivity must be owned to be resized directly"); size_t old_size = size(); if (rows_ == 0) old_size = 0; size_t new_size = old_size + rows * cols; size_t new_rows = rows_ + rows; resize_counts_and_displs(new_rows + 1); for (size_t j = 0; rows_ < new_rows; ++rows_, ++j) { displs_(rows_ + 1) = displs_(rows_) + cols; counts_(rows_) = cols; } maxcols_ = std::max(maxcols_, cols); mincols_ = std::min(mincols_, cols); resize_values(old_size, new_size, true, values, fortran_array); } // ---------------------------------------------------------------------------- void Table::add(size_t rows, const size_t cols[]) { ATLAS_ASSERT(owns_, "HybridConnectivity must be owned to be resized directly"); size_t old_size = size(); size_t new_size = old_size; for (size_t j = 0; j < rows; ++j) new_size += cols[j]; size_t new_rows = rows_ + rows; resize_counts_and_displs(new_rows + 1); for (size_t j = 0; rows_ < new_rows; ++rows_, ++j) { displs_(rows_ + 1) = displs_(rows_) + cols[j]; counts_(rows_) = cols[j]; maxcols_ = std::max(maxcols_, cols[j]); mincols_ = std::min(mincols_, cols[j]); } resize_values(old_size, new_size, false, NULL, false); } // ---------------------------------------------------------------------------- void Table::add(size_t rows, size_t cols) { ATLAS_ASSERT(owns_, "HybridConnectivity must be owned to be resized directly"); size_t old_size = size(); if (rows_ == 0) old_size = 0; size_t new_size = old_size + rows * cols; size_t new_rows = rows_ + rows; resize_counts_and_displs(new_rows + 1); for (size_t j = 0; rows_ < new_rows; ++rows_, ++j) { displs_(rows_ + 1) = displs_(rows_) + cols; counts_(rows_) = cols; } maxcols_ = std::max(maxcols_, cols); mincols_ = std::min(mincols_, cols); const bool dummy_arg_fortran_array = false; const idx_t* dummy_arg_values = nullptr; resize_values(old_size, new_size, false, dummy_arg_values, dummy_arg_fortran_array); } // ---------------------------------------------------------------------------- void Table::insert(size_t position, size_t rows, size_t cols, const idx_t values[], bool fortran_array) { ATLAS_ASSERT(owns_, "HybridConnectivity must be owned to be resized directly"); size_t position_displs = displs_(position); insert_counts_and_displs(position, rows); displs_(position) = position_displs; for (size_t jrow = position; jrow < position + rows; ++jrow) { counts_(jrow) = cols; } for (size_t jrow = position; jrow < displs_.size() - 1; ++jrow) { displs_(jrow + 1) = displs_(jrow) + counts_(jrow); } maxcols_ = std::max(maxcols_, cols); mincols_ = std::min(mincols_, cols); insert_values(position_displs, rows * cols); if (values == nullptr) { for (size_t c = position_displs; c < position_displs + rows * cols; ++c) { values_(c) = missing_value() TO_FORTRAN; } } else { idx_t add_base = fortran_array ? 0 : FORTRAN_BASE; for (size_t c = position_displs; c < position_displs + rows * cols; ++c) { values_(c) = values[c - position_displs] + add_base; } } rows_ += rows; } // ---------------------------------------------------------------------------- void Table::insert(size_t position, size_t rows, size_t cols) { Table::insert(position, rows, cols, nullptr, false); } // ---------------------------------------------------------------------------- void Table::insert(size_t position, size_t rows, const size_t cols[]) { ATLAS_ASSERT(owns_, "HybridConnectivity must be owned to be resized directly"); size_t position_displs = displs_(position); if (rows_ == 0) { if (position > 1) { insert_counts_and_displs(position - 1, rows); } } else { insert_counts_and_displs(position, rows); } displs_(position) = position_displs; for (size_t jrow = position; jrow < position + rows; ++jrow) { counts_(jrow) = cols[jrow - position]; maxcols_ = std::max(maxcols_, counts_(jrow)); mincols_ = std::min(mincols_, counts_(jrow)); } for (size_t jrow = position; jrow < displs_.size() - 1; ++jrow) { displs_(jrow + 1) = displs_(jrow) + counts_(jrow); } size_t insert_size(0); for (size_t j = 0; j < rows; ++j) insert_size += cols[j]; insert_values(position_displs, insert_size); for (size_t c = position_displs; c < position_displs + insert_size; ++c) { values_(c) = missing_value() TO_FORTRAN; } rows_ += rows; } // ---------------------------------------------------------------------------- void Table::updateDevice() const { std::for_each(data_.begin(), data_.end(), [](array::Array* a) { a->updateDevice(); }); } // ---------------------------------------------------------------------------- void Table::updateHost() const { std::for_each(data_.begin(), data_.end(), [](array::Array* a) { a->updateHost(); }); } // ---------------------------------------------------------------------------- void Table::syncHostDevice() const { std::for_each(data_.begin(), data_.end(), [](array::Array* a) { a->syncHostDevice(); }); } // ---------------------------------------------------------------------------- bool Table::valid() const { bool res = true; std::for_each(data_.begin(), data_.end(), [&](array::Array* a) { res &= a->valid(); }); return res; } // ---------------------------------------------------------------------------- bool Table::hostNeedsUpdate() const { bool res = true; std::for_each(data_.begin(), data_.end(), [&](array::Array* a) { res &= a->hostNeedsUpdate(); }); return res; } // ---------------------------------------------------------------------------- bool Table::deviceNeedsUpdate() const { bool res = true; std::for_each(data_.begin(), data_.end(), [&](array::Array* a) { res &= a->deviceNeedsUpdate(); }); return res; } // ---------------------------------------------------------------------------- size_t Table::footprint() const { size_t size = sizeof(*this); if (owns_) { std::for_each(data_.begin(), data_.end(), [&](array::Array* a) { size += a->footprint(); }); } return size; } // ---------------------------------------------------------------------------- void Table::dump(std::ostream& os) const { values_.dump(os); } // ---------------------------------------------------------------------------- } // namespace array } // namespace atlas atlas-0.46.0/src/atlas/array/ArrayDataStore.cc0000664000175000017500000000146215160212070021273 0ustar alastairalastair/* * (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/array/ArrayDataStore.h" #include "atlas/runtime/Exception.h" namespace atlas { namespace array { void throw_OutOfRange(const std::string& class_name, char idx_str, int idx, int max) { std::ostringstream msg; msg << class_name << " index " << idx << " out of bounds: " << idx << " >= " << max; throw_Exception(msg.str(), Here()); } } // namespace array } // namespace atlas atlas-0.46.0/src/atlas/array/ArrayShape.h0000664000175000017500000000576715160212070020323 0ustar alastairalastair/* * (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 #include "atlas/library/config.h" //------------------------------------------------------------------------------------------------------ namespace atlas { namespace array { class ArrayAlignment { public: ArrayAlignment(): alignment_(1) {} ArrayAlignment(int alignment): alignment_(alignment) {} operator int() const { return alignment_; } private: int alignment_; }; class ArrayShape : public std::vector { private: using Base = std::vector; public: ArrayShape() {} ArrayShape(Base&& base): Base(std::forward(base)) {} ArrayShape(std::initializer_list list): Base(list) {} template ArrayShape(idx_t data[], size_t size): Base(data, data + size) {} template ArrayShape(const std::array& list): Base(list.begin(), list.end()) {} template ArrayShape(const std::vector& list): Base(list.begin(), list.end()) {} }; namespace detail { template inline ArrayShape make_shape(Int size1) { return ArrayShape{static_cast(size1)}; } template inline ArrayShape make_shape(Int1 size1, Int2 size2) { return ArrayShape{static_cast(size1), static_cast(size2)}; } template inline ArrayShape make_shape(Int1 size1, Int2 size2, Int3 size3) { return ArrayShape{static_cast(size1), static_cast(size2), static_cast(size3)}; } template inline ArrayShape make_shape(Int1 size1, Int2 size2, Int3 size3, Int4 size4) { return ArrayShape{static_cast(size1), static_cast(size2), static_cast(size3), static_cast(size4)}; } template inline ArrayShape make_shape(Int1 size1, Int2 size2, Int3 size3, Int4 size4, Int5 size5) { return ArrayShape{static_cast(size1), static_cast(size2), static_cast(size3), static_cast(size4), static_cast(size5)}; } } // namespace detail inline ArrayShape make_shape(std::initializer_list sizes) { return ArrayShape(sizes); } template ArrayShape make_shape(idx_t... indices) { return detail::make_shape(std::forward(indices)...); } //------------------------------------------------------------------------------------------------------ } // namespace array } // namespace atlas atlas-0.46.0/src/atlas/array/ArrayViewVariant.h0000664000175000017500000000665015160212070021512 0ustar alastairalastair/* * (C) Crown Copyright 2024 Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ #pragma once #include #include #include "atlas/array/ArrayView.h" namespace atlas { namespace array { namespace detail { // Container struct for a list of types. template struct Types { using add_const = Types...>; }; // Container struct for a list of integers. template struct Ints {}; template struct VariantHelper; // Recursively construct ArrayView std::variant from types Ts and ranks Is. template struct VariantHelper, Ints, ArrayViews...> { using type = typename VariantHelper, Ints, ArrayViews..., ArrayView...>::type; }; // End recursion. template struct VariantHelper, Ints, ArrayViews...> { using type = std::variant; }; template using Variant = typename VariantHelper::type; using VariantValueTypes = detail::Types; using VariantRanks = detail::Ints<1, 2, 3, 4, 5, 6, 7, 8, 9>; } // namespace detail class Array; /// @brief Variant containing all supported non-const ArrayView alternatives. using ArrayViewVariant = detail::Variant; /// @brief Variant containing all supported const ArrayView alternatives. using ConstArrayViewVariant = detail::Variant; /// @brief Create an ArrayView and assign to an ArrayViewVariant. ArrayViewVariant make_view_variant(Array& array); /// @brief Create a const ArrayView and assign to an ArrayViewVariant. ConstArrayViewVariant make_view_variant(const Array& array); /// @brief Create a host ArrayView and assign to an ArrayViewVariant. ArrayViewVariant make_host_view_variant(Array& array); /// @brief Create a const host ArrayView and assign to an ArrayViewVariant. ConstArrayViewVariant make_host_view_variant(const Array& array); /// @brief Create a device ArrayView and assign to an ArrayViewVariant. ArrayViewVariant make_device_view_variant(Array& array); /// @brief Create a const device ArrayView and assign to an ArrayViewVariant. ConstArrayViewVariant make_device_view_variant(const Array& array); /// @brief Return true if View::rank() is any of Ranks... template constexpr bool is_rank(const View&) { return ((std::decay_t::rank() == Ranks) || ...); } /// @brief Return true if View::value_type is any of ValuesTypes... template constexpr bool is_value_type(const View&) { using ValueType = typename std::decay_t::value_type; return ((std::is_same_v) || ...); } /// @brief Return true if View::non_const_value_type is any of ValuesTypes... template constexpr bool is_non_const_value_type(const View&) { using ValueType = typename std::decay_t::non_const_value_type; return ((std::is_same_v) || ...); } } // namespace array } // namespace atlas atlas-0.46.0/src/atlas/array/Vector.h0000664000175000017500000000766115160212070017521 0ustar alastairalastair/* * (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/runtime/Exception.h" #include "hic/hic.h" namespace atlas { namespace array { //------------------------------------------------------------------------------ template class Vector { public: Vector(idx_t N = 0): data_(N ? new T[N] : nullptr), data_gpu_(nullptr), size_(N) {} void resize_impl(idx_t N) { ATLAS_ASSERT(not data_gpu_, "we can not resize a vector after has been cloned to device"); ATLAS_ASSERT(N >= size_); if (N == size_) return; T* d_ = new T[N]; for (idx_t c = 0; c < size_; ++c) { d_[c] = data_[c]; } if (data_) delete[] data_; data_ = d_; } void resize(idx_t N) { resize_impl(N); size_ = N; } void resize(idx_t N, T val) { resize_impl(N); for (idx_t c = size_; c < N; ++c) { data_[c] = val; } size_ = N; } void allocateDevice() { if constexpr (ATLAS_HAVE_GPU) { HIC_CALL(hicMalloc((void**)(&data_gpu_), sizeof(T*) * size_)); T* buff = new T[size_]; for (idx_t i = 0; i < size(); ++i) { data_[i]->updateDevice(); buff[i] = data_[i]->gpu_object_ptr(); } HIC_CALL(hicMemcpy(data_gpu_, buff, sizeof(T*) * size_, hicMemcpyHostToDevice)); delete[] buff; } else { data_gpu_ = data_; } size_gpu_ = size_; } void updateDevice() { if (!data_gpu_) { allocateDevice(); } else { ATLAS_ASSERT(size_gpu_ == size_); if constexpr (ATLAS_HAVE_GPU) { for (idx_t i = 0; i < size(); ++i) { data_[i]->updateDevice(); assert(data_gpu_[i] == data_[i]->gpu_object_ptr()); } } } } void updateHost() { ATLAS_ASSERT(data_gpu_ != nullptr); if constexpr (ATLAS_HAVE_GPU) { for (idx_t i = 0; i < size(); ++i) { data_[i]->updateHost(); } } } T* gpu_object_ptr() { return data_gpu_; } T* data() { return data_; } T* data_gpu() { return data_gpu_; } idx_t size() const { return size_; } private: T* data_; T* data_gpu_; idx_t size_; idx_t size_gpu_; }; template class VectorView { public: VectorView(): vector_(nullptr), data_(nullptr), size_(0) {} VectorView(Vector const& vector, T* data): vector_(&vector), data_(data), size_(vector.size()) {} ATLAS_HOST_DEVICE T& operator[](idx_t idx) { assert(idx < size_); return data_[idx]; } ATLAS_HOST_DEVICE T const& operator[](idx_t idx) const { assert(idx < size_); return data_[idx]; } ATLAS_HOST_DEVICE T base() { return *data_; } ATLAS_HOST_DEVICE idx_t size() const { return size_; } bool is_valid(Vector& vector) { return (&vector) == vector_ && (data_ != nullptr); } public: Vector const* vector_; T* data_; idx_t size_; }; template VectorView make_host_vector_view(Vector vector_) { return VectorView(vector_, vector_.data()); } template VectorView make_device_vector_view(Vector vector_) { return VectorView(vector_, vector_.data_gpu()); } //------------------------------------------------------------------------------ } // namespace array } // namespace atlas atlas-0.46.0/src/atlas/array/Array.h0000664000175000017500000002160415160212070017326 0ustar alastairalastair/* * (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/Object.h" #include "atlas/array/ArrayDataStore.h" #include "atlas/array/DataType.h" #include "atlas/array_fwd.h" namespace atlas { namespace array { // -------------------------------------------------------------------------------------------- // Forward declarations #ifndef DOXYGEN_SHOULD_SKIP_THIS template class ArrayT; template class ArrayT_impl; #endif // -------------------------------------------------------------------------------------------- class Array : public util::Object { public: Array() = default; virtual ~Array(); static Array* create(array::DataType, const ArrayShape&); static Array* create(array::DataType, const ArrayShape&, const ArrayLayout&); static Array* create(array::DataType, ArraySpec&&); static Array* create(ArraySpec&&); virtual size_t footprint() const = 0; template static Array* create(idx_t size0); template static Array* create(idx_t size0, idx_t size1); template static Array* create(idx_t size0, idx_t size1, idx_t size2); template static Array* create(idx_t size0, idx_t size1, idx_t size2, idx_t size3); template static Array* create(idx_t size0, idx_t size1, idx_t size2, idx_t size3, idx_t size4); template static Array* create(const ArrayShape& shape); template static Array* create(const ArrayShape& shape, const ArrayLayout& layout); template static Array* wrap(Value* data, const ArrayShape& shape); template static Array* wrap(Value* data, const ArraySpec& spec); idx_t bytes() const { return datatype().size() * spec().allocatedSize(); } size_t size() const { return spec_.size(); } idx_t rank() const { return spec_.rank(); } idx_t stride(idx_t i) const { return spec_.strides()[i]; } idx_t shape(idx_t i) const { return spec_.shape()[i]; } const ArrayStrides& strides() const { return spec_.strides(); } const ArrayStrides& device_strides() const { return spec_.device_strides(); } const ArrayShape& shape() const { return spec_.shape(); } const std::vector& shapef() const { return spec_.shapef(); } const std::vector& stridesf() const { return spec_.stridesf(); } const std::vector& device_stridesf() const { return spec_.device_stridesf(); } bool contiguous() const { return spec_.contiguous(); } bool hasDefaultLayout() const { return spec_.hasDefaultLayout(); } virtual array::DataType datatype() const = 0; virtual void resize(const ArrayShape& shape) = 0; virtual void resize(idx_t size0) = 0; virtual void resize(idx_t size0, idx_t size1) = 0; virtual void resize(idx_t size0, idx_t size1, idx_t size2) = 0; virtual void resize(idx_t size0, idx_t size1, idx_t size2, idx_t size3) = 0; virtual void resize(idx_t size0, idx_t size1, idx_t size2, idx_t size3, idx_t size4) = 0; virtual void insert(idx_t idx1, idx_t size1) = 0; virtual void dump(std::ostream& os) const = 0; virtual void accMap() const = 0; virtual void accUnmap() const = 0; virtual bool accMapped() const = 0; virtual void* storage() { return data_store_->voidDataStore(); } virtual const void* storage() const { return data_store_->voidDataStore(); } bool valid() const { return data_store_->valid(); } void updateDevice() const { data_store_->updateDevice(); } void updateHost() const { data_store_->updateHost(); } void syncHostDevice() const { data_store_->syncHostDevice(); } void syncHost() const { data_store_->syncHost(); } void syncDevice() const { data_store_->syncDevice(); } bool hostNeedsUpdate() const { return data_store_->hostNeedsUpdate(); } bool deviceNeedsUpdate() const { return data_store_->deviceNeedsUpdate(); } void setHostNeedsUpdate(bool v) const { return data_store_->setHostNeedsUpdate(v); } void setDeviceNeedsUpdate(bool v) const { return data_store_->setDeviceNeedsUpdate(v); } bool deviceAllocated() const { return data_store_->deviceAllocated(); } void allocateDevice() { data_store_->allocateDevice(); } void deallocateDevice() { data_store_->deallocateDevice(); } void reactivateDeviceWriteViews() const { data_store_->reactivateDeviceWriteViews(); } void reactivateHostWriteViews() const { data_store_->reactivateHostWriteViews(); } const ArraySpec& spec() const { return spec_; } struct CopyPolicy { enum class Execution { SERIAL=0, OMP=1 }; bool on_device = false; Execution execution {Execution::SERIAL}; }; virtual void copy(const Array&, const CopyPolicy&) = 0; void copy(const Array& other) { return copy(other,CopyPolicy{}); } // -- dangerous methods... You're on your own interpreting the raw data template DATATYPE const* host_data() const { return data_store_->hostData(); } template DATATYPE* host_data() { return data_store_->hostData(); } template DATATYPE const* device_data() const { return data_store_->deviceData(); } template DATATYPE* device_data() { return data_store_->deviceData(); } template DATATYPE const* data() const { return data_store_->hostData(); } template DATATYPE* data() { return data_store_->hostData(); } void const* data() const { return data(); } void* data() { return data(); } const ArrayDataStore& data_store() const { return *data_store_; } protected: Array(ArraySpec&& spec): spec_(std::move(spec)) {} ArraySpec spec_; std::unique_ptr data_store_; void replace(Array& array) { data_store_.swap(array.data_store_); spec_ = array.spec_; } }; // -------------------------------------------------------------------------------------------- template class ArrayT : public Array { public: ArrayT(idx_t size0); ArrayT(idx_t size0, idx_t size1); ArrayT(idx_t size0, idx_t size1, idx_t size2); ArrayT(idx_t size0, idx_t size1, idx_t size2, idx_t size3); ArrayT(idx_t size0, idx_t size1, idx_t size2, idx_t size3, idx_t size4); ArrayT(ArraySpec&&); ArrayT(const ArrayShape&); ArrayT(const ArrayShape&, const ArrayAlignment&); ArrayT(const ArrayShape&, const ArrayLayout&); virtual void insert(idx_t idx1, idx_t size1); virtual void resize(const ArrayShape&); virtual void resize(idx_t size0); virtual void resize(idx_t size0, idx_t size1); virtual void resize(idx_t size0, idx_t size1, idx_t size2); virtual void resize(idx_t size0, idx_t size1, idx_t size2, idx_t size3); virtual void resize(idx_t size0, idx_t size1, idx_t size2, idx_t size3, idx_t size4); virtual void copy(const Array&, const CopyPolicy&); virtual array::DataType datatype() const { return array::DataType::create(); } virtual void dump(std::ostream& os) const; // This constructor is used through the Array::create() or the Array::wrap() // methods ArrayT(ArrayDataStore*, const ArraySpec&); virtual size_t footprint() const; virtual void accMap() const; virtual void accUnmap() const; virtual bool accMapped() const; using Array::host_data; using Array::device_data; using Array::data; Value const* host_data() const { return data_store_->hostData(); } Value* host_data() { return data_store_->hostData(); } Value const* device_data() const { return data_store_->deviceData(); } Value* device_data() { return data_store_->deviceData(); } Value const* data() const { return data_store_->hostData(); } Value* data() { return data_store_->hostData(); } private: template friend class ArrayT_impl; }; extern template class ArrayT; extern template class ArrayT; extern template class ArrayT; extern template class ArrayT; } // namespace array } // namespace atlas atlas-0.46.0/src/atlas/array/ArrayViewUtil.h0000664000175000017500000000354315160212070021021 0ustar alastairalastair/* * (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/array/ArrayView.h" namespace atlas { namespace array { #ifndef DOXYGEN_SHOULD_SKIP_THIS template constexpr typename std::enable_if<(cnt == RANK), idx_t>::type get_var_size_impl( array::ArrayView& field) { return 1; } template constexpr typename std::enable_if<(cnt != RANK), idx_t>::type get_var_size_impl( array::ArrayView& field) { return (cnt == DimSkip) ? get_var_size_impl(field) : get_var_size_impl(field) * field.template shape(); } template constexpr idx_t get_var_size(array::ArrayView& field) { return get_var_size_impl<0, DimSkip>(field); } template struct get_dim { static constexpr int value = -1; }; template struct get_dim> { static constexpr int value = cDim; }; template constexpr unsigned int get_parallel_dim(array::ArrayView& field) { static_assert(is_dim_policy::value, "DimPolicy required"); return std::is_same::value ? 0 : (std::is_same::value ? RANK - 1 : (get_dim::value)); } #endif } // namespace array } // namespace atlas atlas-0.46.0/src/atlas/array/LocalView.cc0000664000175000017500000001446215160212070020277 0ustar alastairalastair/* * (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 "LocalView.h" #include #include "atlas/array/helpers/ArrayAssigner.h" #include "atlas/runtime/Exception.h" //------------------------------------------------------------------------------------------------------ namespace atlas { namespace array { //------------------------------------------------------------------------------------------------------ #define ENABLE_IF_NON_CONST \ template ::value && EnableBool), int>::type*> template ENABLE_IF_NON_CONST void LocalView::assign(const Value& value) { helpers::array_assigner::apply(*this, value); } //------------------------------------------------------------------------------------------------------ template void LocalView::dump(std::ostream& os) const { ATLAS_ASSERT(contiguous(), "Cannot dump non-contiguous view"); const value_type* data_ = data(); os << "size: " << size() << " , values: "; os << "[ "; for (idx_t j = 0; j < size(); ++j) { os << data_[j] << " "; } os << "]"; } //------------------------------------------------------------------------------------------------------ #define ENABLE_IF_NOT_CONST typename std::enable_if::value, Value>::type* #define ENABLE_IF_CONST typename std::enable_if::value, Value>::type* template LocalView make_view(Value data[], const ArrayShape& shape) { return LocalView(data, shape); } template LocalView make_view(const Value data[], const ArrayShape& shape) { return LocalView(data, shape); } template LocalView make_view(Value data[], const ArrayShape& shape) { return LocalView(data, shape); } template LocalView make_view(typename std::remove_const::type data[], const ArrayShape& shape) { return LocalView(data, shape); } //------------------------------------------------------------------------------------------------------ template LocalView make_view(Value data[], size_t size) { return LocalView(data, ArrayShape{idx_t(size)}); } template LocalView make_view(const Value data[], size_t size) { return LocalView(data, ArrayShape{idx_t(size)}); } template LocalView make_view(Value data[], size_t size) { return LocalView(data, ArrayShape{idx_t(size)}); } template LocalView make_view(typename std::remove_const::type data[], size_t size) { return LocalView(data, ArrayShape{idx_t(size)}); } //------------------------------------------------------------------------------------------------------ } // namespace array } // namespace atlas //----------------------------------------------------------------------- // Explicit instantiation namespace atlas { namespace array { #undef EXPLICIT_TEMPLATE_INSTANTIATION #define EXPLICIT_TEMPLATE_INSTANTIATION_TYPE_RANK(TYPE, RANK) \ template LocalView make_view(TYPE data[], const ArrayShape&); \ template LocalView make_view(TYPE data[], const ArrayShape&); \ template LocalView make_view(const TYPE data[], const ArrayShape&); \ template LocalView make_view(const TYPE data[], const ArrayShape&); \ \ template class LocalView; \ template class LocalView; \ template void LocalView::assign(const TYPE&); #define EXPLICIT_TEMPLATE_INSTANTIATION_TYPE_RANK1(TYPE) \ template LocalView make_view(TYPE data[], size_t); \ template LocalView make_view(TYPE data[], size_t); \ template LocalView make_view(const TYPE data[], size_t); \ template LocalView make_view(const TYPE data[], size_t); #define EXPLICIT_TEMPLATE_INSTANTIATION(RANK) \ EXPLICIT_TEMPLATE_INSTANTIATION_TYPE_RANK(int, RANK) \ EXPLICIT_TEMPLATE_INSTANTIATION_TYPE_RANK(long, RANK) \ EXPLICIT_TEMPLATE_INSTANTIATION_TYPE_RANK(long long, RANK) \ EXPLICIT_TEMPLATE_INSTANTIATION_TYPE_RANK(float, RANK) \ EXPLICIT_TEMPLATE_INSTANTIATION_TYPE_RANK(double, RANK) // For each RANK in [1..9] EXPLICIT_TEMPLATE_INSTANTIATION(1) EXPLICIT_TEMPLATE_INSTANTIATION(2) EXPLICIT_TEMPLATE_INSTANTIATION(3) EXPLICIT_TEMPLATE_INSTANTIATION(4) EXPLICIT_TEMPLATE_INSTANTIATION(5) EXPLICIT_TEMPLATE_INSTANTIATION(6) EXPLICIT_TEMPLATE_INSTANTIATION(7) EXPLICIT_TEMPLATE_INSTANTIATION(8) EXPLICIT_TEMPLATE_INSTANTIATION(9) EXPLICIT_TEMPLATE_INSTANTIATION_TYPE_RANK1(int) EXPLICIT_TEMPLATE_INSTANTIATION_TYPE_RANK1(long) EXPLICIT_TEMPLATE_INSTANTIATION_TYPE_RANK1(long long) EXPLICIT_TEMPLATE_INSTANTIATION_TYPE_RANK1(float) EXPLICIT_TEMPLATE_INSTANTIATION_TYPE_RANK1(double) #undef EXPLICIT_TEMPLATE_INSTANTIATION } // namespace array } // namespace atlas atlas-0.46.0/src/atlas/array/IndexView.h0000664000175000017500000000306015160212070020146 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ /// @file IndexView.h /// This file contains the IndexView class, a class that allows to wrap any /// contiguous raw data into /// a view which is accessible with multiple indices. /// This view is intended to work with Connectivity Tables storing Fortran /// Numbering internally /// All it needs is the strides for each index, and the shape of each index. /// ATTENTION: The last index is stride 1 /// /// Bounds-checking can be turned ON by defining /// "ATLAS_INDEXVIEW_BOUNDS_CHECKING" /// before including this header. /// /// Example: /// int[] array = { 1, 2, 3, 4, 5, 6, 7, 8, 9}; /// idx_t[2] strides = { 3, 1 }; /// idx_t[2] shape = { 3, 3 }; /// IndexView matrix( array, strides, shape ); /// for( idx_t i=0; i fieldview( Field ); /// IndexView INDEXVIEW( Array ); #pragma once #include "atlas/library/config.h" #if ATLAS_HAVE_GRIDTOOLS_STORAGE #include "atlas/array/gridtools/GridToolsIndexView.h" #else #include "atlas/array/native/NativeIndexView.h" #endif atlas-0.46.0/src/atlas/array/TableView.h0000664000175000017500000001727415160212070020142 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ /// @file Table.h /// @author Willem Deconinck /// @date October 2015 /// This file contains the classes /// - Table /// /// It is important to note that connectivity access functions are /// inlined for optimal performance. The connectivity itself is internally /// stored with base 1, to be compatible with Fortran access. /// C++ access operators however convert the resulting connectivity to base 0. #pragma once #include #include "atlas/array/Table.h" #include "atlas/library/config.h" namespace atlas { namespace array { // -------------------------------------------------------------------------- #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 namespace detail { // TableIndex: // Helper class that does +1 and -1 operations on stored values class TableIndex { public: enum { BASE = 1 }; public: TableIndex(idx_t* idx): idx_(idx) {} void set(const idx_t& value) { *(idx_) = value + BASE; } idx_t get() const { return *(idx_)-BASE; } void operator =(const idx_t& value) { set(value); } TableIndex& operator=(const TableIndex& other) { set(other.get()); return *this; } TableIndex& operator+(const idx_t& value) { *(idx_) += value; return *this; } TableIndex& operator-(const idx_t& value) { *(idx_) -= value; return *this; } TableIndex& operator--() { --(*(idx_)); return *this; } TableIndex& operator++() { ++(*(idx_)); return *this; } TableIndex& operator+=(const idx_t& value) { *(idx_) += value; return *this; } TableIndex& operator-=(const idx_t& value) { *(idx_) -= value; return *this; } // implicit conversion operator idx_t() const { return get(); } private: idx_t* idx_; }; } // namespace detail // ------------------------------------------------------------------------------------------------------ template class TableRow { #if ATLAS_HAVE_FORTRAN typedef detail::TableIndex Index; #else typedef idx_t Index; #endif using ReturnType = typename std::conditional::type; public: ATLAS_HOST_DEVICE TableRow(const idx_t* data, size_t size): data_(const_cast(data)), size_(size) {} ATLAS_HOST_DEVICE idx_t operator()(size_t i) const { return data_[i] FROM_FORTRAN; } ATLAS_HOST_DEVICE ReturnType operator()(size_t i) { return INDEX_REF(data_ + i); } ATLAS_HOST_DEVICE size_t size() const { return size_; } // TODO: Following function should only be allowed to compile if // ReadOnly=false (SFINAE?) TableRow& operator=(const idx_t column_values[]) { assert(not ReadOnly); for (size_t n = 0; n < size_; ++n) { data_[n] = column_values[n] TO_FORTRAN; } return *this; } private: idx_t* data_; size_t size_; }; // ------------------------------------------------------------------------------------------------------ template class TableView : public util::Object { #if ATLAS_HAVE_FORTRAN using Index = typename std::conditional::type; #else using Index = idx_t; #endif public: typedef TableRow Row; typedef TableRow ConstRow; static constexpr unsigned short _values_ = 0; static constexpr unsigned short _displs_ = 1; static constexpr unsigned short _counts_ = 2; public: using value_type = idx_t; using Data = typename std::conditional::type; template struct Access_t {}; template struct Access_t { Access_t(const TableView* tv): tv_(const_cast*>(tv)) {} TableView* tv_; idx_t apply(size_t row, size_t col) const { return tv_->values_(tv_->displs_(row) + col) FROM_FORTRAN; } }; template struct Access_t { Access_t(const TableView* tv): tv_(const_cast*>(tv)) {} TableView* tv_; Index apply(size_t row, size_t col) const { return Index(&tv_->values_(tv_->displs_(row) + col)); } }; using Access = Access_t; using ConstAccess = Access_t; //-- Constructors TableView(const Table& table, bool host = true); TableView(const TableView& other); TableView operator=(const TableView& other); ~TableView() {} //-- Accessors /// @brief Number of rows in the connectivity table ATLAS_HOST_DEVICE size_t rows() const { return rows_; } /// @brief Number of columns for specified row in the connectivity table ATLAS_HOST_DEVICE size_t cols(size_t row_idx) const { return counts_(row_idx); } /// @brief Maximum value for number of columns over all rows ATLAS_HOST_DEVICE size_t maxcols() const { return maxcols_; } /// @brief Minimum value for number of columns over all rows ATLAS_HOST_DEVICE size_t mincols() const { return mincols_; } /// @brief Access to connectivity table elements for given row and column /// The returned index has base 0 regardless if ATLAS_HAVE_FORTRAN is defined. ATLAS_HOST_DEVICE idx_t operator()(size_t row_idx, size_t col_idx) const { assert(counts_(row_idx) > col_idx); return const_access_.apply(row_idx, col_idx); } /// @brief Access to connectivity table elements for given row and column /// The returned index has base 0 regardless if ATLAS_HAVE_FORTRAN is defined. ATLAS_HOST_DEVICE Index operator()(size_t row_idx, size_t col_idx) { assert(counts_(row_idx) > col_idx); return access_.apply(row_idx, col_idx); } /// @brief Access to raw data. /// Note that the connectivity base is 1 in case ATLAS_HAVE_FORTRAN is /// defined. const idx_t* data() const { return values_.data(); } Data* data() { return values_.data(); } ATLAS_HOST_DEVICE size_t size() const { return values_.size(); } ATLAS_HOST_DEVICE idx_t missing_value() const { return missing_value_; } ATLAS_HOST_DEVICE ConstRow row(size_t row_idx) const { return ConstRow(values_.data() + displs_(row_idx), counts_(row_idx)); } ATLAS_HOST_DEVICE Row row(size_t row_idx) { return Row(values_.data() + displs_(row_idx), counts_(row_idx)); } ///-- Modifiers ATLAS_HOST_DEVICE size_t displs(const size_t row) const { return displs_(row); } private: const size_t* displs() const { return displs_.data(); } const size_t* counts() const { return counts_.data(); } private: bool host_; idx_t missing_value_; size_t rows_; size_t maxcols_; size_t mincols_; ArrayView values_; ArrayView displs_; ArrayView counts_; ConstAccess const_access_; Access access_; }; // ----------------------------------------------------------------------------------------------------- #undef FROM_FORTRAN #undef TO_FORTRAN #undef INDEX_REF //------------------------------------------------------------------------------------------------------ } // namespace array } // namespace atlas atlas-0.46.0/src/atlas/array/ArrayIdx.h0000664000175000017500000000313215160212070017767 0ustar alastairalastair/* * (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 #include "atlas/library/config.h" //------------------------------------------------------------------------------------------------------ namespace atlas { namespace array { //using ArrayIdx = std::vector; typedef std::vector ArrayIdx; namespace detail { inline ArrayIdx make_idx(idx_t size1) { return std::vector(1, size1); } inline ArrayIdx make_idx(idx_t size1, idx_t size2) { std::vector v(2); v[0] = size1; v[1] = size2; return v; } inline ArrayIdx make_idx(idx_t size1, idx_t size2, idx_t size3) { std::vector v(3); v[0] = size1; v[1] = size2; v[2] = size3; return v; } inline ArrayIdx make_idx(idx_t size1, idx_t size2, idx_t size3, idx_t size4) { std::vector v(4); v[0] = size1; v[1] = size2; v[2] = size3; v[3] = size4; return v; } } // namespace detail template ArrayIdx make_idx(idx_t... indices) { return detail::make_idx(std::forward(indices)...); } //------------------------------------------------------------------------------------------------------ } // namespace array } // namespace atlas atlas-0.46.0/src/atlas/array/ArrayLayout.h0000664000175000017500000000341515160212070020524 0ustar alastairalastair/* * (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 //------------------------------------------------------------------------------------------------------ namespace atlas { namespace array { class ArrayLayout : public std::vector { private: using Base = std::vector; public: ArrayLayout() {} ArrayLayout(std::initializer_list list): Base(list) {} ArrayLayout(Base&& base): Base(std::forward(base)) {} }; namespace detail { inline ArrayLayout make_layout(idx_t size1) { return ArrayLayout{size1}; } inline ArrayLayout make_layout(idx_t size1, idx_t size2) { return ArrayLayout{size1, size2}; } inline ArrayLayout make_layout(idx_t size1, idx_t size2, idx_t size3) { return ArrayLayout{size1, size2, size3}; } inline ArrayLayout make_layout(idx_t size1, idx_t size2, idx_t size3, idx_t size4) { return ArrayLayout{size1, size2, size3, size4}; } inline ArrayLayout make_layout(idx_t size1, idx_t size2, idx_t size3, idx_t size4, idx_t size5) { return ArrayLayout{size1, size2, size3, size4, size5}; } } // namespace detail template ArrayLayout make_layout(idx_t... indices) { return detail::make_layout(std::forward(indices)...); } //------------------------------------------------------------------------------------------------------ } // namespace array } // namespace atlas atlas-0.46.0/src/atlas/array/Table.h0000664000175000017500000001402215160212070017273 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ /// @file Table.h /// @author Willem Deconinck /// @date October 2015 /// This file contains the classes /// - Table #pragma once #include #include #include "atlas/util/Object.h" #include "atlas/array.h" #include "atlas/library/config.h" namespace atlas { namespace array { // -------------------------------------------------------------------------- /// @brief Table /// @author Willem Deconinck /// /// Container for tables. This is e.g. /// for a node-to-X connectivity. /// connectivity = [ /// 1 2 3 4 5 6 # node 1 /// 7 8 # node 2 /// 9 10 11 12 # node 3 /// 13 14 15 # node 4 /// 16 17 18 19 20 21 # node 5 /// ] /// There are 2 modes of construction: /// - It wraps existing raw data /// - It owns the connectivity data /// /// In case ATLAS_HAVE_FORTRAN is defined (which is usually the case), /// the raw data will be stored with base 1 for Fortran interoperability. /// The operator(row,col) in C++ will then do the conversion to base 0. /// /// In the first mode of construction, the connectivity table cannot be resized. /// In the second mode of construction, resizing is possible class Table : public util::Object { private: static constexpr unsigned short _values_ = 0; static constexpr unsigned short _displs_ = 1; static constexpr unsigned short _counts_ = 2; public: //-- Constructors /// @brief Construct connectivity table that needs resizing a-posteriori /// Data is owned Table(const std::string& name = ""); ~Table(); private: /// @brief Construct connectivity table wrapping existing raw data. /// No resizing can be performed as data is not owned. Table(idx_t values[], size_t rows, size_t displs[], size_t counts[]); public: //-- Accessors /// @brief Name associated to this Connetivity const std::string& name() const { return name_; } /// @brief Rename this Connectivity void rename(const std::string& name) { name_ = name; } /// @brief Number of rows in the connectivity table size_t rows() const { return rows_; } /// @brief Number of columns for specified row in the connectivity table size_t cols(size_t row_idx) const { return counts_(row_idx); } /// @brief Maximum value for number of columns over all rows size_t maxcols() const { return maxcols_; } /// @brief Minimum value for number of columns over all rows size_t mincols() const { return mincols_; } /// @brief Value that is given to unassigned entries idx_t missing_value() const { return missing_value_; } /// @brief Number of values stored in the table size_t size() const { return data_[_values_]->size(); } /// @brief Return memory footprint of table virtual size_t footprint() const; /// @brief Clone data to device virtual void updateDevice() const; /// @brief Clone data from device virtual void updateHost() const; /// @brief Synchronise data between host and device virtual void syncHostDevice() const; /// @brief Check if data is valid virtual bool valid() const; /// @brief Check if data is present on host virtual bool hostNeedsUpdate() const; /// @brief Check if data is present on device virtual bool deviceNeedsUpdate() const; /// @brief Print all values unformatted to output stream void dump(std::ostream&) const; /// @brief Check if data is owned or wrapped bool owns() { return owns_; } ///-- Modifiers /// @brief Resize connectivity, and add given rows /// @note Can only be used when data is owned. virtual void add(size_t rows, size_t cols, const idx_t values[], bool fortran_array = false); /// @brief Resize connectivity, and add given rows with missing values /// @note Can only be used when data is owned. virtual void add(size_t rows, size_t cols); /// @brief Resize connectivity, and add given rows with missing values /// @note Can only be used when data is owned. virtual void add(size_t rows, const size_t cols[]); /// @brief Resize connectivity, and insert given rows /// @note Can only be used when data is owned. virtual void insert(size_t position, size_t rows, size_t cols, const idx_t values[], bool fortran_array = false); /// @brief Resize connectivity, and insert given rows with missing values /// @note Can only be used when data is owned. virtual void insert(size_t position, size_t rows, size_t cols); /// @brief Resize connectivity, and insert given rows with missing values /// @note Can only be used when data is owned. virtual void insert(size_t position, size_t rows, const size_t cols[]); /// @brief Resize connectivity, and insert given rows with missing values /// @note Invalidates non-owned Table virtual void clear(); private: ///-- Internal helper functions void resize_values(size_t old_size, size_t size, bool initialize, const idx_t values[], bool fortran_array); void resize_counts_and_displs(size_t size); void insert_counts_and_displs(size_t position, size_t rows); void resize_values(size_t size); void insert_values(size_t position, size_t size); private: template friend class TableView; std::string name_; bool owns_; std::array data_; idx_t missing_value_; size_t rows_; size_t maxcols_; size_t mincols_; ArrayView displs_; ArrayView counts_; ArrayView values_; }; //------------------------------------------------------------------------------------------------------ } // namespace array } // namespace atlas atlas-0.46.0/src/atlas/array/helpers/0000775000175000017500000000000015160212070017536 5ustar alastairalastairatlas-0.46.0/src/atlas/array/helpers/ArrayCopier.h0000664000175000017500000000627015160212070022134 0ustar alastairalastair/* * (C) Crown Copyright 2021 Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ #pragma once #include "atlas/array.h" //------------------------------------------------------------------------------ namespace atlas { namespace array { namespace helpers { //------------------------------------------------------------------------------ /// Helper to copy from source array to target array. template struct array_copier; //------------------------------------------------------------------------------ // Recursive function to copy array elements across all dimensions. template struct array_copier_impl { template static void apply(const SourceView& sourceArr, TargetView& targetArr, const std::array& shape, DimIndex... idxs) { for (idx_t i = 0; i < shape[Dim]; ++i) { array_copier_impl::apply(sourceArr, targetArr, shape, idxs..., i); } } }; // End of recursion when Dim == Rank template struct array_copier_impl { template static void apply(const SourceView& sourceArr, TargetView& targetArr, const std::array& shape, DimIndex... idxs) { targetArr(idxs...) = sourceArr(idxs...); } }; //------------------------------------------------------------------------------ template struct array_copier { // Copy from source array to target array. static void apply(const ArrayView& sourceArr, ArrayView& targetArr) { array_copier_impl::apply(sourceArr, targetArr, shape(sourceArr, targetArr)); } // Copy from const source array to target array. static void apply(const ArrayView& sourceArr, ArrayView& targetArr) { array_copier_impl::apply(sourceArr, targetArr, shape(sourceArr, targetArr)); } template static void apply(const SourceView& sourceArr, TargetView& targetArr) { array_copier_impl::apply(sourceArr, targetArr, shape(sourceArr, targetArr)); } private: // Make an array shape that is compatible with sourceArr and targetArr. // This is useful for arrays from fields with different sized halos. template static std::array shape(const SourceView& sourceArr, const TargetView& targetArr) { auto arrShape = std::array{}; for (unsigned int Dim = 0; Dim < Rank; ++Dim) { arrShape[Dim] = std::min(sourceArr.shape(Dim), targetArr.shape(Dim)); } return arrShape; } }; //------------------------------------------------------------------------------ } // namespace helpers } // namespace array } // namespace atlas atlas-0.46.0/src/atlas/array/helpers/ArrayForEach.h0000664000175000017500000003310215160212070022214 0ustar alastairalastair/* * (C) Crown Copyright 2024 Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ #pragma once #include #include #include #include #include "atlas/array/ArrayView.h" #include "atlas/array/Range.h" #include "atlas/array/helpers/ArraySlicer.h" #include "atlas/parallel/omp/omp.h" #include "atlas/runtime/Exception.h" #include "atlas/util/Config.h" namespace atlas { namespace execution { // As in C++17 std::execution namespace. Note: unsequenced_policy is a C++20 // addition. class sequenced_policy {}; class unsequenced_policy {}; class parallel_unsequenced_policy {}; class parallel_policy {}; // execution policy objects as in C++ std::execution namespace. Note: unseq is a // C++20 addition. inline constexpr sequenced_policy seq{/*unspecified*/}; inline constexpr parallel_policy par{/*unspecified*/}; inline constexpr parallel_unsequenced_policy par_unseq{/*unspecified*/}; inline constexpr unsequenced_policy unseq{/*unspecified*/}; // Type names for execution policy (Not in C++ standard) template constexpr std::string_view policy_name() { return "unsupported"; }; template <> constexpr std::string_view policy_name() { return "sequenced_policy"; }; template <> constexpr std::string_view policy_name() { return "unsequenced_policy"; }; template <> constexpr std::string_view policy_name() { return "parallel_unsequenced_policy"; }; template <> constexpr std::string_view policy_name() { return "parallel_policy"; }; template constexpr std::string_view policy_name(execution_policy) { return policy_name>(); } // Type check for execution policy (Not in C++ standard) template constexpr auto is_execution_policy() { return std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v; } template constexpr auto demote_policy() { if constexpr (std::is_same_v) { return unseq; } else if constexpr (std::is_same_v) { return seq; } else { return ExecutionPolicy{}; } ATLAS_UNREACHABLE(); } template constexpr auto is_omp_policy() { return std::is_same_v || std::is_same_v; } template using demote_policy_t = decltype(demote_policy()); } // namespace execution namespace option { // Convert execution_policy objects to a util::Config template util::Config execution_policy() { return util::Config("execution_policy", execution::policy_name>()); } template util::Config execution_policy(T) { return execution_policy>(); } } // namespace option namespace array { namespace helpers { namespace detail { struct NoMask { template constexpr bool operator()(Args...) const { return 0; } }; inline constexpr NoMask no_mask; template constexpr auto tuplePushBack(const std::tuple& tuple, T value) { return std::tuple_cat(tuple, std::make_tuple(value)); } template void forEach(idx_t idxMax, const Functor& functor) { if constexpr (execution::is_omp_policy()) { atlas_omp_parallel_for(auto idx = idx_t{}; idx < idxMax; ++idx) { functor(idx); } } else { // Simple for-loop for sequenced or unsequenced execution policies. for (auto idx = idx_t{}; idx < idxMax; ++idx) { functor(idx); } } } template constexpr auto argPadding() { if constexpr (NPad > 0) { return std::tuple_cat(std::make_tuple(Range::all()), argPadding()); } else { return std::make_tuple(); } ATLAS_UNREACHABLE(); } template auto makeSlices(const std::tuple& slicerArgs, ArrayViewTuple&& arrayViews) { constexpr auto nb_views = std::tuple_size_v; auto&& arrayView = std::get(arrayViews); using ArrayView = std::decay_t; constexpr auto Dim = sizeof...(SlicerArgs); constexpr auto Rank = ArrayView::rank(); static_assert( Dim <= Rank, "Error: number of slicer arguments exceeds the rank of ArrayView."); const auto paddedArgs = std::tuple_cat(slicerArgs, argPadding()); const auto slicer = [&arrayView](const auto&... args) { return std::make_tuple(arrayView.slice(args...)); }; if constexpr (ViewIdx == nb_views - 1) { return std::apply(slicer, paddedArgs); } else { // recurse return std::tuple_cat( std::apply(slicer, paddedArgs), makeSlices(slicerArgs, std::forward(arrayViews))); } ATLAS_UNREACHABLE(); } template struct ArrayForEachImpl; template struct ArrayForEachImpl { template static void apply(ArrayViewTuple&& arrayViews, const Mask& mask, const Function& function, const std::tuple& slicerArgs, const std::tuple& maskArgs) { // Iterate over this dimension. if constexpr (Dim == ItrDim) { // Get size of iteration dimenion from first view argument. const auto idxMax = std::get<0>(arrayViews).shape(ItrDim); forEach(idxMax, [&](idx_t idx) { // Demote parallel execution policy to a non-parallel one in further // recursion ArrayForEachImpl< execution::demote_policy_t, Dim + 1, ItrDims...>::apply(std::forward(arrayViews), mask, function, tuplePushBack(slicerArgs, idx), tuplePushBack(maskArgs, idx)); }); } // Add a RangeAll to arguments. else { ArrayForEachImpl::apply( std::forward(arrayViews), mask, function, tuplePushBack(slicerArgs, Range::all()), maskArgs); } } }; template struct is_applicable : std::false_type {}; template struct is_applicable> : std::is_invocable {}; template inline constexpr bool is_applicable_v = is_applicable::value; template struct ArrayForEachImpl { template static void apply(ArrayViewTuple&& arrayViews, const Mask& mask, const Function& function, const std::tuple& slicerArgs, const std::tuple& maskArgs) { constexpr auto maskPresent = !std::is_same_v; if constexpr (maskPresent) { constexpr auto invocableMask = std::is_invocable_r_v; static_assert( invocableMask, "Cannot invoke mask function with given arguments.\n" "Make sure you arguments are N integers (or auto...) " "where N == sizeof...(ItrDims). Function must return an int."); if (std::apply(mask, maskArgs)) { return; } } auto slices = makeSlices(slicerArgs, std::forward(arrayViews)); constexpr auto applicable = is_applicable_v; static_assert( applicable, "Cannot invoke function with given arguments. " "Make sure you the arguments are rvalue references (Slice&&) or const " "references (const Slice&) or regular value (Slice)"); std::apply(function, std::move(slices)); } }; } // namespace detail /// brief Array "For-Each" helper struct. /// /// detail Iterates over dimensions given in ItrDims. Slices over full range /// of other dimensions. /// Note: ItrDims must be given in ascending numerical order. TODO: Static /// checking for this. template struct ArrayForEach { /// brief Apply "For-Each" method. /// /// details Visits all elements indexed by ItrDims and creates a slice from /// each ArrayView in arrayViews. Slices are sent to function /// which is executed with the signature f(slice1, slice2,...). /// Iterations are skipped when mask evaluates to "true" /// and is executed with signature g(idx_i, idx_j,...), where the idxs /// are indices of ItrDims. /// When a config is supplied containing "execution_policy" = /// "sequenced_policy" (default). All loops are then executed in /// sequential (row-major) order. With "execution_policy" = /// "parallel_unsequenced" the first loop is executed using OpenMP. /// The remaining loops are executed in serial. Note: The lowest /// ArrayView.rank() must be greater than or equal to the highest dim /// in ItrDims. TODO: static checking for this. template static void apply(const eckit::Parametrisation& conf, std::tuple&& arrayViews, const Mask& mask, const Function& function) { auto execute = [&](auto execution_policy) { apply(execution_policy, std::move(arrayViews), mask, function); }; using namespace execution; std::string execution_policy; if (conf.get("execution_policy", execution_policy)) { if (execution_policy == policy_name(par_unseq)) { execute(par_unseq); } else if (execution_policy == policy_name(par)) { execute(par); } else if (execution_policy == policy_name(unseq)) { execute(unseq); } else if (execution_policy == policy_name(seq)) { execute(seq); } else { throw_Exception("Unrecognized execution policy " + execution_policy, Here()); } } else { execute(seq); } } /// brief Apply "For-Each" method. /// /// details As above, but Execution policy is determined at compile-time. template ()>> static void apply(ExecutionPolicy, std::tuple&& arrayViews, const Mask& mask, const Function& function) { detail::ArrayForEachImpl::apply( std::move(arrayViews), mask, function, std::make_tuple(), std::make_tuple()); } /// brief Apply "For-Each" method /// /// details Apply ForEach with default execution policy. template static void apply(std::tuple&& arrayViews, const Mask& mask, const Function& function) { apply(std::move(arrayViews), mask, function); } /// brief Apply "For-Each" method /// /// details Apply ForEach with run-time determined execution policy and no /// mask. template static void apply(const eckit::Parametrisation& conf, std::tuple&& arrayViews, const Function& function) { apply(conf, std::move(arrayViews), detail::no_mask, function); } /// brief Apply "For-Each" method /// /// details Apply ForEach with compile-time determined execution policy and no /// mask. template ()>> static void apply(ExecutionPolicy executionPolicy, std::tuple&& arrayViews, const Function& function) { apply(executionPolicy, std::move(arrayViews), detail::no_mask, function); } /// brief Apply "For-Each" method /// /// details Apply ForEach with default execution policy and no mask. template static void apply(std::tuple&& arrayViews, const Function& function) { apply(execution::seq, std::move(arrayViews), function); } }; /// brief Construct ArrayForEach and call apply /// /// details Construct an ArrayForEach using std::integer_sequence /// . Remaining arguments are forwarded to apply /// method. template void arrayForEachDim(std::integer_sequence, Args&&... args) { ArrayForEach::apply(std::forward(args)...); } } // namespace helpers } // namespace array } // namespace atlas atlas-0.46.0/src/atlas/array/helpers/ArraySlicer.h0000664000175000017500000002726015160212070022136 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include "atlas/array/ArrayViewDefs.h" #include "atlas/array/Range.h" #include "atlas/library/config.h" namespace atlas { namespace array { template class LocalView; template struct Reference { Value& value; operator Value&() { return value; } operator const Value&() const { return value; } template void operator=(const T a) { value = a; } template Reference& operator+(const T a) { value += a; return *this; } template Reference& operator-(const T a) { value -= a; return *this; } Reference& operator--() { --value; return *this; } Reference& operator++() { ++value; return *this; } template bool operator==(const T a) { return value == a; } template bool operator!=(const T a) { return value != a; } friend std::ostream& operator<<(std::ostream& out, const Reference& ref) { out << ref.value; return out; } constexpr int shape(idx_t) { return 0; } constexpr int stride(idx_t) { return 0; } constexpr int rank() { return 0; } }; template struct get_slice_type { using type = typename std::conditional<(Rank == 0), Reference, LocalView>::type; }; //------------------------------------------------------------------------------ namespace helpers { template struct deduce_slice_rank; template struct SliceRank_impl; template <> struct deduce_slice_rank<1> { template static constexpr int apply() { return std::is_base_of::value; } }; template struct SliceRank_impl<1, Args...> { static constexpr int value{deduce_slice_rank<1>::apply()}; }; #define ATLAS_ARRAY_SLICER_EXPLICIT_TEMPLATE_SPECIALISATION(RANK) \ template <> \ struct deduce_slice_rank { \ template \ static constexpr int apply() { \ return std::is_base_of::value + deduce_slice_rank::apply(); \ } \ }; \ template \ struct SliceRank_impl { \ static constexpr int value{deduce_slice_rank::apply()}; \ } ATLAS_ARRAY_SLICER_EXPLICIT_TEMPLATE_SPECIALISATION(2); ATLAS_ARRAY_SLICER_EXPLICIT_TEMPLATE_SPECIALISATION(3); ATLAS_ARRAY_SLICER_EXPLICIT_TEMPLATE_SPECIALISATION(4); ATLAS_ARRAY_SLICER_EXPLICIT_TEMPLATE_SPECIALISATION(5); ATLAS_ARRAY_SLICER_EXPLICIT_TEMPLATE_SPECIALISATION(6); ATLAS_ARRAY_SLICER_EXPLICIT_TEMPLATE_SPECIALISATION(7); ATLAS_ARRAY_SLICER_EXPLICIT_TEMPLATE_SPECIALISATION(8); ATLAS_ARRAY_SLICER_EXPLICIT_TEMPLATE_SPECIALISATION(9); #undef ATLAS_ARRAY_SLICER_EXPLICIT_TEMPLATE_SPECIALISATION template struct SliceRank { static constexpr int value{SliceRank_impl::value}; }; // template template class ArraySlicer { public: ArraySlicer(View& view): view_(view) {} template struct Slice { using type = typename get_slice_type::value>::type; }; template typename Slice::type apply(const Args... args) const { using slicer_t = Slicer::type, (SliceRank::value == 0)>; static_assert( View::RANK <= sizeof...(Args), "Not enough arguments passed to slice() function. Pehaps you forgot to add a array::Range::all()"); return slicer_t::apply(view_, args...); } private: template struct array { using type = typename std::array::value>; }; template struct Slicer { template static ReturnType apply(View& view, const Args... args) { return ReturnType(view.data() + offset(view, args...), shape(view, args...).data(), strides(view, args...).data()); } }; template struct Slicer { template static ReturnType apply(View& view, const Args... args) { return ReturnType{*(view.data() + offset(view, args...))}; } }; template static idx_t offset_part(View& view, int& i_view, Int idx) { return idx * view.stride(i_view++); } static idx_t offset_part(View& view, int& i_view, Range range) { return range.start() * view.stride(i_view++); } static idx_t offset_part(View& view, int& i_view, RangeAll range) { return range.start() * view.stride(i_view++); } static idx_t offset_part(View& view, int& i_view, RangeTo range) { return range.start() * view.stride(i_view++); } static idx_t offset_part(View& view, int& i_view, RangeFrom range) { return range.start() * view.stride(i_view++); } static idx_t offset_part(View&, int& /*i_view*/, RangeDummy) { return 0; } template static idx_t offset_remaining(View& view, int& i_view, const Int idx, const Ints... next_idx) { const idx_t p = offset_part(view, i_view, idx); return p + offset_remaining(view, i_view, next_idx...); } template static idx_t offset_remaining(View& view, int& i_view, const Int last_idx) { return offset_part(view, i_view, last_idx); } template static idx_t offset(View& view, const Args... args) { int i_view(0); return offset_remaining<0>(view, i_view, args...); } template static void update_shape(View&, Shape&, int& i_view, int& /*i_slice*/, const Int& /*index*/) { // do nothing ++i_view; } template static void update_shape(View&, Shape& shape, int& i_view, int& i_slice, const Range range) { shape[i_slice] = range.end() - range.start(); ++i_slice; ++i_view; } template static void update_shape(View& view, Shape& shape, int& i_view, int& i_slice, const RangeAll range) { shape[i_slice] = range.end(view, i_view) - range.start(); ++i_slice; ++i_view; } template static void update_shape(View& view, Shape& shape, int& i_view, int& i_slice, const RangeFrom range) { shape[i_slice] = range.end(view, i_view) - range.start(); ++i_slice; ++i_view; } template static void update_shape(View&, Shape& shape, int& i_view, int& i_slice, const RangeTo range) { shape[i_slice] = range.end() - range.start(); ++i_slice; ++i_view; } template static void update_shape(View&, Shape& shape, int& /*i_view*/, int& i_slice, const RangeDummy) { shape[i_slice] = 1; ++i_slice; // no update of i_view for dummy-dimension } template static void shape_part(View& view, Shape& shape, int& i_view, int& i_slice, const Int idx, const Ints... next_idx) { update_shape(view, shape, i_view, i_slice, idx); shape_part(view, shape, i_view, i_slice, next_idx...); } template static void shape_part(View& view, Shape& shape, int& i_view, int& i_slice, const Int idx) { update_shape(view, shape, i_view, i_slice, idx); } template static typename array::type shape(View& view, const Args... args) { typename array::type result; int i_slice(0); int i_view(0); shape_part<0>(view, result, i_view, i_slice, args...); return result; } template static void update_strides(View&, Strides&, int& i_view, int& /*i_slice*/, const Int& /*idx*/) { // do nothing ++i_view; } template static void update_strides(View& view, Strides& strides, int& i_view, int& i_slice, const Range& /*range*/) { strides[i_slice] = view.stride(i_view); ++i_slice; ++i_view; } template static void update_strides(View& view, Strides& strides, int& i_view, int& i_slice, const RangeFrom& /*range*/) { strides[i_slice] = view.stride(i_view); ++i_slice; ++i_view; } template static void update_strides(View& view, Strides& strides, int& i_view, int& i_slice, const RangeTo& /*range*/) { strides[i_slice] = view.stride(i_view); ++i_slice; ++i_view; } template static void update_strides(View& view, Strides& strides, int& i_view, int& i_slice, const RangeAll& /*range*/) { strides[i_slice] = view.stride(i_view); ++i_slice; ++i_view; } template static void update_strides(View& /*view*/, Strides& strides, int& /*i_view*/, int& i_slice, const RangeDummy& /*range*/) { strides[i_slice] = 0; ++i_slice; } template static void strides_part(View& view, Strides& strides, int& i_view, int& i_slice, const Int idx, const Ints... next_idx) { update_strides(view, strides, i_view, i_slice, idx); strides_part(view, strides, i_view, i_slice, next_idx...); } template static void strides_part(View& view, Strides& strides, int& i_view, int& i_slice, const Int idx) { update_strides(view, strides, i_view, i_slice, idx); } template static typename array::type strides(View& view, const Args... args) { typename array::type result; int i_slice(0); int i_view(0); strides_part<0>(view, result, i_view, i_slice, args...); return result; } private: View& view_; }; //------------------------------------------------------------------------------ } // namespace helpers } // namespace array } // namespace atlas atlas-0.46.0/src/atlas/array/helpers/ArrayWriter.h0000664000175000017500000000445415160212070022171 0ustar alastairalastair/* * (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/array.h" //------------------------------------------------------------------------------ namespace atlas { namespace array { namespace helpers { //------------------------------------------------------------------------------ /// Helper to assign a value to an array or arrayview // template struct array_writer; //------------------------------------------------------------------------------ // Recursive function to apply value to every index template struct array_writer_impl { template static void apply(const View& arr, std::ostream& out, DimIndex... idxs) { for (idx_t i = 0; i < arr.shape(Dim); ++i) { array_writer_impl::apply(arr, out, idxs..., i); if (i < arr.shape(Dim) - 1) out << " "; } } }; // End of recursion when Dim == Rank template struct array_writer_impl { template static void apply(const View& arr, std::ostream& out, DimIndex... idxs) { out << arr(idxs...); } }; //------------------------------------------------------------------------------ struct array_writer { template static void apply(const ArrayView& arr, std::ostream& out) { array_writer_impl::apply(arr, out); // Note: no need to apply variadic pack (idxs...) } template static void apply(const LocalView& arr, std::ostream& out) { array_writer_impl::apply(arr, out); // Note: no need to apply variadic pack (idxs...) } }; //------------------------------------------------------------------------------ } // namespace helpers } // namespace array } // namespace atlas atlas-0.46.0/src/atlas/array/helpers/ArrayInitializer.h0000664000175000017500000002326515160212070023201 0ustar alastairalastair/* * (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 #include "atlas/array.h" #include "atlas/array/DataType.h" #include "atlas/array_fwd.h" #include "atlas/runtime/Exception.h" //------------------------------------------------------------------------------ namespace atlas { namespace array { namespace helpers { //------------------------------------------------------------------------------ struct array_initializer; template struct array_initializer_partitioned; //------------------------------------------------------------------------------ template struct array_initializer_impl { static void apply(Array const& orig, Array& array_resized) { array_initializer_impl::apply(make_view(orig), make_view(array_resized)); } template static void apply(ArrayView const&& orig, ArrayView&& array_resized, DimIndex... idxs) { const idx_t N = std::min(array_resized.shape(Dim), orig.shape(Dim)); for (idx_t i = 0; i < N; ++i) { array_initializer_impl::apply(std::move(orig), std::move(array_resized), idxs..., i); } } }; //------------------------------------------------------------------------------ template struct array_initializer_impl { template static void apply(ArrayView const&& orig, ArrayView&& array_resized, DimIndex... idxs) { array_resized(idxs...) = orig(idxs...); } }; //------------------------------------------------------------------------------ struct array_initializer { static void apply(Array const& from, Array& to) { ATLAS_ASSERT(from.rank() == to.rank()); switch (from.rank()) { case 1: apply_rank<1>(from, to); break; case 2: apply_rank<2>(from, to); break; case 3: apply_rank<3>(from, to); break; case 4: apply_rank<4>(from, to); break; case 5: apply_rank<5>(from, to); break; case 6: apply_rank<6>(from, to); break; case 7: apply_rank<7>(from, to); break; case 8: apply_rank<8>(from, to); break; case 9: apply_rank<9>(from, to); break; default: ATLAS_NOTIMPLEMENTED; } } template static void apply_rank(Array const& orig, Array& array_resized) { switch (orig.datatype().kind()) { case DataType::KIND_REAL64: return array_initializer_impl::apply(orig, array_resized); case DataType::KIND_REAL32: return array_initializer_impl::apply(orig, array_resized); case DataType::KIND_INT32: return array_initializer_impl::apply(orig, array_resized); case DataType::KIND_INT64: return array_initializer_impl::apply(orig, array_resized); case DataType::KIND_UINT32: return array_initializer_impl::apply(orig, array_resized); case DataType::KIND_UINT64: return array_initializer_impl::apply(orig, array_resized); default: { std::stringstream err; err << "data kind " << orig.datatype().kind() << " not recognised."; throw_NotImplemented(err.str(), Here()); } } } }; //------------------------------------------------------------------------------ template struct array_initializer_partitioned_val_impl { static void apply(Array const& orig, Array& dest, idx_t pos, idx_t offset) { array_initializer_partitioned_val_impl::apply( make_view(orig), make_view(dest), pos, offset); } template static void apply(ArrayView&& orig, ArrayView&& dest, idx_t pos, idx_t offset, DimIndexPair... idxs) { for (idx_t i = 0; i < orig.shape(Dim); ++i) { idx_t displ = i; if (Dim == PartDim && i >= pos) { displ += offset; } std::pair pair_idx{i, displ}; array_initializer_partitioned_val_impl::apply( std::move(orig), std::move(dest), pos, offset, idxs..., pair_idx); } } }; // template< typename stdarray > // inline std::string print_array(const stdarray& v) // { // std::stringstream s; // s << "[ "; // for( int j=0; j struct array_initializer_partitioned_val_impl { template static void apply(ArrayView&& orig, ArrayView&& dest, idx_t /*pos*/, idx_t /*offset*/, DimIndexPair... idxs) { // Log::info() << print_array(std::array{std::get<0>(idxs)...}) << // " --> " << print_array(std::array{std::get<1>(idxs)...}) << " // " << orig(std::get<0>(idxs)...) << std::endl; dest(std::get<1>(idxs)...) = orig(std::get<0>(idxs)...); } }; //------------------------------------------------------------------------------ template struct array_initializer_partitioned_impl { static void apply(Array const& orig, Array& dest, idx_t pos, idx_t offset) { switch (orig.datatype().kind()) { case DataType::KIND_REAL64: return array_initializer_partitioned_val_impl::apply(orig, dest, pos, offset); case DataType::KIND_REAL32: return array_initializer_partitioned_val_impl::apply(orig, dest, pos, offset); case DataType::KIND_INT32: return array_initializer_partitioned_val_impl::apply(orig, dest, pos, offset); case DataType::KIND_INT64: return array_initializer_partitioned_val_impl::apply(orig, dest, pos, offset); case DataType::KIND_UINT32: return array_initializer_partitioned_val_impl::apply(orig, dest, pos, offset); case DataType::KIND_UINT64: return array_initializer_partitioned_val_impl::apply(orig, dest, pos, offset); default: { std::stringstream err; err << "data kind " << orig.datatype().kind() << " not recognised."; throw_NotImplemented(err.str(), Here()); } } } }; //------------------------------------------------------------------------------ template struct array_initializer_partitioned { static void apply(const Array& orig, Array& dest, idx_t pos, idx_t offset) { switch (orig.rank()) { case 1: return array_initializer_partitioned_impl<1, PartDim>::apply(orig, dest, pos, offset); case 2: return array_initializer_partitioned_impl<2, PartDim>::apply(orig, dest, pos, offset); case 3: return array_initializer_partitioned_impl<3, PartDim>::apply(orig, dest, pos, offset); case 4: return array_initializer_partitioned_impl<4, PartDim>::apply(orig, dest, pos, offset); case 5: return array_initializer_partitioned_impl<5, PartDim>::apply(orig, dest, pos, offset); case 6: return array_initializer_partitioned_impl<6, PartDim>::apply(orig, dest, pos, offset); case 7: return array_initializer_partitioned_impl<7, PartDim>::apply(orig, dest, pos, offset); case 8: return array_initializer_partitioned_impl<8, PartDim>::apply(orig, dest, pos, offset); case 9: return array_initializer_partitioned_impl<9, PartDim>::apply(orig, dest, pos, offset); default: { std::stringstream err; err << "too high Rank"; throw_NotImplemented(err.str(), Here()); } } } }; //------------------------------------------------------------------------------ } // namespace helpers } // namespace array } // namespace atlas atlas-0.46.0/src/atlas/array/helpers/ArrayAssigner.h0000664000175000017500000000667615160212070022500 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include "atlas/array.h" #include "atlas/runtime/Exception.h" //------------------------------------------------------------------------------ namespace atlas { namespace array { namespace helpers { //------------------------------------------------------------------------------ /// Helper to assign a value to an array or arrayview template struct array_assigner; //------------------------------------------------------------------------------ // Recursive function to apply value to every index template struct array_assigner_impl { template static void apply(View& arr, Value value, DimIndex... idxs) { for (idx_t i = 0; i < arr.shape(Dim); ++i) { array_assigner_impl::apply(arr, value, idxs..., i); } } template static void apply(View& arr, Iterator& it, DimIndex... idxs) { for (idx_t i = 0; i < arr.shape(Dim); ++i) { array_assigner_impl::apply(arr, it, idxs..., i); } } }; // End of recursion when Dim == Rank template struct array_assigner_impl { template static void apply(View& arr, Value value, DimIndex... idxs) { arr(idxs...) = value; } template static void apply(View& arr, Iterator& it, DimIndex... idxs) { arr(idxs...) = *it; ++it; } }; //------------------------------------------------------------------------------ template struct array_assigner { template static void apply(Array& arr, Value value) { return apply(make_host_view(arr), value); } static void apply(ArrayView& arr, Value value) { array_assigner_impl::apply(arr, value); // Note: no need to apply variadic pack (idxs...) } template static void apply(ArrayView& arr, const Iterable& iterable) { typename Iterable::const_iterator it = iterable.begin(); array_assigner_impl::apply(arr, it); ATLAS_ASSERT(it = iterable.end()); } template static void apply(IndexView& arr, const Iterable& iterable) { typename Iterable::const_iterator it = iterable.begin(); array_assigner_impl::apply(arr, it); ATLAS_ASSERT(it = iterable.end()); } static void apply(LocalView& arr, Value value) { array_assigner_impl::apply(arr, value); // Note: no need to apply variadic pack (idxs...) } }; //------------------------------------------------------------------------------ } // namespace helpers } // namespace array } // namespace atlas atlas-0.46.0/src/atlas/array/Vector.cc0000664000175000017500000000101615160212070017643 0ustar alastairalastair/* * (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 "Vector.h" namespace atlas { namespace array { namespace detail {} // namespace detail } // namespace array } // namespace atlas atlas-0.46.0/src/atlas/array/MakeView.h0000664000175000017500000000722115160212070017757 0ustar alastairalastair/* * (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/array/ArrayView.h" #include "atlas/array/IndexView.h" #include "atlas/array/LocalView.h" #include "atlas/array_fwd.h" namespace atlas { namespace array { extern template IndexView make_indexview(Array&); extern template IndexView make_indexview(Array&); extern template IndexView make_indexview(Array&); extern template IndexView make_indexview(Array&); extern template IndexView make_indexview(const Array&); extern template IndexView make_indexview(const Array&); #define EXPLICIT_TEMPLATE_DECLARATION_TYPE_RANK(TYPE, RANK) \ extern template ArrayView make_view(Array&); \ extern template ArrayView make_view(Array&); \ extern template ArrayView make_view(const Array&); \ extern template ArrayView make_view(const Array&); \ \ extern template LocalView make_view(TYPE data[], const ArrayShape&); \ extern template LocalView make_view(TYPE data[], const ArrayShape&); \ extern template LocalView make_view(const TYPE data[], const ArrayShape&); \ extern template LocalView make_view(const TYPE data[], \ const ArrayShape&); \ \ extern template LocalView make_view(TYPE data[], size_t); \ extern template LocalView make_view(TYPE data[], size_t); \ extern template LocalView make_view(const TYPE data[], size_t); \ extern template LocalView make_view(const TYPE data[], size_t); #define EXPLICIT_TEMPLATE_DECLARATION(RANK) \ EXPLICIT_TEMPLATE_DECLARATION_TYPE_RANK(int, RANK) \ EXPLICIT_TEMPLATE_DECLARATION_TYPE_RANK(long, RANK) \ EXPLICIT_TEMPLATE_DECLARATION_TYPE_RANK(long long, RANK) \ EXPLICIT_TEMPLATE_DECLARATION_TYPE_RANK(float, RANK) \ EXPLICIT_TEMPLATE_DECLARATION_TYPE_RANK(double, RANK) // For each NDims in [1..9] EXPLICIT_TEMPLATE_DECLARATION(1) EXPLICIT_TEMPLATE_DECLARATION(2) EXPLICIT_TEMPLATE_DECLARATION(3) EXPLICIT_TEMPLATE_DECLARATION(4) EXPLICIT_TEMPLATE_DECLARATION(5) EXPLICIT_TEMPLATE_DECLARATION(6) EXPLICIT_TEMPLATE_DECLARATION(7) EXPLICIT_TEMPLATE_DECLARATION(8) EXPLICIT_TEMPLATE_DECLARATION(9) #undef EXPLICIT_TEMPLATE_DECLARATION } // namespace array } // namespace atlas atlas-0.46.0/src/atlas/array/ArraySpec.cc0000664000175000017500000001474115160212070020303 0ustar alastairalastair/* * (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/array/ArrayDataStore.h" #include "atlas/library/config.h" #include "atlas/runtime/Exception.h" namespace atlas { namespace array { namespace { size_t compute_aligned_size(size_t size, size_t alignment) { size_t div = size / alignment; size_t mod = size % alignment; size_t _aligned_size = div * alignment; if (mod > 0) { _aligned_size += alignment; } return _aligned_size; } } // namespace ArraySpec::ArraySpec(): size_(), rank_(), allocated_size_(), datatype_(DataType::KIND_REAL64), contiguous_(true), default_layout_(true) {} ArraySpec::ArraySpec(const ArrayShape& shape): ArraySpec(shape, ArrayAlignment()) {} ArraySpec::ArraySpec(DataType datatype, const ArrayShape& shape): ArraySpec(shape) { datatype_ = datatype; } ArraySpec::ArraySpec(const ArrayShape& shape, const ArrayAlignment& alignment): datatype_(DataType::KIND_REAL64) { ArrayShape aligned_shape = shape; aligned_shape.back() = compute_aligned_size(aligned_shape.back(), size_t(alignment)); rank_ = static_cast(shape.size()); size_ = 1; allocated_size_ = 1; shape_.resize(rank_); strides_.resize(rank_); layout_.resize(rank_); device_strides_.resize(rank_); device_strides_[rank_ - 1] = 1; for (int j = rank_ - 1; j >= 0; --j) { shape_[j] = shape[j]; strides_[j] = allocated_size_; layout_[j] = j; size_ *= size_t(shape_[j]); allocated_size_ *= size_t(aligned_shape[j]); if( j < rank_ - 1) { // Assume contiguous device data! device_strides_[j] = strides_[j+1] * shape[j+1]; } } ATLAS_ASSERT(allocated_size_ == compute_aligned_size(size_t(shape_[0]) * size_t(strides_[0]), size_t(alignment))); contiguous_ = (size_ == allocated_size_); default_layout_ = true; alignment_ = alignment; #ifdef ATLAS_HAVE_FORTRAN allocate_fortran_specs(); #endif }; ArraySpec::ArraySpec(DataType datatype, const ArrayShape& shape, const ArrayAlignment& alignment): ArraySpec(shape, alignment) { datatype_ = datatype; } ArraySpec::ArraySpec(const ArrayShape& shape, const ArrayStrides& strides): ArraySpec(shape, strides, ArrayAlignment()) {} ArraySpec::ArraySpec(const ArrayShape& shape, const ArrayStrides& strides, const ArrayAlignment& alignment): datatype_(DataType::KIND_REAL64) { ATLAS_ASSERT(shape.size() == strides.size(), "dimensions of shape and stride don't match"); rank_ = static_cast(shape.size()); size_ = 1; shape_.resize(rank_); strides_.resize(rank_); layout_.resize(rank_); device_strides_.resize(rank_); device_strides_[rank_ - 1] = strides[rank_ - 1]; for (int j = rank_ - 1; j >= 0; --j) { shape_[j] = shape[j]; strides_[j] = strides[j]; layout_[j] = j; size_ *= size_t(shape_[j]); if( j < rank_ - 1) { // Assume contiguous device data! device_strides_[j] = device_strides_[j+1] * shape[j+1]; } } allocated_size_ = compute_aligned_size(size_t(shape_[0]) * size_t(strides_[0]), size_t(alignment)); contiguous_ = (size_ == allocated_size_); default_layout_ = true; #ifdef ATLAS_HAVE_FORTRAN allocate_fortran_specs(); #endif } ArraySpec::ArraySpec(DataType datatype, const ArrayShape& shape, const ArrayStrides& strides, const ArrayAlignment& alignment): ArraySpec(shape, strides, alignment) { datatype_ = datatype; } ArraySpec::ArraySpec(const ArrayShape& shape, const ArrayStrides& strides, const ArrayLayout& layout): ArraySpec(shape, strides, layout, ArrayAlignment()) {} ArraySpec::ArraySpec(DataType datatype, const ArrayShape& shape, const ArrayStrides& strides, const ArrayLayout& layout): ArraySpec(shape, strides, layout) { datatype_ = datatype; } ArraySpec::ArraySpec(const ArrayShape& shape, const ArrayStrides& strides, const ArrayLayout& layout, const ArrayAlignment& alignment): datatype_(DataType::KIND_REAL64) { ATLAS_ASSERT(shape.size() == strides.size(), "dimensions of shape and stride don't match"); rank_ = static_cast(shape.size()); size_ = 1; shape_.resize(rank_); strides_.resize(rank_); layout_.resize(rank_); device_strides_.resize(rank_); device_strides_[rank_ - 1] = strides[rank_ - 1]; default_layout_ = true; for (int j = rank_ - 1; j >= 0; --j) { shape_[j] = shape[j]; strides_[j] = strides[j]; layout_[j] = layout[j]; size_ *= size_t(shape_[j]); if (layout_[j] != idx_t(j)) { default_layout_ = false; } if( j < rank_ - 1) { // Assume contiguous device data! device_strides_[j] = device_strides_[j+1] * shape[j+1]; } } allocated_size_ = compute_aligned_size(size_t(shape_[layout_[0]]) * size_t(strides_[layout_[0]]), size_t(alignment)); contiguous_ = (size_ == allocated_size_); #ifdef ATLAS_HAVE_FORTRAN allocate_fortran_specs(); #endif } ArraySpec::ArraySpec(DataType datatype, const ArrayShape& shape, const ArrayStrides& strides, const ArrayLayout& layout, const ArrayAlignment& alignment): ArraySpec(shape, strides, layout, alignment) { datatype_ = datatype; } const std::vector& ArraySpec::shapef() const { return shapef_; } const std::vector& ArraySpec::stridesf() const { return stridesf_; } const std::vector& ArraySpec::device_stridesf() const { return device_stridesf_; } void ArraySpec::allocate_fortran_specs() { shapef_.resize(rank_); stridesf_.resize(rank_); device_stridesf_.resize(rank_); for (idx_t j = 0; j < rank_; ++j) { shapef_[j] = shape_[rank_ - 1 - layout_[j]]; stridesf_[j] = strides_[rank_ - 1 - layout_[j]]; device_stridesf_[j] = device_strides_[rank_ - 1 - j]; } } static thread_local std::string label_; std::string_view label::get() { return label_; } void label::set(std::string_view s) { label_.assign(s.data(),s.size()); } } // namespace array } // namespace atlas atlas-0.46.0/src/atlas/array/SVector.h0000664000175000017500000001144315160212070017635 0ustar alastairalastair/* * (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 #include "atlas/library/config.h" #include "atlas/util/Allocate.h" #if ATLAS_HOST_COMPILE #include "atlas/runtime/Exception.h" #endif namespace atlas { namespace array { //------------------------------------------------------------------------------ template class SVector { public: ATLAS_HOST_DEVICE SVector(): data_(nullptr), size_(0), externally_allocated_(false) {} ATLAS_HOST_DEVICE SVector(const T* data, const idx_t size): data_(data), size_(size), externally_allocated_(true) {} #if ATLAS_HIC_COMPILER // Note that this does not copy!!! It is mainly intended to be passed to a CUDA kernel which requires value semantics for this class ATLAS_HOST_DEVICE SVector(SVector const& other): data_(other.data_), size_(other.size_), externally_allocated_(true) {} #endif ATLAS_HOST_DEVICE SVector(SVector&& other): data_(other.data_), size_(other.size_), externally_allocated_(other.externally_allocated_) { other.data_ = nullptr; other.size_ = 0; other.externally_allocated_ = true; } ATLAS_HOST_DEVICE SVector& operator=(SVector&& other) { data_ = other.data_; size_ = other.size_; externally_allocated_ = other.externally_allocated_; other.data_ = nullptr; other.size_ = 0; other.externally_allocated_ = true; return *this; } ATLAS_HOST_DEVICE SVector(T* data, idx_t size): data_(data), size_(size), externally_allocated_(true) {} SVector(idx_t N): data_(nullptr), size_(N), externally_allocated_(false) { allocate(data_, N); } ATLAS_HOST_DEVICE ~SVector() { clear(); } ATLAS_HOST_DEVICE void clear() { if (data_ && !externally_allocated_) { #if ATLAS_HOST_COMPILE deallocate(data_, size_); #endif } data_ = nullptr; size_ = 0; externally_allocated_ = false; } void insert(idx_t pos, idx_t dimsize) { T* data = nullptr; allocate(data, size_ + dimsize); for (idx_t c = 0; c < pos; ++c) { data[c] = std::move(data_[c]); } for (idx_t c = pos; c < size_; ++c) { data[c + dimsize] = std::move(data_[c]); } deallocate(data_, size_); data_ = data; size_ += dimsize; } size_t footprint() const { return sizeof(T) * size_; } ATLAS_HOST_DEVICE T* data() { return data_; } ATLAS_HOST_DEVICE T const* data() const { return data_; } ATLAS_HOST_DEVICE T& operator()(const idx_t idx) { //assert(data_ && idx < size_); return data_[idx]; } ATLAS_HOST_DEVICE T const& operator()(const idx_t idx) const { //assert(data_ && idx < size_); return data_[idx]; } ATLAS_HOST_DEVICE T& operator[](const idx_t idx) { //assert(data_ && idx < size_); return data_[idx]; } ATLAS_HOST_DEVICE T const& operator[](const idx_t idx) const { //assert(data_ && idx < size_); return data_[idx]; } ATLAS_HOST_DEVICE idx_t size() const { return size_; } void resize_impl(idx_t N) { if (N == size_) return; T* d_ = nullptr; allocate(d_, N); for (idx_t c = 0; c < std::min(size_, N); ++c) { d_[c] = std::move(data_[c]); } deallocate(data_, size_); data_ = d_; } void resize(idx_t N) { #if ATLAS_HOST_COMPILE ATLAS_ASSERT(not externally_allocated_, "Cannot resize externally allocated (or wrapped) data"); #endif resize_impl(N); size_ = N; } private: static void allocate(T*& ptr, idx_t size) { if (size > 0) { util::allocate_managedmem(ptr, size); for (idx_t c = 0; c < size; ++c) { new (ptr + c) T(); } } } static void deallocate(T*& ptr, idx_t size) { if (ptr) { for (idx_t c = 0; c < size; ++c) { ptr[c].~T(); } util::delete_managedmem(ptr, size); } } private: T* data_; idx_t size_; bool externally_allocated_; }; //------------------------------------------------------------------------------ } // namespace array } // namespace atlas atlas-0.46.0/src/atlas/array/gridtools/0000775000175000017500000000000015160212070020102 5ustar alastairalastairatlas-0.46.0/src/atlas/array/gridtools/GridToolsArray.cc0000664000175000017500000006257515160212070023335 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include #include #include #include "atlas/array.h" #include "atlas/array/ArrayDataStore.h" #include "atlas/array/ArrayView.h" #include "atlas/array/DataType.h" #include "atlas/array/MakeView.h" #include "atlas/array/gridtools/GridToolsArrayHelpers.h" #include "atlas/array/gridtools/GridToolsDataStore.h" #include "atlas/array/gridtools/GridToolsTraits.h" #include "atlas/array/helpers/ArrayInitializer.h" #include "atlas/array_fwd.h" #include "atlas/runtime/Log.h" #if ATLAS_HAVE_ACC #include "atlas_acc_support/atlas_acc.h" #endif //------------------------------------------------------------------------------ using namespace atlas::array::gridtools; using namespace atlas::array::helpers; namespace atlas { namespace array { //------------------------------------------------------------------------------ namespace gridtools { //------------------------------------------------------------------------------ template using UintSequence = std::make_integer_sequence; template class Storage, typename StorageInfo> static Array* wrap_array(::gridtools::data_store, StorageInfo>* ds, const ArraySpec& spec) { assert(ds); using data_store_t = typename std::remove_pointer::type; ArrayDataStore* data_store = new GridToolsDataStore(ds); return new ArrayT(data_store, spec); } //------------------------------------------------------------------------------ } // namespace gridtools template class ArrayT_impl { public: ArrayT_impl(ArrayT& array): array_(array) {} template > void construct(UInts... dims) { static_assert(sizeof...(UInts) > 0, "1"); auto gt_storage = create_gt_storage::type>(dims...); using data_store_t = typename std::remove_pointer::type; array_.data_store_ = std::make_unique>(gt_storage); array_.spec_ = make_spec(gt_storage, dims...); } template > void construct_aligned(UInts... dims) { static_assert(sizeof...(UInts) > 0, "1"); auto gt_storage = create_gt_storage::type, ::gridtools::alignment>(dims...); using data_store_t = typename std::remove_pointer::type; array_.data_store_ = std::make_unique>(gt_storage); array_.spec_ = make_spec(gt_storage, dims...); } template > void construct(ArrayAlignment alignment, UInts... dims) { static_assert(sizeof...(UInts) > 0, "1"); switch (int(alignment)) { case 1: construct(dims...); break; case 2: construct_aligned<2>(dims...); break; case 4: construct_aligned<4>(dims...); break; case 8: construct_aligned<8>(dims...); break; case 16: construct_aligned<16>(dims...); break; case 32: construct_aligned<32>(dims...); break; case 64: construct_aligned<64>(dims...); break; default: ATLAS_NOTIMPLEMENTED; } } void construct(const ArrayShape& shape) { assert(shape.size() > 0); switch (shape.size()) { case 1: return construct(shape[0]); case 2: return construct(shape[0], shape[1]); case 3: return construct(shape[0], shape[1], shape[2]); case 4: return construct(shape[0], shape[1], shape[2], shape[3]); case 5: return construct(shape[0], shape[1], shape[2], shape[3], shape[4]); default: { std::stringstream err; err << "shape not recognized"; throw_Exception(err.str(), Here()); } } } void construct(const ArraySpec& spec) { auto& shape = spec.shape(); assert(shape.size() > 0); switch (shape.size()) { case 1: return construct(spec.alignment(), shape[0]); case 2: return construct(spec.alignment(), shape[0], shape[1]); case 3: return construct(spec.alignment(), shape[0], shape[1], shape[2]); case 4: return construct(spec.alignment(), shape[0], shape[1], shape[2], shape[3]); case 5: return construct(spec.alignment(), shape[0], shape[1], shape[2], shape[3], shape[4]); default: { std::stringstream err; err << "shape not recognized"; throw_Exception(err.str(), Here()); } } } void construct(const ArrayShape& shape, const ArrayLayout& layout) { ATLAS_ASSERT(shape.size() > 0); ATLAS_ASSERT(shape.size() == layout.size()); switch (shape.size()) { case 1: return construct(shape[0]); case 2: { if (layout[0] == 0 && layout[1] == 1) { return construct_with_layout<::gridtools::layout_map<0, 1>>(shape[0], shape[1]); } if (layout[0] == 1 && layout[1] == 0) { return construct_with_layout<::gridtools::layout_map<1, 0>>(shape[0], shape[1]); } } case 3: { if (layout[0] == 0 && layout[1] == 1 && layout[2] == 2) { return construct_with_layout<::gridtools::layout_map<0, 1, 2>>(shape[0], shape[1], shape[2]); } if (layout[0] == 0 && layout[1] == 2 && layout[2] == 1) { return construct_with_layout<::gridtools::layout_map<0, 2, 1>>(shape[0], shape[1], shape[2]); } if (layout[0] == 1 && layout[1] == 0 && layout[2] == 2) { return construct_with_layout<::gridtools::layout_map<1, 0, 2>>(shape[0], shape[1], shape[2]); } if (layout[0] == 1 && layout[1] == 2 && layout[2] == 0) { return construct_with_layout<::gridtools::layout_map<1, 2, 0>>(shape[0], shape[1], shape[2]); } if (layout[0] == 2 && layout[1] == 0 && layout[2] == 1) { return construct_with_layout<::gridtools::layout_map<2, 0, 1>>(shape[0], shape[1], shape[2]); } if (layout[0] == 2 && layout[1] == 1 && layout[2] == 0) { return construct_with_layout<::gridtools::layout_map<2, 1, 0>>(shape[0], shape[1], shape[2]); } } case 4: { if (layout[0] == 0 && layout[1] == 1 && layout[2] == 2 && layout[3] == 3) { return construct_with_layout<::gridtools::layout_map<0, 1, 2, 3>>(shape[0], shape[1], shape[2], shape[3]); } if (layout[0] == 3 && layout[1] == 2 && layout[2] == 1 && layout[3] == 0) { return construct_with_layout<::gridtools::layout_map<3, 2, 1, 0>>(shape[0], shape[1], shape[2], shape[3]); } } case 5: { if (layout[0] == 0 && layout[1] == 1 && layout[2] == 2 && layout[3] == 3 && layout[4] == 4) { return construct_with_layout<::gridtools::layout_map<0, 1, 2, 3, 4>>(shape[0], shape[1], shape[2], shape[3], shape[4]); } if (layout[0] == 4 && layout[1] == 3 && layout[2] == 2 && layout[3] == 1 && layout[4] == 0) { return construct_with_layout<::gridtools::layout_map<4, 3, 2, 1, 0>>(shape[0], shape[1], shape[2], shape[3], shape[4]); } } default: { std::stringstream err; if (shape.size() > 5) { err << "shape not recognized"; } else { err << "Layout < "; for (size_t j = 0; j < layout.size(); ++j) { err << layout[j] << " "; } err << "> not implemented in Atlas."; } throw_Exception(err.str(), Here()); } } } template > void construct_with_layout(UInts... dims) { auto gt_data_store_ptr = create_gt_storage(dims...); using data_store_t = typename std::remove_pointer::type; array_.data_store_ = std::make_unique>(gt_data_store_ptr); array_.spec_ = make_spec(gt_data_store_ptr, dims...); } template void resize_variadic(Ints... c) { if (sizeof...(c) != array_.rank()) { std::stringstream err; err << "Trying to resize an array of Rank " << array_.rank() << " by dimensions with Rank " << sizeof...(c) << std::endl; throw_Exception(err.str(), Here()); } if (array_.valid()) { array_.syncHostDevice(); } Array* resized = Array::create(ArrayShape{(idx_t)c...}); array_initializer::apply(array_, *resized); array_.replace(*resized); delete resized; } template void apply_resize(const ArrayShape& shape, std::integer_sequence) { return resize_variadic(shape[Indices]...); } private: ArrayT& array_; }; //------------------------------------------------------------------------------ template Array* Array::create(idx_t dim0) { return new ArrayT(dim0); } template Array* Array::create(idx_t dim0, idx_t dim1) { return new ArrayT(dim0, dim1); } template Array* Array::create(idx_t dim0, idx_t dim1, idx_t dim2) { return new ArrayT(dim0, dim1, dim2); } template Array* Array::create(idx_t dim0, idx_t dim1, idx_t dim2, idx_t dim3) { return new ArrayT(dim0, dim1, dim2, dim3); } template Array* Array::create(idx_t dim0, idx_t dim1, idx_t dim2, idx_t dim3, idx_t dim4) { return new ArrayT(dim0, dim1, dim2, dim3, dim4); } template Array* Array::create(const ArrayShape& shape) { return new ArrayT(shape); } template Array* Array::create(const ArrayShape& shape, const ArrayLayout& layout) { return new ArrayT(shape, layout); } template Array* Array::wrap(Value* data, const ArraySpec& spec) { ArrayShape const& shape = spec.shape(); ArrayStrides const& strides = spec.strides(); assert(shape.size() > 0); switch (shape.size()) { case 1: return wrap_array( wrap_gt_storage(data, get_array_from_vector<1>(shape), get_array_from_vector<1>(strides)), spec); case 2: return wrap_array( wrap_gt_storage(data, get_array_from_vector<2>(shape), get_array_from_vector<2>(strides)), spec); case 3: return wrap_array( wrap_gt_storage(data, get_array_from_vector<3>(shape), get_array_from_vector<3>(strides)), spec); case 4: return wrap_array( wrap_gt_storage(data, get_array_from_vector<4>(shape), get_array_from_vector<4>(strides)), spec); case 5: return wrap_array( wrap_gt_storage(data, get_array_from_vector<5>(shape), get_array_from_vector<5>(strides)), spec); case 6: return wrap_array( wrap_gt_storage(data, get_array_from_vector<6>(shape), get_array_from_vector<6>(strides)), spec); case 7: return wrap_array( wrap_gt_storage(data, get_array_from_vector<7>(shape), get_array_from_vector<7>(strides)), spec); case 8: return wrap_array( wrap_gt_storage(data, get_array_from_vector<8>(shape), get_array_from_vector<8>(strides)), spec); case 9: return wrap_array( wrap_gt_storage(data, get_array_from_vector<9>(shape), get_array_from_vector<9>(strides)), spec); default: { std::stringstream err; err << "shape not recognized"; throw_Exception(err.str(), Here()); } } } template Array* Array::wrap(Value* data, const ArrayShape& shape) { return wrap(data, ArraySpec(shape)); } Array* Array::create(DataType datatype, const ArrayShape& shape) { switch (datatype.kind()) { case DataType::KIND_REAL64: return create(shape); case DataType::KIND_REAL32: return create(shape); case DataType::KIND_INT32: return create(shape); case DataType::KIND_INT64: return create(shape); case DataType::KIND_UINT64: return create(shape); default: { std::stringstream err; err << "data kind " << datatype.kind() << " not recognised."; throw_Exception(err.str(), Here()); } } } Array* Array::create(DataType datatype, const ArrayShape& shape, const ArrayLayout& layout) { switch (datatype.kind()) { case DataType::KIND_REAL64: return create(shape, layout); case DataType::KIND_REAL32: return create(shape, layout); case DataType::KIND_INT32: return create(shape, layout); case DataType::KIND_INT64: return create(shape, layout); case DataType::KIND_UINT64: return create(shape, layout); default: { std::stringstream err; err << "data kind " << datatype.kind() << " not recognised."; throw_Exception(err.str(), Here()); } } } Array* Array::create(DataType datatype, ArraySpec&& spec) { switch (datatype.kind()) { case DataType::KIND_REAL64: return new ArrayT(std::move(spec)); case DataType::KIND_REAL32: return new ArrayT(std::move(spec)); case DataType::KIND_INT32: return new ArrayT(std::move(spec)); case DataType::KIND_INT64: return new ArrayT(std::move(spec)); case DataType::KIND_UINT64: return new ArrayT(std::move(spec)); default: { std::stringstream err; err << "data kind " << datatype.kind() << " not recognised."; throw_NotImplemented(err.str(), Here()); } } } Array* Array::create(ArraySpec&& spec) { return create(spec.datatype(), std::move(spec)); } //------------------------------------------------------------------------------ Array::~Array() = default; //------------------------------------------------------------------------------ template size_t ArrayT::footprint() const { size_t size = sizeof(*this); size += bytes(); return size; } //------------------------------------------------------------------------------ template void ArrayT::accMap() const { data_store_->accMap(); } template void ArrayT::accUnmap() const { data_store_->accUnmap(); } template bool ArrayT::accMapped() const { return data_store_->accMapped(); } //------------------------------------------------------------------------------ template void ArrayT::insert(idx_t idx1, idx_t size1) { // if( hostNeedsUpdate() ) { // updateHost(); //} if (not hasDefaultLayout()) { ATLAS_NOTIMPLEMENTED; } ArrayShape nshape = shape(); if (idx1 > nshape[0]) { throw_Exception("can not insert into an array at a position beyond its size", Here()); } nshape[0] += size1; Array* resized = Array::create(nshape); array_initializer_partitioned<0>::apply(*this, *resized, idx1, size1); replace(*resized); delete resized; } //------------------------------------------------------------------------------ template void ArrayT::resize(idx_t dim0) { ArrayT_impl(*this).resize_variadic(dim0); } template void ArrayT::resize(idx_t dim0, idx_t dim1) { ArrayT_impl(*this).resize_variadic(dim0, dim1); } template void ArrayT::resize(idx_t dim0, idx_t dim1, idx_t dim2) { ArrayT_impl(*this).resize_variadic(dim0, dim1, dim2); } template void ArrayT::resize(idx_t dim0, idx_t dim1, idx_t dim2, idx_t dim3) { ArrayT_impl(*this).resize_variadic(dim0, dim1, dim2, dim3); } template void ArrayT::resize(idx_t dim0, idx_t dim1, idx_t dim2, idx_t dim3, idx_t dim4) { ArrayT_impl(*this).resize_variadic(dim0, dim1, dim2, dim3, dim4); } template void ArrayT::copy(const Array& other, const Array::CopyPolicy&) { array_initializer::apply(other, *this); } template void ArrayT::dump(std::ostream& out) const { switch (rank()) { case 1: make_host_view(*this).dump(out); break; case 2: make_host_view(*this).dump(out); break; case 3: make_host_view(*this).dump(out); break; case 4: make_host_view(*this).dump(out); break; case 5: make_host_view(*this).dump(out); break; case 6: make_host_view(*this).dump(out); break; case 7: make_host_view(*this).dump(out); break; case 8: make_host_view(*this).dump(out); break; case 9: make_host_view(*this).dump(out); break; default: ATLAS_NOTIMPLEMENTED; } } template void ArrayT::resize(const ArrayShape& shape) { assert(shape.size() > 0); switch (shape.size()) { case 1: return ArrayT_impl(*this).apply_resize(shape, UintSequence<1>()); case 2: return ArrayT_impl(*this).apply_resize(shape, UintSequence<2>()); case 3: return ArrayT_impl(*this).apply_resize(shape, UintSequence<3>()); case 4: return ArrayT_impl(*this).apply_resize(shape, UintSequence<4>()); case 5: return ArrayT_impl(*this).apply_resize(shape, UintSequence<5>()); case 6: return ArrayT_impl(*this).apply_resize(shape, UintSequence<6>()); case 7: return ArrayT_impl(*this).apply_resize(shape, UintSequence<7>()); case 8: return ArrayT_impl(*this).apply_resize(shape, UintSequence<8>()); case 9: return ArrayT_impl(*this).apply_resize(shape, UintSequence<9>()); default: { std::stringstream err; err << "shape not recognized"; throw_Exception(err.str(), Here()); } } } //------------------------------------------------------------------------------ template ArrayT::ArrayT(ArrayDataStore* ds, const ArraySpec& spec) { data_store_ = std::unique_ptr(ds); spec_ = spec; } template ArrayT::ArrayT(idx_t dim0) { ArrayT_impl(*this).construct(dim0); } template ArrayT::ArrayT(idx_t dim0, idx_t dim1) { ArrayT_impl(*this).construct(dim0, dim1); } template ArrayT::ArrayT(idx_t dim0, idx_t dim1, idx_t dim2) { ArrayT_impl(*this).construct(dim0, dim1, dim2); } template ArrayT::ArrayT(idx_t dim0, idx_t dim1, idx_t dim2, idx_t dim3) { ArrayT_impl(*this).construct(dim0, dim1, dim2, dim3); } template ArrayT::ArrayT(idx_t dim0, idx_t dim1, idx_t dim2, idx_t dim3, idx_t dim4) { ArrayT_impl(*this).construct(dim0, dim1, dim2, dim3, dim4); } template ArrayT::ArrayT(const ArrayShape& shape) { ArrayT_impl(*this).construct(shape); } template ArrayT::ArrayT(const ArrayShape& shape, const ArrayAlignment& alignment) { ArrayT_impl(*this).construct(ArraySpec(shape, alignment)); } template ArrayT::ArrayT(const ArrayShape& shape, const ArrayLayout& layout) { ArrayT_impl(*this).construct(shape, layout); } template ArrayT::ArrayT(ArraySpec&& spec) { ArrayT_impl(*this).construct(spec); } //------------------------------------------------------------------------------ } // namespace array } // namespace atlas //------------------------------------------------------------------------------ // Explicit template instantiations namespace atlas { namespace array { template class ArrayT; template class ArrayT; template class ArrayT; template class ArrayT; template class ArrayT; template Array* Array::create(idx_t); template Array* Array::create(idx_t); template Array* Array::create(idx_t); template Array* Array::create(idx_t); template Array* Array::create(idx_t); template Array* Array::create(idx_t, idx_t); template Array* Array::create(idx_t, idx_t); template Array* Array::create(idx_t, idx_t); template Array* Array::create(idx_t, idx_t); template Array* Array::create(idx_t, idx_t); template Array* Array::create(idx_t, idx_t, idx_t); template Array* Array::create(idx_t, idx_t, idx_t); template Array* Array::create(idx_t, idx_t, idx_t); template Array* Array::create(idx_t, idx_t, idx_t); template Array* Array::create(idx_t, idx_t, idx_t); template Array* Array::create(idx_t, idx_t, idx_t, idx_t); template Array* Array::create(idx_t, idx_t, idx_t, idx_t); template Array* Array::create(idx_t, idx_t, idx_t, idx_t); template Array* Array::create(idx_t, idx_t, idx_t, idx_t); template Array* Array::create(idx_t, idx_t, idx_t, idx_t); template Array* Array::create(idx_t, idx_t, idx_t, idx_t, idx_t); template Array* Array::create(idx_t, idx_t, idx_t, idx_t, idx_t); template Array* Array::create(idx_t, idx_t, idx_t, idx_t, idx_t); template Array* Array::create(idx_t, idx_t, idx_t, idx_t, idx_t); template Array* Array::create(idx_t, idx_t, idx_t, idx_t, idx_t); template Array* Array::create(const ArrayShape&); template Array* Array::create(const ArrayShape&); template Array* Array::create(const ArrayShape&); template Array* Array::create(const ArrayShape&); template Array* Array::create(const ArrayShape&); template Array* Array::create(const ArrayShape&, const ArrayLayout&); template Array* Array::create(const ArrayShape&, const ArrayLayout&); template Array* Array::create(const ArrayShape&, const ArrayLayout&); template Array* Array::create(const ArrayShape&, const ArrayLayout&); template Array* Array::create(const ArrayShape&, const ArrayLayout&); template Array* Array::wrap(int*, const ArrayShape&); template Array* Array::wrap(long*, const ArrayShape&); template Array* Array::wrap(float*, const ArrayShape&); template Array* Array::wrap(double*, const ArrayShape&); template Array* Array::wrap(long unsigned*, const ArrayShape&); template Array* Array::wrap(int*, const ArraySpec&); template Array* Array::wrap(long*, const ArraySpec&); template Array* Array::wrap(float*, const ArraySpec&); template Array* Array::wrap(double*, const ArraySpec&); template Array* Array::wrap(long unsigned*, const ArraySpec&); } // namespace array } // namespace atlas atlas-0.46.0/src/atlas/array/gridtools/GridToolsIndexView.cc0000664000175000017500000001467415160212070024156 0ustar alastairalastair/* * (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 "atlas/array.h" #include "atlas/array/IndexView.h" #include "atlas/field/Field.h" #include "atlas/runtime/Exception.h" //------------------------------------------------------------------------------------------------------ namespace atlas { namespace array { namespace detail { template struct host_device_array { // Copied from GridToolsArrayView.cc ATLAS_HOST_DEVICE host_device_array(std::initializer_list list) { size_t i(0); for (const T v : list) { data_[i++] = v; } } ATLAS_HOST_DEVICE ~host_device_array() {} ATLAS_HOST_DEVICE const T* data() const { return data_; } T operator[](int i) const { return data_[i]; } T data_[Rank]; }; template struct StoragePropBuilder; // Copied from GridToolsArrayView.cc template struct StoragePropBuilder> { // Copied from GridToolsArrayView.cc static host_device_array buildStrides(const StorageInfo& storage_info) { return host_device_array{ (ArrayStrides::value_type)storage_info.template stride()...}; } static host_device_array buildShapes(const StorageInfo& storage_info) { return host_device_array{storage_info.template total_length()...}; } }; } template IndexView::IndexView(const Array& array, bool _device_view): gt_data_view_(_device_view ? gridtools::make_gt_device_view(array) : gridtools::make_gt_host_view(array)), data_store_orig_(&array.data_store()), array_(&array), is_device_view_(_device_view) { if (gt_data_view_.valid()) { constexpr static unsigned int ndims = data_view_t::data_store_t::storage_info_t::ndims; using storage_info_ty = gridtools::storage_traits::storage_info_t<0, ndims>; using data_store_t = gridtools::storage_traits::data_store_t; auto storage_info_ = *((reinterpret_cast(const_cast(array.storage())))->get_storage_info_ptr()); auto stridest = detail::StoragePropBuilder>::buildStrides( storage_info_); auto shapet = detail::StoragePropBuilder>::buildShapes( storage_info_); for (int i = 0; i < Rank; ++i) { strides_[i] = stridest[i]; shape_[i] = shapet[i]; } size_ = storage_info_.total_length(); } else { std::fill_n(shape_, Rank, 0); std::fill_n(strides_, Rank, 0); size_ = 0; } } // IndexView::IndexView(data_view_t data_view): gt_data_view_(data_view) { // if (gt_data_view_.valid()) { // size_ = gt_data_view_.storage_info().total_length(); // } // else { // size_ = 0; // } // if (gt_data_view_.valid()) { // constexpr static unsigned int ndims = data_view_t::data_store_t::storage_info_t::ndims; // using storage_info_ty = gridtools::storage_traits::storage_info_t<0, ndims>; // using data_store_t = gridtools::storage_traits::data_store_t; // auto storage_info_ = // *((reinterpret_cast(const_cast(array.storage())))->get_storage_info_ptr()); // auto stridest = // StoragePropBuilder>::buildStrides( // storage_info_); // auto shapet = // StoragePropBuilder>::buildShapes( // storage_info_); // for (int i = 0; i < Rank; ++i) { // strides_[i] = stridest[i]; // shape_[i] = shapet[i]; // } // size_ = storage_info_.total_length(); // } // else { // std::fill_n(shape_, Rank, 0); // std::fill_n(strides_, Rank, 0); // size_ = 0; // } // } //------------------------------------------------------------------------------------------------------ template void IndexView::dump(std::ostream& os) const { ATLAS_NOTIMPLEMENTED; } 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 } // namespace atlas //------------------------------------------------------------------------------------------------------ // Explicit instantiation namespace atlas { namespace array { #define EXPLICIT_TEMPLATE_INSTANTIATION_TYPE_RANK(TYPE, RANK) \ template class IndexView; \ template class IndexView; \ template class LocalIndexView; \ template class LocalIndexView; #define EXPLICIT_TEMPLATE_INSTANTIATION(RANK) \ EXPLICIT_TEMPLATE_INSTANTIATION_TYPE_RANK(int, RANK) \ EXPLICIT_TEMPLATE_INSTANTIATION_TYPE_RANK(long, RANK) EXPLICIT_TEMPLATE_INSTANTIATION(1) EXPLICIT_TEMPLATE_INSTANTIATION(2) #undef EXPLICIT_TEMPLATE_INSTANTIATION_TYPE_RANK #undef EXPLICIT_TEMPLATE_INSTANTIATION } // namespace array } // namespace atlas atlas-0.46.0/src/atlas/array/gridtools/GridToolsIndexView.h0000664000175000017500000001536315160212070024014 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include "atlas/array/Array.h" #include "atlas/array/gridtools/GridToolsTraits.h" #include "atlas/library/config.h" //------------------------------------------------------------------------------------------------------ namespace atlas { namespace array { //------------------------------------------------------------------------------------------------------ namespace detail { // FortranIndex: // Helper class that does +1 and -1 operations on stored values template class FortranIndex { public: enum { BASE = 1 }; public: ATLAS_HOST_DEVICE FortranIndex(Value* idx): idx_(idx) {} ATLAS_HOST_DEVICE void set(const Value& value) { *(idx_) = value + BASE; } ATLAS_HOST_DEVICE Value get() const { return *(idx_)-BASE; } ATLAS_HOST_DEVICE void operator=(const Value& value) { set(value); } ATLAS_HOST_DEVICE FortranIndex& operator=(const FortranIndex& other) { set(other.get()); return *this; } ATLAS_HOST_DEVICE FortranIndex& operator--() { --(*(idx_)); return *this; } ATLAS_HOST_DEVICE FortranIndex& operator++() { ++(*(idx_)); return *this; } ATLAS_HOST_DEVICE FortranIndex& operator+=(Value v) { *(idx_) += v; return *this; } ATLAS_HOST_DEVICE FortranIndex& operator-=(Value v) { *(idx_) -= v; return *this; } // implicit conversion ATLAS_HOST_DEVICE operator Value() const { return get(); } private: Value* idx_; }; } // namespace detail //------------------------------------------------------------------------------------------------------ template class IndexView { public: // -- Type definitions #if ATLAS_HAVE_FORTRAN typedef detail::FortranIndex Index; #define INDEX_REF Index #define FROM_FORTRAN -1 #define TO_FORTRAN +1 #else typedef Value& Index; #define INDEX_REF * #define FROM_FORTRAN #define TO_FORTRAN #endif using data_view_t = gridtools::data_view_tt::type, Rank, gridtools::get_access_mode()>; using value_type = Value; public: IndexView(data_view_t); IndexView(const Array&, bool device_view); template ::type> ATLAS_HOST_DEVICE Index operator()(Coords... c) { assert(sizeof...(Coords) == Rank); return INDEX_REF(>_data_view_(c...)); } template ::type> ATLAS_HOST_DEVICE Value const operator()(Coords... c) const { return gt_data_view_(c...) FROM_FORTRAN; } ATLAS_HOST_DEVICE idx_t size() const { return size_; } template ATLAS_HOST_DEVICE idx_t shape(Int idx) const { return shape_[idx]; } template ATLAS_HOST_DEVICE idx_t stride(Int idx) const { return strides_[idx]; } void dump(std::ostream& os) const; private: data_view_t gt_data_view_; idx_t size_; idx_t shape_[Rank]; idx_t strides_[Rank]; bool is_device_view_; ArrayDataStore const* data_store_orig_; Array const* array_; #undef INDEX_REF #undef TO_FORTRAN #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 ::type> Index operator()(Ints... idx) { check_bounds(idx...); return INDEX_REF(&data_[index(idx...)]); } template ::type> 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 } // namespace atlas atlas-0.46.0/src/atlas/array/gridtools/GridToolsArrayHelpers.h0000664000175000017500000002440715160212070024512 0ustar alastairalastair/* * (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 #include #include "atlas/array.h" #include "atlas/array/ArrayDataStore.h" #include "atlas/array/DataType.h" #include "atlas/array/gridtools/GridToolsTraits.h" #include "atlas/array_fwd.h" #include "atlas/library/config.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Log.h" //------------------------------------------------------------------------------ namespace atlas { namespace array { namespace gridtools { template std::array get_array_from_vector(std::vector const& values) { std::array array; std::copy(values.begin(), values.end(), array.begin()); return array; } template struct check_dimension_lengths_impl { template static void apply(ArrayShape const& shape, FirstDim first_dim, Dims... d) { if (first_dim < shape[Dim]) { std::stringstream err; err << "Attempt to resize array with original size for dimension " << Dim << " of " << shape[Dim] << " by " << first_dim << std::endl; throw_Exception(err.str(), Here()); } check_dimension_lengths_impl::apply(shape, d...); } }; template struct check_dimension_lengths_impl 1)>::type> { template static void apply(ArrayShape const& shape, FirstDim first_dim) { if (first_dim < shape[Dim]) { std::stringstream err; err << "Attempt to resize array with original size for dimension " << Dim - 1 << " of " << shape[Dim - 1] << " by " << first_dim << std::endl; throw_Exception(err.str(), Here()); } } }; template struct check_dimension_lengths_impl::type> { template static void apply(ArrayShape const& shape, FirstDim first_dim) { if (first_dim < shape[Dim]) { std::stringstream err; err << "Attempt to resize array with original size for dimension " << Dim << " of " << shape[Dim] << " by " << first_dim << std::endl; throw_Exception(err.str(), Here()); } } }; template void check_dimension_lengths(ArrayShape const& shape, Dims... d) { check_dimension_lengths_impl::apply(shape, d...); } template struct default_layout_t { template struct get_layout; template struct get_layout> { using type = ::gridtools::layout_map; }; using type = typename get_layout>::type; }; template struct get_layout_map_component { // TODO: static_assert( ::gridtools::is_layout_map(), "Error: not a // layout_map" ); template struct get_component { ATLAS_HOST_DEVICE constexpr get_component() {} ATLAS_HOST_DEVICE constexpr static Value apply() { return LayoutMap::template at(); } }; }; template struct get_stride_component { template struct get_component { ATLAS_HOST_DEVICE constexpr get_component() {} template ATLAS_HOST_DEVICE constexpr static Value apply(StorageInfoPtr a) { static_assert((::gridtools::is_storage_info::type>::value), "Error: not a storage_info"); return a->template stride(); } }; }; template struct get_shape_component { template struct get_component { ATLAS_HOST_DEVICE constexpr get_component() {} template ATLAS_HOST_DEVICE constexpr static Value apply(StorageInfoPtr a) { static_assert((::gridtools::is_storage_info::type>::value), "Error: not a storage_info"); return a->template total_length(); } }; }; // indirection around C++11 sizeof... since it is buggy for nvcc and cray template struct get_pack_size { using type = ::gridtools::static_uint; }; template struct gt_storage_t { using type = gridtools::storage_traits::data_store_t, Alignment>>; }; template typename gt_storage_t::type::value>::type* create_gt_storage( UInts... dims) { static_assert((sizeof...(dims) > 0), "Error: can not create storages without any dimension"); constexpr static unsigned int rank = get_pack_size::type::value; typedef gridtools::storage_traits::custom_layout_storage_info_align_t<0, LayoutMap, ::gridtools::zero_halo, Alignment> storage_info_ty; typedef gridtools::storage_traits::data_store_t data_store_t; data_store_t* ds; if (::gridtools::accumulate(::gridtools::multiplies(), dims...) == 0) { ds = new data_store_t(); } else { storage_info_ty si(dims...); ds = new data_store_t(si); } return ds; } template typename gt_storage_t, get_pack_size::type::value>::type* create_gt_storage(UInts... dims) { return create_gt_storage>(dims...); } template struct gt_wrap_storage_t { using type = gridtools::storage_traits::data_store_t>; }; template static typename gt_wrap_storage_t::type* wrap_gt_storage(Value* data, std::array&& shape, std::array&& strides) { static_assert((Rank > 0), "Error: can not create storages without any dimension"); typedef gridtools::storage_traits::storage_info_t<0, Rank, ::gridtools::zero_halo> storage_info_ty; typedef gridtools::storage_traits::data_store_t data_store_t; ::gridtools::array<::gridtools::uint_t, Rank> _shape; ::gridtools::array<::gridtools::uint_t, Rank> _strides; for (unsigned int i = 0; i < Rank; ++i) { _shape[i] = shape[i]; _strides[i] = strides[i]; } storage_info_ty si(_shape, _strides); data_store_t* ds = new data_store_t(si, data); return ds; } constexpr idx_t zero(idx_t) { return 0; } template ArrayStrides make_null_strides(std::integer_sequence) { return make_strides(zero(Is)...); } template struct my_apply_gt_integer_sequence { template class Lambda, typename... ExtraTypes> ATLAS_HOST_DEVICE static constexpr Container apply(ExtraTypes const&... args_) { static_assert((std::is_same::value), "ERROR: my_apply_gt_integer_sequence only accepts a " "std::integer_sequence type. Check the call"); return Container(args_...); } }; template struct my_apply_gt_integer_sequence> { /** @brief duplicated interface for the case in which the container is an aggregator */ template class Lambda, typename... ExtraTypes> ATLAS_HOST_DEVICE static constexpr Container apply(ExtraTypes const&... args_) { return Container{Lambda::apply(args_...)...}; } }; #ifndef __CUDACC__ template ArraySpec ATLAS_HOST make_spec(DataStore* gt_data_store_ptr, Dims... dims) { static_assert((::gridtools::is_data_store::value), "Internal Error: passing a non GT data store"); if (gt_data_store_ptr->valid()) { auto storage_info_ptr = gt_data_store_ptr->get_storage_info_ptr().get(); using Layout = typename DataStore::storage_info_t::layout_t; using Alignment = typename DataStore::storage_info_t::alignment_t; using seq = my_apply_gt_integer_sequence>; ArraySpec spec( ArrayShape{(idx_t)dims...}, seq::template apply::template get_component>(storage_info_ptr), seq::template apply::template get_component>(), ArrayAlignment(Alignment::value)); ATLAS_ASSERT(spec.allocatedSize() == storage_info_ptr->padded_total_length()); return spec; } else { return ArraySpec(make_shape({dims...}), make_null_strides(std::make_integer_sequence())); } } #endif //------------------------------------------------------------------------------ } // namespace gridtools } // namespace array } // namespace atlas atlas-0.46.0/src/atlas/array/gridtools/GridToolsMakeView.h0000664000175000017500000000224215160212070023612 0ustar alastairalastair/* * (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/array/gridtools/GridToolsTraits.h" #include "atlas/array_fwd.h" namespace atlas { namespace array { namespace gridtools { template struct gt_view { using value_t = typename std::remove_const::type; using type = ::gridtools::data_view< gridtools::storage_traits::data_store_t>, gridtools::get_access_mode()>; }; template typename gt_view::type make_gt_host_view(const Array& array); template typename gt_view::type make_gt_device_view(const Array& array); } // namespace gridtools } // namespace array } // namespace atlas atlas-0.46.0/src/atlas/array/gridtools/GridToolsArrayView.h0000664000175000017500000001576715160212070024033 0ustar alastairalastair/* * (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 #include #include "atlas/array/Array.h" #include "atlas/array/ArrayDataStore.h" #include "atlas/array/ArrayViewDefs.h" #include "atlas/array/LocalView.h" #include "atlas/array/gridtools/GridToolsMakeView.h" #include "atlas/array/gridtools/GridToolsTraits.h" #include "atlas/library/config.h" //------------------------------------------------------------------------------------------------------ namespace atlas { namespace array { template class ArrayView { template using is_non_const_value_type = typename std::is_same::type>; #define ENABLE_IF_NON_CONST \ template ::value && EnableBool), int>::type* = nullptr> #define ENABLE_IF_CONST_WITH_NON_CONST(T) \ template ::value && is_non_const_value_type::value), \ int>::type* = nullptr> public: // -- Type definitions using value_type = Value; using non_const_value_type = typename std::remove_const::type; static constexpr bool is_const = std::is_const::value; static constexpr bool is_non_const = !std::is_const::value; // static constexpr Intent ACCESS{AccessMode}; static constexpr int RANK{Rank}; using data_view_t = gridtools::data_view_tt()>; private: using slicer_t = typename helpers::ArraySlicer>; using const_slicer_t = typename helpers::ArraySlicer>; template struct slice_t { using type = typename slicer_t::template Slice::type; }; template struct const_slice_t { using type = typename const_slicer_t::template Slice::type; }; public: ATLAS_HOST_DEVICE ArrayView(const ArrayView& other); ArrayView(const Array& array, bool device_view); ENABLE_IF_CONST_WITH_NON_CONST(value_type) ArrayView(const ArrayView& other): gt_data_view_(other.is_device_view_ ? gridtools::make_gt_device_view(*other.array_) : gridtools::make_gt_host_view(*other.array_)), data_store_orig_(other.data_store_orig_), array_(other.array_), is_device_view_(other.is_device_view_) { std::memcpy(shape_, other.shape_, sizeof(ArrayShape::value_type) * Rank); std::memcpy(strides_, other.strides_, sizeof(ArrayStrides::value_type) * Rank); size_ = other.size_; // TODO: check compatibility } ENABLE_IF_CONST_WITH_NON_CONST(value_type) operator const ArrayView&() const { return *(const ArrayView*)(this); } value_type* data() { return gt_data_view_.data(); } value_type const* data() const { return gt_data_view_.data(); } template ::type> ATLAS_HOST_DEVICE value_type& operator()(Ints... c) { assert(sizeof...(Ints) == Rank); using common_type = typename std::common_type::type; return gt_data_view_(static_cast(c)...); } template ::type> ATLAS_HOST_DEVICE value_type const& operator()(Ints... c) const { assert(sizeof...(Ints) == Rank); using common_type = typename std::common_type::type; return gt_data_view_(static_cast(c)...); } template ATLAS_HOST_DEVICE typename std::enable_if<(Rank == 1 && EnableBool), const value_type&>::type operator[]( Int idx) const { return gt_data_view_(idx); } template ATLAS_HOST_DEVICE typename std::enable_if<(Rank == 1 && EnableBool), value_type&>::type operator[](Int idx) { return gt_data_view_(idx); } template ATLAS_HOST_DEVICE idx_t shape() const { return gt_data_view_.template length(); } ATLAS_HOST_DEVICE data_view_t& data_view() { return gt_data_view_; } ATLAS_HOST_DEVICE data_view_t const& data_view() const { return gt_data_view_; } template ATLAS_HOST_DEVICE idx_t stride() const { return gt_data_view_.storage_info().template stride(); } static constexpr idx_t rank() { return Rank; } ATLAS_HOST_DEVICE size_t size() const { return size_; } bool valid() const; bool contiguous() const { return (size_ == size_t(shape_[0]) * size_t(strides_[0]) ? true : false); } void dump(std::ostream& os) const; ENABLE_IF_NON_CONST void assign(const value_type& value); ENABLE_IF_NON_CONST void assign(const std::initializer_list& list); ENABLE_IF_NON_CONST void assign(const ArrayView& other); const idx_t* strides() const { return strides_; } const idx_t* shape() const { return shape_; } template ATLAS_HOST_DEVICE idx_t shape(Int idx) const { return shape_[idx]; } template ATLAS_HOST_DEVICE idx_t stride(Int idx) const { return strides_[idx]; } template typename slice_t::type slice(Args... args) { return slicer_t(*this).apply(args...); } template typename const_slice_t::type slice(Args... args) const { return const_slicer_t(*this).apply(args...); } bool isDeviceView() const { return is_device_view_; } // Befriend all template variations template friend class ArrayView; private: data_view_t gt_data_view_; idx_t shape_[Rank]; idx_t strides_[Rank]; size_t size_; ArrayDataStore const* data_store_orig_; Array const* array_; bool is_device_view_{false}; #undef ENABLE_IF_NON_CONST #undef ENABLE_IF_CONST_WITH_NON_CONST }; //------------------------------------------------------------------------------------------------------ } // namespace array } // namespace atlas atlas-0.46.0/src/atlas/array/gridtools/GridToolsMakeView.cc0000664000175000017500000003740115160212070023755 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "atlas/array/gridtools/GridToolsMakeView.h" #include #include #include "atlas/array/Array.h" #include "atlas/array/ArrayView.h" #include "atlas/array/ArrayViewDefs.h" #include "atlas/array/IndexView.h" #include "atlas/array/gridtools/GridToolsTraits.h" #include "atlas/library/config.h" #include "atlas/runtime/Exception.h" //------------------------------------------------------------------------------ namespace atlas { namespace array { namespace { template static void check_metadata(const Array& array) { if (array.rank() != Rank) { std::stringstream err; err << "Number of dimensions do not match: template argument " << Rank << " expected to be " << array.rank(); throw_Exception(err.str(), Here()); } if (array.datatype() != array::DataType::create()) { std::stringstream err; err << "Data Type does not match: template argument expected to be " << array.datatype().str(); throw_Exception(err.str(), Here()); } } } // namespace namespace gridtools { template typename gt_view::type make_gt_host_view(const Array& array) { using value_t = typename std::remove_const::type; using storage_info_ty = storage_traits::storage_info_t<0, Rank>; using data_store_t = storage_traits::data_store_t; data_store_t* ds = reinterpret_cast(const_cast(array.storage())); return ::gridtools::make_host_view()>(*ds); } template typename gt_view::type make_gt_device_view(const Array& array) { using value_t = typename std::remove_const::type; using storage_info_ty = storage_traits::storage_info_t<0, Rank>; using data_store_t = storage_traits::data_store_t; data_store_t* ds = reinterpret_cast(const_cast(array.storage())); #if ATLAS_GRIDTOOLS_STORAGE_BACKEND_CUDA return ::gridtools::make_device_view()>(*ds); #else return ::gridtools::make_host_view()>(*ds); #endif } } // namespace gridtools constexpr bool host_view = false; constexpr bool device_view = true; template ArrayView make_host_view(Array& array) { check_metadata(array); return ArrayView(array, host_view); } template ArrayView make_host_view(const Array& array) { check_metadata(array); return ArrayView(array, host_view); } template ArrayView make_device_view(Array& array) { check_metadata(array); return ArrayView(array, device_view); } template ArrayView make_device_view(const Array& array) { check_metadata(array); return ArrayView(array, device_view); } template ArrayView make_view(Array& array) { return make_host_view(array); } template ArrayView make_view(const Array& array) { return make_host_view(array); } //template //IndexView make_host_indexview( Array& array ) { // return IndexView( (Value*)( array.storage() ), array.shape().data() ); //} //template //IndexView make_host_indexview( const Array& array ) { // return IndexView( (const Value*)( array.storage() ), array.shape().data() ); //} //template //IndexView make_indexview( Array& array ) { // check_metadata( array ); // return make_host_indexview( array ); //} //template //IndexView make_indexview( const Array& array ) { // check_metadata( array ); // return make_host_indexview( array ); //} template IndexView make_host_indexview(Array& array) { // using value_t = typename std::remove_const::type; // using storage_info_ty = gridtools::storage_traits::storage_info_t<0, Rank>; // using data_store_t = gridtools::storage_traits::data_store_t; // data_store_t* ds = reinterpret_cast(const_cast(array.storage())); // return IndexView(::gridtools::make_host_view()>(*ds)); check_metadata(array); constexpr bool device_view = false; return IndexView(array, device_view); } template IndexView make_host_indexview(const Array& array) { // using value_t = typename std::remove_const::type; // using storage_info_ty = gridtools::storage_traits::storage_info_t<0, Rank>; // using data_store_t = gridtools::storage_traits::data_store_t; // data_store_t* ds = reinterpret_cast(const_cast(array.storage())); // return IndexView(::gridtools::make_host_view()>(*ds)); check_metadata(array); constexpr bool device_view = false; return IndexView(array, device_view); } // -------------------------------------------------------------------------------------------- template IndexView make_indexview(Array& array) { // check_metadata(array); // constexpr bool device_view = false; // return IndexView(array, device_view); return make_host_indexview(array); } template IndexView make_indexview(const Array& array) { return make_host_indexview(array); } } // namespace array } // namespace atlas //----------------------------------------------------------------------- // Explicit instantiation namespace atlas { namespace array { //#define EXPLICIT_TEMPLATE_INSTANTIATION( RANK ) \ // namespace gridtools { \ // template typename gt_view::type \ // make_gt_host_view( const Array& array ); \ // template typename gt_view::type \ // make_gt_host_view( const Array& array ); \ // template typename gt_view::type make_gt_host_view( \ // const Array& array ); \ // template typename gt_view::type make_gt_host_view( \ // const Array& array ); \ // template typename gt_view::type make_gt_host_view( \ // const Array& array ); \ // template typename gt_view::type \ // make_gt_host_view( const Array& array ); \ // \ // template typename gt_view::type make_gt_device_view( \ // const Array& array ); \ // template typename gt_view::type make_gt_device_view( \ // const Array& array ); \ // template typename gt_view::type make_gt_device_view( \ // const Array& array ); \ // template typename gt_view::type make_gt_device_view( \ // const Array& array ); \ // template typename gt_view::type \ // make_gt_device_view( const Array& array ); \ // template typename gt_view::type \ // make_gt_device_view( const Array& array ); \ // template typename gt_view::type make_gt_device_view( \ // const Array& array ); \ // template typename gt_view::type \ // make_gt_device_view( const Array& array ); \ // template typename gt_view::type \ // make_gt_device_view( const Array& array ); \ // template typename gt_view::type \ // make_gt_device_view( const Array& array ); \ // } //// For each Rank in [1..9] //EXPLICIT_TEMPLATE_INSTANTIATION( 1 ) //EXPLICIT_TEMPLATE_INSTANTIATION( 2 ) //EXPLICIT_TEMPLATE_INSTANTIATION( 3 ) //EXPLICIT_TEMPLATE_INSTANTIATION( 4 ) //EXPLICIT_TEMPLATE_INSTANTIATION( 5 ) //EXPLICIT_TEMPLATE_INSTANTIATION( 6 ) //EXPLICIT_TEMPLATE_INSTANTIATION( 7 ) //EXPLICIT_TEMPLATE_INSTANTIATION( 8 ) //EXPLICIT_TEMPLATE_INSTANTIATION( 9 ) //#undef EXPLICIT_TEMPLATE_INSTANTIATION #define EXPLICIT_TEMPLATE_INSTANTIATION_TYPE_RANK(TYPE, RANK) \ template ArrayView make_view(Array&); \ template ArrayView make_view(Array&); \ template ArrayView make_view(const Array&); \ template ArrayView make_view(const Array&); \ \ template ArrayView make_host_view(Array&); \ template ArrayView make_host_view(Array&); \ template ArrayView make_host_view(const Array&); \ template ArrayView make_host_view(const Array&); \ \ template ArrayView make_device_view(Array&); \ template ArrayView make_device_view(Array&); \ template ArrayView make_device_view(const Array&); \ template ArrayView make_device_view(const Array&); \ \ namespace gridtools { \ template typename gt_view::type make_gt_host_view(const Array& array); \ template typename gt_view::type make_gt_host_view(const Array& array); \ template typename gt_view::type make_gt_device_view(const Array& array); \ template typename gt_view::type make_gt_device_view(const Array& array); \ } #define EXPLICIT_TEMPLATE_INSTATIATION(RANK) \ EXPLICIT_TEMPLATE_INSTANTIATION_TYPE_RANK(int, RANK) \ EXPLICIT_TEMPLATE_INSTANTIATION_TYPE_RANK(long, RANK) \ EXPLICIT_TEMPLATE_INSTANTIATION_TYPE_RANK(float, RANK) \ EXPLICIT_TEMPLATE_INSTANTIATION_TYPE_RANK(double, RANK) \ EXPLICIT_TEMPLATE_INSTANTIATION_TYPE_RANK(unsigned long, RANK) EXPLICIT_TEMPLATE_INSTATIATION(1) EXPLICIT_TEMPLATE_INSTATIATION(2) EXPLICIT_TEMPLATE_INSTATIATION(3) EXPLICIT_TEMPLATE_INSTATIATION(4) EXPLICIT_TEMPLATE_INSTATIATION(5) EXPLICIT_TEMPLATE_INSTATIATION(6) EXPLICIT_TEMPLATE_INSTATIATION(7) EXPLICIT_TEMPLATE_INSTATIATION(8) EXPLICIT_TEMPLATE_INSTATIATION(9) #undef EXPLICIT_TEMPLATE_INSTATIATION_TYPE_RANK #undef EXPLICIT_TEMPLATE_INSTATIATION #define EXPLICIT_TEMPLATE_INSTANTIATION_INDEXVIEW_TYPE_RANK(TYPE, RANK) \ template IndexView make_host_indexview(Array&); \ template IndexView make_host_indexview(Array&); \ template IndexView make_host_indexview(const Array&); \ template IndexView make_host_indexview(const Array&); \ \ template IndexView make_indexview(Array&); \ template IndexView make_indexview(Array&); \ template IndexView make_indexview(const Array&); \ template IndexView make_indexview(const Array&); #define EXPLICIT_TEMPLATE_INSTANTIATION_INDEXVIEW(RANK) \ EXPLICIT_TEMPLATE_INSTANTIATION_INDEXVIEW_TYPE_RANK(int, RANK) \ EXPLICIT_TEMPLATE_INSTANTIATION_INDEXVIEW_TYPE_RANK(long, RANK) EXPLICIT_TEMPLATE_INSTANTIATION_INDEXVIEW(1) EXPLICIT_TEMPLATE_INSTANTIATION_INDEXVIEW(2) #undef EXPLICIT_TEMPLATE_INSTANTIATION_INDEXVIEW #undef EXPLICIT_TEMPLATE_INSTANTIATION_INDEXVIEW_TYPE_RANK } // namespace array } // namespace atlas atlas-0.46.0/src/atlas/array/gridtools/GridToolsTraits.h0000664000175000017500000000442615160212070023356 0ustar alastairalastair/* * (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 "gridtools/common/generic_metafunctions/accumulate.hpp" #include "gridtools/storage/storage_facility.hpp" #include "atlas/array/ArrayViewDefs.h" #include "atlas/library/config.h" //------------------------------------------------------------------------------ namespace atlas { namespace array { namespace gridtools { //------------------------------------------------------------------------------ #if ATLAS_GRIDTOOLS_STORAGE_BACKEND_CUDA using backend_t = ::gridtools::backend::cuda; using storage_traits = ::gridtools::storage_traits; #elif ATLAS_GRIDTOOLS_STORAGE_BACKEND_HOST using backend_t = ::gridtools::backend::x86; using storage_traits = ::gridtools::storage_traits; #else #error ATLAS_GRIDTOOLS_STORAGE_BACKEND_ not set #endif //------------------------------------------------------------------------------ template using data_view_tt = ::gridtools::data_view::type, gridtools::storage_traits::storage_info_t<0, Rank>>, AccessMode>; template ::value, Value>::type* = nullptr> inline constexpr ::gridtools::access_mode get_access_mode() { return ::gridtools::access_mode::read_only; } template ::value, Value>::type* = nullptr> inline constexpr ::gridtools::access_mode get_access_mode() { return ::gridtools::access_mode::read_write; } //------------------------------------------------------------------------------ } // namespace gridtools } // namespace array } // namespace atlas atlas-0.46.0/src/atlas/array/gridtools/GridToolsArrayView.cc0000664000175000017500000002204015160212070024147 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "atlas/array/gridtools/GridToolsArrayView.h" #include #include "atlas/array/gridtools/GridToolsArrayHelpers.h" #include "atlas/array/helpers/ArrayAssigner.h" #include "atlas/array/helpers/ArrayCopier.h" #include "atlas/array/helpers/ArrayInitializer.h" #include "atlas/array/helpers/ArrayWriter.h" #include "atlas/runtime/Exception.h" #define ENABLE_IF_NON_CONST \ template ::value && EnableBool), int>::type*> #define ENABLE_IF_CONST \ template ::value && EnableBool), int>::type*> namespace atlas { namespace array { template struct host_device_array { ATLAS_HOST_DEVICE host_device_array(std::initializer_list list) { size_t i(0); for (const T v : list) { data_[i++] = v; } } ATLAS_HOST_DEVICE ~host_device_array() {} ATLAS_HOST_DEVICE const T* data() const { return data_; } T operator[](int i) const { return data_[i]; } T data_[Rank]; }; template struct StoragePropBuilder; template struct StoragePropBuilder> { static host_device_array buildStrides(const StorageInfo& storage_info) { return host_device_array{ (ArrayStrides::value_type)storage_info.template stride()...}; } static host_device_array buildShapes(const StorageInfo& storage_info) { return host_device_array{storage_info.template total_length()...}; } }; template ArrayView::ArrayView(const ArrayView& other): gt_data_view_(other.gt_data_view_), data_store_orig_(other.data_store_orig_), array_(other.array_), is_device_view_(other.is_device_view_) { std::memcpy(shape_, other.shape_, sizeof(ArrayShape::value_type) * Rank); std::memcpy(strides_, other.strides_, sizeof(ArrayStrides::value_type) * Rank); size_ = other.size_; // TODO: check compatibility } template ArrayView::ArrayView(const Array& array, bool _device_view): gt_data_view_(_device_view ? gridtools::make_gt_device_view(array) : gridtools::make_gt_host_view(array)), data_store_orig_(&array.data_store()), array_(&array), is_device_view_(_device_view) { if (gt_data_view_.valid()) { constexpr static unsigned int ndims = data_view_t::data_store_t::storage_info_t::ndims; using storage_info_ty = gridtools::storage_traits::storage_info_t<0, ndims>; using data_store_t = gridtools::storage_traits::data_store_t; auto storage_info_ = *((reinterpret_cast(const_cast(array.storage())))->get_storage_info_ptr()); auto stridest = StoragePropBuilder>::buildStrides( storage_info_); auto shapet = StoragePropBuilder>::buildShapes( storage_info_); for (int i = 0; i < Rank; ++i) { strides_[i] = stridest[i]; shape_[i] = shapet[i]; } size_ = storage_info_.total_length(); } else { std::fill_n(shape_, Rank, 0); std::fill_n(strides_, Rank, 0); size_ = 0; } } template bool ArrayView::valid() const { return gt_data_view_.valid() && (&array_->data_store() == data_store_orig_); } template ENABLE_IF_NON_CONST void ArrayView::assign(const value_type& value) { helpers::array_assigner::apply(*this, value); } //------------------------------------------------------------------------------------------------------ template ENABLE_IF_NON_CONST void ArrayView::assign(const std::initializer_list& list) { helpers::array_assigner::apply(*this, list); } //------------------------------------------------------------------------------------------------------ template ENABLE_IF_NON_CONST void ArrayView::assign(const ArrayView& other) { helpers::array_copier::apply(other, *this); } //------------------------------------------------------------------------------------------------------ template void ArrayView::dump(std::ostream& os) const { os << "size: " << size() << " , values: "; os << "[ "; helpers::array_writer::apply(*this, os); os << " ]"; } //------------------------------------------------------------------------------------------------------ } // namespace array } // namespace atlas //----------------------------------------------------------------------- // Explicit instantiation namespace atlas { namespace array { #define EXPLICIT_TEMPLATE_INSTANTIATION(Rank) \ template class ArrayView; \ template class ArrayView; \ template class ArrayView; \ template class ArrayView; \ template class ArrayView; \ template class ArrayView; \ template class ArrayView; \ template class ArrayView; \ template class ArrayView; \ template class ArrayView; \ \ template void ArrayView::assign(int const&); \ template void ArrayView::assign(long const&); \ template void ArrayView::assign(float const&); \ template void ArrayView::assign(double const&); \ template void ArrayView::assign(long unsigned const&); \ template void ArrayView::assign(std::initializer_list const&); \ template void ArrayView::assign(std::initializer_list const&); \ template void ArrayView::assign(std::initializer_list const&); \ template void ArrayView::assign(std::initializer_list const&); \ template void ArrayView::assign(std::initializer_list const&); \ template void ArrayView::assign(ArrayView const&); \ template void ArrayView::assign(ArrayView const&); \ template void ArrayView::assign(ArrayView const&); \ template void ArrayView::assign(ArrayView const&); // For each Rank in [1..9] EXPLICIT_TEMPLATE_INSTANTIATION(1) EXPLICIT_TEMPLATE_INSTANTIATION(2) EXPLICIT_TEMPLATE_INSTANTIATION(3) EXPLICIT_TEMPLATE_INSTANTIATION(4) EXPLICIT_TEMPLATE_INSTANTIATION(5) EXPLICIT_TEMPLATE_INSTANTIATION(6) EXPLICIT_TEMPLATE_INSTANTIATION(7) EXPLICIT_TEMPLATE_INSTANTIATION(8) EXPLICIT_TEMPLATE_INSTANTIATION(9) #undef EXPLICIT_TEMPLATE_INSTANTIATION } // namespace array } // namespace atlas atlas-0.46.0/src/atlas/array/gridtools/GridToolsDataStore.h0000664000175000017500000000763715160212070024005 0ustar alastairalastair/* * (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/library/config.h" #include "atlas/array/ArrayDataStore.h" #include "atlas/array/gridtools/GridToolsTraits.h" #if ATLAS_HAVE_ACC #include "atlas_acc_support/atlas_acc.h" #endif //------------------------------------------------------------------------------ namespace atlas { namespace array { namespace gridtools { template struct GridToolsDataStore : ArrayDataStore { using Value = typename gt_DataStore::data_t; explicit GridToolsDataStore(gt_DataStore const* ds): data_store_(ds) {} ~GridToolsDataStore() { assert(data_store_); delete data_store_; } void updateDevice() const override { assert(data_store_); allocateDevice(); data_store_->clone_to_device(); } void updateHost() const override { data_store_->clone_from_device(); } bool valid() const override { return data_store_->valid(); } void syncHostDevice() const override { data_store_->sync(); } void allocateDevice() const override { accMap(); } void deallocateDevice() const override { accUnmap(); } bool deviceAllocated() const override { return ATLAS_HAVE_GPU; } bool hostNeedsUpdate() const override { return data_store_->host_needs_update(); } bool deviceNeedsUpdate() const override { return data_store_->device_needs_update(); } void setHostNeedsUpdate(bool v) const override { auto state_machine = data_store_->get_storage_ptr()->get_state_machine_ptr_impl(); if (state_machine) { state_machine->m_hnu = v; } } void setDeviceNeedsUpdate(bool v) const override { auto state_machine = data_store_->get_storage_ptr()->get_state_machine_ptr_impl(); if (state_machine) { state_machine->m_dnu = v; } } void reactivateDeviceWriteViews() const override { data_store_->reactivate_target_write_views(); } void reactivateHostWriteViews() const override { data_store_->reactivate_host_write_views(); } void* voidDataStore() override { return static_cast(const_cast(data_store_)); } void* voidHostData() override { return host_data(); } void* voidDeviceData() override { return device_data(); } void accMap() const override { #if ATLAS_HAVE_ACC if (not acc_mapped_) { ATLAS_ASSERT(deviceAllocated(),"Could not accMap as device data is not allocated"); atlas_acc_map_data(host_data(), device_data(), bytes()); acc_mapped_ = true; } #endif } bool accMapped() const override { return acc_mapped_; } void accUnmap() const override { #if ATLAS_HAVE_ACC if (acc_mapped_) { atlas_acc_unmap_data(host_data()); acc_mapped_ = false; } #endif } private: void* host_data() const { return ::gridtools::make_host_view<::gridtools::access_mode::read_only>(*data_store_).data(); } void* device_data() const { #if ATLAS_HAVE_GPU return ::gridtools::make_device_view<::gridtools::access_mode::read_only>(*data_store_).data(); #else return ::gridtools::make_host_view<::gridtools::access_mode::read_only>(*data_store_).data(); #endif } size_t bytes() const { auto storage_info_ptr = data_store_->get_storage_info_ptr().get(); return storage_info_ptr->padded_total_length() * sizeof(Value); } private: gt_DataStore const* data_store_; mutable bool acc_mapped_{false}; }; } // namespace gridtools } // namespace array } // namespace atlas atlas-0.46.0/src/atlas/array/ArrayViewVariant.cc0000664000175000017500000000637515160212070021654 0ustar alastairalastair/* * (C) Crown Copyright 2024 Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ #include "atlas/array/ArrayViewVariant.h" #include #include #include "atlas/array/Array.h" #include "atlas/array/MakeView.h" #include "atlas/runtime/Exception.h" namespace atlas { namespace array { using namespace detail; namespace { template struct VariantTypeHelper { using type = ArrayViewVariant; }; template <> struct VariantTypeHelper { using type = ConstArrayViewVariant; }; template using VariantType = typename VariantTypeHelper>::type; // Match array.rank() and array.datatype() to variant types. Return result of // makeView on a successful pattern match. template VariantType executeMakeView(ArrayType& array, const MakeView& makeView) { using View = std::variant_alternative_t>; using Value = typename View::non_const_value_type; constexpr auto Rank = View::rank(); if (array.datatype() == DataType::kind() && array.rank() == Rank) { return makeView(array, Value{}, std::integral_constant{}); } if constexpr (TypeIndex < std::variant_size_v> - 1) { return executeMakeView(array, makeView); } else { ATLAS_THROW_EXCEPTION("Array with rank = " + std::to_string(array.rank()) + " and datatype = " + array.datatype().str() + " is not supported."); } ATLAS_UNREACHABLE(); } template VariantType makeViewVariantImpl(ArrayType& array) { const auto makeView = [](auto& array, auto value, auto rank) { return make_view(array); }; return executeMakeView<>(array, makeView); } template VariantType makeHostViewVariantImpl(ArrayType& array) { const auto makeView = [](auto& array, auto value, auto rank) { return make_host_view(array); }; return executeMakeView<>(array, makeView); } template VariantType makeDeviceViewVariantImpl(ArrayType& array) { const auto makeView = [](auto& array, auto value, auto rank) { return make_device_view(array); }; return executeMakeView<>(array, makeView); } } // namespace ArrayViewVariant make_view_variant(Array& array) { return makeViewVariantImpl(array); } ConstArrayViewVariant make_view_variant(const Array& array) { return makeViewVariantImpl(array); } ArrayViewVariant make_host_view_variant(Array& array) { return makeHostViewVariantImpl(array); } ConstArrayViewVariant make_host_view_variant(const Array& array) { return makeHostViewVariantImpl(array); } ArrayViewVariant make_device_view_variant(Array& array) { return makeDeviceViewVariantImpl(array); } ConstArrayViewVariant make_device_view_variant(const Array& array) { return makeDeviceViewVariantImpl(array); } } // namespace array } // namespace atlas atlas-0.46.0/src/atlas/array/ArrayDataStore.h0000664000175000017500000000575315160212070021144 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include "atlas/array/ArrayIdx.h" #include "atlas/array/ArrayLayout.h" #include "atlas/array/ArrayShape.h" #include "atlas/array/ArraySpec.h" #include "atlas/array/ArrayStrides.h" //------------------------------------------------------------------------------------------------------ namespace atlas { namespace array { #ifndef DOXYGEN_SHOULD_SKIP_THIS template struct remove_const { typedef T type; }; template struct remove_const { typedef T type; }; template struct add_const { typedef const typename remove_const::type type; }; template struct add_const { typedef const T type; }; #endif class ArrayDataStore { public: virtual ~ArrayDataStore() {} virtual void updateDevice() const = 0; virtual void updateHost() const = 0; virtual bool valid() const = 0; virtual void syncHostDevice() const = 0; virtual void syncHost() const = 0; virtual void syncDevice() const = 0; virtual void allocateDevice() const = 0; virtual void deallocateDevice() const = 0; virtual bool deviceAllocated() const = 0; virtual bool hostNeedsUpdate() const = 0; virtual bool deviceNeedsUpdate() const = 0; virtual void setHostNeedsUpdate(bool) const = 0; virtual void setDeviceNeedsUpdate(bool) const = 0; virtual void reactivateDeviceWriteViews() const = 0; virtual void reactivateHostWriteViews() const = 0; virtual void* voidDataStore() = 0; virtual void* voidHostData() = 0; virtual void* voidDeviceData() = 0; virtual void accMap() const = 0; virtual void accUnmap() const = 0; virtual bool accMapped() const = 0; template Value* hostData() { return static_cast(voidHostData()); } template Value* deviceData() { return static_cast(voidDeviceData()); } }; #ifndef DOXYGEN_SHOULD_SKIP_THIS template static constexpr char array_dim() { return Dim == 0 ? 'i' : (Dim == 1 ? 'j' : (Dim == 2 ? 'k' : (Dim == 3 ? 'l' : (Dim == 4 ? 'm' : ('*'))))); } void throw_OutOfRange(const std::string& class_name, char idx_str, int idx, int max); #endif //------------------------------------------------------------------------------------------------------ } // namespace array } // namespace atlas atlas-0.46.0/src/atlas/field/0000775000175000017500000000000015160212070016041 5ustar alastairalastairatlas-0.46.0/src/atlas/field/Field.h0000664000175000017500000001576715160212070017255 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ /// @author Willem Deconinck /// @date Sep 2014 #pragma once #include #include #include "eckit/config/Parametrisation.h" #include "atlas/array/ArrayShape.h" #include "atlas/array/DataType.h" #include "atlas/array_fwd.h" #include "atlas/library/config.h" #include "atlas/util/Config.h" #include "atlas/util/ObjectHandle.h" #include "atlas/field/Halo.h" namespace eckit { class Parametrisation; } namespace atlas { namespace field { class FieldImpl; } } // namespace atlas namespace atlas { namespace util { class Config; class Metadata; } } // namespace atlas namespace atlas { class FunctionSpace; } namespace atlas { /// @brief A Field contains an Array, Metadata, and a reference to a FunctionSpace /// /// The Field is a key component of Atlas. It contains an \ref array::Array object and a \ref util::Metadata object. /// Furthermore it contains a link to a FunctionSpace object. /// /// A Field should typically be created via the FunctionSpace::createField() method, which ensures /// that the Field is completely setup. /// /// A field can be implicitly converted to an Array reference, so that it can be passed to routines that /// expect an Array. This is especially useful to create an ArrayView: /// /// @code{.cpp} /// Field field = functionspace.createField( ... ); /// auto view = array::make_view( field ); /// @endcode class Field : DOXYGEN_HIDE(public util::ObjectHandle) { public: using Handle::Handle; Field() = default; /// @brief Create field from parametrisation Field(const eckit::Parametrisation&); /// @brief Create field with given name, Datatype and ArrayShape Field(const std::string& name, array::DataType, const array::ArrayShape& = array::ArrayShape()); /// @brief Create field with given name, Datatype and ArraySpec Field(const std::string& name, array::DataType, array::ArraySpec&&); /// @brief Create field with given name, and take ownership of given Array Field(const std::string& name, array::Array*); /// @brief Create field by wrapping existing data, Datatype of template and /// ArraySpec template Field(const std::string& name, DATATYPE* data, const array::ArraySpec&); /// @brief Create field by wrapping existing data, Datatype of template and /// ArrayShape template Field(const std::string& name, DATATYPE* data, const array::ArrayShape&); /// @brief Deep copy Field clone(const eckit::Parametrisation& = util::Config()) const; // -- Conversion /// @brief Implicit conversion to Array operator const array::Array&() const; operator array::Array&(); /// @brief Access contained Array const array::Array& array() const; array::Array& array(); bool valid() const { return get() != nullptr; } // -- Accessors /// @brief Access to raw data void* storage(); /// @brief Internal data type of field array::DataType datatype() const; /// @brief Name associated to this field const std::string& name() const; /// @brief Rename this field void rename(const std::string& name); /// @brief Access to metadata associated to this field const util::Metadata& metadata() const; util::Metadata& metadata(); /// @brief Resize field to given shape void resize(const array::ArrayShape& shape); void insert(idx_t idx1, idx_t size1); /// @brief Shape of this field in Fortran style (reverse order of C style) const std::vector& shapef() const; /// @brief Strides of this field in Fortran style (reverse order of C style) const std::vector& stridesf() const; /// @brief Shape of this field (reverse order of Fortran style) const array::ArrayShape& shape() const; /// @brief Strides of this field const array::ArrayStrides& strides() const; /// @brief Shape of this field associated to index 'i' idx_t shape(idx_t i) const; /// @brief Stride of this field associated to index 'i' idx_t stride(idx_t i) const; /// @brief Number of values stored in this field size_t size() const; /// @brief Rank of field idx_t rank() const; /// @brief Number of bytes occupied by the values of this field size_t bytes() const; bool contiguous() const; /// @brief Output information of field friend std::ostream& operator<<(std::ostream& os, const Field& v); /// @brief Output information of field plus raw data void dump(std::ostream& os) const; /// Metadata that is more intrinsic to the Field, and queried often void set_levels(idx_t n); idx_t levels() const; /// Metadata that is more intrinsic to the Field, and queried often void set_variables(idx_t n); idx_t variables() const; void set_horizontal_dimension(const std::vector&); std::vector horizontal_dimension() const; void set_functionspace(const FunctionSpace& functionspace); const FunctionSpace& functionspace() const; /// @brief Return the memory footprint of the Field size_t footprint() const; bool dirty() const; void set_dirty(bool = true) const; field::Halo& halo() const; void haloExchange(bool on_device = false) const; void adjointHaloExchange(bool on_device = false) const; // -- Methods related to host-device synchronisation void updateHost() const; void updateDevice() const; void syncHost() const; void syncDevice() const; void syncHostDevice() const; bool hostNeedsUpdate() const; bool deviceNeedsUpdate() const; void setHostNeedsUpdate(bool) const; void setDeviceNeedsUpdate(bool) const; void setHostNeedsUpdate() const; void setDeviceNeedsUpdate() const; bool deviceAllocated() const; void allocateDevice(); void deallocateDevice(); void reactivateDeviceWriteViews() const; void reactivateHostWriteViews() const; }; extern template Field::Field(const std::string&, float*, const array::ArraySpec&); extern template Field::Field(const std::string&, float*, const array::ArrayShape&); extern template Field::Field(const std::string&, double*, const array::ArraySpec&); extern template Field::Field(const std::string&, double*, const array::ArrayShape&); extern template Field::Field(const std::string&, long*, const array::ArraySpec&); extern template Field::Field(const std::string&, long*, const array::ArrayShape&); extern template Field::Field(const std::string&, int*, const array::ArraySpec&); extern template Field::Field(const std::string&, int*, const array::ArrayShape&); //------------------------------------------------------------------------------------------------------ } // namespace atlas atlas-0.46.0/src/atlas/field/FieldCreatorArraySpec.h0000664000175000017500000000272715160212070022377 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ /// @author Willem Deconinck /// @date June 2015 #pragma once #include "atlas/field/FieldCreator.h" namespace eckit { class Parametrisation; } namespace atlas { namespace field { class Field; } } // namespace atlas namespace atlas { namespace field { // ------------------------------------------------------------------ /*! * \brief Field creator using array::ArrayShape parametrisation * \details * \code{.cpp} * FieldImpl* field = Field::create( * Config * ("creator","ArraySpec") // ArraySpec FieldCreator * ("shape",array::make_shape(100,3)) // Rank 2 field with indexing [100][3] * ("datatype",array::DataType::real64()) // Field internal data type * ); * \endcode */ class FieldCreatorArraySpec : public FieldCreator { public: FieldCreatorArraySpec() {} FieldCreatorArraySpec(const eckit::Parametrisation&) {} virtual FieldImpl* createField(const eckit::Parametrisation&) const; }; // ------------------------------------------------------------------ } // namespace field } // namespace atlas atlas-0.46.0/src/atlas/field/MultiFieldCreatorIFS.h0000664000175000017500000000370115160212070022133 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ /// @author Willem Deconinck /// @date June 2015 #pragma once #include "atlas/field/MultiFieldCreator.h" namespace eckit { class Configuration; } namespace atlas { namespace field { class MultiFieldImpl; } } // namespace atlas namespace atlas { namespace field { // ------------------------------------------------------------------ /*! * \brief MultiField creator using IFS parametrisation * \details * Ideally this class should belong to IFS. * The only reference to IFS in Atlas::MultiField should be here. * Example use: * \code{.cpp} * MultiFieldImpl* multifield = MultiField::create( * Config * ("creator","MultiFieldIFS") // MultiFieldIFS FieldCreator * ("ngptot",ngptot) // Total number of grid points * ("nproma",nproma) // Grouping of grid points for vectorlength * ("nlev",nlev) // Number of levels * ("nvar",nvar) // Number of variables * ("kind",8) // Real kind in bytes * ); * \endcode */ class MultiFieldCreatorIFS : public MultiFieldCreator { public: MultiFieldCreatorIFS(); MultiFieldCreatorIFS(const eckit::Configuration& config); ~MultiFieldCreatorIFS() override; MultiFieldImpl* create(const eckit::Configuration& config = util::Config()) const override; MultiFieldImpl* create(const array::DataType datatype, const array::ArrayShape& shape, const std::vector& var_names) const override; }; // ------------------------------------------------------------------ } // namespace field } // namespace atlas atlas-0.46.0/src/atlas/field/FieldSet.h0000664000175000017500000003302615160212070017715 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ /// @file FieldSet.h /// @author Willem Deconinck /// @author Pedro Maciel /// @date Jan 2015 #pragma once #include #include #include #include #include #include #include "eckit/deprecated.h" #include "atlas/array_fwd.h" #include "atlas/field/Field.h" #include "atlas/library/config.h" #include "atlas/runtime/Exception.h" #include "atlas/util/Metadata.h" #include "atlas/util/Object.h" #include "atlas/util/ObjectHandle.h" #include "atlas/field/detail/FieldImpl.h" namespace atlas { class FieldSet; #ifndef DOXYGEN_SHOULD_SKIP_THIS namespace field { /** * @brief Represents a set of fields, where order is preserved */ class FieldSetImpl : public util::Object { public: // types using iterator = std::vector::iterator; using const_iterator = std::vector::const_iterator; template static constexpr bool is_index() { return std::is_integral::value or std::is_enum::value; } template using enable_if_t = typename std::enable_if::type; template using enable_if_index_t = enable_if_t()>; private: class FieldObserver : public field::FieldObserver { public: FieldObserver(FieldSetImpl& fieldset) : fieldset_(fieldset) {} private: void onFieldRename(FieldImpl& field) override; private: FieldSetImpl& fieldset_; }; public: // methods /// Constructs an empty FieldSet FieldSetImpl(const std::string& name = "untitled"); virtual ~FieldSetImpl(); idx_t size() const { return static_cast(fields_.size()); } bool empty() const { return !fields_.size(); } void clear(); const std::string& name() const { return name_; } std::string& name() { return name_; } template = 0> const Field& operator[](Index i) const { return field(i); } template = 0> Field& operator[](Index i) { return field(i); } const Field& operator[](const std::string& name) const { return field(name); } Field& operator[](const std::string& name) { return field(name); } template = 0> const Field& field(Index i) const { if (i >= size()) throw_OutOfRange("fieldset", i, size(), Here()); return fields_[i]; } template = 0> Field& field(Index i) { if (i >= size()) throw_OutOfRange("fieldset", i, size(), Here()); return fields_[i]; } const std::vector& field_names() const; Field add(const Field&); void add(const FieldSet&); bool has(const std::string& name) const; Field& field(const std::string& name) const; iterator begin() { return fields_.begin(); } iterator end() { return fields_.end(); } const_iterator begin() const { return fields_.begin(); } const_iterator end() const { return fields_.end(); } const_iterator cbegin() const { return fields_.begin(); } const_iterator cend() const { return fields_.end(); } const util::Metadata& metadata() const { return metadata_; } util::Metadata& metadata() { return metadata_; } void haloExchange(bool on_device = false) const; void adjointHaloExchange(bool on_device = false) const; void set_dirty(bool = true) const; void updateHost() const; void updateHost(std::initializer_list names) const; void updateHost(std::initializer_list indices) const; void updateDevice() const; void updateDevice(std::initializer_list names) const; void updateDevice(std::initializer_list indices) const; void allocateDevice() const; void allocateDevice(std::initializer_list names) const; void allocateDevice(std::initializer_list indices) const; void deallocateDevice() const; void deallocateDevice(std::initializer_list names) const; void deallocateDevice(std::initializer_list indices) const; void setDeviceNeedsUpdate(bool) const; void setDeviceNeedsUpdate(bool, std::initializer_list names) const; void setDeviceNeedsUpdate(bool, std::initializer_list indices) const; void setHostNeedsUpdate(bool) const; void setHostNeedsUpdate(bool, std::initializer_list names) const; void setHostNeedsUpdate(bool, std::initializer_list indices) const; void setDeviceNeedsUpdate() const; void setDeviceNeedsUpdate(std::initializer_list names) const; void setDeviceNeedsUpdate(std::initializer_list indices) const; void setHostNeedsUpdate() const; void setHostNeedsUpdate(std::initializer_list names) const; void setHostNeedsUpdate(std::initializer_list indices) const; void syncHost() const; void syncHost(std::initializer_list names) const; void syncHost(std::initializer_list indices) const; void syncDevice() const; void syncDevice(std::initializer_list names) const; void syncDevice(std::initializer_list indices) const; protected: // data std::vector fields_; ///< field storage std::string name_; ///< internal name util::Metadata metadata_; ///< metadata associated with the FieldSet std::map index_; ///< name-to-index map, to refer fields by name std::vector field_names_; ///< field names std::map duplicates_; ///< name-to-duplicates map, to refer fields by name friend class FieldObserver; FieldObserver field_observer_; }; // C wrapper interfaces to C++ routines extern "C" { FieldSetImpl* atlas__FieldSet__new(char* name); void atlas__FieldSet__delete(FieldSetImpl* This); void atlas__FieldSet__add_field(FieldSetImpl* This, FieldImpl* field); void atlas__FieldSet__add_fieldset(FieldSetImpl* This, FieldSetImpl* fieldset); int atlas__FieldSet__has_field(const FieldSetImpl* This, char* name); const char* atlas__FieldSet__name(FieldSetImpl* This); idx_t atlas__FieldSet__size(const FieldSetImpl* This); FieldImpl* atlas__FieldSet__field_by_name(FieldSetImpl* This, char* name); FieldImpl* atlas__FieldSet__field_by_idx(FieldSetImpl* This, idx_t idx); void atlas__FieldSet__data_int_specf(FieldSetImpl* This, char* name, int*& field_data, int& rank, int*& field_shapef, int*& field_stridesf); void atlas__FieldSet__data_long_specf(FieldSetImpl* This, char* name, long*& field_data, int& rank, int*& field_shapef, int*& field_stridesf); void atlas__FieldSet__data_float_specf(FieldSetImpl* This, char* name, float*& field_data, int& rank, int*& field_shapef, int*& field_stridesf); void atlas__FieldSet__data_double_specf(FieldSetImpl* This, char* name, double*& field_data, int& rank, int*& field_shapef, int*& field_stridesf); void atlas__FieldSet__data_int_specf_by_idx(FieldSetImpl* This, int& idx, int*& field_data, int& rank, int*& field_shapef, int*& field_stridesf); void atlas__FieldSet__data_long_specf_by_idx(FieldSetImpl* This, int& idx, long*& field_data, int& rank, int*& field_shapef, int*& field_stridesf); void atlas__FieldSet__data_float_specf_by_idx(FieldSetImpl* This, int& idx, float*& field_data, int& rank, int*& field_shapef, int*& field_stridesf); void atlas__FieldSet__data_double_specf_by_idx(FieldSetImpl* This, int& idx, double*& field_data, int& rank, int*& field_shapef, int*& field_stridesf); void atlas__FieldSet__set_dirty(FieldSetImpl* This, int value); void atlas__FieldSet__halo_exchange(FieldSetImpl* This, int on_device); } } // namespace field #endif //--------------------------------------------------------------------------------------------------------------------- /** * @brief Represents a set of fields, where order is preserved */ class FieldSet : DOXYGEN_HIDE(public util::ObjectHandle) { public: // types using iterator = Implementation::iterator; using const_iterator = Implementation::const_iterator; template using enable_if_index_t = Implementation::enable_if_index_t; public: // methods using Handle::Handle; FieldSet(); FieldSet(const std::string& name); FieldSet(const Field&); FieldSet clone(const eckit::Parametrisation& config = util::Config()) const; idx_t size() const { return get()->size(); } bool empty() const { return get()->empty(); } void clear() { get()->clear(); } const std::string& name() const { return get()->name(); } std::string& name() { return get()->name(); } template = 0> const Field& operator[](Index i) const { return get()->operator[](i); } template = 0> Field& operator[](Index i) { return get()->operator[](i); } const Field& operator[](const std::string& name) const { return get()->operator[](name); } Field& operator[](const std::string& name) { return get()->operator[](name); } const Field& operator[](const char* name) const { return get()->operator[](name); } Field& operator[](const char* name) { return get()->operator[](name); } template = 0> const Field& field(Index i) const { return get()->field(i); } template = 0> Field& field(Index i) { return get()->field(i); } const std::vector& field_names() const { return get()->field_names(); } Field add(const Field& field) { return get()->add(field); } void add(const FieldSet& fieldset) { return get()->add(fieldset); } bool has(const std::string& name) const { return get()->has(name); } Field& field(const std::string& name) const { return get()->field(name); } iterator begin() { return get()->begin(); } iterator end() { return get()->end(); } const_iterator begin() const { return get()->begin(); } const_iterator end() const { return get()->end(); } const_iterator cbegin() const { return get()->begin(); } const_iterator cend() const { return get()->end(); } const util::Metadata& metadata() const; util::Metadata& metadata(); void set_dirty(bool = true) const; void haloExchange(bool on_device = false) const { get()->haloExchange(on_device); } void adjointHaloExchange(bool on_device = false) const { get()->adjointHaloExchange(on_device); } // -- Methods related to host-device synchronisation void updateHost() const { get()->updateHost(); } void updateHost(std::initializer_list names) const { get()->updateHost(names); } void updateHost(std::initializer_list indices) const { get()->updateHost(indices); } void updateDevice() const { get()->updateDevice(); } void updateDevice(std::initializer_list names) const { get()->updateDevice(names); } void updateDevice(std::initializer_list indices) const{ get()->updateDevice(indices); } void allocateDevice() const { get()->allocateDevice(); } void allocateDevice(std::initializer_list names) const { get()->allocateDevice(names); } void allocateDevice(std::initializer_list indices) const { get()->allocateDevice(indices); } void deallocateDevice() const { get()->deallocateDevice(); } void deallocateDevice(std::initializer_list names) const { get()->deallocateDevice(names); } void deallocateDevice(std::initializer_list indices) const { get()->deallocateDevice(indices); } void setDeviceNeedsUpdate(bool value) const { get()->setDeviceNeedsUpdate(value); } void setDeviceNeedsUpdate(bool value, std::initializer_list names) const { get()->setDeviceNeedsUpdate(value, names); } void setDeviceNeedsUpdate(bool value, std::initializer_list indices) const { get()->setDeviceNeedsUpdate(value, indices); } void setHostNeedsUpdate(bool value) const { get()->setHostNeedsUpdate(value); } void setHostNeedsUpdate(bool value, std::initializer_list names) const { get()->setHostNeedsUpdate(value, names); } void setHostNeedsUpdate(bool value, std::initializer_list indices) const { get()->setHostNeedsUpdate(value, indices); } void syncHost() const { get()->syncHost(); } void syncHost(std::initializer_list names) const { get()->syncHost(names); } void syncHost(std::initializer_list indices) const { get()->syncHost(indices); } void syncDevice() const { get()->syncDevice(); } void syncDevice(std::initializer_list names) const { get()->syncDevice(names); } void syncDevice(std::initializer_list indices) const { get()->syncDevice(indices); } // Deprecated API DEPRECATED("use 'has' instead") bool has_field(const std::string& name) const { return get()->has(name); } }; } // namespace atlas atlas-0.46.0/src/atlas/field/FieldSet.cc0000664000175000017500000003771015160212070020057 0ustar alastairalastair/* * (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/field/Field.h" #include "atlas/field/detail/FieldInterface.h" #include "atlas/field/FieldSet.h" #include "atlas/grid/Grid.h" #include "atlas/runtime/Exception.h" namespace atlas { namespace field { //------------------------------------------------------------------------------------------------------ void FieldSetImpl::FieldObserver::onFieldRename(FieldImpl& field) { for (idx_t idx=0; idxdetachObserver(field_observer_); } index_.clear(); fields_.clear(); field_names_.clear(); duplicates_.clear(); } Field FieldSetImpl::add(const Field& field) { auto update_duplicates = [&](const std::string& name) { if (duplicates_.find(name) != duplicates_.end()) { ++duplicates_[name]; } else { duplicates_[name] = 1; } }; std::string name; if (field.name().size()) { name = field.name(); } else { std::stringstream name_ss; name_ss << name_ << "[" << size() << "]"; name = name_ss.str(); } index_[name] = size(); fields_.push_back(field); field_names_.push_back(name); update_duplicates(name); field.get()->attachObserver(field_observer_); return field; } void FieldSetImpl::add(const FieldSet& fieldset) { for( auto& field : fieldset) { add(field); } } bool FieldSetImpl::has(const std::string& name) const { return index_.count(name); } Field& FieldSetImpl::field(const std::string& name) const { if (!has(name)) { std::stringstream msg; msg << "FieldSet" << (name_.length() ? " \"" + name_ + "\"" : "") << ": cannot find field \"" << name << "\""; throw_Exception(msg.str(), Here()); } if (duplicates_.at(name) > 1) { std::stringstream msg; msg << "FieldSet" << (name_.length() ? " \"" + name_ + "\"" : "") << ": cannot get field with ambiguous name \n" << name << "\". " << duplicates_.at(name) << " fields are registered with same name. Access field by index or iterator instead."; throw_Exception(msg.str(), Here()); } return const_cast(fields_[index_.at(name)]); } void FieldSetImpl::haloExchange(bool on_device) const { for (idx_t i = 0; i < size(); ++i) { field(i).haloExchange(on_device); } } void FieldSetImpl::adjointHaloExchange(bool on_device) const { for (idx_t i = 0; i < size(); ++i) { field(i).adjointHaloExchange(on_device); } } void FieldSetImpl::set_dirty(bool value) const { for (idx_t i = 0; i < size(); ++i) { field(i).set_dirty(value); } } const std::vector& FieldSetImpl::field_names() const { return field_names_; } void FieldSetImpl::updateHost() const { for (auto field: *this) { field.updateHost(); } } void FieldSetImpl::updateHost(std::initializer_list fnames) const { ATLAS_ASSERT(fnames.size() > 0); for (int i = 0; i < fnames.size(); ++i) { auto field = this->field(fnames.begin()[i]); field.updateHost(); } } void FieldSetImpl::updateHost(std::initializer_list findices) const { ATLAS_ASSERT(findices.size() > 0); for (int i = 0; i < findices.size(); ++i) { auto field = this->field(findices.begin()[i]); field.updateHost(); } } void FieldSetImpl::updateDevice() const { for (auto field: *this) { field.updateDevice(); } } void FieldSetImpl::updateDevice(std::initializer_list fnames) const { ATLAS_ASSERT(fnames.size() > 0); for (int i = 0; i < fnames.size(); ++i) { auto field = this->field(fnames.begin()[i]); field.updateDevice(); } } void FieldSetImpl::updateDevice(std::initializer_list findices) const { ATLAS_ASSERT(findices.size() > 0); for (int i = 0; i < findices.size(); ++i) { auto field = this->field(findices.begin()[i]); field.updateDevice(); } } void FieldSetImpl::allocateDevice() const { for (auto field: *this) { field.allocateDevice(); } } void FieldSetImpl::allocateDevice(std::initializer_list fnames) const { ATLAS_ASSERT(fnames.size() > 0); for (int i = 0; i < fnames.size(); ++i) { auto field = this->field(fnames.begin()[i]); field.allocateDevice(); } } void FieldSetImpl::allocateDevice(std::initializer_list findices) const { ATLAS_ASSERT(findices.size() > 0); for (int i = 0; i < findices.size(); ++i) { auto field = this->field(findices.begin()[i]); field.allocateDevice(); } } void FieldSetImpl::deallocateDevice() const { for (auto field: *this) { field.deallocateDevice(); } } void FieldSetImpl::deallocateDevice(std::initializer_list fnames) const { ATLAS_ASSERT(fnames.size() > 0); for (int i = 0; i < fnames.size(); ++i) { auto field = this->field(fnames.begin()[i]); field.deallocateDevice(); } } void FieldSetImpl::deallocateDevice(std::initializer_list findices) const { ATLAS_ASSERT(findices.size() > 0); for (int i = 0; i < findices.size(); ++i) { auto field = this->field(findices.begin()[i]); field.deallocateDevice(); } } void FieldSetImpl::setDeviceNeedsUpdate(bool value) const { for (auto field: *this) { field.setDeviceNeedsUpdate(value); } } void FieldSetImpl::setDeviceNeedsUpdate(bool value, std::initializer_list fnames) const { ATLAS_ASSERT(fnames.size() > 0); for (int i = 0; i < fnames.size(); ++i) { auto field = this->field(fnames.begin()[i]); field.setDeviceNeedsUpdate(true); } } void FieldSetImpl::setDeviceNeedsUpdate(bool value, std::initializer_list findices) const { ATLAS_ASSERT(findices.size() > 0); for (int i = 0; i < findices.size(); ++i) { auto field = this->field(findices.begin()[i]); field.setDeviceNeedsUpdate(value); } } void FieldSetImpl::setDeviceNeedsUpdate() const { for (auto field: *this) { field.setDeviceNeedsUpdate(); } } void FieldSetImpl::setDeviceNeedsUpdate(std::initializer_list fnames) const { ATLAS_ASSERT(fnames.size() > 0); for (int i = 0; i < fnames.size(); ++i) { auto field = this->field(fnames.begin()[i]); field.setDeviceNeedsUpdate(); } } void FieldSetImpl::setDeviceNeedsUpdate(std::initializer_list findices) const { ATLAS_ASSERT(findices.size() > 0); for (int i = 0; i < findices.size(); ++i) { auto field = this->field(findices.begin()[i]); field.setDeviceNeedsUpdate(); } } void FieldSetImpl::setHostNeedsUpdate(bool value) const { for (auto field: *this) { field.setHostNeedsUpdate(value); } } void FieldSetImpl::setHostNeedsUpdate(bool value, std::initializer_list fnames) const { ATLAS_ASSERT(fnames.size() > 0); for (int i = 0; i < fnames.size(); ++i) { auto field = this->field(fnames.begin()[i]); field.setHostNeedsUpdate(value); } } void FieldSetImpl::setHostNeedsUpdate(bool value, std::initializer_list findices) const { ATLAS_ASSERT(findices.size() > 0); for (int i = 0; i < findices.size(); ++i) { auto field = this->field(findices.begin()[i]); field.setHostNeedsUpdate(value); } } void FieldSetImpl::setHostNeedsUpdate() const { for (auto field: *this) { field.setHostNeedsUpdate(); } } void FieldSetImpl::setHostNeedsUpdate(std::initializer_list fnames) const { ATLAS_ASSERT(fnames.size() > 0); for (int i = 0; i < fnames.size(); ++i) { auto field = this->field(fnames.begin()[i]); field.setHostNeedsUpdate(); } } void FieldSetImpl::setHostNeedsUpdate(std::initializer_list findices) const { ATLAS_ASSERT(findices.size() > 0); for (int i = 0; i < findices.size(); ++i) { auto field = this->field(findices.begin()[i]); field.setHostNeedsUpdate(); } } void FieldSetImpl::syncHost() const { for (auto field: *this) { field.syncHost(); } } void FieldSetImpl::syncHost(std::initializer_list fnames) const { ATLAS_ASSERT(fnames.size() > 0); for (int i = 0; i < fnames.size(); ++i) { auto field = this->field(fnames.begin()[i]); field.syncHost(); } } void FieldSetImpl::syncHost(std::initializer_list findices) const { ATLAS_ASSERT(findices.size() > 0); for (int i = 0; i < findices.size(); ++i) { auto field = this->field(findices.begin()[i]); field.syncHost(); } } void FieldSetImpl::syncDevice() const { for (auto field: *this) { field.syncDevice(); } } void FieldSetImpl::syncDevice(std::initializer_list fnames) const { ATLAS_ASSERT(fnames.size() > 0); for (int i = 0; i < fnames.size(); ++i) { auto field = this->field(fnames.begin()[i]); field.syncDevice(); } } void FieldSetImpl::syncDevice(std::initializer_list findices) const { ATLAS_ASSERT(findices.size() > 0); for (int i = 0; i < findices.size(); ++i) { auto field = this->field(findices.begin()[i]); field.syncDevice(); } } //----------------------------------------------------------------------------- // C wrapper interfaces to C++ routines extern "C" { void atlas__FieldSet__data_int_specf(FieldSetImpl* This, char* name, int*& data, int& rank, int*& shapef, int*& stridesf) { atlas__Field__data_int_specf(This->field(name).get(), data, rank, shapef, stridesf); } void atlas__FieldSet__data_long_specf(FieldSetImpl* This, char* name, long*& data, int& rank, int*& shapef, int*& stridesf) { atlas__Field__data_long_specf(This->field(name).get(), data, rank, shapef, stridesf); } void atlas__FieldSet__data_float_specf(FieldSetImpl* This, char* name, float*& data, int& rank, int*& shapef, int*& stridesf) { atlas__Field__data_float_specf(This->field(name).get(), data, rank, shapef, stridesf); } void atlas__FieldSet__data_double_specf(FieldSetImpl* This, char* name, double*& data, int& rank, int*& shapef, int*& stridesf) { atlas__Field__data_double_specf(This->field(name).get(), data, rank, shapef, stridesf); } void atlas__FieldSet__data_int_specf_by_idx(FieldSetImpl* This, int& idx, int*& data, int& rank, int*& shapef, int*& stridesf) { atlas__Field__data_int_specf(This->operator[](idx).get(), data, rank, shapef, stridesf); } void atlas__FieldSet__data_long_specf_by_idx(FieldSetImpl* This, int& idx, long*& data, int& rank, int*& shapef, int*& stridesf) { atlas__Field__data_long_specf(This->operator[](idx).get(), data, rank, shapef, stridesf); } void atlas__FieldSet__data_float_specf_by_idx(FieldSetImpl* This, int& idx, float*& data, int& rank, int*& shapef, int*& stridesf) { atlas__Field__data_float_specf(This->operator[](idx).get(), data, rank, shapef, stridesf); } void atlas__FieldSet__data_double_specf_by_idx(FieldSetImpl* This, int& idx, double*& data, int& rank, int*& shapef, int*& stridesf) { atlas__Field__data_double_specf(This->operator[](idx).get(), data, rank, shapef, stridesf); } FieldSetImpl* atlas__FieldSet__new(char* name) { FieldSetImpl* fset = new FieldSetImpl(std::string(name)); fset->name() = name; return fset; } void atlas__FieldSet__delete(FieldSetImpl* This) { ATLAS_ASSERT(This != nullptr, "Reason: Use of uninitialised atlas_FieldSet"); delete This; } const char* atlas__FieldSet__name(FieldSetImpl* This) { ATLAS_ASSERT(This != nullptr, "Cannot access name of uninitialised atlas_FieldSet"); return This->name().c_str(); } void atlas__FieldSet__add_field(FieldSetImpl* This, FieldImpl* field) { ATLAS_ASSERT(This != nullptr, "Reason: Use of uninitialised atlas_FieldSet"); ATLAS_ASSERT(field != nullptr, "Reason: Use of uninitialised atlas_Field"); This->add(field); } void atlas__FieldSet__add_fieldset(FieldSetImpl* This, FieldSetImpl* fieldset) { ATLAS_ASSERT(This != nullptr, "Reason: Use of uninitialised atlas_FieldSet"); ATLAS_ASSERT(fieldset != nullptr, "Reason: Use of uninitialised atlas_FieldSet"); This->add(fieldset); } int atlas__FieldSet__has_field(const FieldSetImpl* This, char* name) { ATLAS_ASSERT(This != nullptr, "Reason: Use of uninitialised atlas_FieldSet"); return This->has(std::string(name)); } idx_t atlas__FieldSet__size(const FieldSetImpl* This) { ATLAS_ASSERT(This != nullptr, "Reason: Use of uninitialised atlas_FieldSet"); return This->size(); } FieldImpl* atlas__FieldSet__field_by_name(FieldSetImpl* This, char* name) { ATLAS_ASSERT(This != nullptr, "Reason: Use of uninitialised atlas_FieldSet"); return This->field(std::string(name)).get(); } FieldImpl* atlas__FieldSet__field_by_idx(FieldSetImpl* This, idx_t idx) { ATLAS_ASSERT(This != nullptr, "Reason: Use of uninitialised atlas_FieldSet"); return This->operator[](idx).get(); } void atlas__FieldSet__set_dirty(FieldSetImpl* This, int value) { ATLAS_ASSERT(This != nullptr, "Reason: Use of uninitialised atlas_FieldSet"); This->set_dirty(value); } void atlas__FieldSet__halo_exchange(FieldSetImpl* This, int on_device) { ATLAS_ASSERT(This != nullptr, "Reason: Use of uninitialised atlas_FieldSet"); This->haloExchange(on_device); } } //----------------------------------------------------------------------------- } // namespace field //------------------------------------------------------------------------------------------------------ FieldSet::FieldSet(): Handle(new Implementation()) {} FieldSet::FieldSet(const std::string& name): Handle(new Implementation(name)) {} FieldSet::FieldSet(const Field& field): Handle(new Implementation()) { get()->add(field); } const util::Metadata& FieldSet::metadata() const { return get()->metadata(); } util::Metadata& FieldSet::metadata() { return get()->metadata(); } FieldSet FieldSet::clone(const eckit::Parametrisation& config) const { FieldSet fset; for (idx_t jj = 0; jj < size(); ++jj) { fset.add(field(jj).clone(config)); } fset.metadata() = metadata(); return fset; } void FieldSet::set_dirty(bool value) const { get()->set_dirty(value); } //------------------------------------------------------------------------------------------------------ } // namespace atlas atlas-0.46.0/src/atlas/field/FieldCreatorIFS.cc0000664000175000017500000000500215160212070021252 0ustar alastairalastair/* * (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/field/FieldCreatorIFS.h" #include #include #include "eckit/config/Parametrisation.h" #include "atlas/array/ArrayDataStore.h" #include "atlas/array/DataType.h" #include "atlas/field/detail/FieldImpl.h" #include "atlas/grid/Grid.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Log.h" namespace atlas { namespace field { FieldImpl* FieldCreatorIFS::createField(const eckit::Parametrisation& params) const { size_t ngptot; size_t nblk; size_t nvar = 1; size_t nproma = 1; size_t nlev = 1; if (!params.get("ngptot", ngptot)) { throw_Exception("Could not find parameter 'ngptot' in Parametrisation"); } params.get("nproma", nproma); params.get("nlev", nlev); params.get("nvar", nvar); array::DataType datatype = array::DataType::create(); std::string datatype_str; if (params.get("datatype", datatype_str)) { datatype = array::DataType(datatype_str); } else { array::DataType::kind_t kind(array::DataType::kind()); params.get("kind", kind); if (!array::DataType::kind_valid(kind)) { std::stringstream msg; msg << "Could not create field. kind parameter unrecognized"; throw_Exception(msg.str()); } datatype = array::DataType(kind); } nblk = std::ceil(static_cast(ngptot) / static_cast(nproma)); array::ArrayShape s; bool fortran(false); params.get("fortran", fortran); if (fortran) { s = array::make_shape(nproma, nlev, nvar, nblk); } else { s = array::make_shape(nblk, nvar, nlev, nproma); } std::string name; params.get("name", name); Log::debug() << "Creating IFS " << datatype.str() << " field: " << name << "[nblk=" << nblk << "][nvar=" << nvar << "][nlev=" << nlev << "][nproma=" << nproma << "]\n"; return FieldImpl::create(name, datatype, s); } namespace { static FieldCreatorBuilder __IFS("IFS"); } // ------------------------------------------------------------------ } // namespace field } // namespace atlas atlas-0.46.0/src/atlas/field/Halo.h0000664000175000017500000000162015160212070017074 0ustar alastairalastair/* * (C) Copyright 2025 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include "eckit/config/Parametrisation.h" #include "atlas/functionspace/HaloDescription.h" namespace atlas::field { class FieldImpl; class Halo: public functionspace::HaloDescription { public: bool updated() const; void updated(bool v); void update(); void update(const eckit::Parametrisation&); void invalidate() { updated(false); } bool appended() const; private: friend class FieldImpl; Halo(field::FieldImpl& f); private: field::FieldImpl& field_; }; } // namespace atlas::field atlas-0.46.0/src/atlas/field/FieldCreator.h0000664000175000017500000000541015160212070020555 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ /// @author Willem Deconinck /// @date June 2015 #ifndef atlas_field_FieldCreator_h #define atlas_field_FieldCreator_h #include #include "atlas/util/Object.h" #include "atlas/field/Field.h" namespace eckit { class Parametrisation; } //------------------------------------------------------------------------------------------------------ namespace atlas { namespace field { //------------------------------------------------------------------------------------------------------ /*! * \brief Base class for creating new fields based on Parametrisation * * \details * Example to create field[100][3] of default type double: * \code{.cpp} * FieldImpl* field = Field::create( * Config * ("creator","ArraySpec") // ArraySpec FieldCreator * ("shape",array::make_shape(100,3)) // Rank 2 field with indexing [100][3] * ); * \endcode */ class FieldCreator : public util::Object { public: FieldCreator(); virtual ~FieldCreator(); virtual FieldImpl* createField(const eckit::Parametrisation&) const = 0; }; //------------------------------------------------------------------------------------------------------ class FieldCreatorFactory { public: /*! * \brief build FieldCreator with factory key, and default options * \return FieldCreator */ static FieldCreator* build(const std::string&); /*! * \brief build FieldCreator with options specified in parametrisation * \return mesh generator */ static FieldCreator* build(const std::string&, const eckit::Parametrisation&); /*! * \brief list all registered field creators */ static void list(std::ostream&); private: std::string name_; virtual FieldCreator* make() = 0; virtual FieldCreator* make(const eckit::Parametrisation&) = 0; protected: FieldCreatorFactory(const std::string&); virtual ~FieldCreatorFactory(); }; template class FieldCreatorBuilder : public FieldCreatorFactory { virtual FieldCreator* make() { return new T(); } virtual FieldCreator* make(const eckit::Parametrisation& param) { return new T(param); } public: FieldCreatorBuilder(const std::string& name): FieldCreatorFactory(name) {} }; //------------------------------------------------------------------------------------------------------ } // namespace field } // namespace atlas #endif atlas-0.46.0/src/atlas/field/Field.cc0000664000175000017500000002010215160212070017366 0ustar alastairalastair/* * (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 "atlas/library/config.h" #include "atlas/field/Field.h" #include "atlas/field/detail/FieldImpl.h" #include "atlas/runtime/Exception.h" #if ATLAS_HAVE_FUNCTIONSPACE #include "atlas/functionspace/FunctionSpace.h" #endif namespace atlas { // ------------------------------------------------------------------ std::ostream& operator<<(std::ostream& os, const Field& f) { os << (*f.get()); return os; } Field::Field(const eckit::Parametrisation& config): Handle(Implementation::create(config)) {} Field::Field(const std::string& name, array::DataType datatype, const array::ArrayShape& shape): Handle(Implementation::create(name, datatype, shape)) {} Field::Field(const std::string& name, array::DataType datatype, array::ArraySpec&& spec): Handle(Implementation::create(name, datatype, std::move(spec))) {} Field::Field(const std::string& name, array::Array* array): Handle(Implementation::create(name, array)) {} template Field::Field(const std::string& name, DATATYPE* data, const array::ArraySpec& spec): Handle(Implementation::wrap(name, data, spec)) {} template Field::Field(const std::string& name, DATATYPE* data, const array::ArrayShape& shape): Handle(Implementation::wrap(name, data, shape)) {} /// @brief Implicit conversion to Array Field::operator const array::Array&() const { return get()->array(); } Field::operator array::Array&() { return get()->array(); } const array::Array& Field::array() const { return get()->array(); } array::Array& Field::array() { return get()->array(); } /// @brief Clone Field Field::clone(const eckit::Parametrisation& config) const { Field tmp(get()->name(), get()->datatype(), get()->shape()); tmp.metadata() = this->metadata(); tmp.set_functionspace(this->functionspace()); array::Array::CopyPolicy cp; // To be set up via config. For now use default, as Array does not yet implement it. tmp.array().copy(this->array(),cp); return tmp; } // -- Accessors /// @brief Access to raw data void* Field::storage() { return get()->storage(); } /// @brief Internal data type of field array::DataType Field::datatype() const { return get()->datatype(); } /// @brief Name associated to this field const std::string& Field::name() const { return get()->name(); } /// @brief Rename this field void Field::rename(const std::string& name) { get()->rename(name); } /// @brief Access to metadata associated to this field const util::Metadata& Field::metadata() const { return get()->metadata(); } util::Metadata& Field::metadata() { return get()->metadata(); } field::Halo& Field::halo() const { return get()->halo(); } /// @brief Resize field to given shape void Field::resize(const array::ArrayShape& shape) { get()->resize(shape); } void Field::insert(idx_t idx1, idx_t size1) { get()->insert(idx1, size1); } /// @brief Shape of this field in Fortran style (reverse order of C style) const std::vector& Field::shapef() const { return get()->shapef(); } /// @brief Strides of this field in Fortran style (reverse order of C style) const std::vector& Field::stridesf() const { return get()->stridesf(); } /// @brief Shape of this field (reverse order of Fortran style) const array::ArrayShape& Field::shape() const { return get()->shape(); } /// @brief Strides of this field const array::ArrayStrides& Field::strides() const { return get()->strides(); } /// @brief Shape of this field associated to index 'i' idx_t Field::shape(idx_t i) const { return get()->shape(i); } /// @brief Stride of this field associated to index 'i' idx_t Field::stride(idx_t i) const { return get()->stride(i); } /// @brief Number of values stored in this field size_t Field::size() const { return get()->size(); } /// @brief Rank of field idx_t Field::rank() const { return get()->rank(); } /// @brief Number of bytes occupied by the values of this field size_t Field::bytes() const { return get()->bytes(); } bool Field::contiguous() const { return array().contiguous(); } /// @brief Output information of field plus raw data void Field::dump(std::ostream& os) const { get()->dump(os); } /// Metadata that is more intrinsic to the Field, and queried often void Field::set_levels(idx_t n) { get()->set_levels(n); } idx_t Field::levels() const { return get()->levels(); } /// Metadata that is more intrinsic to the Field, and queried often void Field::set_variables(idx_t n) { get()->set_variables(n); } idx_t Field::variables() const { return get()->variables(); } void Field::set_horizontal_dimension(const std::vector& h_dim) { get()->set_horizontal_dimension(h_dim); } std::vector Field::horizontal_dimension() const { return get()->horizontal_dimension(); } void Field::set_functionspace(const FunctionSpace& functionspace) { #if ATLAS_HAVE_FUNCTIONSPACE get()->set_functionspace(functionspace); #else throw_Exception("Atlas has been compiled without FunctionSpace support",Here()); #endif } const FunctionSpace& Field::functionspace() const { #if ATLAS_HAVE_FUNCTIONSPACE return get()->functionspace(); #else throw_Exception("Atlas has been compiled without FunctionSpace support",Here()); #endif } /// @brief Return the memory footprint of the Field size_t Field::footprint() const { return get()->footprint(); } bool Field::dirty() const { return get()->dirty(); } void Field::set_dirty(bool value) const { get()->set_dirty(value); } void Field::haloExchange(bool on_device) const { get()->haloExchange(on_device); } void Field::adjointHaloExchange(bool on_device) const { get()->adjointHaloExchange(on_device); } // -- Methods related to host-device synchronisation, requires gridtools_storage void Field::updateHost() const { get()->updateHost(); } void Field::updateDevice() const { get()->updateDevice(); } void Field::syncHostDevice() const { get()->syncHostDevice(); } void Field::syncHost() const { get()->syncHost(); } void Field::syncDevice() const { get()->syncDevice(); } bool Field::hostNeedsUpdate() const { return get()->hostNeedsUpdate(); } bool Field::deviceNeedsUpdate() const { return get()->deviceNeedsUpdate(); } void Field::setHostNeedsUpdate(bool v) const { return get()->setHostNeedsUpdate(v); } void Field::setHostNeedsUpdate() const { return get()->setHostNeedsUpdate(true); } void Field::setDeviceNeedsUpdate(bool v) const { return get()->setDeviceNeedsUpdate(v); } void Field::setDeviceNeedsUpdate() const { return get()->setDeviceNeedsUpdate(true); } bool Field::deviceAllocated() const { return get()->deviceAllocated(); } void Field::allocateDevice() { get()->allocateDevice(); } void Field::deallocateDevice() { get()->deallocateDevice(); } void Field::reactivateDeviceWriteViews() const { get()->reactivateDeviceWriteViews(); } void Field::reactivateHostWriteViews() const { get()->reactivateHostWriteViews(); } // ------------------------------------------------------------------ template Field::Field(const std::string&, float*, const array::ArraySpec&); template Field::Field(const std::string&, float*, const array::ArrayShape&); template Field::Field(const std::string&, double*, const array::ArraySpec&); template Field::Field(const std::string&, double*, const array::ArrayShape&); template Field::Field(const std::string&, long*, const array::ArraySpec&); template Field::Field(const std::string&, long*, const array::ArrayShape&); template Field::Field(const std::string&, int*, const array::ArraySpec&); template Field::Field(const std::string&, int*, const array::ArrayShape&); // ------------------------------------------------------------------ } // namespace atlas atlas-0.46.0/src/atlas/field/State.cc0000664000175000017500000001773115160212070017441 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ // file deepcode ignore CppMemoryLeak: static pointers for global registry are OK and will be cleaned up at end #include "atlas/field/State.h" #include #include #include #include #include "eckit/thread/AutoLock.h" #include "eckit/thread/Mutex.h" #include "atlas/field/Field.h" #include "atlas/grid/Grid.h" #include "atlas/mesh/Mesh.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Log.h" namespace atlas { namespace field { namespace { static eckit::Mutex* local_mutex = nullptr; static std::map* m = nullptr; static pthread_once_t once = PTHREAD_ONCE_INIT; static void init() { local_mutex = new eckit::Mutex(); m = new std::map(); } template void load_builder() { StateGeneratorBuilder("tmp"); } void force_link() { static struct Link { Link() = default; } link; [](const Link&) {}(link); // disable unused warnings } } // namespace void State::initialize(const std::string& generator, const eckit::Parametrisation& params) { std::unique_ptr state_generator(StateGeneratorFactory::build(generator, params)); state_generator->generate(*this, params); } //------------------------------------------------------------------------------------------------------ State::State() = default; State::State(const std::string& generator, const eckit::Parametrisation& params) { initialize(generator, params); } const util::Metadata& State::metadata() const { return metadata_; } util::Metadata& State::metadata() { return metadata_; } Field State::add(Field field) { ATLAS_ASSERT(field); if (field.name().empty()) { std::stringstream new_name; new_name << "field_" << std::setw(5) << std::setfill('0') << fields_.size(); ATLAS_ASSERT(!has(new_name.str())); field.rename(new_name.str()); } if (has(field.name())) { std::stringstream msg; msg << "Trying to add field '" << field.name() << "' to State, but State already has a field with this name."; throw_AssertionFailed(msg.str(), Here()); } fields_[field.name()] = field; return field; } const Field& State::field(const std::string& name) const { if (!has(name)) { std::ostringstream msg; msg << "Trying to access field `" << name << "' in State, but no field with this name is present in State."; throw_AssertionFailed(msg.str(), Here()); } return fields_.find(name)->second; } Field& State::field(const std::string& name) { return const_cast(static_cast(this)->field(name)); } const Field& State::field(const idx_t idx) const { if (idx >= size()) { std::ostringstream msg; msg << "Trying to access field in State with index " << idx << ", but there exist only " << size() << " fields in State."; throw_AssertionFailed(msg.str(), Here()); } FieldMap::const_iterator it = fields_.begin(); for (idx_t i = 0; i < idx; ++i) { ++it; } return it->second; } Field& State::field(const idx_t idx) { return const_cast(static_cast(this)->field(idx)); } std::vector State::field_names() const { std::vector ret; if (fields_.size()) { ret.reserve(fields_.size()); } for (FieldMap::const_iterator it = fields_.begin(); it != fields_.end(); ++it) { ret.push_back(it->first); } return ret; } void State::remove(const std::string& name) { if (fields_.find(name) == fields_.end()) { std::stringstream msg; msg << "Trying to remove field '" << name << "' from State, but it is not present in State."; throw_AssertionFailed(msg.str(), Here()); } fields_.erase(name); } //----------------------------------------------------------------------------- StateGenerator::StateGenerator(const eckit::Parametrisation&) {} StateGenerator::~StateGenerator() = default; StateGenerator* StateGeneratorFactory::build(const std::string& name, const eckit::Parametrisation& param) { pthread_once(&once, init); eckit::AutoLock lock(local_mutex); force_link(); std::map::const_iterator j = m->find(name); Log::debug() << "Looking for StateGeneratorFactory [" << name << "]" << std::endl; if (j == m->end()) { Log::error() << "No StateGeneratorFactory for [" << name << "]" << std::endl; Log::error() << "StateFactories are:" << std::endl; for (j = m->begin(); j != m->end(); ++j) { Log::error() << " " << (*j).first << std::endl; } throw_Exception(std::string("No StateGeneratorFactory called ") + name, Here()); } return (*j).second->make(param); } void StateGeneratorFactory::list(std::ostream& out) { pthread_once(&once, init); eckit::AutoLock lock(local_mutex); force_link(); const char* sep = ""; for (std::map::const_iterator j = m->begin(); j != m->end(); ++j) { out << sep << (*j).first; sep = ", "; } } bool StateGeneratorFactory::has(const std::string& name) { pthread_once(&once, init); eckit::AutoLock lock(local_mutex); force_link(); return (m->find(name) != m->end()); } StateGeneratorFactory::StateGeneratorFactory(const std::string& name): name_(name) { pthread_once(&once, init); eckit::AutoLock lock(local_mutex); ATLAS_ASSERT(m->find(name) == m->end()); (*m)[name] = this; } StateGeneratorFactory::~StateGeneratorFactory() { eckit::AutoLock lock(local_mutex); m->erase(name_); } //----------------------------------------------------------------------------- // C wrapper interfaces to C++ routines extern "C" { State* atlas__State__new() { return new State; } void atlas__State__initialize(State* This, const char* generator, const eckit::Parametrisation* params) { ATLAS_ASSERT(This != nullptr, "Reason: Use of uninitialised atlas_State"); This->initialize(std::string(generator), *params); } void atlas__State__delete(State* This) { ATLAS_ASSERT(This != nullptr, "Reason: Use of uninitialised atlas_State"); delete This; } void atlas__State__add(State* This, FieldImpl* field) { ATLAS_ASSERT(This != nullptr, "Reason: Use of uninitialised atlas_State"); This->add(field); } void atlas__State__remove(State* This, const char* name) { ATLAS_ASSERT(This != nullptr, "Reason: Use of uninitialised atlas_State"); This->remove(name); } int atlas__State__has(State* This, const char* name) { ATLAS_ASSERT(This != nullptr, "Reason: Use of uninitialised atlas_State"); return This->has(name); } FieldImpl* atlas__State__field_by_name(State* This, const char* name) { ATLAS_ASSERT(This != nullptr, "Reason: Use of uninitialised atlas_State"); return This->field(std::string(name)).get(); } FieldImpl* atlas__State__field_by_index(State* This, idx_t index) { ATLAS_ASSERT(This != nullptr, "Reason: Use of uninitialised atlas_State"); return This->field(index).get(); } idx_t atlas__State__size(const State* This) { ATLAS_ASSERT(This != nullptr, "Reason: Use of uninitialised atlas_State"); return This->size(); } util::Metadata* atlas__State__metadata(State* This) { ATLAS_ASSERT(This != nullptr, "Reason: Use of uninitialised atlas_State"); return &This->metadata(); } } //----------------------------------------------------------------------------- } // namespace field } // namespace atlas atlas-0.46.0/src/atlas/field/for_each.h0000664000175000017500000006363515160212070017775 0ustar alastairalastair/* * (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 #include "atlas/field/detail/for_each.h" #include "atlas/array/Array.h" #include "Field.h" #include "FieldSet.h" #include "atlas/array/helpers/ArrayForEach.h" #include "atlas/runtime/Exception.h" namespace atlas { namespace field { //------------------------------------------------------------------------------------------------------------------------------------------------ // void for_each_value_masked( const eckit::Parametrisation& , const Mask& , std::tuple&& , const Function& ) template void for_each_value_masked(const eckit::Parametrisation& config, const Mask& mask, std::tuple&& fields, const Function& function) { auto field_1 = std::get<0>(fields); if constexpr (std::is_same_v,atlas::Field>) { auto h_dim = field_1.horizontal_dimension(); ATLAS_ASSERT( mask.datatype() == array::make_datatype() ); ATLAS_ASSERT( mask.rank() <= h_dim.size() ); if (h_dim.size() == 1) { ATLAS_ASSERT(h_dim[0] == 0); auto mask_view = array::make_view(mask); auto mask_wrap = [mask_view](idx_t i, auto&&... args) { return mask_view(i); }; return for_each_value_masked(config, mask_wrap, std::move(fields), function); } else if (h_dim.size() == 2) { if (mask.rank() == 1) { auto mask_view_1d = array::make_view(mask); auto mask_view_shape2d = array::make_shape(field_1.shape(h_dim[0]), field_1.shape(h_dim[1])); auto mask_view = array::View( mask_view_1d.data(), mask_view_shape2d ); if( h_dim[0] == 0 && h_dim[1] == 2) { auto mask_wrap = [mask_view](idx_t i, idx_t /*dummy*/, idx_t j, auto&&... args) { return mask_view(i,j); }; return for_each_value_masked(config, mask_wrap, std::move(fields), function); } else { ATLAS_ASSERT(h_dim[0] == 0 && h_dim[1] == 1); auto mask_wrap = [mask_view](idx_t i, idx_t j, auto&&... args) { return mask_view(i,j); }; return for_each_value_masked(config, mask_wrap, std::move(fields), function); } } else { auto mask_view = array::make_view(mask); if( h_dim[0] == 0 && h_dim[1] == 2) { auto mask_wrap = [mask_view](idx_t i, idx_t /*dummy*/, idx_t j, auto&&... args) { return mask_view(i,j); }; return for_each_value_masked(config, mask_wrap, std::move(fields), function); } else { ATLAS_ASSERT(h_dim[0] == 0 && h_dim[1] == 1); auto mask_wrap = [mask_view](idx_t i, idx_t j, auto&&... args) { return mask_view(i,j); }; return for_each_value_masked(config, mask_wrap, std::move(fields), function); } } } else { ATLAS_THROW_EXCEPTION("More than 2 horizontal indices is not yet supported"); } } else { constexpr auto num_fields = std::tuple_size_v>; static_assert(num_fields == detail::function_traits::arity,"!"); using value_type = std::decay_t>; switch (field_1.rank()) { case 1: return detail::for_each_value_masked_rank(config,mask,std::move(fields),function); case 2: return detail::for_each_value_masked_rank(config,mask,std::move(fields),function); case 3: return detail::for_each_value_masked_rank(config,mask,std::move(fields),function); case 4: return detail::for_each_value_masked_rank(config,mask,std::move(fields),function); case 5: return detail::for_each_value_masked_rank(config,mask,std::move(fields),function); default: ATLAS_THROW_EXCEPTION("Only fields with rank <= 5 are currently supported. Given rank: " << std::get<0>(fields).rank()); } } ATLAS_THROW_EXCEPTION("Invalid function"); } template void for_each_value_masked(const eckit::Parametrisation& config, const Mask& mask, Field field, const Function& function) { return for_each_value_masked(config, mask, std::make_tuple(field), function); } template void for_each_value_masked(const eckit::Parametrisation& config, const Mask& mask, Field field_1, Field field_2, const Function& function) { return for_each_value_masked(config, mask, std::make_tuple(field_1, field_2), function); } template void for_each_value_masked(const eckit::Parametrisation& config, const Mask& mask, Field field_1, Field field_2, Field field_3, const Function& function) { return for_each_value_masked(config, mask, std::make_tuple(field_1, field_2, field_3), function); } template void for_each_value_masked(const eckit::Parametrisation& config, const Mask& mask, Field field_1, Field field_2, Field field_3, Field field_4, const Function& function) { return for_each_value_masked(config, mask, std::make_tuple(field_1, field_2, field_3, field_4), function); } //------------------------------------------------------------------------------------------------------------------------------------------------ // void for_each_value_masked( const ExecutionPolicy&& , const Mask& , std::tuple&& , const Function& ) template ()>> void for_each_value_masked(ExecutionPolicy, const Mask& mask, std::tuple&& fields, const Function& function) { return for_each_value_masked(option::execution_policy(), mask, std::move(fields), function); } template ()>> void for_each_value_masked(ExecutionPolicy execution_policy, const Mask& mask, Field field, const Function& function) { return for_each_value_masked(execution_policy, mask, std::make_tuple(field), function); } template ()>> void for_each_value_masked(ExecutionPolicy execution_policy, const Mask& mask, Field field_1, Field field_2, const Function& function) { return for_each_value_masked(execution_policy, mask, std::make_tuple(field_1, field_2), function); } template ()>> void for_each_value_masked(ExecutionPolicy execution_policy, const Mask& mask, Field field_1, Field field_2, Field field_3, const Function& function) { return for_each_value_masked(execution_policy, mask, std::make_tuple(field_1, field_2, field_3), function); } template ()>> void for_each_value_masked(ExecutionPolicy execution_policy, const Mask& mask, Field field_1, Field field_2, Field field_3, Field field_4, const Function& function) { return for_each_value_masked(execution_policy, mask, std::make_tuple(field_1, field_2, field_3, field_4), function); } //------------------------------------------------------------------------------------------------------------------------------------------------ // void for_each_value_masked( const Mask& , std::tuple&& , const Function& ) template void for_each_value_masked(const Mask& mask, std::tuple&& fields, const Function& function) { return for_each_value_masked(util::NoConfig(), mask, std::move(fields), function); } template void for_each_value_masked(const Mask& mask, Field field, const Function& function) { return for_each_value_masked(mask, std::make_tuple(field), function); } template void for_each_value_masked(const Mask& mask, Field field_1, Field field_2, const Function& function) { return for_each_value_masked(mask, std::make_tuple(field_1, field_2), function); } template void for_each_value_masked(const Mask& mask, Field field_1, Field field_2, Field field_3, const Function& function) { return for_each_value_masked(mask, std::make_tuple(field_1, field_2, field_3), function); } template void for_each_value_masked(const Mask& mask, Field field_1, Field field_2, Field field_3, Field field_4, const Function& function) { return for_each_value_masked(mask, std::make_tuple(field_1, field_2, field_3, field_4), function); } //------------------------------------------------------------------------------------------------------------------------------------------------ // void for_each_value( const eckit::Parametrisation& , std::tuple&& , const Function& ) template void for_each_value(const eckit::Parametrisation& config, std::tuple&& fields, const Function& function) { return for_each_value_masked(config, array::helpers::detail::no_mask, std::move(fields), function); } template void for_each_value(const eckit::Parametrisation& config, Field field, const Function& function) { return for_each_value_masked(config, std::make_tuple(field), function); } template void for_each_value(const eckit::Parametrisation& config, Field field_1, Field field_2, const Function& function) { return for_each_value_masked(config, std::make_tuple(field_1, field_2), function); } template void for_each_value(const eckit::Parametrisation& config, Field field_1, Field field_2, Field field_3, const Function& function) { return for_each_value_masked(config, std::make_tuple(field_1, field_2, field_3), function); } template void for_each_value(const eckit::Parametrisation& config, Field field_1, Field field_2, Field field_3, Field field_4, const Function& function) { return for_each_value_masked(config, std::make_tuple(field_1, field_2, field_3, field_4), function); } //------------------------------------------------------------------------------------------------------------------------------------------------ // void for_each_value( const ExecutionPolicy&& , std::tuple&& , const Function& ) template ()>> void for_each_value(ExecutionPolicy, std::tuple&& fields, const Function& function) { return for_each_value_masked(option::execution_policy(), array::helpers::detail::no_mask, std::move(fields), function); } template ()>> void for_each_value(ExecutionPolicy execution_policy, Field field, const Function& function) { return for_each_value(execution_policy, std::make_tuple(field), function); } template ()>> void for_each_value(ExecutionPolicy execution_policy, Field field_1, Field field_2, const Function& function) { return for_each_value(execution_policy, std::make_tuple(field_1, field_2), function); } template ()>> void for_each_value(ExecutionPolicy execution_policy, Field field_1, Field field_2, Field field_3, const Function& function) { return for_each_value(execution_policy, std::make_tuple(field_1, field_2, field_3), function); } template ()>> void for_each_value(ExecutionPolicy execution_policy, Field field_1, Field field_2, Field field_3, Field field_4, const Function& function) { return for_each_value(execution_policy, std::make_tuple(field_1, field_2, field_3, field_4), function); } //------------------------------------------------------------------------------------------------------------------------------------------------ // void for_each_value( std::tuple&& , const Function& ) template void for_each_value(std::tuple&& fields, const Function& function) { return for_each_value_masked(util::NoConfig(), array::helpers::detail::no_mask, std::move(fields), function); } template void for_each_value(Field field, const Function& function) { return for_each_value(std::make_tuple(field), function); } template void for_each_value(Field field_1, Field field_2, const Function& function) { return for_each_value(std::make_tuple(field_1, field_2), function); } template void for_each_value(Field field_1, Field field_2, Field field_3, const Function& function) { return for_each_value(std::make_tuple(field_1, field_2, field_3), function); } template void for_each_value(Field field_1, Field field_2, Field field_3, Field field_4, const Function& function) { return for_each_value(std::make_tuple(field_1, field_2, field_3, field_4), function); } //------------------------------------------------------------------------------------------------------------------------------------------------ // void for_each_column_masked( const eckit::Parametrisation& , const Mask& , std::tuple&& , const Function& ) template void for_each_column_masked(const eckit::Parametrisation& config, const Mask& mask, std::tuple&& fields, const Function& function) { if constexpr (std::is_same_v,atlas::Field>) { auto field_1 = std::get<0>(fields); auto h_dim = field_1.horizontal_dimension(); ATLAS_ASSERT( mask.datatype() == array::make_datatype() ); ATLAS_ASSERT( mask.rank() <= h_dim.size() ); if (h_dim.size() == 1) { return for_each_column_masked(config, array::make_view(mask), std::move(fields), function); } else if (h_dim.size() == 2) { if (mask.rank() == 1) { auto mask_view_1d = array::make_view(mask); auto mask_view_shape2d = array::make_shape(field_1.shape(h_dim[0]), field_1.shape(h_dim[1])); auto mask_view = array::View( mask_view_1d.data(), mask_view_shape2d ); return for_each_column_masked(config, mask_view, std::move(fields), function); } else { return for_each_column_masked(config, array::make_view(mask), std::move(fields), function); } } else { ATLAS_THROW_EXCEPTION("More than 2 horizontal indices is not yet supported"); } } else { switch (std::get<0>(fields).rank()) { case 2: return detail::for_each_column_masked_rank<2>(config, mask, std::move(fields), function); case 3: return detail::for_each_column_masked_rank<3>(config, mask, std::move(fields), function); case 4: return detail::for_each_column_masked_rank<4>(config, mask, std::move(fields), function); } } ATLAS_THROW_EXCEPTION("Invalid function"); } template void for_each_column_masked(const eckit::Parametrisation& config, const Mask& mask, Field field, const Function& function) { return for_each_column_masked(config, mask, std::make_tuple(field), function); } template void for_each_column_masked(const eckit::Parametrisation& config, const Mask& mask, Field field_1, Field field_2, const Function& function) { return for_each_column_masked(config, mask, std::make_tuple(field_1, field_2), function); } template void for_each_column_masked(const eckit::Parametrisation& config, const Mask& mask, Field field_1, Field field_2, Field field_3, const Function& function) { return for_each_column_masked(config, mask, std::make_tuple(field_1, field_2, field_3), function); } template void for_each_column_masked(const eckit::Parametrisation& config, const Mask& mask, Field field_1, Field field_2, Field field_3, Field field_4, const Function& function) { return for_each_column_masked(config, mask, std::make_tuple(field_1, field_2, field_3, field_4), function); } //------------------------------------------------------------------------------------------------------------------------------------------------ // void for_each_column_masked( const ExecutionPolicy&& , const Mask& , std::tuple&& , const Function& ) template ()>> void for_each_column_masked(ExecutionPolicy, const Mask& mask, std::tuple&& fields, const Function& function) { return for_each_column_masked(option::execution_policy(), mask, std::move(fields), function); } template ()>> void for_each_column_masked(ExecutionPolicy execution_policy, const Mask& mask, Field field, const Function& function) { return for_each_column_masked(execution_policy, mask, std::make_tuple(field), function); } template ()>> void for_each_column_masked(ExecutionPolicy execution_policy, const Mask& mask, Field field_1, Field field_2, const Function& function) { return for_each_column_masked(execution_policy, mask, std::make_tuple(field_1, field_2), function); } template ()>> void for_each_column_masked(ExecutionPolicy execution_policy, const Mask& mask, Field field_1, Field field_2, Field field_3, const Function& function) { return for_each_column_masked(execution_policy, mask, std::make_tuple(field_1, field_2, field_3), function); } template ()>> void for_each_column_masked(ExecutionPolicy execution_policy, const Mask& mask, Field field_1, Field field_2, Field field_3, Field field_4, const Function& function) { return for_each_column_masked(execution_policy, mask, std::make_tuple(field_1, field_2, field_3, field_4), function); } //------------------------------------------------------------------------------------------------------------------------------------------------ // void for_each_column_masked( const Mask& , std::tuple&& , const Function& ) template void for_each_column_masked(const Mask& mask, std::tuple&& fields, const Function& function) { return for_each_column_masked(util::NoConfig(), mask, std::move(fields), function); } template void for_each_column_masked(const Mask& mask, Field field, const Function& function) { return for_each_column_masked(mask, std::make_tuple(field), function); } template void for_each_column_masked(const Mask& mask, Field field_1, Field field_2, const Function& function) { return for_each_column_masked(mask, std::make_tuple(field_1, field_2), function); } template void for_each_column_masked(const Mask& mask, Field field_1, Field field_2, Field field_3, const Function& function) { return for_each_column_masked(mask, std::make_tuple(field_1, field_2, field_3), function); } template void for_each_column_masked(const Mask& mask, Field field_1, Field field_2, Field field_3, Field field_4, const Function& function) { return for_each_column_masked(mask, std::make_tuple(field_1, field_2, field_3, field_4), function); } //------------------------------------------------------------------------------------------------------------------------------------------------ // void for_each_column( const eckit::Parametrisation& , std::tuple&& , const Function& ) template void for_each_column(const eckit::Parametrisation& config, std::tuple&& fields, const Function& function) { return for_each_column_masked(config, array::helpers::detail::no_mask, std::move(fields), function); } template void for_each_column(const eckit::Parametrisation& config, Field field, const Function& function) { return for_each_column_masked(config, std::make_tuple(field), function); } template void for_each_column(const eckit::Parametrisation& config, Field field_1, Field field_2, const Function& function) { return for_each_column_masked(config, std::make_tuple(field_1, field_2), function); } template void for_each_column(const eckit::Parametrisation& config, Field field_1, Field field_2, Field field_3, const Function& function) { return for_each_column_masked(config, std::make_tuple(field_1, field_2, field_3), function); } template void for_each_column(const eckit::Parametrisation& config, Field field_1, Field field_2, Field field_3, Field field_4, const Function& function) { return for_each_column_masked(config, std::make_tuple(field_1, field_2, field_3, field_4), function); } //------------------------------------------------------------------------------------------------------------------------------------------------ // void for_each_column( const ExecutionPolicy&& , std::tuple&& , const Function& ) template ()>> void for_each_column(ExecutionPolicy, std::tuple&& fields, const Function& function) { return for_each_column_masked(option::execution_policy(), array::helpers::detail::no_mask, std::move(fields), function); } template ()>> void for_each_column(ExecutionPolicy execution_policy, Field field, const Function& function) { return for_each_column(execution_policy, std::make_tuple(field), function); } template ()>> void for_each_column(ExecutionPolicy execution_policy, Field field_1, Field field_2, const Function& function) { return for_each_column(execution_policy, std::make_tuple(field_1, field_2), function); } template ()>> void for_each_column(ExecutionPolicy execution_policy, Field field_1, Field field_2, Field field_3, const Function& function) { return for_each_column(execution_policy, std::make_tuple(field_1, field_2, field_3), function); } template ()>> void for_each_column(ExecutionPolicy execution_policy, Field field_1, Field field_2, Field field_3, Field field_4, const Function& function) { return for_each_column(execution_policy, std::make_tuple(field_1, field_2, field_3, field_4), function); } //------------------------------------------------------------------------------------------------------------------------------------------------ // void for_each_column( std::tuple&& , const Function& ) template void for_each_column(std::tuple&& fields, const Function& function) { return for_each_column_masked(util::NoConfig(), array::helpers::detail::no_mask, std::move(fields), function); } template void for_each_column(Field field, const Function& function) { return for_each_column(std::make_tuple(field), function); } template void for_each_column(Field field_1, Field field_2, const Function& function) { return for_each_column(std::make_tuple(field_1, field_2), function); } template void for_each_column(Field field_1, Field field_2, Field field_3, const Function& function) { return for_each_column(std::make_tuple(field_1, field_2, field_3), function); } template void for_each_column(Field field_1, Field field_2, Field field_3, Field field_4, const Function& function) { return for_each_column(std::make_tuple(field_1, field_2, field_3, field_4), function); } //------------------------------------------------------------------------------------------------------------------------------------------------ } // namespace field } // namespace atlas atlas-0.46.0/src/atlas/field/FieldCreatorIFS.h0000664000175000017500000000322515160212070021121 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ /// @author Willem Deconinck /// @date June 2015 #pragma once #include "atlas/field/FieldCreator.h" namespace eckit { class Parametrisation; } namespace atlas { namespace field { class FieldImpl; } } // namespace atlas namespace atlas { namespace field { // ------------------------------------------------------------------ /*! * \brief Field creator using IFS parametrisation * \details * Ideally this class should belong to IFS. * The only reference to IFS in Atlas should be here. * Example use: * \code{.cpp} * FieldImpl* field = Field::create( * Config * ("creator","IFS") // IFS FieldCreator * ("ngptot",ngptot) // Total number of grid points * ("nproma",nproma) // Grouping of grid points for vectorlength * ("nlev",nlev) // Number of levels * ("nvar",nvar) // Number of variables * ("kind",8) // Real kind in bytes * ); * \endcode */ class FieldCreatorIFS : public FieldCreator { public: FieldCreatorIFS() {} FieldCreatorIFS(const eckit::Parametrisation&) {} virtual FieldImpl* createField(const eckit::Parametrisation&) const; }; // ------------------------------------------------------------------ } // namespace field } // namespace atlas atlas-0.46.0/src/atlas/field/MissingValue.h0000664000175000017500000000556115160212070020627 0ustar alastairalastair/* * (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/field/detail/MissingValue.h" #include "atlas/library/config.h" #include "atlas/util/ObjectHandle.h" namespace atlas { class Field; namespace util { class Config; } } // namespace atlas namespace atlas { namespace field { /** * @brief Missing values indicator */ struct MissingValue : DOXYGEN_HIDE(public util::ObjectHandle) { using Spec = util::Config; using Config = detail::MissingValue::Config; /** * @brief ctor */ using Handle::Handle; MissingValue(); MissingValue(const Config&); MissingValue(const std::string& type, const Config&); MissingValue(const Field&); MissingValue(const std::string& type, const Field&); /** * @brief bool operator * @return if MissingValue object has been setup */ using Handle::operator bool; // (ensure this exists) /** * @brief operator() on user-defined values * @param [in] value user-defined value * @return if user-defined value indicates a missing value */ bool operator()(const double& value) const; /** * @brief operator() on user-defined values * @param [in] value user-defined value * @return if user-defined value indicates a missing value */ bool operator()(const float& value) const; /** * @brief operator() on user-defined values * @param [in] value user-defined value * @return if user-defined value indicates a missing value */ bool operator()(const int& value) const; /** * @brief operator() on user-defined values * @param [in] value user-defined value * @return if user-defined value indicates a missing value */ bool operator()(const long& value) const; /** * @brief operator() on user-defined values * @param [in] value user-defined value * @return if user-defined value indicates a missing value */ bool operator()(const unsigned long& value) const; /** * @brief if missing value is represented by not-a-number * @return if missing value is represented by not-a-number */ bool isnan() const; /** * @brief reference to internal implementation * @return reference to internal implementation */ const detail::MissingValue& ref() const; /** * @brief fill missing value metadata on Field * @param [in] field field to set */ void metadata(Field& field) const; }; } // namespace field } // namespace atlas atlas-0.46.0/src/atlas/field/MultiField.cc0000664000175000017500000000572015160212070020412 0ustar alastairalastair/* * (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/field/MultiField.h" #include #include #include #include #include #include #include "atlas/field/MultiFieldCreator.h" #include "atlas/field/detail/MultiFieldImpl.h" #include "atlas/runtime/Exception.h" namespace atlas { namespace field { //----------------------------------------------------------------------------- MultiField::MultiField(const eckit::Configuration& config) { std::string type; if (!config.get("type", type)) { ATLAS_THROW_EXCEPTION("Could not find \"type\" in configuration"); } std::unique_ptr creator(MultiFieldCreatorFactory::build(type, config)); reset(creator->create(config)); } MultiField::MultiField(const array::DataType datatype, const array::ArrayShape& shape, const std::vector& var_names) { std::unique_ptr creator(MultiFieldCreatorFactory::build("MultiFieldCreatorArray")); reset(creator->create(datatype, shape, var_names)); } const Field& MultiField::field(const std::string& name) const { return get()->field(name); } Field& MultiField::field(const std::string& name) { return get()->field(name); } bool MultiField::has(const std::string& name) const { return get()->has(name); } std::vector MultiField::field_names() const { return get()->field_names(); } const Field& MultiField::field(const idx_t idx) const { return get()->field(idx); } Field& MultiField::field(const idx_t idx) { return get()->field(idx); } idx_t MultiField::size() const { return get()->size(); } const Field& MultiField::operator[](const idx_t idx) const { return get()->field(idx); } Field& MultiField::operator[](const idx_t idx) { return get()->field(idx); } const Field& MultiField::operator[](const std::string& name) const { return get()->field(name); } Field& MultiField::operator[](const std::string& name) { return get()->field(name); } const util::Metadata& MultiField::metadata() const { return get()->metadata(); } util::Metadata& MultiField::metadata() { return get()->metadata(); } MultiField::operator const array::Array&() const { return get()->array(); } MultiField::operator array::Array&() { return get()->array(); } MultiField::operator const FieldSet&() const { return get()->fieldset_; } MultiField::operator FieldSet&() { return get()->fieldset_; } const array::Array& MultiField::array() const { return get()->array(); } array::Array& MultiField::array() { return get()->array(); } //----------------------------------------------------------------------------- } // namespace field } // namespace atlas atlas-0.46.0/src/atlas/field/Halo.cc0000664000175000017500000000261115160212070017233 0ustar alastairalastair/* * (C) Copyright 2025 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/library/config.h" #include "atlas/util/Metadata.h" #include "atlas/field/detail/FieldImpl.h" #include "atlas/functionspace/FunctionSpace.h" namespace atlas { namespace field { Halo::Halo(field::FieldImpl& f) : functionspace::HaloDescription(f.functionspace().halo_description()), field_(f) { } bool Halo::updated() const { return field_.metadata().getBool("halo_updated", false); } void Halo::updated(bool v) { field_.metadata().set("halo_updated", v); } void Halo::update() { field_.haloExchange(); } void Halo::update(const eckit::Parametrisation& config) { std::string execution_space{"host"}; config.get("execution_space", execution_space); bool on_device = (execution_space == "device"); field_.haloExchange(on_device); } bool Halo::appended() const { auto size = field_.functionspace().size(); return contiguous() && (end() == size); } // ------------------------------------------------------------------ } // namespace field } // namespace atlas atlas-0.46.0/src/atlas/field/MultiFieldCreatorIFS.cc0000664000175000017500000002407015160212070022273 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ /// @author Willem Deconinck /// @author Slavko Brdar /// @date June 2024 #include #include #include "eckit/config/Parametrisation.h" #include "atlas/array/ArrayDataStore.h" #include "atlas/array/DataType.h" #include "atlas/field/MultiFieldCreatorIFS.h" #include "atlas/field/detail/MultiFieldImpl.h" #include "atlas/grid/Grid.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Log.h" namespace atlas { namespace field { MultiFieldCreatorIFS::MultiFieldCreatorIFS() = default; MultiFieldCreatorIFS::MultiFieldCreatorIFS(const eckit::Configuration& config) {} MultiFieldCreatorIFS::~MultiFieldCreatorIFS() = default; MultiFieldImpl* MultiFieldCreatorIFS::create(const array::DataType datatype, const array::ArrayShape& shape, const std::vector& var_names) const { ATLAS_NOTIMPLEMENTED; return nullptr; } MultiFieldImpl* MultiFieldCreatorIFS::create(const eckit::Configuration& config) const { long nproma; config.get("nproma", nproma); long nlev; config.get("nlev", nlev); long nblk = 0; if (config.has("nblk")) { config.get("nblk", nblk); } else if (config.has("ngptot")) { long ngptot; config.get("ngptot", ngptot); nblk = std::ceil(static_cast(ngptot) / static_cast(nproma)); } else { ATLAS_THROW_EXCEPTION("Configuration not found: ngptot or nblk"); } array::DataType datatype = array::DataType::create(); std::string datatype_str; if (config.get("datatype", datatype_str)) { datatype = array::DataType(datatype_str); } else { array::DataType::kind_t kind(array::DataType::kind()); config.get("kind", kind); if (!array::DataType::kind_valid(kind)) { std::stringstream msg; msg << "Could not create field. kind parameter unrecognized"; throw_Exception(msg.str()); } datatype = array::DataType(kind); } auto fields = config.getSubConfigurations("fields"); long nfld = 0; for (const auto& field_params : fields) { long nvar = 1; field_params.get("nvar", nvar); nfld += nvar; } array::ArrayShape multiarray_shape = ( (nlev > 0 and nfld > 0) ? array::make_shape(nblk, nfld, nlev, nproma) : ( (nlev > 0) ? array::make_shape(nblk, nlev, nproma) : ( (nfld > 0) ? array::make_shape(nblk, nfld, nproma) : array::make_shape(nblk, nproma) ) ) ); MultiFieldImpl* multifield = new MultiFieldImpl{array::ArraySpec{datatype, multiarray_shape}}; auto& multiarray = multifield->array(); size_t multiarray_field_idx = 0; for (size_t i = 0; i < fields.size(); ++i) { std::string name; fields[i].get("name", name); Field field; size_t field_vars = 1; if (fields[i].get("nvar", field_vars)) { array::ArrayShape field_shape = ( (nlev > 0 and field_vars > 0) ? array::make_shape(multiarray.shape(0), field_vars, multiarray.shape(2), multiarray.shape(3)) : ( nlev > 0 ? array::make_shape(multiarray.shape(0), multiarray.shape(1), multiarray.shape(2)) : ( field_vars > 0 ? array::make_shape(multiarray.shape(0), field_vars, multiarray.shape(2)) : array::make_shape(multiarray.shape(0), multiarray.shape(1)) ) ) ); array::ArrayShape multiarray_shape = ( (nlev > 0 and field_vars > 0) ? array::make_shape(nblk, field_vars, nlev, nproma) : ( (nlev > 0) ? array::make_shape(nblk, nlev, nproma) : ( (field_vars > 0) ? array::make_shape(nblk, field_vars, nproma) : array::make_shape(nblk, nproma) ) ) ); auto field_strides = multiarray.strides(); auto field_array_spec = array::ArraySpec(field_shape, field_strides); constexpr auto all = array::Range::all(); const auto range = array::Range(multiarray_field_idx, multiarray_field_idx + field_vars); if (datatype.kind() == array::DataType::KIND_REAL64) { if (nlev > 0 and field_vars > 0) { auto slice = array::make_view(multiarray).slice(all, range, all, all); field = Field(name, slice.data(), field_array_spec); } else if (nlev > 0) { auto slice = array::make_view(multiarray).slice(all, all, all); field = Field(name, slice.data(), field_array_spec); } else if (field_vars > 0) { auto slice = array::make_view(multiarray).slice(all, range, all); field = Field(name, slice.data(), field_array_spec); } else { auto slice = array::make_view(multiarray).slice(all, all); field = Field(name, slice.data(), field_array_spec); } } else if (datatype.kind() == array::DataType::KIND_REAL32) { if (nlev > 0 and field_vars > 0) { auto slice = array::make_view(multiarray).slice(all, range, all, all); field = Field(name, slice.data(), field_array_spec); } else if (nlev > 0) { auto slice = array::make_view(multiarray).slice(all, all, all); field = Field(name, slice.data(), field_array_spec); } else if (field_vars > 0) { auto slice = array::make_view(multiarray).slice(all, range, all); field = Field(name, slice.data(), field_array_spec); } else { auto slice = array::make_view(multiarray).slice(all, all); field = Field(name, slice.data(), field_array_spec); } } else { ATLAS_NOTIMPLEMENTED; } field.set_variables(field_vars); } else { array::ArraySpec field_array_spec; if (nlev > 0) { auto field_shape = array::make_shape(multiarray.shape(0), multiarray.shape(2), multiarray.shape(3)); auto field_strides = array::make_strides(multiarray.stride(0), multiarray.stride(2), multiarray.stride(3)); field_array_spec = array::ArraySpec(field_shape, field_strides); } else if (field_vars > 0) { auto field_shape = array::make_shape(multiarray.shape(0), multiarray.shape(2)); auto field_strides = array::make_strides(multiarray.stride(0), multiarray.stride(2)); field_array_spec = array::ArraySpec(field_shape, field_strides); } constexpr auto all = array::Range::all(); if (datatype.kind() == array::DataType::KIND_REAL64) { if (nlev > 0 and field_vars > 0) { auto slice = array::make_view(multiarray).slice(all, multiarray_field_idx, all, all); field = Field(name, slice.data(), field_array_spec); } else if (nlev > 0) { auto slice = array::make_view(multiarray).slice(all, all, all); field = Field(name, slice.data(), field_array_spec); } else if (field_vars > 0) { auto slice = array::make_view(multiarray).slice(all, multiarray_field_idx, all); field = Field(name, slice.data(), field_array_spec); } else { auto slice = array::make_view(multiarray).slice(all, all); field = Field(name, slice.data(), field_array_spec); } } else if (datatype.kind() == array::DataType::KIND_REAL32) { if (nlev > 0 and field_vars > 0) { auto slice = array::make_view(multiarray).slice(all, multiarray_field_idx, all, all); field = Field(name, slice.data(), field_array_spec); } else if (nlev > 0) { auto slice = array::make_view(multiarray).slice(all, all, all); field = Field(name, slice.data(), field_array_spec); } else if (field_vars > 0) { auto slice = array::make_view(multiarray).slice(all, multiarray_field_idx, all); field = Field(name, slice.data(), field_array_spec); } else { auto slice = array::make_view(multiarray).slice(all, all); field = Field(name, slice.data(), field_array_spec); } } else { ATLAS_NOTIMPLEMENTED; } } field.set_levels(nlev); //field.set_blocks(nblk); multifield->add(field); multiarray_field_idx += field_vars; } std::string name; config.get("name", name); Log::debug() << "Creating IFS " << datatype.str() << " multifield: " << name << "[nblk=" << nblk << "][nvar=" << nfld << "][nlev=" << nlev << "][nproma=" << nproma << "]\n"; return multifield; } // ------------------------------------------------------------------ namespace { static MultiFieldCreatorBuilder __MultiFieldCreatorIFS("MultiFieldCreatorIFS"); } } // namespace field } // namespace atlas atlas-0.46.0/src/atlas/field/detail/0000775000175000017500000000000015160212070017303 5ustar alastairalastairatlas-0.46.0/src/atlas/field/detail/FieldInterface.cc0000664000175000017500000002662615160212070022472 0ustar alastairalastair/* * (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/library/config.h" #include "atlas/field/Field.h" #include "atlas/field/detail/FieldImpl.h" #include "atlas/field/detail/FieldInterface.h" #include "atlas/runtime/Exception.h" #if ATLAS_HAVE_FUNCTIONSPACE #include "atlas/functionspace/FunctionSpace.h" #endif namespace atlas { namespace field { // ------------------------------------------------------------------ // C wrapper interfaces to C++ routines namespace { template void atlas__Field__data_specf(FieldImpl* This, Value*& data, int& rank, int*& shapef, int*& stridesf) { ATLAS_ASSERT(This != nullptr, "Cannot access data of uninitialised atlas_Field"); if (This->datatype() != array::make_datatype()) { throw_Exception("Datatype mismatch for accessing field data"); } data = This->data(); shapef = const_cast(This->shapef().data()); stridesf = const_cast(This->stridesf().data()); rank = This->shapef().size(); } template void atlas__Field__device_data_specf(FieldImpl* This, Value*& data, int& rank, int*& shapef, int*& stridesf) { ATLAS_ASSERT(This != nullptr, "Cannot access data of uninitialised atlas_Field"); if (This->datatype() != array::make_datatype()) { throw_Exception("Datatype mismatch for accessing field data"); } data = This->array().device_data(); shapef = const_cast(This->shapef().data()); if (data == This->array().host_data()) { stridesf = const_cast(This->stridesf().data()); } else { stridesf = const_cast(This->device_stridesf().data()); } rank = This->shapef().size(); } template FieldImpl* atlas__Field__wrap_specf(const char* name, Value data[], int rank, int shapef[], int stridesf[]) { array::ArrayShape shape; shape.resize(rank); array::ArrayStrides strides; strides.resize(rank); idx_t jf = rank - 1; for (int j = 0; j < rank; ++j) { shape[j] = shapef[jf]; strides[j] = stridesf[jf]; --jf; } FieldImpl* field; { Field wrapped(std::string(name), data, array::ArraySpec(shape, strides)); field = wrapped.get(); field->attach(); } field->detach(); ATLAS_ASSERT(field); return field; } } // namespace extern "C" { FieldImpl* atlas__Field__wrap_int_specf(const char* name, int data[], int rank, int shapef[], int stridesf[]) { return atlas__Field__wrap_specf(name, data, rank, shapef, stridesf); } FieldImpl* atlas__Field__wrap_long_specf(const char* name, long data[], int rank, int shapef[], int stridesf[]) { return atlas__Field__wrap_specf(name, data, rank, shapef, stridesf); } FieldImpl* atlas__Field__wrap_float_specf(const char* name, float data[], int rank, int shapef[], int stridesf[]) { return atlas__Field__wrap_specf(name, data, rank, shapef, stridesf); } FieldImpl* atlas__Field__wrap_double_specf(const char* name, double data[], int rank, int shapef[], int stridesf[]) { return atlas__Field__wrap_specf(name, data, rank, shapef, stridesf); } FieldImpl* atlas__Field__create(eckit::Parametrisation* params) { ATLAS_ASSERT(params != nullptr); FieldImpl* field; { Field f(*params); field = f.get(); field->attach(); } field->detach(); ATLAS_ASSERT(field); return field; } void atlas__Field__delete(FieldImpl* This) { delete This; } const char* atlas__Field__name(FieldImpl* This) { ATLAS_ASSERT(This != nullptr, "Cannot access name of uninitialised atlas_Field"); return This->name().c_str(); } void atlas__Field__datatype(FieldImpl* This, char*& datatype, int& size, int& allocated) { ATLAS_ASSERT(This != nullptr, "Cannot access datatype of uninitialised atlas_Field"); std::string s = This->datatype().str(); size = static_cast(s.size()); datatype = new char[size + 1]; std::strncpy(datatype, s.c_str(), size + 1); allocated = true; } int atlas__Field__size(FieldImpl* This) { ATLAS_ASSERT(This != nullptr, "Cannot access size of uninitialised atlas_Field"); return This->size(); } int atlas__Field__rank(FieldImpl* This) { ATLAS_ASSERT(This != nullptr, "Cannot access rank of uninitialised atlas_Field"); return This->rank(); } int atlas__Field__kind(FieldImpl* This) { ATLAS_ASSERT(This != nullptr, "Cannot access data kind of uninitialised atlas_Field"); return This->datatype().kind(); } double atlas__Field__bytes(FieldImpl* This) { ATLAS_ASSERT(This != nullptr, "Cannot access bytes occupied by uninitialised atlas_Field"); return This->bytes(); } int atlas__Field__levels(FieldImpl* This) { ATLAS_ASSERT(This != nullptr, "Cannot access levels of uninitialised atlas_Field"); return This->levels(); } util::Metadata* atlas__Field__metadata(FieldImpl* This) { ATLAS_ASSERT(This != nullptr, "Cannot access metadata of uninitialised atlas_Field"); return &This->metadata(); } int atlas__Field__has_functionspace(FieldImpl* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Field"); #if ATLAS_HAVE_FUNCTIONSPACE return (This->functionspace() != 0); #else return 0; #endif } const functionspace::FunctionSpaceImpl* atlas__Field__functionspace(FieldImpl* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Field"); #if ATLAS_HAVE_FUNCTIONSPACE return This->functionspace().get(); #else throw_Exception("Atlas is not compiled with FunctionSpace support", Here()); #endif } void atlas__Field__shapef(FieldImpl* This, int*& shape, int& rank) { ATLAS_ASSERT(This != nullptr, "Cannot access bytes occupied by uninitialised atlas_Field"); shape = const_cast(&This->shapef().front()); rank = This->shapef().size(); } void atlas__Field__stridesf(FieldImpl* This, int*& shape, int& rank) { ATLAS_ASSERT(This != nullptr, "Cannot access bytes occupied by uninitialised atlas_Field"); shape = const_cast(&This->stridesf().front()); rank = This->stridesf().size(); } void atlas__Field__data_int_specf(FieldImpl* This, int*& data, int& rank, int*& shapef, int*& stridesf) { atlas__Field__data_specf(This, data, rank, shapef, stridesf); } void atlas__Field__data_long_specf(FieldImpl* This, long*& data, int& rank, int*& shapef, int*& stridesf) { atlas__Field__data_specf(This, data, rank, shapef, stridesf); } void atlas__Field__data_float_specf(FieldImpl* This, float*& data, int& rank, int*& shapef, int*& stridesf) { atlas__Field__data_specf(This, data, rank, shapef, stridesf); } void atlas__Field__data_double_specf(FieldImpl* This, double*& data, int& rank, int*& shapef, int*& stridesf) { atlas__Field__data_specf(This, data, rank, shapef, stridesf); } void atlas__Field__device_data_int_specf(FieldImpl* This, int*& data, int& rank, int*& shapef, int*& stridesf) { atlas__Field__device_data_specf(This, data, rank, shapef, stridesf); } void atlas__Field__device_data_long_specf(FieldImpl* This, long*& data, int& rank, int*& shapef, int*& stridesf) { atlas__Field__device_data_specf(This, data, rank, shapef, stridesf); } void atlas__Field__device_data_float_specf(FieldImpl* This, float*& data, int& rank, int*& shapef, int*& stridesf) { atlas__Field__device_data_specf(This, data, rank, shapef, stridesf); } void atlas__Field__device_data_double_specf(FieldImpl* This, double*& data, int& rank, int*& shapef, int*& stridesf) { atlas__Field__device_data_specf(This, data, rank, shapef, stridesf); } int atlas__Field__host_needs_update(const FieldImpl* This) { return This->hostNeedsUpdate(); } int atlas__Field__device_needs_update(const FieldImpl* This) { return This->deviceNeedsUpdate(); } int atlas__Field__device_allocated(const FieldImpl* This) { return This->deviceAllocated(); } void atlas__Field__rename(FieldImpl* This, const char* name) { ATLAS_ASSERT(This, "Cannot rename uninitialised atlas_Field"); This->rename(std::string(name)); } void atlas__Field__set_levels(FieldImpl* This, int levels) { ATLAS_ASSERT(This != nullptr, "Cannot set levels of uninitialised atlas_Field"); This->set_levels(levels); } # void atlas__Field__set_functionspace(FieldImpl* This, const functionspace::FunctionSpaceImpl* functionspace) { ATLAS_ASSERT(This != nullptr, "Cannot set functionspace in uninitialised atlas_Field"); ATLAS_ASSERT(functionspace != nullptr, "Cannot set uninitialised atlas_FunctionSpace in atlas_Field"); #if ATLAS_HAVE_FUNCTIONSPACE This->set_functionspace(functionspace); #else throw_Exception("Atlas is not compiled with FunctionSpace support", Here()); #endif } void atlas__Field__set_host_needs_update(const FieldImpl* This, int value) { ATLAS_ASSERT(This != nullptr, "Cannot set value for uninitialised atlas_Field"); This->setHostNeedsUpdate(value); } void atlas__Field__set_device_needs_update(const FieldImpl* This, int value) { ATLAS_ASSERT(This != nullptr, "Cannot set value for uninitialised atlas_Field"); This->setDeviceNeedsUpdate(value); } void atlas__Field__update_device(FieldImpl* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Field"); This->updateDevice(); } void atlas__Field__update_host(FieldImpl* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Field"); This->updateHost(); } void atlas__Field__sync_device(FieldImpl* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Field"); This->syncDevice(); } void atlas__Field__sync_host(FieldImpl* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Field"); This->syncHost(); } void atlas__Field__sync_host_device(FieldImpl* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Field"); This->syncHostDevice(); } void atlas__Field__allocate_device(FieldImpl* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Field"); This->allocateDevice(); } void atlas__Field__deallocate_device(FieldImpl* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Field"); This->deallocateDevice(); } void atlas__Field__set_dirty(FieldImpl* This, int value) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Field"); This->set_dirty(value); } int atlas__Field__dirty(FieldImpl* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Field"); return This->dirty(); } int atlas__Field__contiguous(FieldImpl* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Field"); return This->array().contiguous(); } void atlas__Field__halo_exchange(FieldImpl* This, int on_device) { ATLAS_ASSERT(This != nullptr, "Cannot halo-exchange uninitialised atlas_Field"); This->haloExchange(on_device); } void atlas__Field__adjoint_halo_exchange(FieldImpl* This, int on_device) { ATLAS_ASSERT(This != nullptr, "Cannot adjoint-halo-exchange uninitialised atlas_Field"); This->adjointHaloExchange(on_device); } } // ------------------------------------------------------------------ } // namespace field } // namespace atlas atlas-0.46.0/src/atlas/field/detail/MultiFieldImpl.cc0000664000175000017500000000171015160212070022471 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ // file deepcode ignore CppMemoryLeak: static pointers for global registry are OK and will be cleaned up at end #include "atlas/field/MultiField.h" #include "atlas/field/MultiFieldCreator.h" #include #include #include #include "atlas/field/detail/MultiFieldImpl.h" namespace atlas { namespace field { void MultiFieldImpl::add(Field& field) { ATLAS_ASSERT(not fieldset_.has(field.name()), "Field with name \"" + field.name() + "\" already exists!"); fieldset_.add(field); MultiFieldArrayRegistry::instance().add(field, array_); } } } atlas-0.46.0/src/atlas/field/detail/MultiFieldImpl.h0000664000175000017500000000546015160212070022341 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ /// @author Willem Deconinck /// @date June 2015 #pragma once #include #include #include "atlas/array/Array.h" #include "atlas/field/Field.h" #include "atlas/field/FieldSet.h" #include "atlas/util/Config.h" #include "atlas/util/Metadata.h" #include "atlas/util/Object.h" namespace atlas { namespace field { class MultiFieldImpl : public util::Object { public: // methods //-- Constructors MultiFieldImpl() {} MultiFieldImpl(const array::ArraySpec& spec) { array::ArraySpec s(spec); array_.reset(array::Array::create(std::move(s))); } virtual ~MultiFieldImpl() {} //-- Accessors const Field& field(const std::string& name) const { return fieldset_.field(name); } Field& field(const std::string& name) { return fieldset_.field(name); } bool has(const std::string& name) const { return fieldset_.has(name); } std::vector field_names() const { return fieldset_.field_names(); } const Field& field(const idx_t idx) const { return fieldset_[idx]; } Field& field(const idx_t idx) { return fieldset_[idx]; } idx_t size() const { return fieldset_.size(); } const Field& operator[](const idx_t idx) const { return fieldset_[idx]; } Field& operator[](const idx_t idx) { return fieldset_[idx]; } const Field& operator[](const std::string& name) const { return fieldset_.field(name); } Field& operator[](const std::string& name) { return fieldset_.field(name); } const util::Metadata& metadata() const { return metadata_; } util::Metadata& metadata() { return metadata_; } // -- Modifiers /// @brief Implicit conversion to Array operator const array::Array&() const { return array(); } operator array::Array&() { return array(); } operator const FieldSet&() const { return fieldset_; } operator FieldSet&() { return fieldset_; } /// @brief Access contained Array const array::Array& array() const { ATLAS_ASSERT(array_); return *array_; } array::Array& array() { ATLAS_ASSERT(array_); return *array_; } /// @brief Access contained FieldSet const FieldSet& fieldset() const { return fieldset_; } FieldSet& fieldset() { return fieldset_; } void add(Field& field); public: // temporary public for prototyping FieldSet fieldset_; std::shared_ptr array_; util::Metadata metadata_; }; } // namespace field } // namespace atlas atlas-0.46.0/src/atlas/field/detail/MultiFieldInterface.h0000664000175000017500000000256115160212070023337 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ /// @file MultiFieldInterface.h /// @author Willem Deconinck /// @date Sep 2014 #pragma once #include "atlas/field/FieldSet.h" #include "atlas/field/MultiField.h" namespace atlas { namespace functionspace { } } // namespace atlas namespace atlas { namespace field { //---------------------------------------------------------------------------------------------------------------------- // C wrapper interfaces to C++ routines extern "C" { MultiFieldImpl* atlas__MultiField__create(eckit::Configuration* config); MultiFieldImpl* atlas__MultiField__create_shape(int kind, int rank, int shapef[], const char* var_names, size_t length, size_t size); void atlas__MultiField__delete(MultiFieldImpl* This); int atlas__MultiField__size(MultiFieldImpl* This); FieldSetImpl* atlas__MultiField__fieldset(MultiFieldImpl* This); } //---------------------------------------------------------------------------------------------------------------------- } // namespace field } // namespace atlas atlas-0.46.0/src/atlas/field/detail/for_each.h0000664000175000017500000002640015160212070021224 0ustar alastairalastair/* * (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 #include "atlas/field/Field.h" #include "atlas/array/helpers/ArrayForEach.h" #include "atlas/runtime/Exception.h" namespace atlas { namespace field { namespace detail { template // primary template struct function_traits : public function_traits::type::operator())> { }; template struct function_traits : function_traits { }; template struct function_traits : function_traits { }; template struct function_traits { using result_type = ReturnType; template using arg_t = typename std::tuple_element< Index, std::tuple >::type; static constexpr std::size_t arity = sizeof...(Arguments); }; template using first_argument = typename function_traits::template arg_t<0>; template constexpr bool valid_value_function() { return std::is_same_v>,std::decay_t>; } template using function_argument_data_type = typename std::decay_t>::value_type>; template constexpr idx_t function_argument_rank() { return std::decay_t>::rank(); } template constexpr bool valid_column_function() { using value_type = std::decay_t; return function_argument_rank() == Rank && std::is_same_v,value_type>; } template void for_each_value_masked_view(std::index_sequence, const Config& config, const Mask& mask, const Function& function, std::tuple&& views ) { if constexpr (std::is_invocable_r_v) { array::helpers::ArrayForEach::apply(config,std::move(views),mask,function); } else { ATLAS_THROW_EXCEPTION("Invalid mask function passed"); } } template auto make_view_tuple(std::tuple&& fields) { constexpr auto num_fields = std::tuple_size_v>; if constexpr (FieldIdx < num_fields) { return std::tuple_cat(std::make_tuple( array::make_view(std::get(fields))), make_view_tuple(std::move(fields))); } else { return std::make_tuple(); } ATLAS_UNREACHABLE(); } template void for_each_value_masked_rank(const Config& config, const Mask& mask, std::tuple&& fields, const Function& function ) { constexpr auto dims = std::make_index_sequence(); if constexpr (valid_value_function()) { return for_each_value_masked_view(dims, config, mask, function, make_view_tuple<0, Value, Rank>(std::move(fields))); } } template void for_each_column_masked_view(const Config& config, const Mask& mask, const std::vector& h_dim, const Function& function, std::tuple&& views ) { using View = std::decay_t>>; using value_type = typename View::value_type; constexpr int rank = View::rank(); if (h_dim.size()>=rank) { ATLAS_THROW_EXCEPTION("Cannot extract column for Rank="<= rank) { ATLAS_THROW_EXCEPTION("Invalid horizontal_dimension " << h_dim[0] << " must less rank " << rank); } } auto has_duplicates = [](const auto &v) { for (const auto& i : v) { for (const auto & j : v) { if (&i == &j) break; if (i == j) return true; } } return false; }; if (has_duplicates(h_dim)) { ATLAS_THROW_EXCEPTION("horizontal_dimension contains duplicates"); } if constexpr (rank==1) { ATLAS_THROW_EXCEPTION("Cannot use for_each_column with Rank=1 fields"); } if constexpr (rank==2) { if constexpr (valid_column_function() && std::is_invocable_r_v) { switch (h_dim[0]) { case 0: array::helpers::ArrayForEach<0>::apply(config,std::move(views),mask,function); return; case 1: array::helpers::ArrayForEach<1>::apply(config,std::move(views),mask,function); return; default: break; } ATLAS_THROW_EXCEPTION("Not implemented for horizontal_dimension = " << h_dim); } ATLAS_THROW_EXCEPTION("Invalid function passed"); } if constexpr (rank==3) { if (h_dim.size() == 1) { if constexpr (valid_column_function() && std::is_invocable_r_v) { switch (h_dim[0]) { case 0: array::helpers::ArrayForEach<0>::apply(config,std::move(views),mask,function); return; case 1: array::helpers::ArrayForEach<1>::apply(config,std::move(views),mask,function); return; case 2: array::helpers::ArrayForEach<2>::apply(config,std::move(views),mask,function); return; default: break; } ATLAS_THROW_EXCEPTION("Not implemented for horizontal_dimension = " << h_dim); } ATLAS_THROW_EXCEPTION("Invalid function passed"); } else if (h_dim.size() == 2) { if constexpr (valid_column_function() && std::is_invocable_r_v) { switch (h_dim[0]) { case 0: { switch (h_dim[1]) { case 1: array::helpers::ArrayForEach<0,1>::apply(config,std::move(views),mask,function); return; case 2: array::helpers::ArrayForEach<0,2>::apply(config,std::move(views),mask,function); return; default: break; } break; } case 1: { switch (h_dim[1]) { case 2: array::helpers::ArrayForEach<1,2>::apply(config,std::move(views),mask,function); return; default: break; } break; } } ATLAS_THROW_EXCEPTION("Not implemented for horizontal_dimension = " << h_dim); } ATLAS_THROW_EXCEPTION("Invalid function passed"); } ATLAS_THROW_EXCEPTION("Not implemented for h_dim = " << h_dim); } if constexpr (rank==4) { if (h_dim.size() == 1) { if constexpr (valid_column_function() && std::is_invocable_r_v) { switch (h_dim[0]) { case 0: array::helpers::ArrayForEach<0>::apply(config,std::move(views),mask,function); return; case 1: array::helpers::ArrayForEach<1>::apply(config,std::move(views),mask,function); return; case 2: array::helpers::ArrayForEach<2>::apply(config,std::move(views),mask,function); return; case 3: array::helpers::ArrayForEach<3>::apply(config,std::move(views),mask,function); return; default: break; } } } else if (h_dim.size() == 2) { if constexpr (valid_column_function() && std::is_invocable_r_v) { switch (h_dim[0]) { case 0: { switch (h_dim[1]) { case 1: array::helpers::ArrayForEach<0,1>::apply(config,std::move(views),mask,function); return; case 2: array::helpers::ArrayForEach<0,2>::apply(config,std::move(views),mask,function); return; case 3: array::helpers::ArrayForEach<0,3>::apply(config,std::move(views),mask,function); return; default: break; } break; } case 1: { switch (h_dim[1]) { case 2: array::helpers::ArrayForEach<1,2>::apply(config,std::move(views),mask,function); return; case 3: array::helpers::ArrayForEach<1,3>::apply(config,std::move(views),mask,function); return; default: break; } break; } case 2: { switch (h_dim[1]) { case 3: array::helpers::ArrayForEach<2,3>::apply(config,std::move(views),mask,function); return; default: break; } break; } default: break; } ATLAS_THROW_EXCEPTION("Not implemented for horizontal_dimension = " << h_dim); } ATLAS_THROW_EXCEPTION("Invalid function passed"); } ATLAS_THROW_EXCEPTION("Not implemented for h_dim = " << h_dim); } ATLAS_THROW_EXCEPTION("Not implemented for rank="< void for_each_column_masked_rank(const eckit::Parametrisation& config, const Mask& mask, std::tuple&& fields, const Function& function) { constexpr auto num_fields = std::tuple_size_v>; ATLAS_ASSERT(num_fields == detail::function_traits::arity); using value_type = detail::function_argument_data_type; ATLAS_ASSERT( std::get<0>(fields).datatype() == array::make_datatype() ); auto h_dim = std::get<0>(fields).horizontal_dimension(); ATLAS_ASSERT(Rank == detail::function_argument_rank() + h_dim.size()); return for_each_column_masked_view(config, mask, h_dim, function, make_view_tuple<0, value_type, Rank>(std::move(fields))); } } // namespace detail } // namespace field } // namespace atlas atlas-0.46.0/src/atlas/field/detail/MissingValue.h0000664000175000017500000000413615160212070022066 0ustar alastairalastair/* * (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 "eckit/config/Parametrisation.h" #include "atlas/runtime/Exception.h" #include "atlas/util/Factory.h" #include "atlas/util/Object.h" namespace atlas { class Field; } namespace atlas { namespace field { namespace detail { /** * @brief Missing values indicator base class */ struct MissingValue : util::Object { using Config = eckit::Parametrisation; virtual ~MissingValue() = default; virtual bool isnan() const = 0; virtual void metadata(Field&) const = 0; virtual bool operator()(const double&) const { ATLAS_NOTIMPLEMENTED; } virtual bool operator()(const float&) const { ATLAS_NOTIMPLEMENTED; } virtual bool operator()(const int&) const { ATLAS_NOTIMPLEMENTED; } virtual bool operator()(const long&) const { ATLAS_NOTIMPLEMENTED; } virtual bool operator()(const unsigned long&) const { ATLAS_NOTIMPLEMENTED; } }; /** * @brief Missing values indicator factory */ struct MissingValueFactory : util::Factory { using Config = MissingValue::Config; using Factory::Factory; static std::string className() { return "MissingValueFactory"; } static const MissingValue* build(const std::string&, const Config&); private: virtual const MissingValue* make(const Config&) = 0; }; /** * @brief Missing values indicator builder for factory registration */ template class MissingValueFactoryBuilder : public MissingValueFactory { private: virtual const MissingValue* make(const Config& config) override { return new T(config); } public: using MissingValueFactory::MissingValueFactory; }; } // namespace detail } // namespace field } // namespace atlas atlas-0.46.0/src/atlas/field/detail/FieldImpl.cc0000664000175000017500000001536615160212070021472 0ustar alastairalastair/* * (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 "atlas/library/config.h" #include "atlas/array/MakeView.h" #include "atlas/field/FieldCreator.h" #include "atlas/field/detail/FieldImpl.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Log.h" #if ATLAS_HAVE_FUNCTIONSPACE #include "atlas/functionspace/FunctionSpace.h" #endif namespace atlas { namespace field { // ------------------------------------------------------------------------- // Static functions FieldImpl* FieldImpl::create(const eckit::Parametrisation& params) { std::string creator_factory; if (params.get("creator", creator_factory)) { std::unique_ptr creator(field::FieldCreatorFactory::build(creator_factory, params)); return creator->createField(params); } else { throw_Exception( "Could not find parameter 'creator' " "in Parametrisation for call to FieldImpl::create()"); } } FieldImpl* FieldImpl::create(const std::string& name, array::DataType datatype, const array::ArrayShape& shape) { return new FieldImpl(name, datatype, shape); } FieldImpl* FieldImpl::create(const std::string& name, array::DataType datatype, array::ArraySpec&& spec) { return new FieldImpl(name, datatype, std::move(spec)); } FieldImpl* FieldImpl::create(const std::string& name, array::Array* array) { return new FieldImpl(name, array); } // ------------------------------------------------------------------------- FieldImpl::FieldImpl(const std::string& name, array::DataType datatype, const array::ArrayShape& shape) #if ATLAS_HAVE_FUNCTIONSPACE :functionspace_(new FunctionSpace()) #endif { array::label label(name); array_ = array::Array::create(datatype, shape); array_->attach(); rename(name); set_levels(0); set_variables(0); } FieldImpl::FieldImpl(const std::string& name, array::DataType datatype, array::ArraySpec&& spec) #if ATLAS_HAVE_FUNCTIONSPACE :functionspace_(new FunctionSpace()) #endif { array::label label(name); array_ = array::Array::create(datatype, std::move(spec)); array_->attach(); rename(name); set_levels(0); set_variables(0); } FieldImpl::FieldImpl(const std::string& name, array::Array* array) #if ATLAS_HAVE_FUNCTIONSPACE :functionspace_(new FunctionSpace()) #endif { array_ = array; array_->attach(); rename(name); set_levels(0); set_variables(0); } FieldImpl::~FieldImpl() { for (FieldObserver* observer : field_observers_) { observer->onFieldDestruction(*this); } array_->detach(); if (array_->owners() == 0) { for (auto& f : callback_on_destruction_) { f(); } delete array_; } #if ATLAS_HAVE_FUNCTIONSPACE delete functionspace_; #endif } size_t FieldImpl::footprint() const { size_t size = sizeof(*this); #if ATLAS_HAVE_FUNCTIONSPACE size += functionspace_->footprint(); #endif size += array_->footprint(); size += metadata_.footprint(); size += name_.capacity() * sizeof(std::string::value_type); return size; } bool FieldImpl::dirty() const { return not metadata().getBool("halo_updated", false); } void FieldImpl::set_dirty(bool value) const { const_cast(*this).metadata().set("halo_updated", !value); } void FieldImpl::dump(std::ostream& os) const { print(os, true); } Halo& FieldImpl::halo() const { if (not halo_) { halo_.reset(new Halo(*const_cast(this))); } return *halo_; } namespace { template std::string vector_to_str(const std::vector& t) { std::stringstream s; s << '['; for (size_t i = 0; i < t.size(); i++) { if (i != 0) { s << ','; } s << t[i]; } s << ']'; return s.str(); } } // namespace void FieldImpl::rename(const std::string& name) { metadata().set("name", name); for (FieldObserver* observer : field_observers_) { observer->onFieldRename(*this); } } const std::string& FieldImpl::name() const { name_ = metadata().get("name"); return name_; } void FieldImpl::print(std::ostream& os, bool dump) const { os << "FieldImpl[name=" << name() << ",datatype=" << datatype().str() << ",size=" << size() << ",shape=" << vector_to_str(shape()) << ",strides=" << vector_to_str(strides()) #if !ATLAS_HAVE_GRIDTOOLS_STORAGE << ",bytes=" << bytes() #endif << ",metadata=" << metadata(); if (dump) { os << ",array=["; array_->dump(os); os << "]"; } os << "]"; } std::ostream& operator<<(std::ostream& os, const FieldImpl& f) { f.print(os); return os; } void FieldImpl::resize(const array::ArrayShape& shape) { array_->resize(shape); } void FieldImpl::insert(idx_t idx1, idx_t size1) { array_->insert(idx1, size1); } void FieldImpl::set_functionspace(const FunctionSpace& functionspace) { #if ATLAS_HAVE_FUNCTIONSPACE *functionspace_ = functionspace; #else throw_Exception("Atlas is compiled without FunctionSpace support", Here()); #endif } const FunctionSpace& FieldImpl::functionspace() const { #if ATLAS_HAVE_FUNCTIONSPACE return *functionspace_; #else throw_Exception("Atlas is compiled without FunctionSpace support", Here()); #endif } void FieldImpl::haloExchange(bool on_device) const { if (dirty()) { #if ATLAS_HAVE_FUNCTIONSPACE ATLAS_ASSERT(functionspace()); functionspace().haloExchange(Field(this), on_device); set_dirty(false); #else throw_Exception("Atlas is compiled without FunctionSpace support", Here()); #endif } } void FieldImpl::adjointHaloExchange(bool on_device) const { #if ATLAS_HAVE_FUNCTIONSPACE set_dirty(); ATLAS_ASSERT(functionspace()); functionspace().adjointHaloExchange(Field(this), on_device); #else throw_Exception("Atlas is compiled without FunctionSpace support", Here()); #endif } void FieldImpl::attachObserver(FieldObserver& observer) const { if (std::find(field_observers_.begin(), field_observers_.end(), &observer) == field_observers_.end()) { field_observers_.push_back(&observer); } } void FieldImpl::detachObserver(FieldObserver& observer) const { field_observers_.erase(std::remove(field_observers_.begin(), field_observers_.end(), &observer), field_observers_.end()); } // ------------------------------------------------------------------ } // namespace field } // namespace atlas atlas-0.46.0/src/atlas/field/detail/FieldInterface.h0000664000175000017500000001117215160212070022322 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ /// @file FieldInterface.h /// @author Willem Deconinck /// @date Sep 2014 #pragma once #include "atlas/field/detail/FieldImpl.h" namespace atlas { namespace functionspace { class FunctionSpaceImpl; } } // namespace atlas namespace atlas { namespace field { //---------------------------------------------------------------------------------------------------------------------- // C wrapper interfaces to C++ routines extern "C" { FieldImpl* atlas__Field__wrap_int_specf(const char* name, int data[], int rank, int shapef[], int stridesf[]); FieldImpl* atlas__Field__wrap_long_specf(const char* name, long data[], int rank, int shapef[], int stridesf[]); FieldImpl* atlas__Field__wrap_float_specf(const char* name, float data[], int rank, int shapef[], int stridesf[]); FieldImpl* atlas__Field__wrap_double_specf(const char* name, double data[], int rank, int shapef[], int stridesf[]); FieldImpl* atlas__Field__create(eckit::Parametrisation* params); void atlas__Field__delete(FieldImpl* This); const char* atlas__Field__name(FieldImpl* This); void atlas__Field__datatype(FieldImpl* This, char*& datatype, int& size, int& allocated); int atlas__Field__kind(FieldImpl* This); int atlas__Field__rank(FieldImpl* This); int atlas__Field__size(FieldImpl* This); int atlas__Field__levels(FieldImpl* This); double atlas__Field__bytes(FieldImpl* This); void atlas__Field__shapef(FieldImpl* This, int*& shape, int& rank); void atlas__Field__stridesf(FieldImpl* This, int*& strides, int& rank); void atlas__Field__data_int_specf(FieldImpl* This, int*& field_data, int& rank, int*& field_shapef, int*& field_stridesf); void atlas__Field__data_long_specf(FieldImpl* This, long*& field_data, int& rank, int*& field_shapef, int*& field_stridesf); void atlas__Field__data_float_specf(FieldImpl* This, float*& field_data, int& rank, int*& field_shapef, int*& field_stridesf); void atlas__Field__data_double_specf(FieldImpl* This, double*& field_data, int& rank, int*& field_shapef, int*& field_stridesf); void atlas__Field__device_data_int_specf(FieldImpl* This, int*& field_data, int& rank, int*& field_shapef, int*& field_stridesf); void atlas__Field__device_data_long_specf(FieldImpl* This, long*& field_data, int& rank, int*& field_shapef, int*& field_stridesf); void atlas__Field__device_data_float_specf(FieldImpl* This, float*& field_data, int& rank, int*& field_shapef, int*& field_stridesf); void atlas__Field__device_data_double_specf(FieldImpl* This, double*& field_data, int& rank, int*& field_shapef, int*& field_stridesf); util::Metadata* atlas__Field__metadata(FieldImpl* This); const functionspace::FunctionSpaceImpl* atlas__Field__functionspace(FieldImpl* This); void atlas__Field__rename(FieldImpl* This, const char* name); void atlas__Field__set_levels(FieldImpl* This, int levels); void atlas__Field__set_functionspace(FieldImpl* This, const functionspace::FunctionSpaceImpl* functionspace); int atlas__Field__host_needs_update(const FieldImpl* This); int atlas__Field__device_needs_update(const FieldImpl* This); int atlas__Field__device_allocated(const FieldImpl* This); void atlas__Field__set_host_needs_update(const FieldImpl* This, int value); void atlas__Field__set_device_needs_update(const FieldImpl* This, int value); void atlas__Field__update_device(FieldImpl* This); void atlas__Field__update_host(FieldImpl* This); void atlas__Field__sync_device(FieldImpl* This); void atlas__Field__sync_host(FieldImpl* This); void atlas__Field__sync_host_device(FieldImpl* This); void atlas__Field__allocate_device(FieldImpl* This); void atlas__Field__deallocate_device(FieldImpl* This); void atlas__Field__set_dirty(FieldImpl* This, int value); void atlas__Field__halo_exchange(FieldImpl* This, int on_device); void atlas__Field__adjoint_halo_exchange(FieldImpl* This, int on_device); int atlas__Field__dirty(FieldImpl* This); int atlas__Field__contiguous(FieldImpl* This); } //---------------------------------------------------------------------------------------------------------------------- } // namespace field } // namespace atlas atlas-0.46.0/src/atlas/field/detail/MissingValue.cc0000664000175000017500000001201415160212070022216 0ustar alastairalastair/* * (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 "atlas/field/detail/MissingValue.h" #include #include #include "eckit/types/FloatCompare.h" #include "atlas/array/DataType.h" #include "atlas/field/Field.h" #include "atlas/util/Metadata.h" namespace atlas { namespace field { namespace detail { namespace { static const std::string type_key = "missing_value_type"; static const std::string value_key = "missing_value"; static const std::string epsilon_key = "missing_value_epsilon"; template T config_value(const MissingValue::Config& c) { T value; ATLAS_ASSERT(c.get(value_key, value)); return value; } template T config_epsilon(const MissingValue::Config& c) { T value = 0.; c.get(epsilon_key, value); return value; } } // namespace /** * @brief Missing value if NaN */ template struct MissingValueNaN : MissingValue { MissingValueNaN(const Config&) { ATLAS_ASSERT(std::is_floating_point::value); } using MissingValue::operator(); bool operator()(const T& value) const override { return std::isnan(value); } bool isnan() const override { return true; } void metadata(Field& field) const override { field.metadata().set(type_key, static_type()); } static std::string static_type() { return "nan"; } }; /** * @brief Missing value if comparing equally to pre-defined value */ template struct MissingValueEquals : MissingValue { MissingValueEquals(const Config& config): MissingValueEquals(config_value(config)) {} MissingValueEquals(T missingValue): missingValue_(missingValue), missingValue2_(missingValue_) { ATLAS_ASSERT(missingValue_ == missingValue2_); // FIXME this succeeds ATLAS_ASSERT(!std::isnan(missingValue2_)); } using MissingValue::operator(); bool operator()(const T& value) const override { // ATLAS_ASSERT(missingValue_ == missingValue2_); // FIXME this fails (copy ellision problem on POD!?) return value == missingValue2_; } bool isnan() const override { return false; } void metadata(Field& field) const override { field.metadata().set(type_key, static_type()); field.metadata().set(value_key, missingValue2_); } static std::string static_type() { return "equals"; } const T missingValue_; const T missingValue2_; }; /** * @brief Missing value if comparing approximately to pre-defined value */ template struct MissingValueApprox : MissingValue { MissingValueApprox(const Config& config): MissingValueApprox(config_value(config), config_epsilon(config)) {} MissingValueApprox(T missingValue, T epsilon): missingValue_(missingValue), epsilon_(epsilon) { ATLAS_ASSERT(!std::isnan(missingValue_)); ATLAS_ASSERT(std::is_floating_point::value); ATLAS_ASSERT(epsilon_ >= 0.); } using MissingValue::operator(); bool operator()(const T& value) const override { return eckit::types::is_approximately_equal(value, missingValue_, epsilon_); } bool isnan() const override { return false; } void metadata(Field& field) const override { field.metadata().set(type_key, static_type()); field.metadata().set(value_key, missingValue_); field.metadata().set(epsilon_key, epsilon_); } static std::string static_type() { return "approximately-equals"; } const T missingValue_; const T epsilon_; }; const MissingValue* MissingValueFactory::build(const std::string& builder, const Config& config) { return has(builder) ? get(builder)->make(config) : nullptr; } #define B MissingValueFactoryBuilder #define M1 MissingValueNaN #define M2 MissingValueEquals #define M3 MissingValueApprox #define T array::DataType::str static B> __mv1(M1::static_type()); static B> __mv2(M1::static_type() + "-" + T()); static B> __mv3(M1::static_type() + "-" + T()); static B> __mv4(M2::static_type()); static B> __mv5(M2::static_type() + "-" + T()); static B> __mv6(M2::static_type() + "-" + T()); static B> __mv7(M2::static_type() + "-" + T()); static B> __mv8(M2::static_type() + "-" + T()); static B> __mv9(M2::static_type() + "-" + T()); static B> __mv10(M3::static_type()); static B> __mv11(M3::static_type() + "-" + T()); static B> __mv12(M3::static_type() + "-" + T()); #undef T #undef M3 #undef M2 #undef M1 #undef B } // namespace detail } // namespace field } // namespace atlas atlas-0.46.0/src/atlas/field/detail/FieldImpl.h0000664000175000017500000002523115160212070021324 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ /// @author Willem Deconinck /// @date Sep 2014 #pragma once #include #include #include #include #include #include "atlas/util/Object.h" #include "atlas/array.h" #include "atlas/array/ArrayDataStore.h" #include "atlas/array/DataType.h" #include "atlas/util/Metadata.h" #include "atlas/field/Halo.h" namespace eckit { class Parametrisation; } namespace atlas { class FunctionSpace; } // namespace atlas namespace atlas { namespace field { //---------------------------------------------------------------------------------------------------------------------- class FieldObserver; // Definition below //---------------------------------------------------------------------------------------------------------------------- class FieldImpl : public util::Object { public: // Static methods /// @brief Create field from parametrisation static FieldImpl* create(const eckit::Parametrisation&); /// @brief Create field with given name, Datatype and ArrayShape static FieldImpl* create(const std::string& name, array::DataType, const array::ArrayShape& = array::ArrayShape()); /// @brief Create field with given name, Datatype and ArrayShape static FieldImpl* create(const std::string& name, array::DataType, array::ArraySpec&&); /// @brief Create field with given name, Datatype of template and ArrayShape template static FieldImpl* create(const std::string& name, const array::ArrayShape& = array::ArrayShape()); /// @brief Create field with given name, and take ownership of given Array static FieldImpl* create(const std::string& name, array::Array*); /// @brief Create field by wrapping existing data, Datatype of template and /// ArraySpec template static FieldImpl* wrap(const std::string& name, DATATYPE* data, const array::ArraySpec&); /// @brief Create field by wrapping existing data, Datatype of template and /// ArrayShape template static FieldImpl* wrap(const std::string& name, DATATYPE* data, const array::ArrayShape&); private: // Private constructors to force use of static create functions /// Allocate new Array internally FieldImpl(const std::string& name, array::DataType, const array::ArrayShape&); /// Allocate new Array internally FieldImpl(const std::string& name, array::DataType, array::ArraySpec&&); /// Transfer ownership of Array FieldImpl(const std::string& name, array::Array*); public: // Destructor virtual ~FieldImpl(); // -- Conversion /// @brief Implicit conversion to Array operator const array::Array&() const { return *array_; } operator array::Array&() { return *array_; } const array::Array& array() const { return *array_; } array::Array& array() { return *array_; } // -- Accessors /// @brief Access to raw data void* storage() { return array_->storage(); } /// @brief Internal data type of field array::DataType datatype() const { return array_->datatype(); } /// @brief Name associated to this field const std::string& name() const; /// @brief Rename this field void rename(const std::string& name); /// @brief Access to metadata associated to this field const util::Metadata& metadata() const { return metadata_; } util::Metadata& metadata() { return metadata_; } /// @brief Resize field to given shape void resize(const array::ArrayShape&); void insert(idx_t idx1, idx_t size1); /// @brief Shape of this field in Fortran style (reverse order of C style) const std::vector& shapef() const { return array_->shapef(); } /// @brief Strides of this field in Fortran style (reverse order of C style) const std::vector& stridesf() const { return array_->stridesf(); } /// @brief Strides of this field on the device in Fortran style (reverse order of C style) const std::vector& device_stridesf() const { return array_->device_stridesf(); } /// @brief Shape of this field (reverse order of Fortran style) const array::ArrayShape& shape() const { return array_->shape(); } /// @brief Strides of this field const array::ArrayStrides& strides() const { return array_->strides(); } /// @brief Shape of this field associated to index 'i' idx_t shape(idx_t i) const { return array_->shape(i); } /// @brief Stride of this field associated to index 'i' idx_t stride(idx_t i) const { return array_->stride(i); } /// @brief Number of values stored in this field size_t size() const { return array_->size(); } /// @brief Rank of field idx_t rank() const { return array_->rank(); } /// @brief Number of bytes occupied by the values of this field size_t bytes() const { return array_->bytes(); } /// @brief Output information of field friend std::ostream& operator<<(std::ostream& os, const FieldImpl& v); /// @brief Output information of field plus raw data void dump(std::ostream& os) const; /// Metadata that is more intrinsic to the Field, and queried often void set_levels(idx_t n) { metadata().set("levels", n); } void set_variables(idx_t n) { metadata().set("variables", n); } idx_t levels() const { return metadata().get("levels"); } idx_t variables() const { return metadata().get("variables"); } void set_horizontal_dimension(const std::vector& h_dim) { metadata().set("horizontal_dimension", h_dim); } std::vector horizontal_dimension() const { std::vector h_dim{0}; metadata().get("horizontal_dimension", h_dim); return h_dim; } void set_functionspace(const FunctionSpace&); const FunctionSpace& functionspace() const; /// @brief Return the memory footprint of the Field size_t footprint() const; bool dirty() const; void set_dirty(bool = true) const; // -- dangerous methods template DATATYPE const* host_data() const { return array_->host_data(); } template DATATYPE* host_data() { return array_->host_data(); } template DATATYPE const* device_data() const { return array_->device_data(); } template DATATYPE* device_data() { return array_->device_data(); } template DATATYPE const* data() const { return array_->host_data(); } template DATATYPE* data() { return array_->host_data(); } // -- Methods related to host-device synchronisation, requires // gridtools_storage void updateHost() const { array_->updateHost(); } void updateDevice() const { array_->updateDevice(); } void syncHostDevice() const { array_->syncHostDevice(); } void syncHost() const { array_->syncHost(); } void syncDevice() const { array_->syncDevice(); } bool deviceAllocated() const { return array_->deviceAllocated(); } void allocateDevice() const { array_->allocateDevice(); } void deallocateDevice() const { array_->deallocateDevice(); } bool hostNeedsUpdate() const { return array_->hostNeedsUpdate(); } bool deviceNeedsUpdate() const { return array_->deviceNeedsUpdate(); } void setHostNeedsUpdate(bool v) const { return array_->setHostNeedsUpdate(v); } void setDeviceNeedsUpdate(bool v) const { return array_->setDeviceNeedsUpdate(v); } void reactivateDeviceWriteViews() const { array_->reactivateDeviceWriteViews(); } void reactivateHostWriteViews() const { array_->reactivateHostWriteViews(); } void haloExchange(bool on_device = false) const; void adjointHaloExchange(bool on_device = false) const; Halo& halo() const; void attachObserver(FieldObserver&) const; void detachObserver(FieldObserver&) const; void callbackOnDestruction(std::function&& f) { callback_on_destruction_.emplace_back(std::move(f)); } private: // methods void print(std::ostream& os, bool dump = false) const; private: // members mutable std::string name_; util::Metadata metadata_; array::Array* array_; FunctionSpace* functionspace_; mutable std::vector field_observers_; std::vector> callback_on_destruction_; mutable std::unique_ptr halo_; }; //---------------------------------------------------------------------------------------------------------------------- class FieldObserver { private: std::vector registered_fields_; public: void registerField(const FieldImpl& field) { if (std::find(registered_fields_.begin(), registered_fields_.end(), &field) == registered_fields_.end()) { registered_fields_.push_back(&field); field.attachObserver(*this); } } void unregisterField(const FieldImpl& field) { auto found = std::find(registered_fields_.begin(), registered_fields_.end(), &field); if (found != registered_fields_.end()) { registered_fields_.erase(found); field.detachObserver(*this); } } virtual ~FieldObserver() { for (auto field : registered_fields_) { field->detachObserver(*this); } } virtual void onFieldRename(FieldImpl&) {} virtual void onFieldDestruction(FieldImpl&) {} }; //---------------------------------------------------------------------------------------------------------------------- template FieldImpl* FieldImpl::create(const std::string& name, const array::ArrayShape& shape) { return create(name, array::DataType::create(), shape); } template FieldImpl* FieldImpl::wrap(const std::string& name, DATATYPE* data, const array::ArraySpec& spec) { FieldImpl* wrapped = create(name, array::Array::wrap(data, spec)); wrapped->set_dirty(false); return wrapped; } template FieldImpl* FieldImpl::wrap(const std::string& name, DATATYPE* data, const array::ArrayShape& shape) { FieldImpl* wrapped = create(name, array::Array::wrap(data, shape)); wrapped->set_dirty(false); return wrapped; } //---------------------------------------------------------------------------------------------------------------------- } // namespace field } // namespace atlas atlas-0.46.0/src/atlas/field/detail/MultiFieldInterface.cc0000664000175000017500000000443615160212070023500 0ustar alastairalastair/* * (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/library/config.h" #include "atlas/field/MultiField.h" #include "atlas/field/detail/MultiFieldImpl.h" #include "atlas/field/detail/MultiFieldInterface.h" #include "atlas/runtime/Exception.h" namespace atlas { namespace field { extern "C" { MultiFieldImpl* atlas__MultiField__create(eckit::Configuration* config) { ATLAS_ASSERT(config != nullptr); MultiFieldImpl* multifield; { MultiField f(*config); multifield = f.get(); multifield->attach(); } multifield->detach(); ATLAS_ASSERT(multifield); return multifield; } MultiFieldImpl* atlas__MultiField__create_shape(int kind, int rank, int shapef[], const char* var_names, size_t length, size_t size) { array::ArrayShape shape; shape.resize(rank); for (idx_t j = 0, jf = rank - 1; j < rank; ++j) { shape[j] = shapef[jf--]; } std::vector var_names_str; for (size_t jj = 0; jj < size; ++jj) { std::string str(std::string_view(var_names + jj * length, length)); str.erase(std::find_if(str.rbegin(), str.rend(), [](unsigned char ch) { return !std::isspace(ch); }).base(), str.end()); var_names_str.push_back(str); } MultiFieldImpl* multifield; { MultiField f(array::DataType{kind}, shape, var_names_str); multifield = f.get(); multifield->attach(); } multifield->detach(); ATLAS_ASSERT(multifield); return multifield; } void atlas__MultiField__delete(MultiFieldImpl* This) { delete This; } int atlas__MultiField__size(MultiFieldImpl* This) { return This->size(); } FieldSetImpl* atlas__MultiField__fieldset(MultiFieldImpl* This) { return This->fieldset().get(); } } // ------------------------------------------------------------------ } // namespace field } // namespace atlas atlas-0.46.0/src/atlas/field/FieldCreator.cc0000664000175000017500000000777615160212070020734 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ // file deepcode ignore CppMemoryLeak: static pointers for global registry are OK and will be cleaned up at end #include "atlas/field/FieldCreator.h" #include #include #include #include "eckit/thread/AutoLock.h" #include "eckit/thread/Mutex.h" #include "atlas/field/Field.h" #include "atlas/field/FieldCreatorArraySpec.h" #include "atlas/field/FieldCreatorIFS.h" #include "atlas/grid/Grid.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Log.h" namespace { static eckit::Mutex* local_mutex = nullptr; static std::map* m = nullptr; static pthread_once_t once = PTHREAD_ONCE_INIT; static void init() { local_mutex = new eckit::Mutex(); m = new std::map(); } } // namespace namespace atlas { namespace field { namespace { template void load_builder(const std::string& name) { FieldCreatorBuilder tmp(name); } struct force_link { force_link() { load_builder("tmp_IFS"); load_builder("tmp_ArraySpec"); } }; } // namespace // ------------------------------------------------------------------ FieldCreator::FieldCreator() = default; FieldCreator::~FieldCreator() = default; FieldCreatorFactory::FieldCreatorFactory(const std::string& name): name_(name) { pthread_once(&once, init); eckit::AutoLock lock(local_mutex); if (m->find(name) != m->end()) { throw_Exception("FieldCreatorFactory [" + name + "] already registered\n\nBacktrace:\n" + backtrace(), Here()); } (*m)[name] = this; } FieldCreatorFactory::~FieldCreatorFactory() { eckit::AutoLock lock(local_mutex); m->erase(name_); } void FieldCreatorFactory::list(std::ostream& out) { pthread_once(&once, init); eckit::AutoLock lock(local_mutex); static force_link static_linking; const char* sep = ""; for (std::map::const_iterator j = m->begin(); j != m->end(); ++j) { out << sep << (*j).first; sep = ", "; } } FieldCreator* FieldCreatorFactory::build(const std::string& name) { pthread_once(&once, init); eckit::AutoLock lock(local_mutex); static force_link static_linking; std::map::const_iterator j = m->find(name); if (j == m->end()) { Log::error() << "No FieldCreatorFactory for [" << name << "]" << '\n'; Log::error() << "FieldCreatorFactories are:" << '\n'; for (j = m->begin(); j != m->end(); ++j) { Log::error() << " " << (*j).first << '\n'; } throw_Exception(std::string("No FieldCreatorFactory called ") + name); } return (*j).second->make(); } FieldCreator* FieldCreatorFactory::build(const std::string& name, const eckit::Parametrisation& param) { pthread_once(&once, init); eckit::AutoLock lock(local_mutex); static force_link static_linking; std::map::const_iterator j = m->find(name); if (j == m->end()) { Log::error() << "No FieldCreatorFactory for [" << name << "]" << '\n'; Log::error() << "FieldCreatorFactories are:" << '\n'; for (j = m->begin(); j != m->end(); ++j) { Log::error() << " " << (*j).first << '\n'; } throw_Exception(std::string("No FieldCreatorFactory called ") + name); } return (*j).second->make(param); } } // namespace field } // namespace atlas atlas-0.46.0/src/atlas/field/State.h0000664000175000017500000001012515160212070017271 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ /// @author Willem Deconinck /// @date June 2015 #pragma once #include #include "atlas/field/Field.h" #include "atlas/util/Config.h" #include "atlas/util/Metadata.h" #include "atlas/util/Object.h" namespace eckit { class Parametrisation; } namespace atlas { namespace field { /** * \brief State class that owns a collection of fields */ class State : public util::Object { public: // methods //-- Constructors State(); State(const std::string& generator, const eckit::Parametrisation& = util::Config()); //-- Accessors const Field& field(const std::string& name) const; Field& field(const std::string& name); bool has(const std::string& name) const { return (fields_.find(name) != fields_.end()); } std::vector field_names() const; const Field& field(const idx_t idx) const; Field& field(const idx_t idx); idx_t size() const { return static_cast(fields_.size()); } const Field& operator[](const idx_t idx) const { return field(idx); } Field& operator[](const idx_t idx) { return field(idx); } const Field& operator[](const std::string& name) const { return field(name); } Field& operator[](const std::string& name) { return field(name); } const util::Metadata& metadata() const; util::Metadata& metadata(); // -- Modifiers void initialize(const std::string& generator, const eckit::Parametrisation& = util::Config()); Field add(Field); void remove(const std::string& name); private: typedef std::map FieldMap; private: FieldMap fields_; util::Metadata metadata_; }; //------------------------------------------------------------------------------------------------------ class StateGenerator : public util::Object { public: StateGenerator(const eckit::Parametrisation& = util::Config()); virtual ~StateGenerator(); virtual void generate(State&, const eckit::Parametrisation& = util::Config()) const = 0; }; //------------------------------------------------------------------------------------------------------ class StateGeneratorFactory { public: /*! * \brief build StateCreator with options specified in parametrisation * \return mesh generator */ static StateGenerator* build(const std::string& state_generator, const eckit::Parametrisation& = util::Config()); /*! * \brief list all registered field creators */ static void list(std::ostream&); static bool has(const std::string& name); private: virtual StateGenerator* make(const eckit::Parametrisation& = util::Config()) = 0; std::string name_; protected: StateGeneratorFactory(const std::string&); virtual ~StateGeneratorFactory(); }; template class StateGeneratorBuilder : public StateGeneratorFactory { virtual StateGenerator* make(const eckit::Parametrisation& param = util::Config()) { return new T(param); } public: StateGeneratorBuilder(const std::string& name): StateGeneratorFactory(name) {} }; // ------------------------------------------------------------------------------------ // C wrapper interfaces to C++ routines extern "C" { State* atlas__State__new(); void atlas__State__initialize(State* This, const char* generator, const eckit::Parametrisation* params); void atlas__State__delete(State* This); void atlas__State__add(State* This, FieldImpl* field); void atlas__State__remove(State* This, const char* name); int atlas__State__has(State* This, const char* name); FieldImpl* atlas__State__field_by_name(State* This, const char* name); FieldImpl* atlas__State__field_by_index(State* This, idx_t index); idx_t atlas__State__size(const State* This); util::Metadata* atlas__State__metadata(State* This); } } // namespace field } // namespace atlas atlas-0.46.0/src/atlas/field/MultiFieldCreatorArray.h0000664000175000017500000000320315160212070022565 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ /// @author Slavko Brdar /// @date September 2024 #pragma once #include "atlas/field/MultiFieldCreator.h" namespace eckit { class Configuration; } namespace atlas { namespace field { class MultiFieldImpl; } } // namespace atlas namespace atlas { namespace field { // ------------------------------------------------------------------ /*! * \brief MultiField creator using datatype, shape, variable names as arguments * \details * shape argument contains -1 at the position which gets filled with variable names * Example use: * \code{.cpp} * MultiFieldImpl* multifield = MultiField::create( * datatype, * shape, * var_names * ); * \endcode */ class MultiFieldCreatorArray : public MultiFieldCreator { public: MultiFieldCreatorArray(); MultiFieldCreatorArray(const eckit::Configuration& config); ~MultiFieldCreatorArray() override; MultiFieldImpl* create(const eckit::Configuration& config = util::Config()) const override; MultiFieldImpl* create(const array::DataType datatype, const array::ArrayShape& shape, const std::vector& var_names) const override; }; // ------------------------------------------------------------------ } // namespace field } // namespace atlas atlas-0.46.0/src/atlas/field/MultiFieldCreatorArray.cc0000664000175000017500000001216215160212070022727 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ /// @author Slavko Brdar /// @author Willem Deconinck /// @date September 2024 #include #include #include "atlas/array/ArrayDataStore.h" #include "atlas/array/DataType.h" #include "atlas/array/Range.h" #include "atlas/field/MultiFieldCreatorArray.h" #include "atlas/field/detail/MultiFieldImpl.h" #include "atlas/grid/Grid.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Log.h" namespace atlas { namespace field { MultiFieldCreatorArray::MultiFieldCreatorArray() = default; MultiFieldCreatorArray::MultiFieldCreatorArray(const eckit::Configuration&) {} MultiFieldCreatorArray::~MultiFieldCreatorArray() = default; MultiFieldImpl* MultiFieldCreatorArray::create(const eckit::Configuration& config) const { array::DataType datatype = array::DataType::create(); std::string datatype_str; if (config.get("datatype", datatype_str)) { datatype = array::DataType(datatype_str); } else { array::DataType::kind_t kind(array::DataType::kind()); config.get("kind", kind); if (!array::DataType::kind_valid(kind)) { std::stringstream msg; msg << "Could not create field. kind parameter unrecognized"; throw_Exception(msg.str()); } datatype = array::DataType(kind); } std::vector shape; config.get("shape", shape); const auto fields = config.getSubConfigurations("fields"); int nflds = 0; for (size_t i = 0; i < fields.size(); ++i) { long nvar = 1; fields[i].get("nvar", nvar); nflds += nvar; } std::vector var_names; var_names.resize(nflds); for (size_t i = 0, cnt = 0; i < fields.size(); ++i) { std::string name; fields[i].get("name", name); long nvar = 1; fields[i].get("nvar", nvar); if (nvar > 1) { for (int ivar = 0; ivar < nvar; ivar++) { std::stringstream ss; ss << name << "_" << ivar; var_names[cnt++] = ss.str(); } } else { var_names[cnt++] = name; } } return create(datatype, shape, var_names); } MultiFieldImpl* MultiFieldCreatorArray::create(const array::DataType datatype, const array::ArrayShape& shape, const std::vector& var_names) const { const int dim = shape.size(); const int nvar = var_names.size(); ATLAS_ASSERT(nvar > 0 && dim > 2, "MultiField must have at least one field name."); int varidx = -1; for (int i = 0; i < dim; i++) { if (shape[i] == -1) { varidx = i; break; } } array::ArrayShape multiarray_shape = shape; multiarray_shape[varidx] = nvar; MultiFieldImpl* multifield = new MultiFieldImpl{array::ArraySpec{datatype, multiarray_shape}}; auto& multiarray = multifield->array(); array::ArrayShape field_shape; field_shape.resize(dim - 1); array::ArrayStrides field_strides; field_strides.resize(dim - 1); for (int i = 0, j = 0; i < dim; i++) { if (i != varidx) { field_shape[j] = multiarray.shape()[i]; field_strides[j] = multiarray.strides()[i]; ++j; } } array::ArraySpec field_array_spec(field_shape, field_strides); for (size_t ifield = 0; ifield < nvar; ++ifield) { idx_t start_index = multiarray.strides()[varidx] * ifield; Field field; if (datatype.kind() == array::DataType::KIND_REAL64) { double* slice_begin = multiarray.data() + start_index; field = Field(var_names[ifield], slice_begin, field_array_spec); } else if (datatype.kind() == array::DataType::KIND_REAL32) { float* slice_begin = multiarray.data() + start_index; field = Field(var_names[ifield], slice_begin, field_array_spec); } else if (datatype.kind() == array::DataType::KIND_INT32) { int* slice_begin = multiarray.data() + start_index; field = Field(var_names[ifield], slice_begin, field_array_spec); } else if (datatype.kind() == array::DataType::KIND_INT64) { long* slice_begin = multiarray.data() + start_index; field = Field(var_names[ifield], slice_begin, field_array_spec); } else { ATLAS_NOTIMPLEMENTED; } multifield->add(field); } Log::debug() << "Creating multifield of " << datatype.str() << " type" << std::endl; return multifield; } // ------------------------------------------------------------------ namespace { static MultiFieldCreatorBuilder __MultiFieldCreatorArray("MultiFieldCreatorArray"); } } // namespace field } // namespace atlas atlas-0.46.0/src/atlas/field/MultiFieldCreator.h0000664000175000017500000000546315160212070021600 0ustar alastairalastair/* * (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. */ #ifndef atlas_field_MultiFieldCreator_h #define atlas_field_MultiFieldCreator_h #include #include "atlas/util/Object.h" #include "atlas/field/MultiField.h" namespace eckit { class Configuration; } //------------------------------------------------------------------------------------------------------ namespace atlas { namespace field { //------------------------------------------------------------------------------------------------------ /*! * \brief Base class for creating new multifields based on Configuration * * \details * Example to create field[100][3] of default type double: * \code{.cpp} * FieldImpl* field = Field::create( * Config * ("creator","ArraySpec") // ArraySpec MultiFieldCreator * ("shape",array::make_shape(100,3)) // Rank 2 field with indexing [100][3] * ); * \endcode */ class MultiFieldCreator : public util::Object { public: MultiFieldCreator(); MultiFieldCreator(const eckit::Configuration& config); virtual ~MultiFieldCreator(); virtual MultiFieldImpl* create(const eckit::Configuration& config = util::Config()) const = 0; virtual MultiFieldImpl* create(const array::DataType datatype, const array::ArrayShape& shape, const std::vector& var_names) const = 0; }; //------------------------------------------------------------------------------------------------------ class MultiFieldCreatorFactory : public util::Factory { public: static std::string className() { return "MultiFieldCreatorFactory"; } /*! * \brief build MultiFieldCreator with options specified in parametrisation * \return mesh generator */ static MultiFieldCreator* build(const std::string&, const eckit::Configuration& = util::Config()); using Factory::Factory; private: virtual MultiFieldCreator* make() = 0; virtual MultiFieldCreator* make(const eckit::Configuration&) = 0; }; template class MultiFieldCreatorBuilder : public MultiFieldCreatorFactory { virtual MultiFieldCreator* make() { return new T(); } virtual MultiFieldCreator* make(const eckit::Configuration& config) { return new T(config); } public: using MultiFieldCreatorFactory::MultiFieldCreatorFactory; }; //------------------------------------------------------------------------------------------------------ } // namespace field } // namespace atlas #endif atlas-0.46.0/src/atlas/field/MultiField.h0000664000175000017500000000655615160212070020264 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ /// @author Willem Deconinck /// @date June 2015 #pragma once #include #include #include "atlas/array/Array.h" #include "atlas/field/Field.h" #include "atlas/field/FieldSet.h" #include "atlas/util/Config.h" #include "atlas/util/Factory.h" #include "atlas/util/Metadata.h" #include "atlas/util/Object.h" #include "atlas/util/ObjectHandle.h" namespace eckit { class Parametrisation; } namespace atlas { namespace field { class MultiFieldImpl; } } namespace atlas { namespace field { /** * \brief MultiField class that owns a collection of fields that are co-allocated * * Fields can only be described by parametrisation during the construction. * Once setup, no additional fields can be added. * * Fields have to all be of same memory layout and data type */ class MultiField : public util::ObjectHandle { public: // methods //-- Constructors using Handle::Handle; MultiField(const eckit::Configuration&); MultiField(const array::DataType datatype, const array::ArrayShape& shape, const std::vector& var_names); //-- Accessors const Field& field(const std::string& name) const; Field& field(const std::string& name); bool has(const std::string& name) const; std::vector field_names() const; const Field& field(const idx_t idx) const; Field& field(const idx_t idx); idx_t size() const; const Field& operator[](const idx_t idx) const; Field& operator[](const idx_t idx); const Field& operator[](const std::string& name) const; Field& operator[](const std::string& name); const util::Metadata& metadata() const; util::Metadata& metadata(); // -- Modifiers /// @brief Implicit conversion to Array operator const array::Array&() const; operator array::Array&(); operator const FieldSet&() const; operator FieldSet&(); /// @brief Access contained Array const array::Array& array() const; array::Array& array(); private: template void create(const std::vector shape, const std::vector var_names); }; /** * \brief MultiFieldArrayRegistry */ class MultiFieldArrayRegistry : public field::FieldObserver { private: MultiFieldArrayRegistry() {} public: static MultiFieldArrayRegistry& instance() { static MultiFieldArrayRegistry inst; return inst; } void onFieldDestruction(FieldImpl& field) override { std::lock_guard guard(lock_); map_.erase(&field); } ~MultiFieldArrayRegistry() override = default; void add(Field& field, std::shared_ptr array) { std::lock_guard guard(lock_); map_.emplace(field.get(), array); field->attachObserver(*this); } public: std::mutex lock_; std::map> map_; }; // ------------------------------------------------------------------------------------ } // namespace field } // namespace atlas atlas-0.46.0/src/atlas/field/MissingValue.cc0000664000175000017500000000624215160212070020762 0ustar alastairalastair/* * (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 "atlas/field/MissingValue.h" #include #include "eckit/types/FloatCompare.h" #include "atlas/field/Field.h" #include "atlas/runtime/Exception.h" #include "atlas/util/Config.h" #include "atlas/util/Metadata.h" namespace atlas { namespace field { namespace { std::string config_type(const MissingValue::Config& c) { std::string value; c.get("type", value); return value; } std::string field_type(const Field& f) { std::string value; if (f.metadata().get("missing_value_type", value)) { value += "-" + f.datatype().str(); } return value; } } // namespace MissingValue::MissingValue(): Handle(nullptr) {} MissingValue::MissingValue(const MissingValue::Config& config): Handle(detail::MissingValueFactory::build(config_type(config), config)) {} MissingValue::MissingValue(const std::string& type, const MissingValue::Config& config): Handle(detail::MissingValueFactory::build(type, config)) {} MissingValue::MissingValue(const Field& field): Handle(detail::MissingValueFactory::build(field_type(field), field.metadata())) {} MissingValue::MissingValue(const std::string& type, const Field& field): Handle(detail::MissingValueFactory::build(type, field.metadata())) {} bool MissingValue::operator()(const double& value) const { ATLAS_ASSERT_MSG(operator bool(), "MissingValue::operator()( const double& ) ObjectHandle not setup"); return get()->operator()(value); } bool MissingValue::operator()(const float& value) const { ATLAS_ASSERT_MSG(operator bool(), "MissingValue::operator()( const float& ): ObjectHandle not setup"); return get()->operator()(value); } bool MissingValue::operator()(const int& value) const { ATLAS_ASSERT_MSG(operator bool(), "MissingValue::operator()( const int& ): ObjectHandle not setup"); return get()->operator()(value); } bool MissingValue::operator()(const long& value) const { ATLAS_ASSERT_MSG(operator bool(), "MissingValue::operator()( const long& ): ObjectHandle not setup"); return get()->operator()(value); } bool MissingValue::operator()(const unsigned long& value) const { ATLAS_ASSERT_MSG(operator bool(), "MissingValue::operator()( const unsigned long& ): ObjectHandle not setup"); return get()->operator()(value); } bool MissingValue::isnan() const { ATLAS_ASSERT_MSG(operator bool(), "MissingValue: ObjectHandle not setup"); return get()->isnan(); } const detail::MissingValue& MissingValue::ref() const { ATLAS_ASSERT_MSG(operator bool(), "MissingValue: ObjectHandle not setup"); return *get(); // (one dereferencing level less) } void MissingValue::metadata(Field& field) const { ATLAS_ASSERT_MSG(operator bool(), "MissingValue: ObjectHandle not setup"); get()->metadata(field); } } // namespace field } // namespace atlas atlas-0.46.0/src/atlas/field/FieldCreatorArraySpec.cc0000664000175000017500000000501215160212070022523 0ustar alastairalastair/* * (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/field/FieldCreatorArraySpec.h" #include #include #include "eckit/config/Parametrisation.h" #include "atlas/array/DataType.h" #include "atlas/field/detail/FieldImpl.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Log.h" namespace atlas { namespace field { FieldImpl* FieldCreatorArraySpec::createField(const eckit::Parametrisation& params) const { std::vector shape; if (!params.get("shape", shape)) { throw_Exception("Could not find parameter 'shape' in Parametrisation"); } std::vector s(shape.size()); bool fortran(false); params.get("fortran", fortran); if (fortran) { std::reverse_copy(shape.begin(), shape.end(), s.begin()); } else { s.assign(shape.begin(), shape.end()); } array::DataType datatype = array::DataType::create(); std::string datatype_str; if (params.get("datatype", datatype_str)) { datatype = array::DataType(datatype_str); } else { array::DataType::kind_t kind(array::DataType::kind()); params.get("kind", kind); if (!array::DataType::kind_valid(kind)) { std::stringstream msg; msg << "Could not create field. kind parameter unrecognized"; throw_Exception(msg.str()); } datatype = array::DataType(kind); } int alignment = 1; params.get("alignment", alignment); std::string name; params.get("name", name); Log::trace() << "Create field " << name << "\t shape=["; for (size_t i = 0; i < s.size(); ++i) { Log::trace() << s[i] << (i < s.size() - 1 ? "," : ""); } Log::trace() << "]" << std::endl; auto field = FieldImpl::create(name, datatype, array::ArraySpec(std::move(s), array::ArrayAlignment(alignment))); field->callbackOnDestruction([field]() { Log::trace() << "Destroy field " << field->name() << std::endl; }); return field; } namespace { static FieldCreatorBuilder __ArraySpec("ArraySpec"); } // ------------------------------------------------------------------ } // namespace field } // namespace atlas atlas-0.46.0/src/atlas/field/MultiFieldCreator.cc0000664000175000017500000000276515160212070021740 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ // file deepcode ignore CppMemoryLeak: static pointers for global registry are OK and will be cleaned up at end #include "atlas/field/MultiFieldCreator.h" #include #include #include "eckit/thread/AutoLock.h" #include "eckit/thread/Mutex.h" #include "atlas/field/MultiFieldCreatorIFS.h" #include "atlas/field/MultiFieldCreatorArray.h" #include "atlas/grid/Grid.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Log.h" namespace atlas { namespace field { namespace { void force_link() { static struct Link { Link() { MultiFieldCreatorBuilder(); MultiFieldCreatorBuilder(); } } link; } } // namespace // ------------------------------------------------------------------ MultiFieldCreator::MultiFieldCreator() = default; MultiFieldCreator::~MultiFieldCreator() = default; MultiFieldCreator* MultiFieldCreatorFactory::build(const std::string& builder, const eckit::Configuration& config) { force_link(); auto factory = get(builder); return factory->make(config); } } // namespace field } // namespace atlas atlas-0.46.0/src/atlas/CMakeLists.txt0000664000175000017500000007615315160212070017532 0ustar alastairalastair# (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. ### config headers ecbuild_generate_config_headers( DESTINATION ${INSTALL_INCLUDE_DIR}/atlas ) configure_file( library/defines.h.in library/defines.h ) configure_file( library/version.h.in library/version.h ) configure_file( library/git_sha1.h.in library/git_sha1.h ) install( FILES ${CMAKE_CURRENT_BINARY_DIR}/library/defines.h ${CMAKE_CURRENT_BINARY_DIR}/library/version.h ${CMAKE_CURRENT_BINARY_DIR}/library/git_sha1.h DESTINATION ${INSTALL_INCLUDE_DIR}/atlas/library ) ### sources list( APPEND atlas_srcs ${CMAKE_CURRENT_BINARY_DIR}/library/git_sha1.h ${CMAKE_CURRENT_BINARY_DIR}/library/defines.h library.h library/config.h library/FloatingPointExceptions.h library/FloatingPointExceptions.cc library/Library.h library/Library.cc library/Plugin.h library/Plugin.cc runtime/AtlasTool.h runtime/AtlasTool.cc runtime/Log.h runtime/Log.cc runtime/Trace.h runtime/trace/CallStack.h runtime/trace/CallStack.cc runtime/trace/CodeLocation.cc runtime/trace/CodeLocation.h runtime/trace/TraceT.h runtime/trace/Nesting.cc runtime/trace/Nesting.h runtime/trace/Barriers.cc runtime/trace/Barriers.h runtime/trace/Logging.cc runtime/trace/Logging.h runtime/trace/Timings.h runtime/trace/Timings.cc runtime/Exception.cc runtime/Exception.h library/detail/BlackMagic.h library/detail/Debug.h parallel/acc/acc.cc parallel/acc/acc.h parallel/mpi/mpi.cc parallel/mpi/mpi.h parallel/omp/omp.cc parallel/omp/omp.h parallel/omp/copy.h parallel/omp/fill.h parallel/omp/sort.h util/Config.cc util/Config.h mdspan.h ) list( APPEND atlas_grid_srcs option.h option/Options.h option/Options.cc option/TransOptions.h option/TransOptions.cc projection.h projection/Projection.cc projection/Projection.h projection/Jacobian.h projection/detail/CubedSphereEquiAnglProjection.cc projection/detail/CubedSphereEquiAnglProjection.h projection/detail/CubedSphereEquiDistProjection.cc projection/detail/CubedSphereEquiDistProjection.h projection/detail/CubedSphereProjectionBase.cc projection/detail/CubedSphereProjectionBase.h projection/detail/LambertAzimuthalEqualAreaProjection.cc projection/detail/LambertAzimuthalEqualAreaProjection.h projection/detail/LambertConformalConicProjection.cc projection/detail/LambertConformalConicProjection.h projection/detail/LonLatProjection.cc projection/detail/LonLatProjection.h projection/detail/MercatorProjection.cc projection/detail/MercatorProjection.h projection/detail/ProjectionFactory.cc projection/detail/ProjectionFactory.h projection/detail/ProjectionImpl.cc projection/detail/ProjectionImpl.h projection/detail/ProjectionUtilities.h projection/detail/SchmidtProjection.cc projection/detail/SchmidtProjection.h projection/detail/VariableResolutionProjection.cc projection/detail/VariableResolutionProjection.h domain.h domain/Domain.cc domain/Domain.h domain/detail/Domain.cc domain/detail/Domain.h domain/detail/RectangularDomain.h domain/detail/RectangularDomain.cc domain/detail/EmptyDomain.h domain/detail/EmptyDomain.cc domain/detail/GlobalDomain.h domain/detail/GlobalDomain.cc domain/detail/ZonalBandDomain.h domain/detail/ZonalBandDomain.cc domain/detail/DomainFactory.h domain/detail/DomainFactory.cc grid.h grid/CubedSphereGrid.cc grid/CubedSphereGrid.h grid/CubedSphereGrid2.cc grid/CubedSphereGrid2.h grid/Grid.cc grid/Grid.h grid/SpecRegistry.cc grid/SpecRegistry.h grid/StructuredGrid.cc grid/StructuredGrid.h grid/UnstructuredGrid.cc grid/UnstructuredGrid.h util/GaussianLatitudes.cc util/GaussianLatitudes.h grid/Spacing.cc grid/Spacing.h grid/Iterator.h grid/Iterator.cc grid/Stencil.h grid/StencilComputer.h grid/StencilComputer.cc grid/Tiles.h grid/Tiles.cc grid/detail/grid/CubedSphere.cc grid/detail/grid/CubedSphere.h grid/detail/grid/CubedSphere2.cc grid/detail/grid/CubedSphere2.h grid/detail/grid/GridBuilder.h grid/detail/grid/GridBuilder.cc grid/detail/grid/GridFactory.h grid/detail/grid/GridFactory.cc grid/detail/grid/Grid.h grid/detail/grid/Grid.cc grid/detail/grid/Structured.cc grid/detail/grid/Structured.h grid/detail/grid/Unstructured.cc grid/detail/grid/Unstructured.h grid/detail/grid/Gaussian.h grid/detail/grid/Gaussian.cc grid/detail/grid/LonLat.h grid/detail/grid/LonLat.cc grid/detail/grid/Regional.h grid/detail/grid/Regional.cc grid/detail/grid/RegionalVariableResolution.h grid/detail/grid/RegionalVariableResolution.cc grid/detail/grid/Healpix.h grid/detail/grid/Healpix.cc grid/detail/grid/StencilComputerInterface.h grid/detail/grid/StencilComputerInterface.cc grid/detail/tiles/Tiles.cc grid/detail/tiles/Tiles.h grid/detail/tiles/TilesFactory.h grid/detail/tiles/TilesFactory.cc grid/detail/tiles/FV3Tiles.cc grid/detail/tiles/FV3Tiles.h grid/detail/tiles/LFRicTiles.cc grid/detail/tiles/LFRicTiles.h grid/detail/spacing/Spacing.cc grid/detail/spacing/Spacing.h grid/detail/spacing/SpacingFactory.cc grid/detail/spacing/SpacingFactory.h grid/detail/spacing/CustomSpacing.h grid/detail/spacing/CustomSpacing.cc grid/detail/spacing/LinearSpacing.h grid/detail/spacing/LinearSpacing.cc grid/detail/spacing/FocusSpacing.h grid/detail/spacing/FocusSpacing.cc grid/detail/spacing/GaussianSpacing.h grid/detail/spacing/GaussianSpacing.cc grid/detail/spacing/gaussian/Latitudes.cc grid/detail/spacing/gaussian/Latitudes.h grid/detail/spacing/gaussian/N.cc grid/detail/spacing/gaussian/N.h grid/detail/spacing/gaussian/N16.cc # TL31 grid/detail/spacing/gaussian/N24.cc # TL47 grid/detail/spacing/gaussian/N32.cc # TL63 grid/detail/spacing/gaussian/N48.cc # TL95 grid/detail/spacing/gaussian/N64.cc # TL127 grid/detail/spacing/gaussian/N80.cc # TL159 grid/detail/spacing/gaussian/N96.cc # TL191 grid/detail/spacing/gaussian/N128.cc # TL255 grid/detail/spacing/gaussian/N160.cc # TL319 grid/detail/spacing/gaussian/N200.cc # TL399 grid/detail/spacing/gaussian/N256.cc # TL511 grid/detail/spacing/gaussian/N320.cc # TL639 grid/detail/spacing/gaussian/N400.cc # TL799 grid/detail/spacing/gaussian/N512.cc # TL1023 grid/detail/spacing/gaussian/N576.cc # TL1151 grid/detail/spacing/gaussian/N640.cc # TL1279 grid/detail/spacing/gaussian/N800.cc # TL1599 grid/detail/spacing/gaussian/N1024.cc # TL2047 grid/detail/spacing/gaussian/N1280.cc # TL2559 grid/detail/spacing/gaussian/N1600.cc # TL3199 grid/detail/spacing/gaussian/N2000.cc # TL3999 grid/detail/spacing/gaussian/N4000.cc # TL7999 grid/detail/spacing/gaussian/N8000.cc # TL15999 grid/detail/pl/classic_gaussian/N.h grid/detail/pl/classic_gaussian/N.cc grid/detail/pl/classic_gaussian/PointsPerLatitude.h grid/detail/pl/classic_gaussian/PointsPerLatitude.cc grid/detail/pl/classic_gaussian/N16.cc # TL31 grid/detail/pl/classic_gaussian/N24.cc # TL47 grid/detail/pl/classic_gaussian/N32.cc # TL63 grid/detail/pl/classic_gaussian/N48.cc # TL95 grid/detail/pl/classic_gaussian/N64.cc # TL127 grid/detail/pl/classic_gaussian/N80.cc # TL159 grid/detail/pl/classic_gaussian/N96.cc # TL191 grid/detail/pl/classic_gaussian/N128.cc # TL255 grid/detail/pl/classic_gaussian/N160.cc # TL319 grid/detail/pl/classic_gaussian/N200.cc # TL399 grid/detail/pl/classic_gaussian/N256.cc # TL511 grid/detail/pl/classic_gaussian/N320.cc # TL639 grid/detail/pl/classic_gaussian/N400.cc # TL799 grid/detail/pl/classic_gaussian/N512.cc # TL1023 grid/detail/pl/classic_gaussian/N576.cc # TL1151 grid/detail/pl/classic_gaussian/N640.cc # TL1279 grid/detail/pl/classic_gaussian/N800.cc # TL1599 grid/detail/pl/classic_gaussian/N1024.cc # TL2047 grid/detail/pl/classic_gaussian/N1280.cc # TL2559 grid/detail/pl/classic_gaussian/N1600.cc # TL3199 grid/detail/pl/classic_gaussian/N2000.cc # TL3999 grid/detail/pl/classic_gaussian/N4000.cc # TL7999 grid/detail/pl/classic_gaussian/N8000.cc # TL15999 grid/Vertical.h grid/Vertical.cc ) list( APPEND atlas_grid_partitioning_srcs grid/StructuredPartitionPolygon.h grid/StructuredPartitionPolygon.cc grid/Distribution.cc grid/Distribution.h grid/Partitioner.h grid/Partitioner.cc grid/detail/distribution/DistributionImpl.h grid/detail/distribution/DistributionImpl.cc grid/detail/distribution/DistributionArray.cc grid/detail/distribution/DistributionArray.h grid/detail/distribution/DistributionFunction.cc grid/detail/distribution/DistributionFunction.h grid/detail/distribution/BandsDistribution.cc grid/detail/distribution/BandsDistribution.h grid/detail/distribution/SerialDistribution.cc grid/detail/distribution/SerialDistribution.h grid/detail/partitioner/BandsPartitioner.cc grid/detail/partitioner/BandsPartitioner.h grid/detail/partitioner/CheckerboardPartitioner.cc grid/detail/partitioner/CheckerboardPartitioner.h grid/detail/partitioner/CubedSpherePartitioner.cc grid/detail/partitioner/CubedSpherePartitioner.h grid/detail/partitioner/EqualBandsPartitioner.cc grid/detail/partitioner/EqualBandsPartitioner.h grid/detail/partitioner/EqualAreaPartitioner.cc grid/detail/partitioner/EqualAreaPartitioner.h grid/detail/partitioner/EqualRegionsPartitioner.cc grid/detail/partitioner/EqualRegionsPartitioner.h grid/detail/partitioner/MatchingMeshPartitioner.h grid/detail/partitioner/MatchingMeshPartitioner.cc grid/detail/partitioner/MatchingMeshPartitionerBruteForce.cc grid/detail/partitioner/MatchingMeshPartitionerBruteForce.h grid/detail/partitioner/MatchingMeshPartitionerCubedSphere.cc grid/detail/partitioner/MatchingMeshPartitionerCubedSphere.h grid/detail/partitioner/MatchingMeshPartitionerLonLatPolygon.cc grid/detail/partitioner/MatchingMeshPartitionerLonLatPolygon.h grid/detail/partitioner/MatchingMeshPartitionerSphericalPolygon.cc grid/detail/partitioner/MatchingMeshPartitionerSphericalPolygon.h grid/detail/partitioner/MatchingFunctionSpacePartitioner.h grid/detail/partitioner/MatchingFunctionSpacePartitioner.cc grid/detail/partitioner/MatchingFunctionSpacePartitionerLonLatPolygon.cc grid/detail/partitioner/MatchingFunctionSpacePartitionerLonLatPolygon.h grid/detail/partitioner/Partitioner.cc grid/detail/partitioner/Partitioner.h grid/detail/partitioner/RegularBandsPartitioner.cc grid/detail/partitioner/RegularBandsPartitioner.h grid/detail/partitioner/SerialPartitioner.cc grid/detail/partitioner/SerialPartitioner.h ) if( atlas_HAVE_ECTRANS ) list( APPEND atlas_grid_partitioning_srcs grid/detail/partitioner/TransPartitioner.h 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 ) set_source_files_properties( mesh/actions/BuildConvexHull3D.cc PROPERTIES COMPILE_FLAGS "${CGAL_COMPILE_FLAGS}" ) endif() list( APPEND atlas_mesh_srcs grid/detail/vertical/VerticalInterface.h # Uses Field grid/detail/vertical/VerticalInterface.cc # Uses Field mesh.h mesh/Connectivity.cc mesh/Connectivity.h mesh/ElementType.cc mesh/ElementType.h mesh/elementtypes/Classification.h mesh/elementtypes/Triangle.h mesh/elementtypes/Quadrilateral.h mesh/elementtypes/Pentagon.h mesh/Elements.cc mesh/Elements.h mesh/Halo.cc mesh/Halo.h mesh/HybridElements.cc mesh/HybridElements.h mesh/Mesh.cc mesh/Mesh.h mesh/MeshBuilder.cc mesh/MeshBuilder.h mesh/Nodes.cc mesh/Nodes.h mesh/IsGhostNode.h mesh/PartitionPolygon.cc mesh/PartitionPolygon.h mesh/detail/MeshImpl.cc mesh/detail/MeshImpl.h mesh/detail/MeshIntf.cc mesh/detail/MeshIntf.h mesh/detail/MeshBuilderIntf.cc mesh/detail/MeshBuilderIntf.h mesh/detail/PartitionGraph.cc mesh/detail/PartitionGraph.h mesh/detail/AccumulateFacets.h mesh/detail/AccumulateFacets.cc util/Unique.h util/Unique.cc mesh/actions/BuildDualMesh.h mesh/actions/BuildCellCentres.cc mesh/actions/BuildCellCentres.h mesh/actions/Build2DCellCentres.cc mesh/actions/Build2DCellCentres.h mesh/actions/BuildConvexHull3D.cc mesh/actions/BuildConvexHull3D.h mesh/actions/BuildDualMesh.cc mesh/actions/BuildDualMesh.h mesh/actions/BuildEdges.cc mesh/actions/BuildEdges.h mesh/actions/BuildHalo.cc mesh/actions/BuildHalo.h mesh/actions/BuildNode2CellConnectivity.cc mesh/actions/BuildNode2CellConnectivity.h mesh/actions/BuildParallelFields.cc mesh/actions/BuildParallelFields.h mesh/actions/BuildPeriodicBoundaries.cc mesh/actions/BuildPeriodicBoundaries.h mesh/actions/BuildStatistics.cc mesh/actions/BuildStatistics.h mesh/actions/BuildXYZField.cc mesh/actions/BuildXYZField.h mesh/actions/ExtendNodesGlobal.h mesh/actions/ExtendNodesGlobal.cc mesh/actions/GetCubedSphereNodalArea.cc mesh/actions/GetCubedSphereNodalArea.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 meshgenerator/MeshGenerator.h meshgenerator/detail/CubedSphereMeshGenerator.h meshgenerator/detail/CubedSphereMeshGenerator.cc meshgenerator/detail/CubedSphereDualMeshGenerator.h meshgenerator/detail/CubedSphereDualMeshGenerator.cc meshgenerator/detail/NodalCubedSphereMeshGenerator.h meshgenerator/detail/NodalCubedSphereMeshGenerator.cc meshgenerator/detail/DelaunayMeshGenerator.h meshgenerator/detail/DelaunayMeshGenerator.cc meshgenerator/detail/StructuredMeshGenerator.h meshgenerator/detail/StructuredMeshGenerator.cc meshgenerator/detail/RegularMeshGenerator.cc meshgenerator/detail/RegularMeshGenerator.h meshgenerator/detail/MeshGeneratorFactory.cc meshgenerator/detail/MeshGeneratorFactory.h meshgenerator/detail/MeshGeneratorImpl.cc meshgenerator/detail/MeshGeneratorImpl.h meshgenerator/detail/MeshGeneratorInterface.cc meshgenerator/detail/MeshGeneratorInterface.h meshgenerator/detail/HealpixMeshGenerator.h meshgenerator/detail/HealpixMeshGenerator.cc meshgenerator/detail/cubedsphere/CubedSphereUtility.h meshgenerator/detail/cubedsphere/CubedSphereUtility.cc ) list( APPEND atlas_output_srcs output/Output.h output/Output.cc output/Gmsh.h output/Gmsh.cc output/detail/GmshIO.cc output/detail/GmshIO.h output/detail/GmshImpl.cc output/detail/GmshImpl.h output/detail/GmshInterface.cc output/detail/GmshInterface.h output/detail/PointCloudIO.cc output/detail/PointCloudIO.h ) list( APPEND atlas_field_srcs field.h field/Field.cc field/Field.h field/FieldCreator.cc field/FieldCreator.h field/FieldCreatorArraySpec.cc field/FieldCreatorArraySpec.h field/FieldCreatorIFS.cc field/FieldCreatorIFS.h field/FieldSet.cc field/FieldSet.h field/Halo.cc field/Halo.h field/MissingValue.cc field/MissingValue.h field/MultiField.cc field/MultiField.h field/MultiFieldCreator.cc field/MultiFieldCreator.h field/MultiFieldCreatorIFS.cc field/MultiFieldCreatorIFS.h field/MultiFieldCreatorArray.cc field/MultiFieldCreatorArray.h field/State.cc field/State.h field/detail/FieldImpl.cc field/detail/FieldImpl.h field/detail/FieldInterface.cc field/detail/FieldInterface.h field/detail/MultiFieldImpl.cc field/detail/MultiFieldImpl.h field/detail/MultiFieldInterface.cc field/detail/MultiFieldInterface.h field/detail/MissingValue.cc field/detail/MissingValue.h ) list( APPEND atlas_functionspace_srcs functionspace.h functionspace/BlockStructuredColumns.h functionspace/BlockStructuredColumns.cc functionspace/CellColumns.h functionspace/CellColumns.cc functionspace/EdgeColumns.h functionspace/EdgeColumns.cc functionspace/FunctionSpace.h functionspace/FunctionSpace.cc functionspace/HaloDescription.h functionspace/Locate.cc functionspace/Locate.h functionspace/NodeColumns.h functionspace/NodeColumns.cc functionspace/StructuredColumns.h functionspace/StructuredColumns.cc functionspace/Spectral.h functionspace/Spectral.cc functionspace/PointCloud.h functionspace/PointCloud.cc functionspace/CubedSphereColumns.h functionspace/CubedSphereColumns.cc functionspace/detail/BlockStructuredColumns.h functionspace/detail/BlockStructuredColumns.cc functionspace/detail/BlockStructuredColumnsInterface.h functionspace/detail/BlockStructuredColumnsInterface.cc functionspace/detail/CellColumnsInterface.h functionspace/detail/CellColumnsInterface.cc functionspace/detail/FunctionSpaceImpl.h functionspace/detail/FunctionSpaceImpl.cc functionspace/detail/FunctionSpaceInterface.h functionspace/detail/FunctionSpaceInterface.cc functionspace/detail/NodeColumnsInterface.h functionspace/detail/NodeColumnsInterface.cc functionspace/detail/NodeColumns_FieldStatistics.cc functionspace/detail/SpectralInterface.h functionspace/detail/SpectralInterface.cc functionspace/detail/StructuredColumns.h functionspace/detail/StructuredColumns.cc functionspace/detail/StructuredColumnsInterface.h functionspace/detail/StructuredColumnsInterface.cc functionspace/detail/StructuredColumns_setup.cc functionspace/detail/StructuredColumns_create_remote_index.cc functionspace/detail/PointCloudInterface.h functionspace/detail/PointCloudInterface.cc functionspace/detail/CubedSphereStructure.h functionspace/detail/CubedSphereStructure.cc # for cubedsphere matching mesh partitioner interpolation/method/cubedsphere/CellFinder.cc interpolation/method/cubedsphere/CellFinder.h interpolation/Vector2D.cc interpolation/Vector2D.h interpolation/Vector3D.cc interpolation/Vector3D.h interpolation/element/Quad2D.h interpolation/element/Quad2D.cc interpolation/element/Quad3D.cc interpolation/element/Quad3D.h interpolation/element/Triag2D.cc interpolation/element/Triag2D.h interpolation/element/Triag3D.cc interpolation/element/Triag3D.h interpolation/method/Intersect.cc interpolation/method/Intersect.h interpolation/method/Ray.cc # For testing Quad interpolation/method/Ray.h # For testing Quad # for BuildConvexHull3D interpolation/method/PointSet.cc interpolation/method/PointSet.h ) list( APPEND atlas_numerics_srcs numerics/Method.h numerics/Method.cc numerics/Nabla.h numerics/Nabla.cc numerics/fvm/Method.h numerics/fvm/Method.cc numerics/fvm/Nabla.h numerics/fvm/Nabla.cc ) list( APPEND atlas_trans_srcs trans/Cache.h trans/Cache.cc trans/Trans.h trans/Trans.cc trans/VorDivToUV.h trans/VorDivToUV.cc trans/LegendreCacheCreator.h trans/LegendreCacheCreator.cc trans/local/TransLocal.h trans/local/TransLocal.cc trans/local/LegendrePolynomials.h trans/local/LegendrePolynomials.cc trans/local/VorDivToUVLocal.h trans/local/VorDivToUVLocal.cc trans/local/LegendreCacheCreatorLocal.h trans/local/LegendreCacheCreatorLocal.cc trans/detail/TransFactory.h trans/detail/TransFactory.cc trans/detail/TransImpl.h trans/detail/TransImpl.cc trans/detail/TransInterface.h trans/detail/TransInterface.cc ) if( atlas_HAVE_ECTRANS ) list( APPEND atlas_trans_srcs trans/ifs/LegendreCacheCreatorIFS.h trans/ifs/LegendreCacheCreatorIFS.cc trans/ifs/TransIFS.h trans/ifs/TransIFS.cc trans/ifs/TransIFSNodeColumns.h trans/ifs/TransIFSNodeColumns.cc trans/ifs/TransIFSStructuredColumns.h trans/ifs/TransIFSStructuredColumns.cc trans/ifs/VorDivToUVIFS.h trans/ifs/VorDivToUVIFS.cc ) endif() list( APPEND atlas_interpolation_srcs interpolation.h interpolation/Cache.cc interpolation/Cache.h interpolation/Interpolation.cc interpolation/Interpolation.h interpolation/NonLinear.cc interpolation/NonLinear.h interpolation/AssembleGlobalMatrix.h interpolation/AssembleGlobalMatrix.cc interpolation/method/Method.cc interpolation/method/Method.h interpolation/method/MethodFactory.cc interpolation/method/MethodFactory.h interpolation/method/PointIndex3.cc interpolation/method/PointIndex3.h interpolation/method/PointIndex2.cc interpolation/method/PointIndex2.h interpolation/method/binning/Binning.cc interpolation/method/binning/Binning.h interpolation/method/cubedsphere/CubedSphereBilinear.cc interpolation/method/cubedsphere/CubedSphereBilinear.h interpolation/method/knn/GridBox.cc interpolation/method/knn/GridBox.h interpolation/method/knn/GridBoxAverage.cc interpolation/method/knn/GridBoxAverage.h interpolation/method/knn/GridBoxMaximum.cc interpolation/method/knn/GridBoxMaximum.h interpolation/method/knn/GridBoxMethod.cc interpolation/method/knn/GridBoxMethod.h interpolation/method/knn/KNearestNeighbours.cc interpolation/method/knn/KNearestNeighbours.h interpolation/method/knn/KNearestNeighboursBase.cc interpolation/method/knn/KNearestNeighboursBase.h interpolation/method/knn/NearestNeighbour.cc interpolation/method/knn/NearestNeighbour.h interpolation/method/sphericalvector/ComplexMatrixMultiply.h interpolation/method/sphericalvector/SparseMatrix.h interpolation/method/sphericalvector/SphericalVector.cc interpolation/method/sphericalvector/SphericalVector.h interpolation/method/sphericalvector/Types.h interpolation/method/structured/Cubic2D.cc interpolation/method/structured/Cubic2D.h interpolation/method/structured/Cubic3D.cc interpolation/method/structured/Cubic3D.h interpolation/method/structured/Linear2D.cc interpolation/method/structured/Linear2D.h interpolation/method/structured/Linear3D.cc interpolation/method/structured/Linear3D.h interpolation/method/structured/QuasiCubic2D.cc interpolation/method/structured/QuasiCubic2D.h interpolation/method/structured/QuasiCubic3D.cc interpolation/method/structured/QuasiCubic3D.h interpolation/method/structured/RegionalLinear2D.cc interpolation/method/structured/RegionalLinear2D.h interpolation/method/structured/StructuredInterpolation2D.h interpolation/method/structured/StructuredInterpolation2D.tcc interpolation/method/structured/StructuredInterpolation3D.h interpolation/method/structured/StructuredInterpolation3D.tcc interpolation/method/structured/kernels/Cubic3DKernel.h interpolation/method/structured/kernels/CubicHorizontalKernel.h interpolation/method/structured/kernels/CubicVerticalKernel.h interpolation/method/structured/kernels/Linear3DKernel.h interpolation/method/structured/kernels/LinearHorizontalKernel.h interpolation/method/structured/kernels/LinearVerticalKernel.h interpolation/method/structured/kernels/QuasiCubic3DKernel.cc interpolation/method/structured/kernels/QuasiCubic3DKernel.h interpolation/method/structured/kernels/QuasiCubicHorizontalKernel.h interpolation/method/unstructured/ConservativeSphericalPolygonInterpolation.cc interpolation/method/unstructured/ConservativeSphericalPolygonInterpolation.h interpolation/method/unstructured/FiniteElement.cc interpolation/method/unstructured/FiniteElement.h interpolation/method/unstructured/SphericalMeanValue.cc interpolation/method/unstructured/SphericalMeanValue.h interpolation/method/unstructured/UnstructuredBilinearLonLat.cc interpolation/method/unstructured/UnstructuredBilinearLonLat.h interpolation/nonlinear/Missing.cc interpolation/nonlinear/Missing.h interpolation/nonlinear/NonLinear.cc interpolation/nonlinear/NonLinear.h ) list( APPEND atlas_linalg_srcs linalg/Indexing.h linalg/Introspection.h linalg/View.h linalg/sparse.h linalg/sparse/Backend.h linalg/sparse/Backend.cc linalg/sparse/MakeEckitSparseMatrix.h linalg/sparse/MakeSparseMatrixStorageEckit.h linalg/sparse/MakeSparseMatrixStorageEigen.h linalg/sparse/SparseMatrixMultiply.h linalg/sparse/SparseMatrixMultiply.tcc linalg/sparse/SparseMatrixMultiply_EckitLinalg.h linalg/sparse/SparseMatrixMultiply_EckitLinalg.cc linalg/sparse/SparseMatrixMultiply_HicSparse.h linalg/sparse/SparseMatrixMultiply_HicSparse.cc linalg/sparse/SparseMatrixMultiply_OpenMP.h linalg/sparse/SparseMatrixMultiply_OpenMP.cc linalg/sparse/SparseMatrixStorage.cc linalg/sparse/SparseMatrixStorage.h linalg/sparse/SparseMatrixToTriplets.h linalg/sparse/SparseMatrixView.h linalg/sparse/SparseMatrixTriplet.h linalg/dense.h linalg/dense/Backend.h linalg/dense/Backend.cc linalg/dense/MatrixMultiply.h linalg/dense/MatrixMultiply.tcc linalg/dense/MatrixMultiply_EckitLinalg.h linalg/dense/MatrixMultiply_EckitLinalg.cc linalg/fft.h linalg/fft/FFT.h linalg/fft/FFTW.cc linalg/fft/pocketfft.cc ) list (APPEND atlas_redistribution_srcs redistribution/Redistribution.h redistribution/Redistribution.cc redistribution/detail/RedistributionInterface.h redistribution/detail/RedistributionInterface.cc redistribution/detail/RedistributionImpl.h redistribution/detail/RedistributionImpl.cc redistribution/detail/RedistributionImplFactory.h redistribution/detail/RedistributionImplFactory.cc redistribution/detail/RedistributeGeneric.h redistribution/detail/RedistributeGeneric.cc redistribution/detail/RedistributeStructuredColumns.h redistribution/detail/RedistributeStructuredColumns.cc ) list( APPEND atlas_array_srcs array.h array_fwd.h array/Array.h array/ArrayDataStore.cc array/ArrayDataStore.h array/ArrayIdx.h array/ArrayLayout.h array/ArrayShape.h array/ArraySpec.cc array/ArraySpec.h array/ArrayStrides.h array/ArrayView.h array/ArrayViewUtil.h array/ArrayViewDefs.h array/DataType.h array/IndexView.h array/LocalView.cc array/LocalView.h array/Range.h array/Vector.h array/Vector.cc array/SVector.h array/ArrayViewVariant.h array/ArrayViewVariant.cc array/helpers/ArrayInitializer.h array/helpers/ArrayAssigner.h array/helpers/ArrayWriter.h array/helpers/ArraySlicer.h array/helpers/ArrayCopier.h array/helpers/ArrayForEach.h #array/Table.h #array/Table.cc #array/TableView.h #array/TableView.cc ) if( atlas_HAVE_GRIDTOOLS_STORAGE ) list( APPEND atlas_array_srcs array/gridtools/GridToolsArray.cc array/gridtools/GridToolsArrayHelpers.h array/gridtools/GridToolsArrayView.cc array/gridtools/GridToolsArrayView.h array/gridtools/GridToolsDataStore.h array/gridtools/GridToolsIndexView.cc array/gridtools/GridToolsIndexView.h array/gridtools/GridToolsMakeView.cc array/gridtools/GridToolsMakeView.h array/gridtools/GridToolsTraits.h ) else() list( APPEND atlas_array_srcs array/native/NativeArray.cc array/native/NativeArrayView.cc array/native/NativeArrayView.h array/native/NativeDataStore.h array/native/NativeIndexView.cc array/native/NativeIndexView.h array/native/NativeMakeView.cc ) endif() list( APPEND atlas_parallel_srcs parallel/Checksum.cc parallel/Checksum.h parallel/Collect.cc parallel/Collect.h parallel/GatherScatter.cc parallel/GatherScatter.h parallel/HaloExchange.cc parallel/HaloExchange.h parallel/Locate.cc parallel/Locate.h parallel/mpi/Buffer.h parallel/detail/Packer.h parallel/detail/Packer.cc parallel/detail/pack_index.h parallel/detail/adjoint_unpack_index.h parallel/detail/zero_index.h ) list( APPEND atlas_util_srcs util/Object.h util/Object.cc util/ObjectHandle.h util/ObjectHandle.cc util/Factory.h util/Factory.cc util/Bitflags.h util/Checksum.h util/Checksum.cc util/MicroDeg.h util/LonLatMicroDeg.h util/CoordinateEnums.h util/PeriodicTransform.h util/Topology.h util/Allocate.h util/Allocate.cc util/GPUClonable.h util/Constants.h util/ConvexSphericalPolygon.cc util/ConvexSphericalPolygon.h util/DataType.cc util/DataType.h util/Earth.h util/Geometry.cc util/Geometry.h util/GridPointsJSONWriter.cc util/GridPointsJSONWriter.h util/KDTree.cc util/KDTree.h util/PolygonXY.cc util/PolygonXY.h util/Metadata.cc util/Metadata.h util/PackVectorFields.cc util/PackVectorFields.h util/Point.cc util/Point.h util/Polygon.cc util/Polygon.h util/Rotation.cc util/Rotation.h util/Registry.h util/SphericalPolygon.cc util/SphericalPolygon.h util/UnitSphere.h util/vector.h util/VectorOfAbstract.h util/QhullSphericalTriangulation.h util/QhullSphericalTriangulation.cc util/CGALSphericalTriangulation.h util/CGALSphericalTriangulation.cc util/detail/Cache.h util/detail/KDTree.h util/detail/filesystem.cc util/detail/filesystem.h util/function/MDPI_functions.h util/function/MDPI_functions.cc util/function/SolidBodyRotation.h util/function/SolidBodyRotation.cc util/function/SphericalHarmonic.h util/function/SphericalHarmonic.cc util/function/VortexRollup.h util/function/VortexRollup.cc ) list( APPEND atlas_io_adaptor_srcs io/ArrayAdaptor.cc io/ArrayAdaptor.h io/VectorAdaptor.h ) ### atlas c++ library if( NOT atlas_HAVE_ATLAS_GRID ) unset( atlas_grid_srcs) endif() if( NOT atlas_HAVE_ATLAS_FUNCTIONSPACE ) unset( atlas_grid_partitioning_srcs ) unset( atlas_mesh_srcs ) unset( atlas_functionspace_srcs ) unset( atlas_parallel_srcs ) unset( atlas_output_srcs ) unset( atlas_redistribution_srcs ) unset( atlas_linalg_srcs ) # only depends on array endif() if( NOT atlas_HAVE_ATLAS_INTERPOLATION ) unset( atlas_interpolation_srcs ) endif() if( NOT atlas_HAVE_ATLAS_TRANS ) unset( atlas_trans_srcs ) endif() if( NOT atlas_HAVE_ATLAS_NUMERICS ) unset( atlas_numerics_srcs ) endif() # # atlas_src _________ io_adaptor # | / # array -------------- linalg # | \________________________field # util / # | / # grid / ( optional link ) # | / # mesh + functionspace + parallel # ___________________________________________________________________ # | | | | | # trans interpolation output numerics redistribution list( APPEND source_list ${atlas_srcs} ${atlas_util_srcs} ${atlas_grid_srcs} ${atlas_array_srcs} ${atlas_field_srcs} ${atlas_linalg_srcs} ${atlas_grid_partitioning_srcs} ${atlas_mesh_srcs} ${atlas_parallel_srcs} ${atlas_functionspace_srcs} ${atlas_trans_srcs} ${atlas_interpolation_srcs} ${atlas_redistribution_srcs} ${atlas_numerics_srcs} ${atlas_output_srcs} ${atlas_io_adaptor_srcs} ) if( atlas_HAVE_GPU ) include( atlas_host_device ) list( APPEND source_list parallel/detail/DevicePacker.hic ) atlas_host_device( source_list SOURCES parallel/detail/DevicePacker.hic mesh/Connectivity.cc ) if( atlas_HAVE_GRIDTOOLS_STORAGE ) atlas_host_device( source_list SOURCES array/gridtools/GridToolsArrayView.cc array/gridtools/GridToolsIndexView.cc ) else() atlas_host_device( source_list SOURCES array/native/NativeArrayView.cc array/native/NativeIndexView.cc ) endif() endif() ecbuild_add_library( TARGET atlas AUTO_VERSION INSTALL_HEADERS ALL HEADER_DESTINATION include/atlas SOURCES ${source_list} PRIVATE_LIBS $<${atlas_HAVE_FORTRAN}:fckit> $<${atlas_HAVE_ECTRANS}:transi> $<${atlas_HAVE_ACC}:atlas_acc_support> ${CGAL_LIBRARIES} ${FFTW_LIBRARIES} ${PROJ_LIBRARIES} ${QHULL_LIBRARIES} PUBLIC_LIBS eckit eckit_geometry eckit_linalg eckit_maths eckit_mpi eckit_option atlas_io hic hicsparse pluto $<${atlas_HAVE_EIGEN}:Eigen3::Eigen> $<${atlas_HAVE_OMP_CXX}:OpenMP::OpenMP_CXX> $<${atlas_HAVE_GRIDTOOLS_STORAGE}:GridTools::gridtools> PRIVATE_INCLUDES ${CGAL_INCLUDE_DIRS} ${FFTW_INCLUDES} ${POCKETFFT_INCLUDE_DIRS} ${PROJ_INCLUDE_DIRS} PUBLIC_INCLUDES $ $ $ ) if( HAVE_ACC AND CMAKE_Fortran_COMPILER_ID MATCHES NVHPC ) target_link_options( atlas INTERFACE $<$:SHELL:${ACC_LINK_OPTIONS}> $<$:SHELL:${ACC_LINK_OPTIONS}> $<$:SHELL:${ACC_LINK_OPTIONS}> ) # $<$:SHELL:${ACC_LINK_OPTIONS}> #  This only works when CUDA HOST COMPILER is NVIDIA, by default it is g++ so we cannot rely on this. # CUDA executables should set LINKER_LANGUAGE to C or CXX instead, which will have ACC_LINK_OPTIONS endif() target_compile_features( atlas PUBLIC cxx_std_17 ) atlas-0.46.0/src/atlas/domain/0000775000175000017500000000000015160212070016225 5ustar alastairalastairatlas-0.46.0/src/atlas/domain/Domain.h0000664000175000017500000001226615160212070017614 0ustar alastairalastair/* * (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/ObjectHandle.h" //--------------------------------------------------------------------------------------------------------------------- // Forward declarations namespace eckit { class Parametrisation; class Hash; } // namespace eckit //--------------------------------------------------------------------------------------------------------------------- namespace atlas { class PointXY; namespace util { class Config; } // namespace util #ifndef DOXYGEN_SHOULD_SKIP_THIS namespace domain { class Domain; class GlobalDomain; class ZonalBandDomain; class RectangularDomain; class RectangularLonLatDomain; } // namespace domain #endif //--------------------------------------------------------------------------------------------------------------------- class Domain : DOXYGEN_HIDE(public util::ObjectHandle) { public: using Spec = util::Config; public: using Handle::Handle; Domain() = default; Domain(const eckit::Parametrisation&); /// Type of the domain std::string type() const; /// Checks if the point is contained in the domain bool contains(double x, double y) const; /// Checks if the point is contained in the domain bool contains(const PointXY& p) const; // Specification of Domain Spec spec() const; /// Check if domain represents the complete globe surface bool global() const; /// Check if domain does not represent any area on the globe surface bool empty() const; /// Add domain to the given hash void hash(eckit::Hash&) const; /// Check if grid includes the North pole (can only be true when units are in /// degrees) bool containsNorthPole() const; /// Check if grid includes the South pole (can only be true when units are in /// degrees) bool containsSouthPole() const; /// String that defines units of the domain ("degrees" or "meters") std::string units() const; private: /// Output to stream void print(std::ostream&) const; friend std::ostream& operator<<(std::ostream& s, const Domain& d); }; //--------------------------------------------------------------------------------------------------------------------- class RectangularDomain : public Domain { public: using Interval = std::array; public: using Domain::Domain; RectangularDomain(): Domain() {} RectangularDomain(const Interval& x, const Interval& y, const std::string& units = "degrees"); RectangularDomain(const Domain&); operator bool() const { return domain_; } /// Checks if the x-value is contained in the domain bool contains_x(double x) const; /// Checks if the y-value is contained in the domain bool contains_y(double y) const; bool zonal_band() const; double xmin() const; double xmax() const; double ymin() const; double ymax() const; private: const ::atlas::domain::RectangularDomain* domain_; }; //--------------------------------------------------------------------------------------------------------------------- class RectangularLonLatDomain : public RectangularDomain { public: using RectangularDomain::RectangularDomain; RectangularLonLatDomain(const Interval& x, const Interval& y): RectangularDomain(x, y, "degrees") {} RectangularLonLatDomain(const double& north, const double& west, const double& south, const double& east): RectangularLonLatDomain({west, east}, {south, north}) {} operator bool() const { return RectangularDomain::operator bool() && units() == "degrees"; } double west() const { return xmin(); } double east() const { return xmax(); } double north() const { return ymax(); } double south() const { return ymin(); } }; //--------------------------------------------------------------------------------------------------------------------- class ZonalBandDomain : public RectangularLonLatDomain { public: using Interval = std::array; public: using RectangularLonLatDomain::RectangularLonLatDomain; ZonalBandDomain(const Interval& y, const double& west); ZonalBandDomain(const Interval& y); ZonalBandDomain(const Domain&); operator bool() const { return domain_; } private: const ::atlas::domain::ZonalBandDomain* domain_; }; //--------------------------------------------------------------------------------------------------------------------- class GlobalDomain : public ZonalBandDomain { public: GlobalDomain(const double& west); GlobalDomain(); GlobalDomain(const Domain&); operator bool() const { return domain_; } private: const ::atlas::domain::GlobalDomain* domain_; }; //--------------------------------------------------------------------------------------------------------------------- } // namespace atlas atlas-0.46.0/src/atlas/domain/detail/0000775000175000017500000000000015160212070017467 5ustar alastairalastairatlas-0.46.0/src/atlas/domain/detail/Domain.h0000664000175000017500000000506115160212070021051 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include "atlas/util/Config.h" #include "atlas/util/Object.h" #include "atlas/util/Point.h" namespace eckit { class Parametrisation; } namespace atlas { class Projection; namespace domain { class Domain : public util::Object { public: using Spec = util::Config; public: static const Domain* create(); // Create a global domain static const Domain* create(const eckit::Parametrisation&); virtual std::string type() const = 0; /// Checks if the point is contained in the domain virtual bool contains(double x, double y) const = 0; bool contains(const PointXY& p) const { return contains(p.x(), p.y()); } // Specification of grid virtual Spec spec() const = 0; /// Check if domain represents the complete globe surface virtual bool global() const = 0; /// Check if domain does not represent any area on the globe surface virtual bool empty() const = 0; // Unless the domain is global, we can never be sure about these functions // without knowing also the projection /// Check if grid includes the North pole virtual bool containsNorthPole() const = 0; /// Check if grid includes the South pole virtual bool containsSouthPole() const = 0; /// Output to stream virtual void print(std::ostream&) const = 0; friend std::ostream& operator<<(std::ostream& s, const Domain& d) { d.print(s); return s; } virtual std::string units() const = 0; virtual void hash(eckit::Hash&) const = 0; }; class RectangularDomain; extern "C" { const Domain* atlas__Domain__ctor_config(const eckit::Parametrisation* config); void atlas__Domain__type(const Domain* This, char*& type, int& size); void atlas__Domain__hash(const Domain* This, char*& hash, int& size); Domain::Spec* atlas__Domain__spec(const Domain* This); double atlas__LonLatRectangularDomain__north(const RectangularDomain* This); double atlas__LonLatRectangularDomain__west(const RectangularDomain* This); double atlas__LonLatRectangularDomain__south(const RectangularDomain* This); double atlas__LonLatRectangularDomain__east(const RectangularDomain* This); } } // namespace domain } // namespace atlas atlas-0.46.0/src/atlas/domain/detail/ZonalBandDomain.h0000664000175000017500000000365215160212070022646 0ustar alastairalastair/* * (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/domain/detail/RectangularDomain.h" namespace atlas { class RectangularDomain; class ZonalBandDomain; } // namespace atlas namespace atlas { namespace domain { class ZonalBandDomain : public atlas::domain::RectangularLonLatDomain { protected: static constexpr char units_[] = "degrees"; public: static bool is_global(const Interval& y); public: // constructor ZonalBandDomain(const eckit::Parametrisation&); ZonalBandDomain(const Interval&); ZonalBandDomain(const Interval&, const double west); static std::string static_type() { return "zonal_band"; } virtual std::string type() const override { return static_type(); } /// Checks if the point is contained in the domain virtual bool contains(double x, double y) const override; virtual bool global() const override { return global_; } virtual bool empty() const override { return (ymin() == ymax()); } virtual Spec spec() const override; virtual void print(std::ostream&) const override; virtual void hash(eckit::Hash&) const override; virtual std::string units() const override { return units_; } /// Check if grid includes the North pole virtual bool containsNorthPole() const override; /// Check if grid includes the South pole virtual bool containsSouthPole() const override; protected: friend class ::atlas::RectangularDomain; private: bool global_; double ymin_tol_; double ymax_tol_; }; } // namespace domain } // namespace atlas atlas-0.46.0/src/atlas/domain/detail/DomainFactory.h0000664000175000017500000000301315160212070022374 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include "atlas/util/Config.h" #include "atlas/util/Factory.h" namespace atlas { namespace domain { class Domain; //---------------------------------------------------------------------------------------------------------------------- class DomainFactory : public util::Factory { public: static std::string className() { return "DomainFactory"; } static const Domain* build(const std::string&); static const Domain* build(const std::string&, const eckit::Parametrisation&); using Factory::Factory; private: virtual const Domain* make(const eckit::Parametrisation&) = 0; }; //---------------------------------------------------------------------------------------------------------------------- template class DomainBuilder : public DomainFactory { private: virtual const Domain* make(const eckit::Parametrisation& param) { return new T(param); } public: using DomainFactory::DomainFactory; }; //---------------------------------------------------------------------------------------------------------------------- } // namespace domain } // namespace atlas atlas-0.46.0/src/atlas/domain/detail/GlobalDomain.h0000664000175000017500000000301415160212070022166 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include "atlas/domain/detail/ZonalBandDomain.h" namespace atlas { namespace domain { class GlobalDomain : public ZonalBandDomain { public: GlobalDomain(); GlobalDomain(const double west); GlobalDomain(const eckit::Parametrisation& p); static std::string static_type() { return "global"; } virtual std::string type() const override { return static_type(); } /// Checks if the point is contained in the domain virtual bool contains(double x, double y) const override { return true; } // Domain properties virtual bool global() const override { return true; } virtual bool empty() const override { return false; } virtual Spec spec() const override; virtual void print(std::ostream&) const override; virtual void hash(eckit::Hash&) const override; /// Check if grid includes the North pole virtual bool containsNorthPole() const override { return true; } /// Check if grid includes the South pole virtual bool containsSouthPole() const override { return true; } private: friend class ::atlas::RectangularDomain; }; } // namespace domain } // namespace atlas atlas-0.46.0/src/atlas/domain/detail/DomainFactory.cc0000664000175000017500000000330715160212070022540 0ustar alastairalastair/* * (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/domain/detail/Domain.h" #include "atlas/domain/detail/DomainFactory.h" #include "atlas/domain/detail/EmptyDomain.h" #include "atlas/domain/detail/GlobalDomain.h" #include "atlas/domain/detail/RectangularDomain.h" #include "atlas/domain/detail/ZonalBandDomain.h" namespace atlas { namespace domain { //---------------------------------------------------------------------------------------------------------------------- namespace { void force_link() { static struct Link { Link() { DomainBuilder(); DomainBuilder(); DomainBuilder(); DomainBuilder(); } } link; } } // namespace //---------------------------------------------------------------------------------------------------------------------- const Domain* DomainFactory::build(const std::string& builder) { return build(builder, util::NoConfig()); } const Domain* DomainFactory::build(const std::string& builder, const eckit::Parametrisation& param) { force_link(); auto factory = get(builder); return factory->make(param); } //---------------------------------------------------------------------------------------------------------------------- } // namespace domain } // namespace atlas atlas-0.46.0/src/atlas/domain/detail/ZonalBandDomain.cc0000664000175000017500000000646115160212070023005 0ustar alastairalastair/* * (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/domain/detail/DomainFactory.h" #include "atlas/domain/detail/ZonalBandDomain.h" #include "atlas/runtime/Exception.h" namespace atlas { namespace domain { namespace { static bool _is_global(double ymin, double ymax) { const double eps = 1.e-12; return std::abs((ymax - ymin) - 180.) < eps; } static std::array get_interval_y(const eckit::Parametrisation& params) { constexpr double invalid = std::numeric_limits::max(); auto is_valid = [](double y) { // deepcode ignore FloatingPointEquals: We want exact comparison return (y != invalid); }; double ymax = invalid; double ymin = invalid; if (params.get("south", ymin) || params.get("north", ymax)) { ymax = is_valid(ymax) ? ymax : 90.; ymin = is_valid(ymin) ? ymin : -90.; } else { if (!params.get("ymin", ymin)) { throw_Exception("ymin missing in Params", Here()); } if (!params.get("ymax", ymax)) { throw_Exception("ymax missing in Params", Here()); } } return {ymin, ymax}; } static double get_west(const eckit::Parametrisation& params) { double west = 0.; params.get("west", west); return west; } /* constexpr std::array interval_x() { return {0., 360.}; } */ } // namespace constexpr char ZonalBandDomain::units_[]; bool ZonalBandDomain::is_global(const Interval& y) { const double eps = 1.e-12; return std::abs(std::abs(y[1] - y[0]) - 180.) < eps; } ZonalBandDomain::ZonalBandDomain(const eckit::Parametrisation& params): ZonalBandDomain(get_interval_y(params), get_west(params)) {} ZonalBandDomain::ZonalBandDomain(const Interval& interval_y): ZonalBandDomain(interval_y, /*west*/ 0.) {} ZonalBandDomain::ZonalBandDomain(const Interval& interval_y, const double west): RectangularLonLatDomain({west, west + 360.}, interval_y) { global_ = _is_global(ymin(), ymax()); ymin_tol_ = ymin() - 1.e-6; ymax_tol_ = ymax() + 1.e-6; } bool ZonalBandDomain::contains(double /*x*/, double y) const { return contains_y(y); } ZonalBandDomain::Spec ZonalBandDomain::spec() const { Spec domain_spec; domain_spec.set("type", type()); domain_spec.set("ymin", ymin()); domain_spec.set("ymax", ymax()); if (xmin() != 0.) { domain_spec.set("west", xmin()); } return domain_spec; } void ZonalBandDomain::hash(eckit::Hash& h) const { spec().hash(h); } void ZonalBandDomain::print(std::ostream& os) const { os << "ZonalBandDomain[" << "ymin=" << ymin() << ",ymax=" << ymax() << ",west=" << xmin() << "]"; } bool ZonalBandDomain::containsNorthPole() const { return ymax_tol_ >= 90.; } bool ZonalBandDomain::containsSouthPole() const { return ymin_tol_ <= -90.; } namespace { static DomainBuilder register_builder(ZonalBandDomain::static_type()); } } // namespace domain } // namespace atlas atlas-0.46.0/src/atlas/domain/detail/EmptyDomain.cc0000664000175000017500000000226115160212070022225 0ustar alastairalastair/* * (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 "eckit/utils/Hash.h" #include "atlas/domain/detail/DomainFactory.h" #include "atlas/domain/detail/EmptyDomain.h" #include "atlas/runtime/Exception.h" namespace atlas { namespace domain { EmptyDomain::EmptyDomain() = default; EmptyDomain::EmptyDomain(const eckit::Parametrisation& p) {} EmptyDomain::Spec EmptyDomain::spec() const { Spec domain_spec; domain_spec.set("type", type()); return domain_spec; } void EmptyDomain::print(std::ostream& os) const { os << "EmptyDomain"; } void EmptyDomain::hash(eckit::Hash& h) const { h.add(type()); } std::string EmptyDomain::units() const { ATLAS_NOTIMPLEMENTED; } namespace { static DomainBuilder register_builder(EmptyDomain::static_type()); } } // namespace domain } // namespace atlas atlas-0.46.0/src/atlas/domain/detail/RectangularDomain.cc0000664000175000017500000001117415160212070023401 0ustar alastairalastair/* * (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/domain/detail/DomainFactory.h" #include "atlas/domain/detail/RectangularDomain.h" #include "atlas/runtime/Exception.h" namespace atlas { namespace domain { using Interval = RectangularDomain::Interval; namespace { static std::array get_interval_x(const eckit::Parametrisation& params) { double xmin, xmax; if (!params.get("xmin", xmin)) { throw_Exception("xmin missing in Params", Here()); } if (!params.get("xmax", xmax)) { throw_Exception("xmax missing in Params", Here()); } return {xmin, xmax}; } static std::array get_interval_y(const eckit::Parametrisation& params) { double ymin, ymax; if (!params.get("ymin", ymin)) { throw_Exception("ymin missing in Params", Here()); } if (!params.get("ymax", ymax)) { throw_Exception("ymax missing in Params", Here()); } return {ymin, ymax}; } static std::string get_units(const eckit::Parametrisation& params) { std::string units; if (!params.get("units", units)) { throw_Exception("units missing in Params", Here()); } return units; } } // namespace bool RectangularDomain::is_global(const Interval& x, const Interval& y, const std::string& units) { if (units != "degrees") { return false; } const double eps = 1.e-12; return std::abs((x[1] - x[0]) - 360.) < eps && std::abs(std::abs(y[1] - y[0]) - 180.) < eps; } bool RectangularDomain::is_zonal_band(const Interval& x, const std::string& units) { if (units != "degrees") { return false; } const double eps = 1.e-12; return std::abs((x[1] - x[0]) - 360.) < eps; } RectangularDomain::RectangularDomain(const eckit::Parametrisation& params): RectangularDomain(get_interval_x(params), get_interval_y(params), get_units(params)) {} RectangularDomain::RectangularDomain(const Interval& x, const Interval& y, const std::string& units): xmin_(x[0]), xmax_(x[1]), ymin_(y[0]), ymax_(y[1]), units_(units) { unit_degrees_ = (units_ == "degrees") ? true : false; // Make sure xmax>=xmin and ymax>=ymin if (xmin_ > xmax_) { std::swap(xmin_, xmax_); } if (ymin_ > ymax_) { std::swap(ymin_, ymax_); } global_ = is_global({xmin_, xmax_}, {ymin_, ymax_}, units_); const double tol = 1.e-6; xmin_tol_ = xmin_ - tol; ymin_tol_ = ymin_ - tol; xmax_tol_ = xmax_ + tol; ymax_tol_ = ymax_ + tol; } bool RectangularDomain::contains(double x, double y) const { return contains_x(x) and contains_y(y); } RectangularDomain::Spec RectangularDomain::spec() const { Spec domain_spec; domain_spec.set("type", type()); domain_spec.set("xmin", xmin()); domain_spec.set("xmax", xmax()); domain_spec.set("ymin", ymin()); domain_spec.set("ymax", ymax()); domain_spec.set("units", units()); return domain_spec; } void RectangularDomain::print(std::ostream& os) const { os << "RectangularDomain[" << "xmin=" << xmin() << ",xmax=" << xmax() << ",ymin=" << ymin() << ",ymax=" << ymax() << ",units=" << units() << "]"; } void RectangularDomain::hash(eckit::Hash& h) const { double multiplier = units() == "meters" ? 1e2 : 1e8; auto add_double = [&](const double& x) { h.add(std::round(x * multiplier)); }; h.add(type()); h.add(units()); add_double(xmin()); add_double(xmax()); add_double(ymin()); add_double(ymax()); } bool RectangularDomain::containsNorthPole() const { return unit_degrees_ && ymax_tol_ >= 90.; } bool RectangularDomain::containsSouthPole() const { return unit_degrees_ && ymin_tol_ <= -90.; } namespace { static DomainBuilder register_builder(RectangularDomain::static_type()); } extern "C" { double atlas__LonLatRectangularDomain__north(const RectangularDomain* This) { return This->ymax(); } double atlas__LonLatRectangularDomain__west(const RectangularDomain* This) { return This->xmin(); } double atlas__LonLatRectangularDomain__south(const RectangularDomain* This) { return This->ymin(); } double atlas__LonLatRectangularDomain__east(const RectangularDomain* This) { return This->xmax(); } } } // namespace domain } // namespace atlas atlas-0.46.0/src/atlas/domain/detail/Domain.cc0000664000175000017500000000434215160212070021210 0ustar alastairalastair/* * (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 "eckit/utils/MD5.h" #include "atlas/domain/detail/Domain.h" #include "atlas/domain/detail/DomainFactory.h" #include "atlas/projection/Projection.h" #include "atlas/runtime/Exception.h" namespace atlas { namespace domain { const Domain* Domain::create() { // default: global domain util::Config projParams; projParams.set("type", "global"); return Domain::create(projParams); } const Domain* Domain::create(const eckit::Parametrisation& p) { std::string domain_type; if (p.get("type", domain_type)) { return DomainFactory::build(domain_type, p); } // should return error here throw_Exception("type missing in Params", Here()); } //--------------------------------------------------------------------------------------------------------------------- extern "C" { const Domain* atlas__Domain__ctor_config(const eckit::Parametrisation* config) { return Domain::create(*config); } void atlas__Domain__type(const Domain* This, char*& type, int& size) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Domain"); std::string s = This->type(); size = static_cast(s.size()); type = new char[size + 1]; std::strncpy(type, s.c_str(), size + 1); } void atlas__Domain__hash(const Domain* This, char*& hash, int& size) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Domain"); eckit::MD5 md5; This->hash(md5); std::string s = md5.digest(); size = static_cast(s.size()); hash = new char[size + 1]; std::strncpy(hash, s.c_str(), size + 1); } Domain::Spec* atlas__Domain__spec(const Domain* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_Domain"); return new Domain::Spec(This->spec()); } } // extern "C" } // namespace domain } // namespace atlas atlas-0.46.0/src/atlas/domain/detail/EmptyDomain.h0000664000175000017500000000271115160212070022067 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include "atlas/domain/detail/Domain.h" namespace atlas { namespace domain { class EmptyDomain : public Domain { public: EmptyDomain(); EmptyDomain(const eckit::Parametrisation& p); /// Checks if the point is contained in the domain virtual bool contains(double x, double y) const override { return false; } static std::string static_type() { return "empty"; } virtual std::string type() const override { return static_type(); } virtual bool empty() const override { return true; } virtual bool global() const override { return false; } virtual Spec spec() const override; virtual void print(std::ostream&) const override; virtual std::string units() const override; // Not implemented virtual void hash(eckit::Hash&) const override; /// Check if grid includes the North pole virtual bool containsNorthPole() const override { return false; } /// Check if grid includes the South pole virtual bool containsSouthPole() const override { return false; } }; } // namespace domain } // namespace atlas atlas-0.46.0/src/atlas/domain/detail/RectangularDomain.h0000664000175000017500000000601615160212070023242 0ustar alastairalastair/* * (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/domain/detail/Domain.h" namespace atlas { namespace domain { class RectangularDomain : public Domain { public: using Interval = std::array; public: static bool is_global(const Interval& x, const Interval& y, const std::string& units = "degrees"); static bool is_zonal_band(const Interval& x, const std::string& units = "degrees"); public: // constructor RectangularDomain(const eckit::Parametrisation&); RectangularDomain(const Interval& x, const Interval& y, const std::string& units); static std::string static_type() { return "rectangular"; } virtual std::string type() const override { return static_type(); } /// Checks if the point is contained in the domain virtual bool contains(double x, double y) const override; /// Checks if the x-value is contained in the domain bool contains_x(double x) const { return (xmin_tol_ <= x && x <= xmax_tol_); } /// Checks if the y-value is contained in the domain bool contains_y(double y) const { return (ymin_tol_ <= y && y <= ymax_tol_); } /// Check if grid includes the North pole virtual bool containsNorthPole() const override; /// Check if grid includes the South pole virtual bool containsSouthPole() const override; virtual bool global() const override { return global_; } bool zonal_band() const { return is_zonal_band({xmin_, xmax_}, units_); } virtual bool empty() const override { // deepcode ignore FloatingPointEquals: We want exact comparison return (xmin_ == xmax_) or (ymin_ == ymax_); } virtual Spec spec() const override; virtual void print(std::ostream&) const override; virtual void hash(eckit::Hash&) const override; virtual std::string units() const override { return units_; } double xmin() const { return xmin_; } double xmax() const { return xmax_; } double ymin() const { return ymin_; } double ymax() const { return ymax_; } private: double xmin_, xmax_, ymin_, ymax_; double xmin_tol_, xmax_tol_, ymin_tol_, ymax_tol_; bool global_; std::string units_; bool unit_degrees_; }; class RectangularLonLatDomain : public RectangularDomain { public: RectangularLonLatDomain(const eckit::Parametrisation& config): RectangularDomain(config) {} RectangularLonLatDomain(const Interval& x, const Interval& y): RectangularDomain(x, y, "degrees") {} double north() const { return ymax(); } double south() const { return ymin(); } double west() const { return xmin(); } double east() const { return xmax(); } }; } // namespace domain } // namespace atlas atlas-0.46.0/src/atlas/domain/detail/GlobalDomain.cc0000664000175000017500000000304415160212070022327 0ustar alastairalastair/* * (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 "eckit/utils/Hash.h" #include "atlas/domain/detail/DomainFactory.h" #include "atlas/domain/detail/GlobalDomain.h" namespace atlas { namespace domain { namespace { constexpr std::array yrange() { return {-90., 90.}; } static double get_west(const eckit::Parametrisation& params) { double west = 0.; params.get("west", west); return west; } } // namespace GlobalDomain::GlobalDomain(const double west): ZonalBandDomain(yrange(), west) {} GlobalDomain::GlobalDomain(): ZonalBandDomain(yrange()) {} GlobalDomain::GlobalDomain(const eckit::Parametrisation& params): GlobalDomain(get_west(params)) {} GlobalDomain::Spec GlobalDomain::spec() const { Spec domain_spec; domain_spec.set("type", type()); if (xmin() != 0.) { domain_spec.set("west", xmin()); } return domain_spec; } void GlobalDomain::hash(eckit::Hash& h) const { h.add(type()); } void GlobalDomain::print(std::ostream& os) const { os << "GlobalDomain[west=" << xmin() << "]"; } namespace { static DomainBuilder register_builder(GlobalDomain::static_type()); } } // namespace domain } // namespace atlas atlas-0.46.0/src/atlas/domain/Domain.cc0000664000175000017500000001020615160212070017742 0ustar alastairalastair/* * (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/domain/Domain.h" #include "atlas/domain/detail/Domain.h" #include "atlas/domain/detail/EmptyDomain.h" #include "atlas/domain/detail/GlobalDomain.h" #include "atlas/domain/detail/RectangularDomain.h" #include "atlas/domain/detail/ZonalBandDomain.h" using RD = atlas::domain::RectangularDomain; using ZD = atlas::domain::ZonalBandDomain; namespace atlas { Domain::Domain(const eckit::Parametrisation& p): Handle(atlas::domain::Domain::create(p)) {} RectangularDomain::RectangularDomain(const Interval& x, const Interval& y, const std::string& units): Domain((RD::is_global(x, y, units)) ? new atlas::domain::GlobalDomain(x[0]) : (RD::is_zonal_band(x, units) ? new atlas::domain::ZonalBandDomain(y, x[0]) : new atlas::domain::RectangularDomain(x, y, units))), domain_(dynamic_cast(get())) {} RectangularDomain::RectangularDomain(const Domain& domain): Domain(domain), domain_(dynamic_cast(get())) {} bool RectangularDomain::contains_x(double x) const { return domain_->contains_x(x); } bool RectangularDomain::contains_y(double y) const { return domain_->contains_y(y); } bool RectangularDomain::zonal_band() const { return domain_->zonal_band(); } double RectangularDomain::xmin() const { return domain_->xmin(); } double RectangularDomain::xmax() const { return domain_->xmax(); } double RectangularDomain::ymin() const { return domain_->ymin(); } double RectangularDomain::ymax() const { return domain_->ymax(); } ZonalBandDomain::ZonalBandDomain(const Interval& y): RectangularLonLatDomain((ZD::is_global(y)) ? new atlas::domain::GlobalDomain() : new atlas::domain::ZonalBandDomain(y)), domain_(dynamic_cast(get())) {} ZonalBandDomain::ZonalBandDomain(const Interval& y, const double& west): RectangularLonLatDomain((ZD::is_global(y)) ? new atlas::domain::GlobalDomain(west) : new atlas::domain::ZonalBandDomain(y, west)), domain_(dynamic_cast(get())) {} ZonalBandDomain::ZonalBandDomain(const Domain& domain): RectangularLonLatDomain(domain), domain_(dynamic_cast(get())) {} GlobalDomain::GlobalDomain(const double& west): ZonalBandDomain(new atlas::domain::GlobalDomain(west)), domain_(dynamic_cast(get())) {} GlobalDomain::GlobalDomain(): ZonalBandDomain(new atlas::domain::GlobalDomain()), domain_(dynamic_cast(get())) {} GlobalDomain::GlobalDomain(const Domain& domain): ZonalBandDomain(domain), domain_(dynamic_cast(get())) {} std::string atlas::Domain::type() const { return get()->type(); } bool Domain::contains(double x, double y) const { return get()->contains(x, y); } bool Domain::contains(const PointXY& p) const { return get()->contains(p); } std::string Domain::units() const { return get()->units(); } Domain::Spec Domain::spec() const { return get()->spec(); } bool Domain::global() const { return get()->global(); } bool Domain::empty() const { return get()->empty(); } void Domain::hash(eckit::Hash& h) const { get()->hash(h); } bool Domain::containsNorthPole() const { return get()->containsNorthPole(); } bool Domain::containsSouthPole() const { return get()->containsSouthPole(); } void Domain::print(std::ostream& os) const { get()->print(os); } std::ostream& operator<<(std::ostream& os, const Domain& d) { d.print(os); return os; } } // namespace atlas atlas-0.46.0/src/atlas/functionspace.h0000664000175000017500000000144715160212070017776 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ /// @author Willem Deconinck #pragma once #include "atlas/functionspace/CellColumns.h" #include "atlas/functionspace/CubedSphereColumns.h" #include "atlas/functionspace/EdgeColumns.h" #include "atlas/functionspace/FunctionSpace.h" #include "atlas/functionspace/NodeColumns.h" #include "atlas/functionspace/PointCloud.h" #include "atlas/functionspace/Spectral.h" #include "atlas/functionspace/StructuredColumns.h" atlas-0.46.0/src/atlas/functionspace/0000775000175000017500000000000015160212070017617 5ustar alastairalastairatlas-0.46.0/src/atlas/functionspace/NodeColumns.cc0000664000175000017500000006427015160212070022365 0ustar alastairalastair/* * (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/MD5.h" #include "atlas/array.h" #include "atlas/field/Field.h" #include "atlas/field/FieldSet.h" #include "atlas/functionspace/NodeColumns.h" #include "atlas/grid/Grid.h" #include "atlas/grid/UnstructuredGrid.h" #include "atlas/library/config.h" #include "atlas/mesh/IsGhostNode.h" #include "atlas/mesh/Mesh.h" #include "atlas/mesh/Nodes.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/Trace.h" #include "atlas/util/detail/Cache.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(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 NodeColumnsHaloExchangeCache : public util::Cache, public mesh::detail::MeshObserver { private: using Base = util::Cache; NodeColumnsHaloExchangeCache(): Base("NodeColumnsHaloExchangeCache") {} public: static NodeColumnsHaloExchangeCache& instance() { static NodeColumnsHaloExchangeCache inst; return inst; } util::ObjectHandle get_or_create(const Mesh& mesh, long halo) { registerMesh(*mesh.get()); creator_type creator = std::bind(&NodeColumnsHaloExchangeCache::create, mesh, halo); return Base::get_or_create(key(*mesh.get(), halo), creator); } void onMeshDestruction(mesh::detail::MeshImpl& mesh) override { for (long jhalo = 0; jhalo <= mesh::Halo(mesh).size(); ++jhalo) { remove(key(mesh, jhalo)); } } private: static Base::key_type key(const mesh::detail::MeshImpl& mesh, long halo) { std::ostringstream key; key << "mesh[address=" << &mesh << "],halo[size=" << halo << "]"; return key.str(); } static value_type* create(const Mesh& mesh, long halo) { value_type* value = new value_type(); std::ostringstream ss; ss << "nb_nodes_including_halo[" << halo << "]"; idx_t nb_nodes(mesh.nodes().size()); mesh.metadata().get(ss.str(), nb_nodes); value->setup(mesh.mpi_comm(), array::make_view(mesh.nodes().partition()).data(), array::make_view(mesh.nodes().remote_index()).data(), REMOTE_IDX_BASE, nb_nodes); return value; } }; class NodeColumnsGatherScatterCache : public util::Cache, public mesh::detail::MeshObserver { private: using Base = util::Cache; NodeColumnsGatherScatterCache(): Base("NodeColumnsGatherScatterCache") {} public: static NodeColumnsGatherScatterCache& instance() { static NodeColumnsGatherScatterCache inst; return inst; } util::ObjectHandle get_or_create(const Mesh& mesh) { registerMesh(*mesh.get()); creator_type creator = std::bind(&NodeColumnsGatherScatterCache::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) { value_type* value = new value_type(); mesh::IsGhostNode is_ghost(mesh.nodes()); std::vector mask(mesh.nodes().size()); const idx_t npts = mask.size(); atlas_omp_parallel_for(idx_t n = 0; n < npts; ++n) { mask[n] = is_ghost(n) ? 1 : 0; // --> This would add periodic west-bc to the gather, but means that // global-sums, means, etc are computed wrong // if( mask[j] == 1 && // internals::Topology::check(flags(j),internals::Topology::BC) ) { // mask[j] = 0; //} } value->setup(mesh.mpi_comm(), array::make_view(mesh.nodes().partition()).data(), array::make_view(mesh.nodes().remote_index()).data(), REMOTE_IDX_BASE, array::make_view(mesh.nodes().global_index()).data(), mask.data(), mesh.nodes().size()); return value; } }; class NodeColumnsChecksumCache : public util::Cache, public mesh::detail::MeshObserver { private: using Base = util::Cache; NodeColumnsChecksumCache(): Base("NodeColumnsChecksumCache") {} public: static NodeColumnsChecksumCache& instance() { static NodeColumnsChecksumCache inst; return inst; } util::ObjectHandle get_or_create(const Mesh& mesh) { registerMesh(*mesh.get()); creator_type creator = std::bind(&NodeColumnsChecksumCache::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) { value_type* value = new value_type(); util::ObjectHandle gather( NodeColumnsGatherScatterCache::instance().get_or_create(mesh)); value->setup(gather); return value; } }; NodeColumns::NodeColumns(Mesh mesh): NodeColumns(mesh, util::NoConfig()) {} NodeColumns::NodeColumns(Mesh mesh, const eckit::Configuration& config): mesh_(mesh), nodes_(mesh_.nodes()), nb_levels_(config.getInt("levels", 0)), nb_nodes_(0) { ATLAS_TRACE(); if (config.has("halo")) { halo_ = mesh::Halo(config.getInt("halo")); } else { halo_ = mesh::Halo(mesh); } mesh::actions::build_nodes_parallel_fields(mesh_); mesh::actions::build_periodic_boundaries(mesh_); if (halo_.size() > 0) { mesh::actions::build_halo(mesh_, halo_.size()); std::stringstream ss; ss << "nb_nodes_including_halo[" << halo_.size() << "]"; mesh_.metadata().get(ss.str(), nb_nodes_); } if (!nb_nodes_) { std::stringstream ss; ss << "nb_nodes_including_halo[" << halo_.size() << "]"; if (!mesh_.metadata().get(ss.str(), nb_nodes_)) { nb_nodes_ = mesh_.nodes().size(); } } if (mesh_.grid()) { grid_ = mesh_.grid(); } } NodeColumns::~NodeColumns() = default; std::string NodeColumns::distribution() const { return mesh().metadata().getString("distribution"); } idx_t NodeColumns::nb_nodes() const { return nb_nodes_; } idx_t NodeColumns::nb_nodes_global() const { if (nb_nodes_global_ >= 0) { return nb_nodes_global_; } if (Grid grid = mesh().grid()) { nb_nodes_global_ = grid.size(); } else { nb_nodes_global_ = gather().glb_dof(); } return nb_nodes_global_; } idx_t NodeColumns::config_nb_nodes(const eckit::Configuration& config) const { idx_t size = nb_nodes(); bool global(false); if (config.get("global", global)) { if (global) { idx_t owner(0); config.get("owner", owner); idx_t _nb_nodes_global = nb_nodes_global(); idx_t rank = mpi::comm(mpi_comm()).rank(); size = (rank == owner ? _nb_nodes_global : 0); } } return size; } void NodeColumns::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); if (config.has("type")) { field.metadata().set("type", config.getString("type")); } if (config.has("vector_component")) { field.metadata().set("vector_component", config.getSubConfiguration("vector_component")); } } array::DataType NodeColumns::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 NodeColumns::config_name(const eckit::Configuration& config) const { std::string name; config.get("name", name); return name; } idx_t NodeColumns::config_levels(const eckit::Configuration& config) const { idx_t levels(nb_levels_); config.get("levels", levels); return levels; } array::ArrayShape NodeColumns::config_shape(const eckit::Configuration& config) const { array::ArrayShape shape; shape.push_back(config_nb_nodes(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; } Field NodeColumns::createField(const eckit::Configuration& config) const { Field field = Field(config_name(config), config_datatype(config), config_shape(config)); set_field_metadata(config, field); return field; } Field NodeColumns::createField(const Field& other, const eckit::Configuration& config) const { return createField(option::name(other.name()) | 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); } template void dispatch_adjointHaloExchange(Field& field, const parallel::HaloExchange& halo_exchange, bool on_device) { if (field.datatype() == array::DataType::kind()) { halo_exchange.template execute_adjoint(field.array(), on_device); } else if (field.datatype() == array::DataType::kind()) { halo_exchange.template execute_adjoint(field.array(), on_device); } else if (field.datatype() == array::DataType::kind()) { halo_exchange.template execute_adjoint(field.array(), on_device); } else if (field.datatype() == array::DataType::kind()) { halo_exchange.template execute_adjoint(field.array(), on_device); } else { throw_Exception("datatype not supported", Here()); } field.set_dirty(false); } } // namespace void NodeColumns::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()); } } } void NodeColumns::adjointHaloExchange(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_adjointHaloExchange<1>(field, halo_exchange(), on_device); break; case 2: dispatch_adjointHaloExchange<2>(field, halo_exchange(), on_device); break; case 3: dispatch_adjointHaloExchange<3>(field, halo_exchange(), on_device); break; case 4: dispatch_adjointHaloExchange<4>(field, halo_exchange(), on_device); break; default: throw_Exception("Rank not supported", Here()); } } } void NodeColumns::haloExchange(const Field& field, bool on_device) const { FieldSet fieldset; fieldset.add(field); haloExchange(fieldset, on_device); } void NodeColumns::adjointHaloExchange(const Field& field, bool) const { FieldSet fieldset; fieldset.add(field); adjointHaloExchange(fieldset); } const parallel::HaloExchange& NodeColumns::halo_exchange() const { if (halo_exchange_) { return *halo_exchange_; } halo_exchange_ = NodeColumnsHaloExchangeCache::instance().get_or_create(mesh_, halo_.size()); return *halo_exchange_; } void NodeColumns::gather(const FieldSet& local_fieldset, FieldSet& global_fieldset) const { ATLAS_ASSERT(local_fieldset.size() == global_fieldset.size()); mpi::Scope mpi_scope(mpi_comm()); 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 NodeColumns::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& NodeColumns::gather() const { if (gather_scatter_) { return *gather_scatter_; } gather_scatter_ = NodeColumnsGatherScatterCache::instance().get_or_create(mesh_); return *gather_scatter_; } const parallel::GatherScatter& NodeColumns::scatter() const { if (gather_scatter_) { return *gather_scatter_; } gather_scatter_ = NodeColumnsGatherScatterCache::instance().get_or_create(mesh_); return *gather_scatter_; } void NodeColumns::scatter(const FieldSet& global_fieldset, FieldSet& local_fieldset) const { ATLAS_ASSERT(local_fieldset.size() == global_fieldset.size()); mpi::Scope mpi_scope(mpi_comm()); 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()); } auto name = loc.name(); glb.metadata().broadcast(loc.metadata(), root); loc.metadata().set("global", false); if( !name.empty() ) { loc.metadata().set("name", name); } loc.set_dirty(true); } } void NodeColumns::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) { auto values = make_leveled_view(field); array::ArrayT surface_field(values.shape(0), values.shape(2)); auto surface = array::make_view(surface_field); const idx_t npts = values.shape(0); atlas_omp_for(idx_t n = 0; n < npts; ++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)); } } // namespace std::string NodeColumns::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()) { md5 << checksum_3d_field(checksum(), field); } else if (field.datatype() == array::DataType::kind()) { md5 << checksum_3d_field(checksum(), field); } else if (field.datatype() == array::DataType::kind()) { md5 << checksum_3d_field(checksum(), field); } else if (field.datatype() == array::DataType::kind()) { md5 << checksum_3d_field(checksum(), field); } else { throw_Exception("datatype not supported", Here()); } } return md5; } std::string NodeColumns::checksum(const Field& field) const { FieldSet fieldset; fieldset.add(field); return checksum(fieldset); } const parallel::Checksum& NodeColumns::checksum() const { if (checksum_) { return *checksum_; } checksum_ = NodeColumnsChecksumCache::instance().get_or_create(mesh_); return *checksum_; } // std::string NodesFunctionSpace::checksum( const FieldSet& fieldset ) const { // const parallel::Checksum& checksum = mesh_.checksum().get(checksum_name()); // eckit::MD5 md5; // for( idx_t f=0; f() ) // md5 << checksum.execute( field.data(), field.stride(0) ); // else if( field.datatype() == array::DataType::kind() ) // md5 << checksum.execute( field.data(), field.stride(0) ); // else if( field.datatype() == array::DataType::kind() ) // md5 << checksum.execute( field.data(), field.stride(0) ); // else if( field.datatype() == array::DataType::kind() ) // md5 << checksum.execute( field.data(), field.stride(0) ); // else throw_Exception("datatype not supported",Here()); // } // return md5; //} // std::string NodesFunctionSpace::checksum( const Field& field ) const { // FieldSet fieldset; // fieldset.add(field); // return checksum(fieldset); //} const Grid& NodeColumns::grid() const { if (grid_) { return grid_; } const auto& comm = mpi::comm(mpi_comm()); std::vector points; if (comm.size() == 1) { const auto xy = atlas::array::make_view(mesh_.nodes().xy()); for (auto i = 0; i < xy.shape(0); i++) { points.push_back({xy(i, 0), xy(i, 1)}); } } else { std::vector gidx; std::vector x, y; const auto gidxView = array::make_view(global_index()); const auto ghostView = array::make_view(ghost()); const auto xy = atlas::array::make_view(mesh_.nodes().xy()); for (auto i = 0; i < xy.shape(0); i++) { if (ghostView(i) == 0) { gidx.push_back(gidxView(i)); x.push_back(xy(i, 0)); y.push_back(xy(i, 1)); } } eckit::mpi::Buffer gidxBuffer(comm.size()); eckit::mpi::Buffer xBuffer(comm.size()); eckit::mpi::Buffer yBuffer(comm.size()); comm.allGatherv(gidx.begin(), gidx.end(), gidxBuffer); comm.allGatherv(x.begin(), x.end(), xBuffer); comm.allGatherv(y.begin(), y.end(), yBuffer); points.reserve(gidxBuffer.buffer.size()); for (auto i : gidxBuffer.buffer) { points[i - 1] = atlas::PointXY{xBuffer.buffer[i - 1], yBuffer.buffer[i - 1]}; } } grid_ = UnstructuredGrid(points); return grid_; } } // namespace detail NodeColumns::NodeColumns(): FunctionSpace(), functionspace_(nullptr) {} NodeColumns::NodeColumns(const FunctionSpace& functionspace): FunctionSpace(functionspace), functionspace_(dynamic_cast(get())) {} namespace { detail::NodeColumns* make_functionspace(Mesh mesh, const eckit::Configuration& config) { return new detail::NodeColumns(mesh, config); } } // namespace NodeColumns::NodeColumns(Mesh mesh): FunctionSpace(make_functionspace(mesh, util::NoConfig())), functionspace_(dynamic_cast(get())) {} NodeColumns::NodeColumns(Mesh mesh, const eckit::Configuration& config): FunctionSpace(make_functionspace(mesh, config)), functionspace_(dynamic_cast(get())) {} idx_t NodeColumns::nb_nodes() const { return functionspace_->nb_nodes(); } idx_t NodeColumns::nb_nodes_global() const { // All MPI ranks will have same output return functionspace_->nb_nodes_global(); } const Mesh& NodeColumns::mesh() const { return functionspace_->mesh(); } mesh::Nodes& NodeColumns::nodes() const { return functionspace_->nodes(); } // -- Parallelisation aware methods const mesh::Halo& NodeColumns::halo() const { return functionspace_->halo(); } void NodeColumns::haloExchange(const FieldSet& fieldset, bool on_device) const { functionspace_->haloExchange(fieldset, on_device); } void NodeColumns::haloExchange(const Field& field, bool on_device) const { functionspace_->haloExchange(field, on_device); } const parallel::HaloExchange& NodeColumns::halo_exchange() const { return functionspace_->halo_exchange(); } std::string NodeColumns::checksum(const FieldSet& fieldset) const { return functionspace_->checksum(fieldset); } std::string NodeColumns::checksum(const Field& field) const { return functionspace_->checksum(field); } const parallel::Checksum& NodeColumns::checksum() const { return functionspace_->checksum(); } } // namespace functionspace } // namespace atlas atlas-0.46.0/src/atlas/functionspace/CellColumns.h0000664000175000017500000001735615160212070022224 0ustar alastairalastair/* * (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 functionspace::FunctionSpaceImpl { public: CellColumns(const Mesh&, const eckit::Configuration& = util::NoConfig()); ~CellColumns() override; static std::string static_type() { return "CellColumns"; } std::string type() const override { return static_type(); } 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 idx_t levels() const; 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 Field createField(const eckit::Configuration&) const override; Field createField(const Field&, const eckit::Configuration&) const override; // -- Parallelisation aware methods const mesh::Halo& halo() const { return halo_; } void haloExchange(const FieldSet&, bool on_device = false) const override; void haloExchange(const Field&, bool on_device = false) const override; const parallel::HaloExchange& halo_exchange() const; void gather(const FieldSet&, FieldSet&) const override; void gather(const Field&, Field&) const override; const parallel::GatherScatter& gather() const override; void scatter(const FieldSet&, FieldSet&) const override; void scatter(const Field&, Field&) const override; const parallel::GatherScatter& scatter() const override; std::string checksum(const FieldSet&) const; std::string checksum(const Field&) const; const parallel::Checksum& checksum() const; idx_t size() const override { return nb_cells_; } const Grid& grid() const override; Field lonlat() const override; Field remote_index() const override; Field global_index() const override; Field ghost() const override; Field partition() const override; std::string mpi_comm() const override { return mesh_.mpi_comm(); } 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; size_t footprint() const override; private: // data mutable Grid grid_; 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: using Implementation = detail::CellColumns; public: CellColumns(); CellColumns(const FunctionSpace&); CellColumns(const Mesh&, const eckit::Configuration&); CellColumns(const Mesh& mesh); static std::string type() { return detail::CellColumns::static_type(); } 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 idx_t levels() const; const Mesh& mesh() const; const mesh::HybridElements& cells() const; // -- Parallelisation aware methods const mesh::Halo& halo() const; const parallel::HaloExchange& halo_exchange() 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 atlas-0.46.0/src/atlas/functionspace/BlockStructuredColumns.h0000664000175000017500000000734015160212070024454 0ustar alastairalastair/* * (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/functionspace/FunctionSpace.h" #include "atlas/functionspace/StructuredColumns.h" #include "atlas/functionspace/detail/BlockStructuredColumns.h" namespace atlas { namespace functionspace { // ------------------------------------------------------------------- class BlockStructuredColumns : public FunctionSpace { public: class Block; public: BlockStructuredColumns(); BlockStructuredColumns(const FunctionSpace&); BlockStructuredColumns(const Grid&, const eckit::Configuration& = util::NoConfig()); BlockStructuredColumns(const Grid&, const grid::Partitioner&, const eckit::Configuration& = util::NoConfig()); BlockStructuredColumns(const Grid&, const grid::Distribution&, const eckit::Configuration& = util::NoConfig()); BlockStructuredColumns(const Grid&, const Vertical&, const eckit::Configuration& = util::NoConfig()); BlockStructuredColumns(const Grid&, const Vertical&, const grid::Partitioner&, const eckit::Configuration& = util::NoConfig()); BlockStructuredColumns(const Grid&, const grid::Distribution&, const Vertical&, const eckit::Configuration& = util::NoConfig()); static std::string type() { return detail::BlockStructuredColumns::static_type(); } operator bool() const { return valid(); } bool valid() const { return functionspace_; } idx_t size() const { return functionspace_->size(); } idx_t levels() const { return functionspace_->levels(); } const Vertical& vertical() const { return functionspace_->vertical(); } const StructuredGrid& grid() const { return functionspace_->grid(); } std::string checksum(const FieldSet&) const; std::string checksum(const Field&) const; idx_t index(idx_t blk, idx_t rof) const { return functionspace_->index(blk, rof); } idx_t k_begin() const { return functionspace_->k_begin(); } idx_t k_end() const { return functionspace_->k_end(); } idx_t nproma() const { return functionspace_->nproma(); } idx_t nblks() const { return functionspace_->nblks(); } Field xy() const { return functionspace_->xy(); } Field partition() const { return functionspace_->partition(); } Field global_index() const { return functionspace_->global_index(); } Field remote_index() const { return functionspace_->remote_index(); } Field index_i() const { return functionspace_->index_i(); } Field index_j() const { return functionspace_->index_j(); } Field ghost() const { return functionspace_->ghost(); } const Block block(idx_t jblk) const { return Block(functionspace_->block_begin(jblk), functionspace_->block_size(jblk)); } size_t footprint() const { return functionspace_->footprint(); } class Block { public: Block(idx_t begin, idx_t size) : begin_(begin), size_(size) {} idx_t index(idx_t j) const { return begin_ + j; }; idx_t size() const { return size_; } private: idx_t begin_; idx_t size_; }; private: const detail::BlockStructuredColumns* functionspace_; void setup(const Grid& grid, const Vertical& vertical, const grid::Distribution& distribution, const eckit::Configuration& config); }; // ------------------------------------------------------------------- } // namespace functionspace } // namespace atlas atlas-0.46.0/src/atlas/functionspace/CubedSphereColumns.cc0000664000175000017500000001346015160212070023664 0ustar alastairalastair/* * (C) Crown Copyright 2021 Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ #include "atlas/functionspace/CubedSphereColumns.h" #include "atlas/field/Field.h" #include "atlas/mesh/HybridElements.h" #include "atlas/mesh/Mesh.h" #include "atlas/mesh/Nodes.h" #include "atlas/util/detail/Cache.h" namespace atlas { namespace functionspace { // Helper functions to get fields. namespace { template Field getTij(const Mesh& mesh); template Field getGhost(const Mesh& mesh); template <> Field getTij(const Mesh& mesh) { return mesh.nodes().field("tij"); } template <> Field getTij(const Mesh& mesh) { return mesh.cells().field("tij"); } template <> Field getGhost(const Mesh& mesh) { return mesh.nodes().ghost(); } template <> Field getGhost(const Mesh& mesh) { // No ghost field for CellColumns. Halo field is next best thing. return mesh.cells().halo(); } } // namespace namespace { template class CubedSphereStructureCache : public util::Cache, public mesh::detail::MeshObserver { private: using Base = util::Cache; CubedSphereStructureCache(): Base("CubedSphereStructureCache<" + BaseFunctionSpace::type() + ">") {} public: static CubedSphereStructureCache& instance() { static CubedSphereStructureCache inst; return inst; } util::ObjectHandle get_or_create(const BaseFunctionSpace* functionspace) { ATLAS_ASSERT(functionspace); auto& mesh = functionspace->mesh(); ATLAS_ASSERT(mesh); auto& mesh_impl = *mesh.get(); registerMesh(mesh_impl); creator_type creator = std::bind(&CubedSphereStructureCache::create, mesh, functionspace->size()); util::ObjectHandle value = Base::get_or_create(key(mesh_impl), creator); return value; } 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, idx_t size) { value_type* value = new value_type(getTij(mesh), getGhost(mesh), size); return value; } }; } // namespace // All constructors pass arguments through to BaseFunctionSpace, then construct // CubedSphereStructure. template CubedSphereColumns::CubedSphereColumns(): BaseFunctionSpace(), cubedSphereColumnsHandle_(new detail::CubedSphereStructure()) {} template CubedSphereColumns::CubedSphereColumns(const FunctionSpace& functionspace): BaseFunctionSpace([&]() { bool compatible = dynamic_cast(functionspace.get()); if (not compatible) { ATLAS_THROW_EXCEPTION("FunctionSpace " << functionspace.type() << " can not be interpreted as a " << BaseFunctionSpace::type()); } return functionspace; }()), cubedSphereColumnsHandle_(CubedSphereStructureCache::instance().get_or_create(this)) {} template CubedSphereColumns::CubedSphereColumns(const Mesh& mesh, const eckit::Configuration& configuration): BaseFunctionSpace(mesh, configuration), cubedSphereColumnsHandle_(CubedSphereStructureCache::instance().get_or_create(this)) {} template CubedSphereColumns::CubedSphereColumns(const Mesh& mesh): BaseFunctionSpace(mesh), cubedSphereColumnsHandle_(CubedSphereStructureCache::instance().get_or_create(this)) {} template idx_t CubedSphereColumns::invalid_index() const { return cubedSphereColumnsHandle_.get()->invalid_index(); } template idx_t CubedSphereColumns::sizeOwned() const { return cubedSphereColumnsHandle_.get()->sizeOwned(); } template idx_t CubedSphereColumns::i_begin(idx_t t) const { return cubedSphereColumnsHandle_.get()->i_begin(t); } template idx_t CubedSphereColumns::i_end(idx_t t) const { return cubedSphereColumnsHandle_.get()->i_end(t); } template idx_t CubedSphereColumns::j_begin(idx_t t) const { return cubedSphereColumnsHandle_.get()->j_begin(t); } template idx_t CubedSphereColumns::j_end(idx_t t) const { return cubedSphereColumnsHandle_.get()->j_end(t); } template idx_t CubedSphereColumns::index(idx_t t, idx_t i, idx_t j) const { return cubedSphereColumnsHandle_.get()->index(t, i, j); } template bool CubedSphereColumns::is_valid_index(idx_t t, idx_t i, idx_t j) const { return cubedSphereColumnsHandle_.get()->is_valid_index(t, i, j); } template Field CubedSphereColumns::tij() const { return cubedSphereColumnsHandle_.get()->tij(); } // Explicit instantiation of template classes. template class CubedSphereColumns; template class CubedSphereColumns; } // namespace functionspace } // namespace atlas atlas-0.46.0/src/atlas/functionspace/PointCloud.h0000664000175000017500000002035015160212070022050 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF * (C) Crown Copyright 2023 Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include "atlas/array/ArrayView.h" #include "atlas/field/Field.h" #include "atlas/field/FieldSet.h" #include "atlas/functionspace/FunctionSpace.h" #include "atlas/functionspace/detail/FunctionSpaceImpl.h" #include "atlas/grid/Grid.h" #include "atlas/parallel/HaloExchange.h" #include "atlas/parallel/GatherScatter.h" #include "atlas/runtime/Exception.h" #include "atlas/util/Config.h" #include "atlas/util/Point.h" #include "atlas/grid/Partitioner.h" namespace atlas { namespace parallel { class HaloExchange; class GatherScatter; } // namespace parallel } // namespace atlas namespace atlas { class Grid; namespace functionspace { //------------------------------------------------------------------------------------------------------ namespace detail { class PointCloud : public functionspace::FunctionSpaceImpl { public: template PointCloud(const std::vector&, const eckit::Configuration& = util::NoConfig()); PointCloud(const Field& lonlat, const eckit::Configuration& = util::NoConfig()); PointCloud(const Field& lonlat, const Field& ghost, const eckit::Configuration& = util::NoConfig()); PointCloud(const FieldSet&, const eckit::Configuration& = util::NoConfig()); // assuming lonlat ghost ridx and partition present PointCloud(const Grid&, const eckit::Configuration& = util::NoConfig()); PointCloud(const Grid&, const grid::Distribution&, const eckit::Configuration& = util::NoConfig()); PointCloud(const Grid&, const grid::Partitioner&, const eckit::Configuration& = util::NoConfig()); ~PointCloud() override {} std::string type() const override { return "PointCloud"; } operator bool() const override { return true; } size_t footprint() const override { return sizeof(*this); } std::string distribution() const override; const Grid& grid() const override; Field lonlat() const override { return lonlat_; } const Field& vertical() const { return vertical_; } Field ghost() const override; Field remote_index() const override { return remote_index_; } Field global_index() const override { return global_index_; } Field partition() const override { return partition_; } idx_t size() const override { return lonlat_.shape(0); } idx_t part() const override { return part_; } idx_t nb_parts() const override { return nb_partitions_; } std::string mpi_comm() const override { return mpi_comm_; } using FunctionSpaceImpl::createField; Field createField(const eckit::Configuration&) const override; Field createField(const Field&, const eckit::Configuration&) const override; void haloExchange(const FieldSet&, bool on_device = false) const override; void haloExchange(const Field&, bool on_device = false) const override; void adjointHaloExchange(const FieldSet&, bool on_device = false) const override; void adjointHaloExchange(const Field&, bool on_device = false) const override; const parallel::HaloExchange& halo_exchange() const; void gather(const FieldSet&, FieldSet&) const override; void gather(const Field&, Field&) const override; const parallel::GatherScatter& gather() const override; void scatter(const FieldSet&, FieldSet&) const override; void scatter(const Field&, Field&) const override; const parallel::GatherScatter& scatter() const override; template class IteratorT { public: using difference_type = std::ptrdiff_t; using value_type = Point; using pointer = Point*; using reference = Point&; using iterator_category = std::output_iterator_tag; IteratorT(const PointCloud& fs, bool begin = true); bool next(Point&); const Point operator*() const; const IteratorT& operator++() { ++n_; return *this; } bool operator==(const IteratorT& other) const { return n_ == other.n_; } bool operator!=(const IteratorT& other) const { return n_ != other.n_; } private: const PointCloud& fs_; const array::ArrayView xy_; const array::ArrayView z_; idx_t n_; idx_t size_; }; template class IterateT { public: using value_type = Point; using iterator = IteratorT; using const_iterator = iterator; public: IterateT(const PointCloud& fs): fs_(fs) {} iterator begin() const { return IteratorT(fs_); } iterator end() const { return IteratorT(fs_, false); } idx_t size() const { return fs_.size(); } private: const PointCloud& fs_; }; class Iterate { public: Iterate(const PointCloud& fs): fs_(fs) {} IterateT xyz() const { ATLAS_ASSERT(fs_.vertical()); return IterateT{fs_}; } IterateT xy() const { return IterateT{fs_}; } IterateT lonlat() const { return IterateT{fs_}; } private: const PointCloud& fs_; }; Iterate iterate() const { return Iterate(*this); } private: array::ArrayShape config_shape(const eckit::Configuration& config) const; array::ArrayAlignment config_alignment(const eckit::Configuration& config) const; array::ArraySpec config_spec(const eckit::Configuration& config) const; array::DataType config_datatype(const eckit::Configuration& config) const; std::string config_name(const eckit::Configuration& config) const; void set_field_metadata(const eckit::Configuration& config, Field& field) const; void create_remote_index() const; idx_t size_global() const; private: mutable Grid grid_; Field lonlat_; Field vertical_; mutable Field ghost_; mutable Field remote_index_; Field global_index_; Field partition_; idx_t size_owned_; mutable idx_t size_global_{-1}; idx_t max_glb_idx_{0}; idx_t levels_{0}; idx_t part_{0}; idx_t nb_partitions_{1}; std::string mpi_comm_; bool parallel_{false}; mutable std::unique_ptr halo_exchange_; mutable std::unique_ptr gather_scatter_; void setupParallel(); void setupHaloExchange(); void setupGatherScatter(); }; //------------------------------------------------------------------------------------------------------ } // namespace detail //------------------------------------------------------------------------------------------------------ class PointCloud : public FunctionSpace { public: PointCloud(const FunctionSpace&); PointCloud(const Field& points, const eckit::Configuration& = util::NoConfig()); PointCloud(const Field&, const Field&, const eckit::Configuration& = util::NoConfig()); PointCloud(const FieldSet& flds, const eckit::Configuration& = util::NoConfig()); PointCloud(const std::vector&, const eckit::Configuration& = util::NoConfig()); PointCloud(const std::vector&, const eckit::Configuration& = util::NoConfig()); PointCloud(const std::initializer_list>&, const eckit::Configuration& = util::NoConfig()); PointCloud(const Grid&, const eckit::Configuration& = util::NoConfig()); PointCloud(const Grid&, const grid::Distribution&, const eckit::Configuration& = util::NoConfig()); PointCloud(const Grid&, const grid::Partitioner&, const eckit::Configuration& = util::NoConfig()); operator bool() const { return valid(); } bool valid() const { return functionspace_; } const Field& vertical() const { return functionspace_->vertical(); } detail::PointCloud::Iterate iterate() const { return functionspace_->iterate(); } private: const detail::PointCloud* functionspace_; }; } // namespace functionspace } // namespace atlas atlas-0.46.0/src/atlas/functionspace/EdgeColumns.h0000664000175000017500000001656015160212070022205 0ustar alastairalastair/* * (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 EdgeColumns : public functionspace::FunctionSpaceImpl { public: EdgeColumns(const Mesh&, const eckit::Configuration& = util::NoConfig()); ~EdgeColumns() override; std::string type() const override { return "Edges"; } std::string distribution() const override; idx_t nb_edges() const; idx_t nb_edges_global() const; // Only on MPI rank 0, will this be different from 0 std::vector nb_edges_global_foreach_rank() const; const Mesh& mesh() const { return mesh_; } Mesh& mesh() { return mesh_; } const mesh::HybridElements& edges() const { return edges_; } mesh::HybridElements& edges() { return edges_; } // -- Field creation methods Field createField(const eckit::Configuration&) const override; Field createField(const Field&, const eckit::Configuration&) const override; // -- Parallelisation aware methods void haloExchange(const FieldSet&, bool on_device = false) const override; void haloExchange(const Field&, bool on_device = false) const override; const parallel::HaloExchange& halo_exchange() const; void gather(const FieldSet&, FieldSet&) const override; void gather(const Field&, Field&) const override; const parallel::GatherScatter& gather() const override; void scatter(const FieldSet&, FieldSet&) const override; void scatter(const Field&, Field&) const override; const parallel::GatherScatter& scatter() const override; std::string checksum(const FieldSet&) const; std::string checksum(const Field&) const; const parallel::Checksum& checksum() const; idx_t size() const override { return nb_edges_; } const Grid& grid() const override; Field lonlat() const override; Field global_index() const override; Field remote_index() const override; Field partition() const override; std::string mpi_comm() const override { return mesh_.mpi_comm(); } 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; size_t footprint() const override; private: // data mutable Grid grid_; Mesh mesh_; // non-const because functionspace may modify mesh mesh::HybridElements& edges_; // non-const because functionspace may modify mesh idx_t nb_levels_; mesh::Halo halo_; idx_t nb_edges_; mutable long nb_edges_global_{-1}; mutable util::ObjectHandle gather_scatter_; // without ghost mutable util::ObjectHandle halo_exchange_; mutable util::ObjectHandle checksum_; }; // ------------------------------------------------------------------- extern "C" { EdgeColumns* atlas__fs__EdgeColumns__new(Mesh::Implementation* mesh, const eckit::Configuration* config); void atlas__fs__EdgeColumns__delete(EdgeColumns* This); int atlas__fs__EdgeColumns__nb_edges(const EdgeColumns* This); Mesh::Implementation* atlas__fs__EdgeColumns__mesh(EdgeColumns* This); mesh::Edges* atlas__fs__EdgeColumns__edges(EdgeColumns* This); field::FieldImpl* atlas__fs__EdgeColumns__create_field(const EdgeColumns* This, const eckit::Configuration* options); field::FieldImpl* atlas__fs__EdgeColumns__create_field_template(const EdgeColumns* This, const field::FieldImpl* field_template, const eckit::Configuration* options); void atlas__fs__EdgeColumns__halo_exchange_fieldset(const EdgeColumns* This, field::FieldSetImpl* fieldset); void atlas__fs__EdgeColumns__halo_exchange_field(const EdgeColumns* This, field::FieldImpl* field); const parallel::HaloExchange* atlas__fs__EdgeColumns__get_halo_exchange(const EdgeColumns* This); void atlas__fs__EdgeColumns__gather_fieldset(const EdgeColumns* This, const field::FieldSetImpl* local, field::FieldSetImpl* global); void atlas__fs__EdgeColumns__gather_field(const EdgeColumns* This, const field::FieldImpl* local, field::FieldImpl* global); const parallel::GatherScatter* atlas__fs__EdgeColumns__get_gather(const EdgeColumns* This); void atlas__fs__EdgeColumns__scatter_fieldset(const EdgeColumns* This, const field::FieldSetImpl* global, field::FieldSetImpl* local); void atlas__fs__EdgeColumns__scatter_field(const EdgeColumns* This, const field::FieldImpl* global, field::FieldImpl* local); const parallel::GatherScatter* atlas__fs__EdgeColumns__get_scatter(const EdgeColumns* This); void atlas__fs__EdgeColumns__checksum_fieldset(const EdgeColumns* This, const field::FieldSetImpl* fieldset, char*& checksum, int& size, int& allocated); void atlas__fs__EdgeColumns__checksum_field(const EdgeColumns* This, const field::FieldImpl* field, char*& checksum, int& size, int& allocated); const parallel::Checksum* atlas__fs__EdgeColumns__get_checksum(const EdgeColumns* This); } } // namespace detail // ------------------------------------------------------------------- class EdgeColumns : public FunctionSpace { public: EdgeColumns(); EdgeColumns(const FunctionSpace&); EdgeColumns(const Mesh&, const eckit::Configuration&); EdgeColumns(const Mesh& mesh); operator bool() const { return valid(); } bool valid() const { return functionspace_; } idx_t nb_edges() const; idx_t nb_edges_global() const; // Only on MPI rank 0, will this be different from 0 const Mesh& mesh() const; const mesh::HybridElements& edges() const; // -- Parallelisation aware methods const parallel::HaloExchange& halo_exchange() const; std::string checksum(const FieldSet&) const; std::string checksum(const Field&) const; const parallel::Checksum& checksum() const; private: const detail::EdgeColumns* functionspace_; }; } // namespace functionspace } // namespace atlas atlas-0.46.0/src/atlas/functionspace/EdgeColumns.cc0000664000175000017500000007344215160212070022345 0ustar alastairalastair/* * (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/field/detail/FieldImpl.h" #include "atlas/functionspace/EdgeColumns.h" #include "atlas/grid/UnstructuredGrid.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/BuildEdges.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(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 EdgeColumnsHaloExchangeCache : public util::Cache, public mesh::detail::MeshObserver { private: using Base = util::Cache; EdgeColumnsHaloExchangeCache(): Base("EdgeColumnsHaloExchangeCache") {} public: static EdgeColumnsHaloExchangeCache& instance() { static EdgeColumnsHaloExchangeCache inst; return inst; } util::ObjectHandle get_or_create(const Mesh& mesh) { registerMesh(*mesh.get()); creator_type creator = std::bind(&EdgeColumnsHaloExchangeCache::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) { value_type* value = new value_type(); value->setup(mesh.mpi_comm(), array::make_view(mesh.edges().partition()).data(), array::make_view(mesh.edges().remote_index()).data(), REMOTE_IDX_BASE, mesh.edges().size()); return value; } }; class EdgeColumnsGatherScatterCache : public util::Cache, public mesh::detail::MeshObserver { private: using Base = util::Cache; EdgeColumnsGatherScatterCache(): Base("EdgeColumnsGatherScatterCache") {} public: static EdgeColumnsGatherScatterCache& instance() { static EdgeColumnsGatherScatterCache inst; return inst; } util::ObjectHandle get_or_create(const Mesh& mesh) { registerMesh(*mesh.get()); creator_type creator = std::bind(&EdgeColumnsGatherScatterCache::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) { value_type* value = new value_type(); value->setup(mesh.mpi_comm(), array::make_view(mesh.edges().partition()).data(), array::make_view(mesh.edges().remote_index()).data(), REMOTE_IDX_BASE, array::make_view(mesh.edges().global_index()).data(), mesh.edges().size()); return value; } }; class EdgeColumnsChecksumCache : public util::Cache, public mesh::detail::MeshObserver { private: using Base = util::Cache; EdgeColumnsChecksumCache(): Base("EdgeColumnsChecksumCache") {} public: static EdgeColumnsChecksumCache& instance() { static EdgeColumnsChecksumCache inst; return inst; } util::ObjectHandle get_or_create(const Mesh& mesh) { registerMesh(*mesh.get()); creator_type creator = std::bind(&EdgeColumnsChecksumCache::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) { value_type* value = new value_type(); util::ObjectHandle gather( EdgeColumnsGatherScatterCache::instance().get_or_create(mesh)); value->setup(gather); return value; } }; void EdgeColumns::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 EdgeColumns::config_size(const eckit::Configuration& config) const { idx_t size = nb_edges(); bool global(false); if (config.get("global", global)) { if (global) { idx_t owner(0); config.get("owner", owner); idx_t _nb_edges_global(nb_edges_global()); const idx_t rank = mpi::comm(mpi_comm()).rank(); size = (rank == owner ? _nb_edges_global : 0); } } return size; } array::DataType EdgeColumns::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 EdgeColumns::config_name(const eckit::Configuration& config) const { std::string name; config.get("name", name); return name; } idx_t EdgeColumns::config_levels(const eckit::Configuration& config) const { idx_t levels(nb_levels_); config.get("levels", levels); return levels; } array::ArrayShape EdgeColumns::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; } EdgeColumns::EdgeColumns(const Mesh& mesh, const eckit::Configuration& config): mesh_(mesh), edges_(mesh_.edges()), nb_levels_(config.getInt("levels", 0)), nb_edges_(0) { ATLAS_TRACE(); if (config.has("halo")) { halo_ = mesh::Halo(config.getInt("halo")); } else { halo_ = mesh::Halo(mesh_); } auto get_nb_edges_from_metadata = [&]() { idx_t _nb_edges(0); std::stringstream ss; ss << "nb_edges_including_halo[" << halo_.size() << "]"; mesh_.metadata().get(ss.str(), _nb_edges); return _nb_edges; }; mesh::actions::build_nodes_parallel_fields(mesh_); mesh::actions::build_periodic_boundaries(mesh_); if (halo_.size() > 0) { mesh::actions::build_halo(mesh_, halo_.size()); nb_edges_ = get_nb_edges_from_metadata(); } if (!nb_edges_) { mesh::actions::build_edges(mesh_, config); mesh::actions::build_edges_parallel_fields(mesh_); nb_edges_ = get_nb_edges_from_metadata(); } ATLAS_ASSERT(nb_edges_); if (mesh_.grid()) { grid_ = mesh_.grid(); } } EdgeColumns::~EdgeColumns() = default; size_t EdgeColumns::footprint() const { size_t size = sizeof(*this); // TODO return size; } std::string EdgeColumns::distribution() const { return mesh().metadata().getString("distribution"); } idx_t EdgeColumns::nb_edges() const { return nb_edges_; } idx_t EdgeColumns::nb_edges_global() const { if (nb_edges_global_ >= 0) { return nb_edges_global_; } nb_edges_global_ = gather().glb_dof(); return nb_edges_global_; } Field EdgeColumns::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 EdgeColumns::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 EdgeColumns::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 EdgeColumns::haloExchange(const Field& field, bool on_device) const { FieldSet fieldset; fieldset.add(field); haloExchange(fieldset, on_device); } const parallel::HaloExchange& EdgeColumns::halo_exchange() const { if (halo_exchange_) { return *halo_exchange_; } halo_exchange_ = EdgeColumnsHaloExchangeCache::instance().get_or_create(mesh_); return *halo_exchange_; } void EdgeColumns::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 EdgeColumns::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& EdgeColumns::gather() const { if (gather_scatter_) { return *gather_scatter_; } gather_scatter_ = EdgeColumnsGatherScatterCache::instance().get_or_create(mesh_); return *gather_scatter_; } const parallel::GatherScatter& EdgeColumns::scatter() const { if (gather_scatter_) { return *gather_scatter_; } gather_scatter_ = EdgeColumnsGatherScatterCache::instance().get_or_create(mesh_); return *gather_scatter_; } void EdgeColumns::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); loc.set_dirty(true); } } void EdgeColumns::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) { auto values = array::make_view(field); array::ArrayT surface_field(field.shape(0), field.shape(2)); auto 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) { auto values = array::make_view(field); return checksum.execute(values.data(), field.stride(0)); } } // namespace std::string EdgeColumns::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 EdgeColumns::checksum(const Field& field) const { FieldSet fieldset; fieldset.add(field); return checksum(fieldset); } const parallel::Checksum& EdgeColumns::checksum() const { if (checksum_) { return *checksum_; } checksum_ = EdgeColumnsChecksumCache::instance().get_or_create(mesh_); return *checksum_; } const Grid& EdgeColumns::grid() const { if (grid_) { return grid_; } const auto& comm = mpi::comm(mpi_comm()); std::vector points; if (comm.size() == 1) { const auto xy = atlas::array::make_view(mesh_.nodes().xy()); for (auto i = 0; i < xy.shape(0); i++) { points.push_back({xy(i, 0), xy(i, 1)}); } } else { std::vector gidx; std::vector x, y; const auto gidxView = array::make_view(global_index()); const auto ghostView = array::make_view(ghost()); const auto xy = atlas::array::make_view(mesh_.nodes().xy()); for (auto i = 0; i < xy.shape(0); i++) { if (ghostView(i) == 0) { gidx.push_back(gidxView(i)); x.push_back(xy(i, 0)); y.push_back(xy(i, 1)); } } eckit::mpi::Buffer gidxBuffer(comm.size()); eckit::mpi::Buffer xBuffer(comm.size()); eckit::mpi::Buffer yBuffer(comm.size()); comm.allGatherv(gidx.begin(), gidx.end(), gidxBuffer); comm.allGatherv(x.begin(), x.end(), xBuffer); comm.allGatherv(y.begin(), y.end(), yBuffer); points.reserve(gidxBuffer.buffer.size()); for (auto i : gidxBuffer.buffer) { points[i - 1] = atlas::PointXY{xBuffer.buffer[i - 1], yBuffer.buffer[i - 1]}; } } grid_ = UnstructuredGrid(points); return grid_; } Field EdgeColumns::lonlat() const { return edges().field("lonlat"); } Field EdgeColumns::global_index() const { return edges().global_index(); }; Field EdgeColumns::remote_index() const { return edges().remote_index(); }; Field EdgeColumns::partition() const { return edges().partition(); } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ extern "C" { //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ EdgeColumns* atlas__fs__EdgeColumns__new(Mesh::Implementation* mesh, const eckit::Configuration* config) { ATLAS_ASSERT(mesh != nullptr); Mesh m(mesh); return new EdgeColumns(m, *config); } //------------------------------------------------------------------------------ void atlas__fs__EdgeColumns__delete(EdgeColumns* This) { ATLAS_ASSERT(This != nullptr); delete (This); } //------------------------------------------------------------------------------ int atlas__fs__EdgeColumns__nb_edges(const EdgeColumns* This) { ATLAS_ASSERT(This != nullptr); return This->nb_edges(); } //------------------------------------------------------------------------------ Mesh::Implementation* atlas__fs__EdgeColumns__mesh(EdgeColumns* This) { ATLAS_ASSERT(This != nullptr); return This->mesh().get(); } //------------------------------------------------------------------------------ mesh::Edges* atlas__fs__EdgeColumns__edges(EdgeColumns* This) { ATLAS_ASSERT(This != nullptr); return &This->edges(); } //------------------------------------------------------------------------------ using field::FieldImpl; using field::FieldSetImpl; field::FieldImpl* atlas__fs__EdgeColumns__create_field(const EdgeColumns* 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__EdgeColumns__create_field_template(const EdgeColumns* 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__EdgeColumns__halo_exchange_fieldset(const EdgeColumns* This, field::FieldSetImpl* fieldset) { ATLAS_ASSERT(This != nullptr); ATLAS_ASSERT(fieldset != nullptr); FieldSet f(fieldset); This->haloExchange(f); } // ----------------------------------------------------------------------------------- void atlas__fs__EdgeColumns__halo_exchange_field(const EdgeColumns* This, field::FieldImpl* field) { ATLAS_ASSERT(This != nullptr); ATLAS_ASSERT(field != nullptr); Field f(field); This->haloExchange(f); } // ----------------------------------------------------------------------------------- const parallel::HaloExchange* atlas__fs__EdgeColumns__get_halo_exchange(const EdgeColumns* This) { ATLAS_ASSERT(This != nullptr); return &This->halo_exchange(); } // ----------------------------------------------------------------------------------- void atlas__fs__EdgeColumns__gather_fieldset(const EdgeColumns* 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__EdgeColumns__gather_field(const EdgeColumns* 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__EdgeColumns__get_gather(const EdgeColumns* This) { ATLAS_ASSERT(This); return &This->gather(); } // ----------------------------------------------------------------------------------- const parallel::GatherScatter* atlas__fs__EdgeColumns__get_scatter(const EdgeColumns* This) { ATLAS_ASSERT(This); return &This->scatter(); } // ----------------------------------------------------------------------------------- void atlas__fs__EdgeColumns__scatter_fieldset(const EdgeColumns* 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__EdgeColumns__scatter_field(const EdgeColumns* 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__EdgeColumns__get_checksum(const EdgeColumns* This) { ATLAS_ASSERT(This); return &This->checksum(); } // ----------------------------------------------------------------------------------- void atlas__fs__EdgeColumns__checksum_fieldset(const EdgeColumns* 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 = static_cast(checksum_str.size()); checksum = new char[size + 1]; allocated = true; std::strncpy(checksum, checksum_str.c_str(), size + 1); } // ----------------------------------------------------------------------------------- void atlas__fs__EdgeColumns__checksum_field(const EdgeColumns* 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 = static_cast(checksum_str.size()); checksum = new char[size + 1]; allocated = true; std::strncpy(checksum, checksum_str.c_str(), size + 1); } } // ----------------------------------------------------------------------------------- } // namespace detail // ----------------------------------------------------------------------------------- EdgeColumns::EdgeColumns(): FunctionSpace(), functionspace_(nullptr) {} EdgeColumns::EdgeColumns(const FunctionSpace& functionspace): FunctionSpace(functionspace), functionspace_(dynamic_cast(get())) {} EdgeColumns::EdgeColumns(const Mesh& mesh, const eckit::Configuration& config): FunctionSpace(new detail::EdgeColumns(mesh, config)), functionspace_(dynamic_cast(get())) {} EdgeColumns::EdgeColumns(const Mesh& mesh): FunctionSpace(new detail::EdgeColumns(mesh)), functionspace_(dynamic_cast(get())) {} idx_t EdgeColumns::nb_edges() const { return functionspace_->nb_edges(); } idx_t EdgeColumns::nb_edges_global() const { // Only on MPI rank 0, will this be different from 0 return functionspace_->nb_edges_global(); } const Mesh& EdgeColumns::mesh() const { return functionspace_->mesh(); } const mesh::HybridElements& EdgeColumns::edges() const { return functionspace_->edges(); } const parallel::HaloExchange& EdgeColumns::halo_exchange() const { return functionspace_->halo_exchange(); } std::string EdgeColumns::checksum(const FieldSet& fieldset) const { return functionspace_->checksum(fieldset); } std::string EdgeColumns::checksum(const Field& field) const { return functionspace_->checksum(field); } const parallel::Checksum& EdgeColumns::checksum() const { return functionspace_->checksum(); } } // namespace functionspace } // namespace atlas atlas-0.46.0/src/atlas/functionspace/FunctionSpace.h0000664000175000017500000000700115160212070022527 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include "atlas/library/config.h" #include "atlas/util/ObjectHandle.h" namespace eckit { class Configuration; } namespace atlas { class Field; class FieldSet; class Grid; class Projection; namespace functionspace { class FunctionSpaceImpl; class HaloDescription; } namespace parallel { class GatherScatter; } // namespace parallel namespace util { class PartitionPolygon; class PartitionPolygons; } // namespace util } // namespace atlas namespace atlas { //------------------------------------------------------------------------------------------------------ class FunctionSpace : DOXYGEN_HIDE(public util::ObjectHandle) { public: using Handle::Handle; FunctionSpace(); std::string type() const; operator bool() const; size_t footprint() const; std::string distribution() const; Field createField(const eckit::Configuration&) const; Field createField(const Field&) const; Field createField(const Field&, const eckit::Configuration&) const; template Field createField(const eckit::Configuration&) const; template Field createField() const; void haloExchange(const FieldSet&, bool on_device = false) const; void haloExchange(const Field&, bool on_device = false) const; void adjointHaloExchange(const FieldSet&, bool on_device = false) const; void adjointHaloExchange(const Field&, bool on_device = false) const; void gather(const FieldSet&, FieldSet&) const; void gather(const Field&, Field&) const; void scatter(const FieldSet&, FieldSet&) const; void scatter(const Field&, Field&) const; const util::PartitionPolygon& polygon(idx_t halo = 0) const; const util::PartitionPolygons& polygons() const; const Projection& projection() const; idx_t part() const; idx_t nb_parts() const; idx_t size() const; const Grid& grid() const; Field lonlat() const; Field ghost() const; Field global_index() const; Field remote_index() const; Field partition() const; const functionspace::HaloDescription& halo_description() const; const parallel::GatherScatter& gather() const; const parallel::GatherScatter& scatter() const; std::string mpi_comm() const; }; //------------------------------------------------------------------------------------------------------ extern template Field FunctionSpace::createField() const; extern template Field FunctionSpace::createField() const; extern template Field FunctionSpace::createField() const; extern template Field FunctionSpace::createField() const; extern template Field FunctionSpace::createField(const eckit::Configuration&) const; extern template Field FunctionSpace::createField(const eckit::Configuration&) const; extern template Field FunctionSpace::createField(const eckit::Configuration&) const; extern template Field FunctionSpace::createField(const eckit::Configuration&) const; //------------------------------------------------------------------------------------------------------ } // namespace atlas atlas-0.46.0/src/atlas/functionspace/StructuredColumns.cc0000664000175000017500000000600715160212070023636 0ustar alastairalastair/* * (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/functionspace/StructuredColumns.h" namespace atlas { namespace functionspace { // ---------------------------------------------------------------------------- StructuredColumns::StructuredColumns(): FunctionSpace(), functionspace_(nullptr) {} StructuredColumns::StructuredColumns(const FunctionSpace& functionspace): FunctionSpace(functionspace), functionspace_(dynamic_cast(get())) {} StructuredColumns::StructuredColumns(const Grid& grid, const eckit::Configuration& config): FunctionSpace(new detail::StructuredColumns(grid, config)), functionspace_(dynamic_cast(get())) {} StructuredColumns::StructuredColumns(const Grid& grid, const grid::Partitioner& partitioner, const eckit::Configuration& config): FunctionSpace(new detail::StructuredColumns(grid, partitioner, config)), functionspace_(dynamic_cast(get())) {} StructuredColumns::StructuredColumns(const Grid& grid, const grid::Distribution& distribution, const eckit::Configuration& config): FunctionSpace(new detail::StructuredColumns(grid, distribution, config)), functionspace_(dynamic_cast(get())) {} StructuredColumns::StructuredColumns(const Grid& grid, const Vertical& vertical, const eckit::Configuration& config): FunctionSpace(new detail::StructuredColumns(grid, vertical, config)), functionspace_(dynamic_cast(get())) {} StructuredColumns::StructuredColumns(const Grid& grid, const Vertical& vertical, const grid::Partitioner& partitioner, const eckit::Configuration& config): FunctionSpace(new detail::StructuredColumns(grid, vertical, partitioner, config)), functionspace_(dynamic_cast(get())) {} StructuredColumns::StructuredColumns(const Grid& grid, const grid::Distribution& distribution, const Vertical& vertical, const eckit::Configuration& config): FunctionSpace(new detail::StructuredColumns(grid, distribution, vertical, config)), functionspace_(dynamic_cast(get())) {} std::string StructuredColumns::checksum(const FieldSet& fieldset) const { return functionspace_->checksum(fieldset); } std::string StructuredColumns::checksum(const Field& field) const { return functionspace_->checksum(field); } // ---------------------------------------------------------------------------- } // namespace functionspace } // namespace atlas atlas-0.46.0/src/atlas/functionspace/HaloDescription.h0000664000175000017500000000172415160212070023063 0ustar alastairalastair#pragma once #include #include "atlas/library/config.h" namespace atlas::functionspace { class HaloDescription { public: HaloDescription() = default; HaloDescription(idx_t size) : halo_size_(size) { } HaloDescription(idx_t begin, idx_t end) : halo_begin_(begin), halo_end_(end), halo_size_(halo_end_ - halo_begin_) { } template HaloDescription(const View& ghost) { for (idx_t i=0; itype(); } FunctionSpace::operator bool() const { return get()->operator bool(); } size_t FunctionSpace::footprint() const { return get()->footprint(); } const Grid& FunctionSpace::grid() const { return get()->grid(); } Field FunctionSpace::createField(const eckit::Configuration& config) const { return get()->createField(config); } Field FunctionSpace::createField(const Field& other) const { return get()->createField(other); } Field FunctionSpace::createField(const Field& other, const eckit::Configuration& config) const { return get()->createField(other, config); } std::string FunctionSpace::distribution() const { return get()->distribution(); } void FunctionSpace::haloExchange(const Field& field, bool on_device) const { get()->haloExchange(field, on_device); } void FunctionSpace::adjointHaloExchange(const Field& field, bool on_device) const { get()->adjointHaloExchange(field, on_device); } idx_t FunctionSpace::size() const { return get()->size(); } idx_t FunctionSpace::part() const { return get()->part(); } idx_t FunctionSpace::nb_parts() const { return get()->nb_parts(); } Field FunctionSpace::lonlat() const { return get()->lonlat(); } Field FunctionSpace::ghost() const { return get()->ghost(); } Field FunctionSpace::global_index() const { return get()->global_index(); } Field FunctionSpace::remote_index() const { return get()->remote_index(); } Field FunctionSpace::partition() const { return get()->partition(); } void FunctionSpace::haloExchange(const FieldSet& fields, bool on_device) const { get()->haloExchange(fields, on_device); } void FunctionSpace::adjointHaloExchange(const FieldSet& fields, bool on_device) const { get()->adjointHaloExchange(fields, on_device); } void FunctionSpace::gather(const FieldSet& local, FieldSet& global) const { get()->gather(local, global); } void FunctionSpace::gather(const Field& local, Field& global) const { get()->gather(local, global); } void FunctionSpace::scatter(const FieldSet& global, FieldSet& local) const { get()->scatter(global, local); } void FunctionSpace::scatter(const Field& global, Field& local) const { get()->scatter(global, local); } const parallel::GatherScatter& FunctionSpace::gather() const { return get()->gather(); } const parallel::GatherScatter& FunctionSpace::scatter() const { return get()->scatter(); } std::string FunctionSpace::mpi_comm() const { return get()->mpi_comm(); } const util::PartitionPolygon& FunctionSpace::polygon(idx_t halo) const { return get()->polygon(halo); } const util::PartitionPolygons& FunctionSpace::polygons() const { return get()->polygons(); } const Projection& FunctionSpace::projection() const { return get()->projection(); } const functionspace::HaloDescription& FunctionSpace::halo_description() const { return get()->halo_description(); } template Field FunctionSpace::createField() const { return get()->createField(); } template Field FunctionSpace::createField(const eckit::Configuration& options) const { return get()->createField(options); } template Field FunctionSpace::createField() const; template Field FunctionSpace::createField() const; template Field FunctionSpace::createField() const; template Field FunctionSpace::createField() const; template Field FunctionSpace::createField(const eckit::Configuration&) const; template Field FunctionSpace::createField(const eckit::Configuration&) const; template Field FunctionSpace::createField(const eckit::Configuration&) const; template Field FunctionSpace::createField(const eckit::Configuration&) const; // ------------------------------------------------------------------ } // namespace atlas atlas-0.46.0/src/atlas/functionspace/PointCloud.cc0000664000175000017500000012635115160212070022216 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF * (C) Crown Copyright 2023 Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include #include #include "atlas/functionspace/PointCloud.h" #include "atlas/array.h" #include "atlas/field/Field.h" #include "atlas/field/FieldSet.h" #include "atlas/grid/Distribution.h" #include "atlas/grid/Grid.h" #include "atlas/grid/Iterator.h" #include "atlas/grid/UnstructuredGrid.h" #include "atlas/option/Options.h" #include "atlas/parallel/HaloExchange.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/runtime/Exception.h" #include "atlas/util/KDTree.h" #include "atlas/util/CoordinateEnums.h" #include "atlas/util/Metadata.h" #include "atlas/util/Point.h" #include "atlas/util/Unique.h" #include "eckit/mpi/Comm.h" #include "eckit/log/Bytes.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(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 static std::string get_mpi_comm(const eckit::Configuration& config) { if(config.has("mpi_comm")) { return config.getString("mpi_comm"); } return mpi::comm().name(); } template <> PointCloud::PointCloud(const std::vector& points, const eckit::Configuration& config) { ATLAS_TRACE("PointCloud(std::vector, eckit::Configuration)"); mpi_comm_ = get_mpi_comm(config); lonlat_ = Field("lonlat", array::make_datatype(), array::make_shape(points.size(), 2)); auto lonlat = array::make_view(lonlat_); for (idx_t j = 0, size = points.size(); j < size; ++j) { lonlat(j, 0) = points[j].x(); lonlat(j, 1) = points[j].y(); } } template <> PointCloud::PointCloud(const std::vector& points, const eckit::Configuration& config) { ATLAS_TRACE("PointCloud(std::vector, eckit::Configuration)"); mpi_comm_ = get_mpi_comm(config); lonlat_ = Field("lonlat", array::make_datatype(), array::make_shape(points.size(), 2)); vertical_ = Field("vertical", array::make_datatype(), array::make_shape(points.size())); auto lonlat = array::make_view(lonlat_); auto vertical = array::make_view(vertical_); for (idx_t j = 0, size = points.size(); j < size; ++j) { lonlat(j, 0) = points[j].x(); lonlat(j, 1) = points[j].y(); vertical(j) = points[j].z(); } } PointCloud::PointCloud(const Field& lonlat, const eckit::Configuration& config): lonlat_(lonlat) { ATLAS_TRACE("PointCloud(Field lonlat, eckit::Configuration)"); mpi_comm_ = get_mpi_comm(config); } PointCloud::PointCloud(const Field& lonlat, const Field& ghost, const eckit::Configuration& config): lonlat_(lonlat), ghost_(ghost) { ATLAS_TRACE("PointCloud(Field lonlat, Field ghost, eckit::Configuration)"); mpi_comm_ = get_mpi_comm(config); setupParallel(); } PointCloud::PointCloud(const FieldSet& flds, const eckit::Configuration& config): lonlat_(flds["lonlat"]) { ATLAS_TRACE("PointCloud(Fieldset, eckit::Configuration)"); mpi_comm_ = get_mpi_comm(config); if (flds.has("ghost")) { ghost_ = flds["ghost"]; } if (flds.has("remote_index")) { remote_index_ = flds["remote_index"]; } if (flds.has("partition")) { partition_ = flds["partition"]; } if (flds.has("global_index")) { global_index_ = flds["global_index"]; } if( ghost_ && remote_index_ && partition_ ) { setupParallel(); } } grid::Partitioner make_partitioner(const Grid& grid, const eckit::Configuration& config) { auto mpi_comm = get_mpi_comm(config); auto partitioner = grid.partitioner(); if( config.has("partitioner") ) { partitioner.set("type",config.getString("partitioner")); } if( not partitioner.has("type") ) { partitioner.set("type","equal_regions"); } partitioner.set("mpi_comm",mpi_comm); return grid::Partitioner(partitioner); } PointCloud::PointCloud(const Grid& grid, const eckit::Configuration& config) : PointCloud(grid, make_partitioner(grid,config), config) { } PointCloud::PointCloud(const Grid& grid, const grid::Distribution& distribution, const eckit::Configuration& config) { ATLAS_TRACE("PointCloud(grid,distribution,config)"); mpi_comm_ = get_mpi_comm(config); auto& comm = mpi::comm(mpi_comm_); double halo_radius; config.get("halo_radius", halo_radius = 0.); grid_ = grid; part_ = comm.rank(); nb_partitions_ = distribution.nb_partitions(); auto size_owned = distribution.nb_pts()[part_]; size_owned_ = size_owned; size_global_ = grid.size(); if (halo_radius == 0. || nb_partitions_ == 1) { idx_t size_halo = size_owned; ATLAS_ASSERT(size_owned > 0); lonlat_ = Field("lonlat", array::make_datatype(), array::make_shape(size_halo, 2)); ghost_ = Field("ghost", array::make_datatype(), array::make_shape(size_halo)); global_index_ = Field("global_index", array::make_datatype(), array::make_shape(size_halo)); partition_ = Field("partition", array::make_datatype(), array::make_shape(size_halo)); remote_index_ = Field("remote_index", array::make_datatype(), array::make_shape(size_halo)); auto lonlat = array::make_view(lonlat_); auto ridx = array::make_indexview(remote_index_); auto gidx = array::make_view(global_index_); array::make_view(ghost_).assign(0); array::make_view(partition_).assign(part_); ATLAS_ASSERT(grid.size() == distribution.size()); idx_t j{0}; gidx_t g{0}; for (auto p : grid.lonlat()) { if( distribution.partition(g) == part_ ) { ATLAS_ASSERT(j < size_halo); gidx(j) = g+1; ridx(j) = j; lonlat(j, 0) = p.lon(); lonlat(j, 1) = p.lat(); ++j; } ++g; } } else { std::vector keep; { ATLAS_TRACE("Build list of points to keep"); std::vector owned_lonlat; owned_lonlat.reserve(size_owned); auto kdtree = util::IndexKDTree(config); { ATLAS_TRACE("build kdtree"); kdtree.reserve(grid.size()); idx_t j{0}; for (auto p : grid.lonlat()) { if( distribution.partition(j) == part_ ) { owned_lonlat.emplace_back(p); } kdtree.insert(p,j); ++j; } kdtree.build(); } keep.resize(grid.size()); { ATLAS_TRACE("search kdtree"); for (idx_t j=0; j(), array::make_shape(size_halo, 2)); partition_ = Field("partition", array::make_datatype(), array::make_shape(size_halo)); ghost_ = Field("ghost", array::make_datatype(), array::make_shape(size_halo)); global_index_ = Field("global_index", array::make_datatype(), array::make_shape(size_halo)); max_glb_idx_ = grid.size(); auto lonlat = array::make_view(lonlat_); auto partition = array::make_view(partition_); auto ghost = array::make_view(ghost_); auto glb_idx = array::make_view(global_index_); gidx_t g=0; idx_t j = 0; for (auto p : grid.lonlat()) { if (keep[g]) { lonlat(j, 0) = p.lon(); lonlat(j, 1) = p.lat(); partition(j) = distribution.partition(g); ghost(j) = partition(j) != part_; glb_idx(j) = g+1; ++j; } ++g; } } } setupParallel(); } PointCloud::PointCloud(const Grid& grid, const grid::Partitioner& _partitioner, const eckit::Configuration& config): PointCloud( grid, grid::Distribution{grid, (_partitioner) ? _partitioner : grid::Partitioner{grid.partitioner() | util::Config("mpi_comm",get_mpi_comm(config))} }, config) { ATLAS_TRACE("PointCloud(grid,partitioner,config)"); } Field PointCloud::ghost() const { if (not ghost_) { ghost_ = Field("ghost", array::make_datatype(), array::make_shape(size())); array::make_view(ghost_).assign(0); } return ghost_; } const Grid& PointCloud::grid() const { if (grid_) { return grid_; } std::vector points; points.reserve(size_global()); if (nb_partitions_ == 1) { for (const auto& point : iterate().xy()) { points.push_back(point); } } else { std::vector gidx; gidx.reserve(size_global()); std::vector x, y; x.reserve(size_global()); y.reserve(size_global()); const auto gidxView = array::make_view(global_index_); const auto ghostView = array::make_view(ghost_); int i = 0; for (const auto& point : iterate().xy()) { if (ghostView(i) == 0) { gidx.push_back(gidxView(i)); x.push_back(point.x()); y.push_back(point.y()); } i++; } const auto& comm = mpi::comm(mpi_comm_); eckit::mpi::Buffer gidxBuffer(comm.size()); eckit::mpi::Buffer xBuffer(comm.size()); eckit::mpi::Buffer yBuffer(comm.size()); comm.allGatherv(gidx.begin(), gidx.end(), gidxBuffer); comm.allGatherv(x.begin(), x.end(), xBuffer); comm.allGatherv(y.begin(), y.end(), yBuffer); for (auto i : gidxBuffer.buffer) { points[i - 1] = atlas::PointXY{xBuffer.buffer[i - 1], yBuffer.buffer[i - 1]}; } } grid_ = UnstructuredGrid(points); return grid_; } array::ArrayShape PointCloud::config_shape(const eckit::Configuration& config) const { idx_t _size = size(); bool global(false); if (config.get("global", global)) { if (global) { idx_t owner(0); config.get("owner", owner); idx_t rank = mpi::comm(mpi_comm()).rank(); _size = (rank == owner ? size_global() : 0); } } array::ArrayShape shape; shape.emplace_back(_size); idx_t levels(levels_); config.get("levels", levels); if (levels > 0) { shape.emplace_back(levels); } idx_t variables(0); config.get("variables", variables); if (variables > 0) { shape.emplace_back(variables); } return shape; } array::ArrayAlignment PointCloud::config_alignment(const eckit::Configuration& config) const { int alignment(1); config.get("alignment", alignment); return alignment; } array::ArraySpec PointCloud::config_spec(const eckit::Configuration& config) const { return array::ArraySpec(config_shape(config), config_alignment(config)); } array::DataType PointCloud::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 PointCloud::config_name(const eckit::Configuration& config) const { std::string name; config.get("name", name); return name; } const parallel::HaloExchange& PointCloud::halo_exchange() const { if (halo_exchange_) { return *halo_exchange_; } const_cast(*this).setupHaloExchange(); ATLAS_ASSERT(halo_exchange_); return *halo_exchange_; } void PointCloud::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 PointCloud::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& PointCloud::gather() const { if (gather_scatter_) { return *gather_scatter_; } const_cast(*this).setupGatherScatter(); ATLAS_ASSERT(gather_scatter_); return *gather_scatter_; } const parallel::GatherScatter& PointCloud::scatter() const { if (gather_scatter_) { return *gather_scatter_; } const_cast(*this).setupGatherScatter(); ATLAS_ASSERT(gather_scatter_); return *gather_scatter_; } void PointCloud::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()); } auto name = loc.name(); glb.metadata().broadcast(loc.metadata(), root); loc.metadata().set("global", false); if( !name.empty() ) { loc.metadata().set("name", name); } loc.set_dirty(true); } } void PointCloud::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); } void PointCloud::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(levels_); config.get("levels", levels); field.set_levels(levels); idx_t variables(0); config.get("variables", variables); field.set_variables(variables); if (config.has("type")) { field.metadata().set("type", config.getString("type")); } if (config.has("vector_component")) { field.metadata().set("vector_component", config.getSubConfiguration("vector_component")); } } Field PointCloud::createField(const eckit::Configuration& options) const { Field field(config_name(options), config_datatype(options), config_spec(options)); set_field_metadata(options, field); return field; } Field PointCloud::createField(const Field& other, const eckit::Configuration& config) const { return createField(option::datatype(other.datatype()) | option::levels(other.levels()) | option::variables(other.variables()) | option::type(other.metadata().getString("type", "scalar")) | config); } std::string PointCloud::distribution() const { return (partition_ ? std::string("pointcloud") : std::string("serial")); } static const array::Array& get_dummy() { static array::ArrayT dummy_{1}; return dummy_; } template PointCloud::IteratorT::IteratorT(const atlas::functionspace::detail::PointCloud& fs, bool begin): fs_(fs), xy_(array::make_view(fs_.lonlat())), z_(array::make_view(bool(fs_.vertical()) ? fs_.vertical().array() : get_dummy())), n_(begin ? 0 : fs_.size()), size_(fs_.size()) {} template bool PointCloud::IteratorT::next(Point& p) { if (n_ < size_) { p[XX] = xy_(n_, XX); p[YY] = xy_(n_, YY); if (Point::DIMS == 3) { p[ZZ] = z_(n_); } ++n_; return true; } return false; } template const Point PointCloud::IteratorT::operator*() const { Point p; p[XX] = xy_(n_, XX); p[YY] = xy_(n_, YY); if (Point::DIMS == 3) { p[ZZ] = z_(n_); } return p; } template class PointCloud::IteratorT; template class PointCloud::IteratorT; template class PointCloud::IteratorT; 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); } template void dispatch_adjointHaloExchange(Field& field, const parallel::HaloExchange& halo_exchange, bool on_device) { if (field.datatype() == array::DataType::kind()) { halo_exchange.template execute_adjoint(field.array(), on_device); } else if (field.datatype() == array::DataType::kind()) { halo_exchange.template execute_adjoint(field.array(), on_device); } else if (field.datatype() == array::DataType::kind()) { halo_exchange.template execute_adjoint(field.array(), on_device); } else if (field.datatype() == array::DataType::kind()) { halo_exchange.template execute_adjoint(field.array(), on_device); } else { throw_Exception("datatype not supported", Here()); } field.set_dirty(false); } } // namespace void PointCloud::haloExchange(const FieldSet& fieldset, bool on_device) const { if (parallel_) { 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 PointCloud::haloExchange(const Field& field, bool on_device) const { FieldSet fieldset; fieldset.add(field); haloExchange(fieldset, on_device); } void PointCloud::create_remote_index() const { ATLAS_TRACE(); const auto& comm = mpi::comm(mpi_comm_); const int mpi_rank = comm.rank(); const int mpi_size = comm.size(); auto size_halo = lonlat_.shape(0); remote_index_ = Field("remote_idx", array::make_datatype(), array::make_shape(size_halo)); auto remote_idx = array::make_indexview(remote_index_); auto ghost = array::make_view(ghost_); atlas_omp_parallel_for(idx_t n = 0; n < size_halo; ++n) { if(not ghost(n)) { remote_idx(n) = n; } else { remote_idx(n) = -1; } } auto build_partition_graph = [this,mpi_size,mpi_rank,size_halo,&comm]() -> std::unique_ptr { auto part = array::make_view(this->partition_); auto ghost = array::make_view(this->ghost_); std::vector others_set(mpi_size, 0); others_set[mpi_rank] = 1; for( idx_t i = 0; i others; others.reserve(mpi_size); for (idx_t p = 0; p < mpi_size; ++p) { if (others_set[p]) { others.emplace_back(p); } } eckit::mpi::Buffer recv_others(mpi_size); ATLAS_TRACE_MPI(ALLGATHER) { comm.allGatherv(others.begin(), others.end(), recv_others); } std::vector counts(recv_others.counts.begin(), recv_others.counts.end()); std::vector displs(recv_others.displs.begin(), recv_others.displs.end()); std::vector values(recv_others.buffer.begin(), recv_others.buffer.end()); return std::unique_ptr( new Mesh::PartitionGraph(values.data(), mpi_size, displs.data(), counts.data())); }; std::unique_ptr graph_ptr; ATLAS_TRACE_SCOPE("Building partition graph...") { graph_ptr = build_partition_graph(); } const Mesh::PartitionGraph& graph = *graph_ptr; ATLAS_TRACE_SCOPE("Setup remote_index fields...") { auto p = array::make_view(partition_); auto g = array::make_view(global_index_); auto neighbours = graph.nearestNeighbours(mpi_rank); const idx_t nb_neighbours = static_cast(neighbours.size()); std::unordered_map part_to_neighbour; ATLAS_TRACE_SCOPE("part_to_neighbour") { part_to_neighbour.reserve(nb_neighbours); for (idx_t j = 0; j < nb_neighbours; ++j) { part_to_neighbour[neighbours[j]] = j; } } std::vector halo_per_neighbour(neighbours.size(), 0); ATLAS_TRACE_SCOPE("set halo_per_neighbour") for( idx_t i = 0; i> g_per_neighbour(neighbours.size()); ATLAS_TRACE_SCOPE("assemble g_per_neighbour") { for (idx_t j = 0; j < nb_neighbours; ++j) { g_per_neighbour[j].reserve(halo_per_neighbour[j]); } for( idx_t j = 0; j send_requests(neighbours.size()); std::vector recv_requests(neighbours.size()); std::vector recv_size(neighbours.size()); std::vector send_size(neighbours.size()); int tag = 0; ATLAS_TRACE_SCOPE("send-receive g_per_neighbour size") { for (idx_t j = 0; j < nb_neighbours; ++j) { send_size[j] = static_cast(g_per_neighbour[j].size()); send_requests[j] = comm.iSend(send_size[j], neighbours[j], tag); recv_requests[j] = comm.iReceive(recv_size[j], neighbours[j], tag); } ATLAS_TRACE_MPI(WAIT) { for (idx_t j = 0; j < nb_neighbours; ++j) { comm.wait(send_requests[j]); } for (idx_t j = 0; j < nb_neighbours; ++j) { comm.wait(recv_requests[j]); } } } std::vector> recv_g_per_neighbour(neighbours.size()); ATLAS_TRACE_SCOPE("send-receive g_per_neighbour") for (idx_t j = 0; j < nb_neighbours; ++j) { recv_g_per_neighbour[j].resize(recv_size[j]); send_requests[j] = comm.iSend(g_per_neighbour[j].data(), g_per_neighbour[j].size(), neighbours[j], tag); recv_requests[j] = comm.iReceive(recv_g_per_neighbour[j].data(), recv_g_per_neighbour[j].size(), neighbours[j], tag); } std::vector> send_r_per_neighbour(neighbours.size()); // Assemble "send_r_per_neighbour" // Depending if we can afford memory for a globally sized array, we can have a // much faster version of g_to_r map using std::vector. // TODO: using c++14 we can create a polymorphic lambda to avoid duplicated // code in the two branches of following if. // size_t max_glb_idx = grid().size(); ATLAS_ASSERT(max_glb_idx_); atlas::vector g_to_r_vector; bool use_unordered_map_fallback = false; try { g_to_r_vector.resize(max_glb_idx_ + 1); } catch (std::bad_alloc& e) { if (comm.size() > 1) { Log::warning() << "Could not allocate " << eckit::Bytes((max_glb_idx_ + 1) * sizeof(idx_t)) << Here() << "\n" << "Using slower unordered_map fallback to map global to remote indices" << std::endl; use_unordered_map_fallback = true; } else { throw_Exception( "Could not allocate " + std::string(eckit::Bytes((max_glb_idx_ + 1) * sizeof(idx_t))), Here()); } } if (not use_unordered_map_fallback) { auto& g_to_r = g_to_r_vector; ATLAS_TRACE_SCOPE("g_to_r (using vector)") { atlas_omp_parallel_for(idx_t j = 0; j < size_halo; ++j) { if (not ghost(j)) { // parallel omp possible for ``` g_to_r[g(j)] = j ``` as we only loop over size_owned, // where global_index is such that race-conditions cannot occur #if ATLAS_ARRAYVIEW_BOUNDS_CHECKING if (g(j) >= max_glb_idx_ + 1) { ATLAS_DEBUG_VAR(g(j)); throw_OutOfRange("g_to_r", g(j), max_glb_idx_ + 1, Here()); } #endif g_to_r[g(j)] = j; } } } for (idx_t j = 0; j < nb_neighbours; ++j) { send_r_per_neighbour[j].reserve(recv_size[j]); comm.wait(recv_requests[j]); // wait for recv_g_per_neighbour[j] for (gidx_t gidx : recv_g_per_neighbour[j]) { send_r_per_neighbour[j].emplace_back(g_to_r[gidx]); } } } else { std::unordered_map g_to_r; g_to_r.reserve(size_halo); ATLAS_TRACE_SCOPE("g_to_r (using unordered set)") { for (idx_t j = 0; j < size_halo; ++j) { if (not ghost(j)) { g_to_r[g(j)] = j; } } } ATLAS_TRACE_SCOPE("assemble r_per_neighbour") for (idx_t j = 0; j < nb_neighbours; ++j) { send_r_per_neighbour[j].reserve(recv_size[j]); comm.wait(recv_requests[j]); // wait for recv_g_per_neighbour[j] for (gidx_t gidx : recv_g_per_neighbour[j]) { send_r_per_neighbour[j].emplace_back(g_to_r[gidx]); } } } std::vector> r_per_neighbour(neighbours.size()); ATLAS_TRACE_SCOPE("send-receive r_per_neighbour") { for (idx_t j = 0; j < nb_neighbours; ++j) { r_per_neighbour[j].resize(halo_per_neighbour[j]); } ATLAS_TRACE_MPI(ALLTOALL) { for (idx_t j = 0; j < nb_neighbours; ++j) { comm.wait(send_requests[j]); send_requests[j] = comm.iSend(send_r_per_neighbour[j].data(), send_r_per_neighbour[j].size(), neighbours[j], tag); recv_requests[j] = comm.iReceive(r_per_neighbour[j].data(), r_per_neighbour[j].size(), neighbours[j], tag); } } ATLAS_TRACE_MPI(WAIT) { for (idx_t j = 0; j < nb_neighbours; ++j) { comm.wait(recv_requests[j]); } } } std::vector counters(neighbours.size(), 0); ATLAS_TRACE_SCOPE("set remote_idx") for (idx_t j = 0; j < size_halo; ++j) { if (ghost(j)) { idx_t neighbour = part_to_neighbour[p(j)]; remote_idx(j) = r_per_neighbour[neighbour][counters[neighbour]++]; } ATLAS_ASSERT(remote_idx(j) >= 0); } ATLAS_TRACE_MPI(WAIT) { for (idx_t j = 0; j < nb_neighbours; ++j) { comm.wait(send_requests[j]); } } } } void PointCloud::setupParallel() { ATLAS_TRACE(); if (ghost_ and partition_ and global_index_ and not remote_index_) { create_remote_index(); } else if (not partition_ or not remote_index_) { ATLAS_TRACE("do setup"); const auto& comm = mpi::comm(mpi_comm_); const int mpi_rank = comm.rank(); const int mpi_size = comm.size(); auto lonlat_v = array::make_view(lonlat_); // data structure containing a flag to identify the 'ghost points'; // 0={is not a ghost point}, 1={is a ghost point} auto is_ghost = array::make_view(ghost_); std::vector opoints_local; std::vector gpoints_local; std::vector lonlat_u; std::vector opoints_local_u; lonlat_u.reserve(lonlat_v.shape(0)); opoints_local_u.reserve(is_ghost.shape(0)); gpoints_local.reserve(is_ghost.shape(0)); opoints_local.reserve(is_ghost.shape(0)); for (idx_t i = 0; i < lonlat_v.shape(0); ++i) { lonlat_u.emplace_back(util::unique_lonlat(lonlat_v(i, XX), lonlat_v(i, YY))); } idx_t j{0}; for (idx_t i = 0; i < is_ghost.shape(0); ++i) { PointXY loc(lonlat_v(j, XX), lonlat_v(j, YY)); if (is_ghost(i)) { gpoints_local.emplace_back(loc); } else { opoints_local.emplace_back(loc); opoints_local_u.emplace_back(util::unique_lonlat(loc.x(), loc.y())); } ++j; } std::vector coords_gp_local; coords_gp_local.reserve(gpoints_local.size() * 2); for (auto& gp : gpoints_local) { coords_gp_local.push_back(gp[XX]); coords_gp_local.push_back(gp[YY]); } eckit::mpi::Buffer buffers_rec(mpi_size); ATLAS_TRACE_SCOPE("mpi all gather") { comm.allGatherv(coords_gp_local.begin(), coords_gp_local.end(), buffers_rec); } std::vector gpoints_global; for (std::size_t pe = 0; pe < mpi_size; ++pe) { for (std::size_t j = 0; j < buffers_rec.counts[pe] / 2; ++j) { PointXY loc_gp(*(buffers_rec.begin() + buffers_rec.displs[pe] + 2 * j + XX), *(buffers_rec.begin() + buffers_rec.displs[pe] + 2 * j + YY)); gpoints_global.emplace_back(loc_gp); } } std::vector gpoints_global_u; gpoints_global_u.reserve(gpoints_global.size()); for (atlas::PointXY& loc : gpoints_global) { gpoints_global_u.emplace_back(util::unique_lonlat(loc.x(), loc.y())); } std::vector partition_ids_gp_global(gpoints_global.size(), -1); std::vector remote_index_gp_global(gpoints_global.size(), -1); std::vector::iterator iter_xy_gp_01; ATLAS_TRACE_SCOPE("find global") for (std::size_t idx = 0; idx < gpoints_global_u.size(); ++idx) { iter_xy_gp_01 = std::find(opoints_local_u.begin(), opoints_local_u.end(), gpoints_global_u.at(idx)); if (iter_xy_gp_01 != opoints_local_u.end()) { std::size_t ridx = std::distance(opoints_local_u.begin(), iter_xy_gp_01); partition_ids_gp_global.at(idx) = mpi_rank; remote_index_gp_global.at(idx) = ridx; } } ATLAS_TRACE_SCOPE("mpi all reduce") { comm.allReduceInPlace(partition_ids_gp_global.begin(), partition_ids_gp_global.end(), eckit::mpi::max()); comm.allReduceInPlace(remote_index_gp_global.begin(), remote_index_gp_global.end(), eckit::mpi::max()); } std::vector partition_ids_local(lonlat_v.shape(0), -1); std::vector remote_index_local(lonlat_v.shape(0), -1); idx_t idx_loc{0}; std::vector::iterator iter_xy_gp_02; ATLAS_TRACE_SCOPE("find local") for (idx_t i = 0; i < lonlat_v.shape(0); ++i) { iter_xy_gp_02 = std::find(gpoints_global_u.begin(), gpoints_global_u.end(), lonlat_u.at(i)); if (iter_xy_gp_02 != gpoints_global_u.end()) { std::size_t idx_gp = std::distance(gpoints_global_u.begin(), iter_xy_gp_02); partition_ids_local[idx_loc] = partition_ids_gp_global[idx_gp]; remote_index_local[idx_loc] = remote_index_gp_global[idx_gp]; } else { partition_ids_local[idx_loc] = mpi_rank; remote_index_local[idx_loc] = idx_loc; } ++idx_loc; } partition_ = Field("partition", array::make_datatype(), array::make_shape(partition_ids_local.size())); auto partitionv = array::make_view(partition_); for (idx_t i = 0; i < partitionv.shape(0); ++i) { partitionv(i) = partition_ids_local.at(i); } remote_index_ = Field("remote_index", array::make_datatype(), array::make_shape(remote_index_local.size())); auto remote_indexv = array::make_indexview(remote_index_); for (idx_t i = 0; i < remote_indexv.shape(0); ++i) { remote_indexv(i) = remote_index_local.at(i); } } ATLAS_ASSERT(partition_); ATLAS_ASSERT(ghost_); ATLAS_ASSERT(remote_index_); ATLAS_ASSERT(ghost_.size() == remote_index_.size()); ATLAS_ASSERT(ghost_.size() == partition_.size()); parallel_ = true; } void PointCloud::setupHaloExchange() { ATLAS_TRACE(); if (ghost_ and partition_ and remote_index_) { halo_exchange_.reset(new parallel::HaloExchange()); halo_exchange_->setup(mpi_comm_, array::make_view(partition_).data(), array::make_view(remote_index_).data(), REMOTE_IDX_BASE, ghost_.size()); } } void PointCloud::setupGatherScatter() { ATLAS_TRACE(); if (ghost_ and partition_ and remote_index_ and global_index_) { gather_scatter_.reset(new parallel::GatherScatter()); gather_scatter_->setup(mpi_comm_, array::make_view(partition_).data(), array::make_view(remote_index_).data(), REMOTE_IDX_BASE, array::make_view(global_index_).data(), array::make_view(ghost_).data(), ghost_.size()); } } idx_t PointCloud::size_global() const { if (size_global_ == -1) { if (not parallel_) { size_global_ = lonlat().size(); } else { if( !gather_scatter_ ) { const_cast(*this).setupGatherScatter(); } if (gather_scatter_) { size_global_ = gather_scatter_->glb_dof(); } } } return size_global_; } void PointCloud::adjointHaloExchange(const FieldSet& fieldset, bool on_device) const { if (parallel_) { for (idx_t f = 0; f < fieldset.size(); ++f) { Field& field = const_cast(fieldset)[f]; switch (field.rank()) { case 1: dispatch_adjointHaloExchange<1>(field, halo_exchange(), on_device); break; case 2: dispatch_adjointHaloExchange<2>(field, halo_exchange(), on_device); break; case 3: dispatch_adjointHaloExchange<3>(field, halo_exchange(), on_device); break; case 4: dispatch_adjointHaloExchange<4>(field, halo_exchange(), on_device); break; default: throw_Exception("Rank not supported", Here()); } } } } void PointCloud::adjointHaloExchange(const Field& field, bool) const { FieldSet fieldset; fieldset.add(field); adjointHaloExchange(fieldset); } } // namespace detail PointCloud::PointCloud(const FunctionSpace& functionspace): FunctionSpace(functionspace), functionspace_(dynamic_cast(get())) {} PointCloud::PointCloud(const Field& field, const eckit::Configuration& config): FunctionSpace(new detail::PointCloud(field,config)), functionspace_(dynamic_cast(get())) {} PointCloud::PointCloud(const Field& field1, const Field& field2, const eckit::Configuration& config): FunctionSpace(new detail::PointCloud(field1, field2, config)), functionspace_(dynamic_cast(get())) {} PointCloud::PointCloud(const FieldSet& fset, const eckit::Configuration& config): FunctionSpace(new detail::PointCloud(fset, config)), functionspace_(dynamic_cast(get())) {} PointCloud::PointCloud(const std::vector& points, const eckit::Configuration& config): FunctionSpace(new detail::PointCloud(points, config)), functionspace_(dynamic_cast(get())) {} PointCloud::PointCloud(const std::vector& points, const eckit::Configuration& config): FunctionSpace(new detail::PointCloud(points, config)), functionspace_(dynamic_cast(get())) {} PointCloud::PointCloud(const std::initializer_list>& points, const eckit::Configuration& config): FunctionSpace((points.begin()->size() == 2 ? new detail::PointCloud{std::vector(points.begin(), points.end()), config} : new detail::PointCloud{std::vector(points.begin(), points.end()), config})), functionspace_(dynamic_cast(get())) {} PointCloud::PointCloud(const Grid& grid, const eckit::Configuration& config): FunctionSpace(new detail::PointCloud(grid, config)), functionspace_(dynamic_cast(get())) {} PointCloud::PointCloud(const Grid& grid, const grid::Distribution& distribution, const eckit::Configuration& config): FunctionSpace(new detail::PointCloud(grid, distribution, config)), functionspace_(dynamic_cast(get())) {} PointCloud::PointCloud(const Grid& grid, const grid::Partitioner& partitioner, const eckit::Configuration& config): FunctionSpace(new detail::PointCloud(grid, partitioner,config)), functionspace_(dynamic_cast(get())) {} } // namespace functionspace } // namespace atlas atlas-0.46.0/src/atlas/functionspace/NodeColumns.h0000664000175000017500000010140615160212070022220 0ustar alastairalastair/* * (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/functionspace/FunctionSpace.h" #include "atlas/functionspace/detail/FunctionSpaceImpl.h" #include "atlas/library/config.h" #include "atlas/mesh.h" #include "atlas/option.h" // ---------------------------------------------------------------------------- // Forward declarations namespace atlas { namespace array { class ArrayShape; } namespace mesh { class Nodes; } } // namespace atlas 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 NodeColumns : public functionspace::FunctionSpaceImpl { public: NodeColumns(Mesh mesh, const eckit::Configuration&); NodeColumns(Mesh mesh); ~NodeColumns() override; static std::string static_type() { return "NodeColumns"; } std::string type() const override { return static_type(); } std::string distribution() const override; idx_t nb_nodes() const; idx_t nb_nodes_global() const; // All MPI ranks will have same output const Mesh& mesh() const { return mesh_; } idx_t levels() const { return nb_levels_; } mesh::Nodes& nodes() const { return nodes_; } // -- Field creation methods using FunctionSpaceImpl::createField; /// @brief Create a field Field createField(const eckit::Configuration&) const override; Field createField(const Field&, const eckit::Configuration&) const override; // -- Parallelisation aware methods const mesh::Halo& halo() const { return halo_; } void haloExchange(const FieldSet&, bool on_device = false) const override; void haloExchange(const Field&, bool on_device = false) const override; const parallel::HaloExchange& halo_exchange() const; void adjointHaloExchange(const FieldSet&, bool on_device = false) const override; void adjointHaloExchange(const Field&, bool on_device = false) const override; void gather(const FieldSet&, FieldSet&) const override; void gather(const Field&, Field&) const override; const parallel::GatherScatter& gather() const override; void scatter(const FieldSet&, FieldSet&) const override; void scatter(const Field&, Field&) const override; const parallel::GatherScatter& scatter() const override; std::string checksum(const FieldSet&) const; std::string checksum(const Field&) const; const parallel::Checksum& checksum() const; /// @brief Compute sum of scalar field /// @param [out] sum Scalar value containing the sum of the full 3D field /// @param [out] N Number of values that are contained in the sum /// (nodes*levels) template void sum(const Field&, Value& sum, idx_t& N) const; // /// @brief Compute sum of field for each variable // /// @param [out] sum For each field-variable, the sum of the full 3D // field // /// @param [out] N Number of values that are contained in the sum // (nodes*levels) // template< typename Value > // void sum( const Field&, std::vector& sum, idx_t& N ) const; /// @brief Compute sum of field for each vertical level separately /// @param [out] sum Field of dimension of input without the nodes index /// @param [out] N Number of nodes used to sum each level void sumPerLevel(const Field&, Field& sum, idx_t& N) const; /// @brief Compute order independent sum of scalar field /// @param [out] sum Scalar value containing the sum of the full 3D field /// @param [out] N Number of values that are contained in the sum /// (nodes*levels) template void orderIndependentSum(const Field&, Value& sum, idx_t& N) const; // /// @brief Compute order independent sum of field for each variable // /// @param [out] sum For each field-variable, the sum of the full 3D // field // /// @param [out] N Number of values that are contained in the sum // (nodes*levels) // template< typename Value > // void orderIndependentSum( const Field&, std::vector&, idx_t& N ) // const; /// @brief Compute order independent sum of field for each vertical level /// separately /// @param [out] sum Field of dimension of input without the nodes index /// @param [out] N Number of nodes used to sum each level void orderIndependentSumPerLevel(const Field&, Field& sum, idx_t& N) const; /// @brief Compute minimum of scalar field template void minimum(const Field&, Value& minimum) const; /// @brief Compute maximum of scalar field template void maximum(const Field&, Value& maximum) const; // /// @brief Compute minimum of field for each field-variable // template< typename Value > // void minimum( const Field&, std::vector& ) const; // /// @brief Compute maximum of field for each field-variable // template< typename Value > // void maximum( const Field&, std::vector& ) const; /// @brief Compute minimum of field for each vertical level separately /// @param [out] min Field of dimension of input without the nodes index void minimumPerLevel(const Field&, Field& min) const; /// @brief Compute maximum of field for each vertical level separately /// @param [out] max Field of dimension of input without the nodes index void maximumPerLevel(const Field&, Field& max) const; /// @brief Compute minimum of scalar field, as well as the global index and /// level. template void minimumAndLocation(const Field&, Value& minimum, gidx_t& glb_idx) const; /// @brief Compute maximum of scalar field, as well as the global index and /// level. template void maximumAndLocation(const Field&, Value& maximum, gidx_t& glb_idx) const; /// @brief Compute minimum of scalar field, as well as the global index and /// level. template void minimumAndLocation(const Field&, Value& minimum, gidx_t& glb_idx, idx_t& level) const; /// @brief Compute maximum of scalar field, as well as the global index and /// level. template void maximumAndLocation(const Field&, Value& maximum, gidx_t& glb_idx, idx_t& level) const; /// @brief Compute minimum of field for each field-variable, as well as the /// global indices and levels. template void minimumAndLocation(const Field&, Vector& minimum, std::vector& glb_idx) const; /// @brief Compute maximum of field for each field-variable, as well as the /// global indices and levels. template void maximumAndLocation(const Field&, Vector& maximum, std::vector& glb_idx) const; /// @brief Compute minimum of field for each field-variable, as well as the /// global indices and levels. template void minimumAndLocation(const Field&, Vector& minimum, std::vector& glb_idx, std::vector& level) const; /// @brief Compute maximum of field for each field-variable, as well as the /// global indices and levels. template void maximumAndLocation(const Field&, Vector& maximum, std::vector& glb_idx, std::vector& level) const; /// @brief Compute minimum and its location of a field for each vertical level /// separately void minimumAndLocationPerLevel(const Field&, Field& column, Field& glb_idx) const; /// @brief Compute maximum and its location of a field for each vertical level /// separately void maximumAndLocationPerLevel(const Field&, Field& column, Field& glb_idx) const; /// @brief Compute mean value of scalar field /// @param [out] mean Mean value /// @param [out] N Number of value used to create the mean template void mean(const Field&, Value& mean, idx_t& N) const; // /// @brief Compute mean value of field for each field-variable // /// @param [out] mean Mean values for each variable // /// @param [out] N Number of values used to create the means // template< typename Value > // void mean( const Field&, std::vector& mean, idx_t& N ) const; /// @brief Compute mean values of field for vertical level separately /// @param [out] mean Field of dimension of input without the nodes index /// @param [out] N Number of values used to create the means void meanPerLevel(const Field&, Field& mean, idx_t& N) const; /// @brief Compute mean value and standard deviation of scalar field /// @param [out] mean Mean value /// @param [out] stddev Standard deviation /// @param [out] N Number of value used to create the mean template void meanAndStandardDeviation(const Field&, Value& mean, Value& stddev, idx_t& N) const; // /// @brief Compute mean values and standard deviations of scalar field // for each field-variable // /// @param [out] mean Mean values for each field-variable // /// @param [out] stddev Standard deviation for each field-variable // /// @param [out] N Number of value used to create the means // template< typename Value > // void meanAndStandardDeviation( const Field&, std::vector& mean, // std::vector& stddev, idx_t& N ) const; /// @brief Compute mean values and standard deviations of field for vertical /// level separately /// @param [out] mean Field of dimension of input without the nodes index /// @param [out] stddev Field of dimension of input without the nodes index /// @param [out] N Number of values used to create the means void meanAndStandardDeviationPerLevel(const Field&, Field& mean, Field& stddev, idx_t& N) const; virtual idx_t size() const override { return nb_nodes_; } idx_t part() const override { return mesh_.part(); } idx_t nb_parts() const override { return mesh_.nb_parts(); } const Grid& grid() const override; Field lonlat() const override { return nodes_.lonlat(); } Field ghost() const override { return nodes_.ghost(); } Field global_index() const override { return nodes_.global_index(); } Field remote_index() const override { return nodes_.remote_index(); } Field partition() const override { return nodes_.partition(); } const util::PartitionPolygon& polygon(idx_t halo = 0) const override { return mesh_.polygon(halo); } const util::PartitionPolygons& polygons() const override { return mesh_.polygons(); } const Projection& projection() const override { return mesh_.projection(); } std::string mpi_comm() const override { return mesh_.mpi_comm(); } private: // methods void constructor(); idx_t config_nb_nodes(const eckit::Configuration&) 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 { return 0; } private: // data mutable Grid grid_; Mesh mesh_; // non-const because functionspace may modify mesh mesh::Nodes& nodes_; // non-const because functionspace may modify mesh mesh::Halo halo_; idx_t nb_levels_; idx_t nb_nodes_; mutable idx_t nb_nodes_global_{-1}; mutable util::ObjectHandle gather_scatter_; // without ghost mutable util::ObjectHandle halo_exchange_; mutable util::ObjectHandle checksum_; private: template struct FieldStatisticsT { FieldStatisticsT(const NodeColumns*); void sum(const Field&, Value& sum, idx_t& N) const; void orderIndependentSum(const Field&, Value& sum, idx_t& N) const; void minimum(const Field&, Value& minimum) const; void maximum(const Field&, Value& maximum) const; void minimumAndLocation(const Field&, Value& minimum, gidx_t& glb_idx) const; void maximumAndLocation(const Field&, Value& maximum, gidx_t& glb_idx) const; void minimumAndLocation(const Field&, Value& minimum, gidx_t& glb_idx, idx_t& level) const; void maximumAndLocation(const Field&, Value& maximum, gidx_t& glb_idx, idx_t& level) const; void mean(const Field&, Value& mean, idx_t& N) const; void meanAndStandardDeviation(const Field&, Value& mean, Value& stddev, idx_t& N) const; const NodeColumns& functionspace; }; template struct FieldStatisticsVectorT { FieldStatisticsVectorT(const NodeColumns*); void sum(const Field&, Vector& sum, idx_t& N) const; void orderIndependentSum(const Field&, Vector&, idx_t& N) const; void minimum(const Field&, Vector&) const; void maximum(const Field&, Vector&) const; void minimumAndLocation(const Field&, Vector& minimum, std::vector& glb_idx) const; void maximumAndLocation(const Field&, Vector& maximum, std::vector& glb_idx) const; void minimumAndLocation(const Field&, Vector& minimum, std::vector& glb_idx, std::vector& level) const; void maximumAndLocation(const Field&, Vector& maximum, std::vector& glb_idx, std::vector& level) const; void mean(const Field&, Vector& mean, idx_t& N) const; void meanAndStandardDeviation(const Field&, Vector& mean, Vector& stddev, idx_t& N) const; const NodeColumns& functionspace; }; struct FieldStatistics { FieldStatistics(const NodeColumns*); void sumPerLevel(const Field&, Field& sum, idx_t& N) const; void orderIndependentSumPerLevel(const Field&, Field& sum, idx_t& N) const; void minimumPerLevel(const Field&, Field& min) const; void maximumPerLevel(const Field&, Field& max) const; void minimumAndLocationPerLevel(const Field&, Field& column, Field& glb_idx) const; void maximumAndLocationPerLevel(const Field&, Field& column, Field& glb_idx) const; void meanPerLevel(const Field&, Field& mean, idx_t& N) const; void meanAndStandardDeviationPerLevel(const Field&, Field& mean, Field& stddev, idx_t& N) const; const NodeColumns& functionspace; }; template struct FieldStatisticsSelector { using type = typename std::conditional::value, FieldStatisticsT, FieldStatisticsVectorT>::type; }; }; // ------------------------------------------------------------------- template void NodeColumns::sum(const Field& field, Value& sum, idx_t& N) const { typename FieldStatisticsSelector::type(this).sum(field, sum, N); } inline void NodeColumns::sumPerLevel(const Field& field, Field& sum, idx_t& N) const { FieldStatistics(this).sumPerLevel(field, sum, N); } template void NodeColumns::orderIndependentSum(const Field& field, Value& sum, idx_t& N) const { typename FieldStatisticsSelector::type(this).orderIndependentSum(field, sum, N); } inline void NodeColumns::orderIndependentSumPerLevel(const Field& field, Field& sum, idx_t& N) const { FieldStatistics(this).orderIndependentSumPerLevel(field, sum, N); } template void NodeColumns::minimum(const Field& field, Value& minimum) const { typename FieldStatisticsSelector::type(this).minimum(field, minimum); } template void NodeColumns::maximum(const Field& field, Value& maximum) const { typename FieldStatisticsSelector::type(this).maximum(field, maximum); } inline void NodeColumns::minimumPerLevel(const Field& field, Field& minimum) const { return FieldStatistics(this).minimumPerLevel(field, minimum); } inline void NodeColumns::maximumPerLevel(const Field& field, Field& maximum) const { FieldStatistics(this).maximumPerLevel(field, maximum); } template void NodeColumns::minimumAndLocation(const Field& field, Value& minimum, gidx_t& glb_idx) const { FieldStatisticsT(this).minimumAndLocation(field, minimum, glb_idx); } template void NodeColumns::maximumAndLocation(const Field& field, Value& maximum, gidx_t& glb_idx) const { FieldStatisticsT(this).maximumAndLocation(field, maximum, glb_idx); } template void NodeColumns::minimumAndLocation(const Field& field, Value& minimum, gidx_t& glb_idx, idx_t& level) const { FieldStatisticsT(this).minimumAndLocation(field, minimum, glb_idx, level); } template void NodeColumns::maximumAndLocation(const Field& field, Value& maximum, gidx_t& glb_idx, idx_t& level) const { FieldStatisticsT(this).maximumAndLocation(field, maximum, glb_idx, level); } template void NodeColumns::minimumAndLocation(const Field& field, Vector& minimum, std::vector& glb_idx) const { FieldStatisticsVectorT(this).minimumAndLocation(field, minimum, glb_idx); } template void NodeColumns::maximumAndLocation(const Field& field, Vector& maximum, std::vector& glb_idx) const { FieldStatisticsVectorT(this).maximumAndLocation(field, maximum, glb_idx); } template void NodeColumns::minimumAndLocation(const Field& field, Vector& minimum, std::vector& glb_idx, std::vector& level) const { FieldStatisticsVectorT(this).minimumAndLocation(field, minimum, glb_idx, level); } template void NodeColumns::maximumAndLocation(const Field& field, Vector& maximum, std::vector& glb_idx, std::vector& level) const { FieldStatisticsVectorT(this).maximumAndLocation(field, maximum, glb_idx, level); } inline void NodeColumns::minimumAndLocationPerLevel(const Field& field, Field& column, Field& glb_idx) const { FieldStatistics(this).minimumAndLocationPerLevel(field, column, glb_idx); } inline void NodeColumns::maximumAndLocationPerLevel(const Field& field, Field& column, Field& glb_idx) const { FieldStatistics(this).maximumAndLocationPerLevel(field, column, glb_idx); } template void NodeColumns::mean(const Field& field, Value& mean, idx_t& N) const { typename FieldStatisticsSelector::type(this).mean(field, mean, N); } inline void NodeColumns::meanPerLevel(const Field& field, Field& mean, idx_t& N) const { FieldStatistics(this).meanPerLevel(field, mean, N); } template void NodeColumns::meanAndStandardDeviation(const Field& field, Value& mean, Value& stddev, idx_t& N) const { typename FieldStatisticsSelector::type(this).meanAndStandardDeviation(field, mean, stddev, N); } inline void NodeColumns::meanAndStandardDeviationPerLevel(const Field& field, Field& mean, Field& stddev, idx_t& N) const { FieldStatistics(this).meanAndStandardDeviationPerLevel(field, mean, stddev, N); } } // namespace detail // ------------------------------------------------------------------- class NodeColumns : public FunctionSpace { public: using Implementation = detail::NodeColumns; public: NodeColumns(); NodeColumns(const FunctionSpace&); NodeColumns(Mesh mesh); NodeColumns(Mesh mesh, const eckit::Configuration&); static std::string type() { return detail::NodeColumns::static_type(); } operator bool() const { return valid(); } bool valid() const { return functionspace_; } idx_t nb_nodes() const; idx_t nb_nodes_global() const; // All MPI ranks will have same output const Mesh& mesh() const; idx_t levels() const; mesh::Nodes& nodes() const; // -- Parallelisation aware methods const mesh::Halo& halo() const; void haloExchange(const FieldSet&, bool on_device = false) const; void haloExchange(const Field&, bool on_device = false) const; const parallel::HaloExchange& halo_exchange() const; std::string checksum(const FieldSet&) const; std::string checksum(const Field&) const; const parallel::Checksum& checksum() const; /// @brief Compute sum of scalar field /// @param [out] sum Scalar value containing the sum of the full 3D field /// @param [out] N Number of values that are contained in the sum /// (nodes*levels) template void sum(const Field&, Value& sum, idx_t& N) const; // /// @brief Compute sum of field for each variable // /// @param [out] sum For each field-variable, the sum of the full 3D // field // /// @param [out] N Number of values that are contained in the sum // (nodes*levels) // template< typename Value > // void sum( const Field&, std::vector& sum, idx_t& N ) const; /// @brief Compute sum of field for each vertical level separately /// @param [out] sum Field of dimension of input without the nodes index /// @param [out] N Number of nodes used to sum each level void sumPerLevel(const Field&, Field& sum, idx_t& N) const; /// @brief Compute order independent sum of scalar field /// @param [out] sum Scalar value containing the sum of the full 3D field /// @param [out] N Number of values that are contained in the sum /// (nodes*levels) template void orderIndependentSum(const Field&, Value& sum, idx_t& N) const; // /// @brief Compute order independent sum of field for each variable // /// @param [out] sum For each field-variable, the sum of the full 3D // field // /// @param [out] N Number of values that are contained in the sum // (nodes*levels) // template< typename Value > // void orderIndependentSum( const Field&, std::vector&, idx_t& N ) // const; /// @brief Compute order independent sum of field for each vertical level /// separately /// @param [out] sum Field of dimension of input without the nodes index /// @param [out] N Number of nodes used to sum each level void orderIndependentSumPerLevel(const Field&, Field& sum, idx_t& N) const; /// @brief Compute minimum of scalar field template void minimum(const Field&, Value& minimum) const; /// @brief Compute maximum of scalar field template void maximum(const Field&, Value& maximum) const; // /// @brief Compute minimum of field for each field-variable // template< typename Value > // void minimum( const Field&, std::vector& ) const; // /// @brief Compute maximum of field for each field-variable // template< typename Value > // void maximum( const Field&, std::vector& ) const; /// @brief Compute minimum of field for each vertical level separately /// @param [out] min Field of dimension of input without the nodes index void minimumPerLevel(const Field&, Field& min) const; /// @brief Compute maximum of field for each vertical level separately /// @param [out] max Field of dimension of input without the nodes index void maximumPerLevel(const Field&, Field& max) const; /// @brief Compute minimum of scalar field, as well as the global index and /// level. template void minimumAndLocation(const Field&, Value& minimum, gidx_t& glb_idx) const; /// @brief Compute maximum of scalar field, as well as the global index and /// level. template void maximumAndLocation(const Field&, Value& maximum, gidx_t& glb_idx) const; /// @brief Compute minimum of scalar field, as well as the global index and /// level. template void minimumAndLocation(const Field&, Value& minimum, gidx_t& glb_idx, idx_t& level) const; /// @brief Compute maximum of scalar field, as well as the global index and /// level. template void maximumAndLocation(const Field&, Value& maximum, gidx_t& glb_idx, idx_t& level) const; /// @brief Compute minimum of field for each field-variable, as well as the /// global indices and levels. template void minimumAndLocation(const Field&, std::vector& minimum, std::vector& glb_idx) const; /// @brief Compute maximum of field for each field-variable, as well as the /// global indices and levels. template void maximumAndLocation(const Field&, std::vector& maximum, std::vector& glb_idx) const; /// @brief Compute minimum of field for each field-variable, as well as the /// global indices and levels. template void minimumAndLocation(const Field&, std::vector& minimum, std::vector& glb_idx, std::vector& level) const; /// @brief Compute maximum of field for each field-variable, as well as the /// global indices and levels. template void maximumAndLocation(const Field&, std::vector& maximum, std::vector& glb_idx, std::vector& level) const; /// @brief Compute minimum and its location of a field for each vertical level /// separately void minimumAndLocationPerLevel(const Field&, Field& column, Field& glb_idx) const; /// @brief Compute maximum and its location of a field for each vertical level /// separately void maximumAndLocationPerLevel(const Field&, Field& column, Field& glb_idx) const; /// @brief Compute mean value of scalar field /// @param [out] mean Mean value /// @param [out] N Number of value used to create the mean template void mean(const Field&, Value& mean, idx_t& N) const; // /// @brief Compute mean value of field for each field-variable // /// @param [out] mean Mean values for each variable // /// @param [out] N Number of values used to create the means // template< typename Value > // void mean( const Field&, std::vector& mean, idx_t& N ) const; /// @brief Compute mean values of field for vertical level separately /// @param [out] mean Field of dimension of input without the nodes index /// @param [out] N Number of values used to create the means void meanPerLevel(const Field&, Field& mean, idx_t& N) const; /// @brief Compute mean value and standard deviation of scalar field /// @param [out] mean Mean value /// @param [out] stddev Standard deviation /// @param [out] N Number of value used to create the mean template void meanAndStandardDeviation(const Field&, Value& mean, Value& stddev, idx_t& N) const; // /// @brief Compute mean values and standard deviations of scalar field // for each field-variable // /// @param [out] mean Mean values for each field-variable // /// @param [out] stddev Standard deviation for each field-variable // /// @param [out] N Number of value used to create the means // template< typename Value > // void meanAndStandardDeviation( const Field&, std::vector& mean, // std::vector& stddev, idx_t& N ) const; /// @brief Compute mean values and standard deviations of field for vertical /// level separately /// @param [out] mean Field of dimension of input without the nodes index /// @param [out] stddev Field of dimension of input without the nodes index /// @param [out] N Number of values used to create the means void meanAndStandardDeviationPerLevel(const Field&, Field& mean, Field& stddev, idx_t& N) const; private: const detail::NodeColumns* functionspace_; }; // ------------------------------------------------------------------- inline idx_t NodeColumns::levels() const { return functionspace_->levels(); } template void NodeColumns::sum(const Field& field, Value& sum, idx_t& N) const { functionspace_->sum(field, sum, N); } inline void NodeColumns::sumPerLevel(const Field& field, Field& sum, idx_t& N) const { functionspace_->sumPerLevel(field, sum, N); } template void NodeColumns::orderIndependentSum(const Field& field, Value& sum, idx_t& N) const { functionspace_->orderIndependentSum(field, sum, N); } inline void NodeColumns::orderIndependentSumPerLevel(const Field& field, Field& sum, idx_t& N) const { functionspace_->orderIndependentSumPerLevel(field, sum, N); } template void NodeColumns::minimum(const Field& field, Value& minimum) const { functionspace_->minimum(field, minimum); } template void NodeColumns::maximum(const Field& field, Value& maximum) const { functionspace_->maximum(field, maximum); } inline void NodeColumns::minimumPerLevel(const Field& field, Field& minimum) const { return functionspace_->minimumPerLevel(field, minimum); } inline void NodeColumns::maximumPerLevel(const Field& field, Field& maximum) const { functionspace_->maximumPerLevel(field, maximum); } template void NodeColumns::minimumAndLocation(const Field& field, Value& minimum, gidx_t& glb_idx) const { functionspace_->minimumAndLocation(field, minimum, glb_idx); } template void NodeColumns::maximumAndLocation(const Field& field, Value& maximum, gidx_t& glb_idx) const { functionspace_->maximumAndLocation(field, maximum, glb_idx); } template void NodeColumns::minimumAndLocation(const Field& field, Value& minimum, gidx_t& glb_idx, idx_t& level) const { functionspace_->minimumAndLocation(field, minimum, glb_idx, level); } template void NodeColumns::maximumAndLocation(const Field& field, Value& maximum, gidx_t& glb_idx, idx_t& level) const { functionspace_->maximumAndLocation(field, maximum, glb_idx, level); } template void NodeColumns::minimumAndLocation(const Field& field, std::vector& minimum, std::vector& glb_idx) const { functionspace_->minimumAndLocation(field, minimum, glb_idx); } template void NodeColumns::maximumAndLocation(const Field& field, std::vector& maximum, std::vector& glb_idx) const { functionspace_->maximumAndLocation(field, maximum, glb_idx); } template void NodeColumns::minimumAndLocation(const Field& field, std::vector& minimum, std::vector& glb_idx, std::vector& level) const { functionspace_->minimumAndLocation(field, minimum, glb_idx, level); } template void NodeColumns::maximumAndLocation(const Field& field, std::vector& maximum, std::vector& glb_idx, std::vector& level) const { functionspace_->maximumAndLocation(field, maximum, glb_idx, level); } inline void NodeColumns::minimumAndLocationPerLevel(const Field& field, Field& column, Field& glb_idx) const { functionspace_->minimumAndLocationPerLevel(field, column, glb_idx); } inline void NodeColumns::maximumAndLocationPerLevel(const Field& field, Field& column, Field& glb_idx) const { functionspace_->maximumAndLocationPerLevel(field, column, glb_idx); } template void NodeColumns::mean(const Field& field, Value& mean, idx_t& N) const { functionspace_->mean(field, mean, N); } inline void NodeColumns::meanPerLevel(const Field& field, Field& mean, idx_t& N) const { functionspace_->meanPerLevel(field, mean, N); } template void NodeColumns::meanAndStandardDeviation(const Field& field, Value& mean, Value& stddev, idx_t& N) const { functionspace_->meanAndStandardDeviation(field, mean, stddev, N); } inline void NodeColumns::meanAndStandardDeviationPerLevel(const Field& field, Field& mean, Field& stddev, idx_t& N) const { functionspace_->meanAndStandardDeviationPerLevel(field, mean, stddev, N); } // ------------------------------------------------------------------- } // namespace functionspace } // namespace atlas atlas-0.46.0/src/atlas/functionspace/CubedSphereColumns.h0000664000175000017500000001334515160212070023530 0ustar alastairalastair/* * (C) Crown Copyright 2021 Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ #pragma once #include #include #include "atlas/array/ArrayView.h" #include "atlas/functionspace/CellColumns.h" #include "atlas/functionspace/NodeColumns.h" #include "atlas/functionspace/detail/CubedSphereStructure.h" #include "atlas/meshgenerator/detail/cubedsphere/CubedSphereUtility.h" #include "atlas/parallel/omp/omp.h" #include "atlas/util/ObjectHandle.h" namespace atlas { class Mesh; } namespace atlas { namespace functionspace { /// Extend NodeColumns and CellColumns so that they can exploit CubedSphere structure. template class CubedSphereColumns : public BaseFunctionSpace { public: /// Constructors. CubedSphereColumns(); CubedSphereColumns(const FunctionSpace& functionSpace); CubedSphereColumns(const Mesh& mesh, const eckit::Configuration& configuration); CubedSphereColumns(const Mesh& mesh); /// Invalid index. idx_t invalid_index() const; /// Get number of owned elements. idx_t sizeOwned() const; /// i lower bound for tile t (including halo) idx_t i_begin(idx_t t) const; /// i lower bound for tile t (including halo) idx_t i_end(idx_t t) const; /// j lower bound for tile t (including halo) idx_t j_begin(idx_t t) const; /// j lower bound for tile t (including halo) idx_t j_end(idx_t t) const; /// Return array_view index for (t, i, j). idx_t index(idx_t t, idx_t i, idx_t j) const; /// Return true if (t, i, j) is a valid index. bool is_valid_index(idx_t t, idx_t i, idx_t j) const; /// Return tij field. Field tij() const; private: class For { public: For(const CubedSphereColumns& functionSpace, const util::Config& config = util::NoConfig()): functionSpace_{functionSpace}, indexMax_{config.getBool("include_halo", false) ? functionSpace.size() : functionSpace.sizeOwned()}, levels_{config.getInt("levels", functionSpace_.levels())}, tijView_(array::make_view(functionSpace_.tij())) {} // Define template to disable invalid functors. template using EnableFunctor = typename std::enable_if>::value>::type*; // Functor: void f(index, t, i, j, k) template = nullptr> void operator()(const Functor& f) const { using namespace meshgenerator::detail::cubedsphere; // Loop over elements. atlas_omp_parallel_for(idx_t index = 0; index < indexMax_; ++index) { const idx_t t = tijView_(index, Coordinates::T); const idx_t i = tijView_(index, Coordinates::I); const idx_t j = tijView_(index, Coordinates::J); for (idx_t k = 0; k < levels_; ++k) { f(index, t, i, j, k); } } } // Functor: void f(index, t, i, j) template = nullptr> void operator()(const Functor& f) const { using namespace meshgenerator::detail::cubedsphere; // Loop over elements. atlas_omp_parallel_for(idx_t index = 0; index < indexMax_; ++index) { const idx_t t = tijView_(index, Coordinates::T); const idx_t i = tijView_(index, Coordinates::I); const idx_t j = tijView_(index, Coordinates::J); f(index, t, i, j); } } // Functor: void f(index, k) template = nullptr> void operator()(const Functor& f) const { using namespace meshgenerator::detail::cubedsphere; // Loop over elements. atlas_omp_parallel_for(idx_t index = 0; index < indexMax_; ++index) { for (idx_t k = 0; k < levels_; ++k) { f(index, k); } } } // Functor: void f(index ) template = nullptr> void operator()(const Functor& f) const { using namespace meshgenerator::detail::cubedsphere; // Loop over elements. atlas_omp_parallel_for(idx_t index = 0; index < indexMax_; ++index) { f(index); } } private: const CubedSphereColumns& functionSpace_; idx_t indexMax_; idx_t levels_; array::ArrayView tijView_; }; public: /// Visit each element index and apply functor with signature: /// f(index, t, i, j, k), f(index t, i, j), f(index, k) or f(index). template void parallel_for(const Functor& f) const { For(*this, util::NoConfig())(f); } /// Visit each element index and apply functor with signature: /// f(index, t, i, j, k), f(index t, i, j), f(index, k) or f(index). /// Can also specify "idx_t levels" and "bool include_halo". template void parallel_for(const util::Config& config, const Functor& f) const { For(*this, config)(f); } private: // Object hanldle for CubedSphereStructure util::ObjectHandle cubedSphereColumnsHandle_; }; using CubedSphereNodeColumns = CubedSphereColumns; using CubedSphereCellColumns = CubedSphereColumns; } // namespace functionspace } // namespace atlas atlas-0.46.0/src/atlas/functionspace/BlockStructuredColumns.cc0000664000175000017500000000625415160212070024615 0ustar alastairalastair/* * (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/functionspace/BlockStructuredColumns.h" namespace atlas { namespace functionspace { // ---------------------------------------------------------------------------- BlockStructuredColumns::BlockStructuredColumns(): FunctionSpace(), functionspace_(nullptr) {} BlockStructuredColumns::BlockStructuredColumns(const FunctionSpace& functionspace): FunctionSpace(functionspace), functionspace_(dynamic_cast(get())) {} BlockStructuredColumns::BlockStructuredColumns(const Grid& grid, const eckit::Configuration& config): FunctionSpace(new detail::BlockStructuredColumns(grid, config)), functionspace_(dynamic_cast(get())) {} BlockStructuredColumns::BlockStructuredColumns(const Grid& grid, const grid::Partitioner& partitioner, const eckit::Configuration& config): FunctionSpace(new detail::BlockStructuredColumns(grid, partitioner, config)), functionspace_(dynamic_cast(get())) {} BlockStructuredColumns::BlockStructuredColumns(const Grid& grid, const grid::Distribution& distribution, const eckit::Configuration& config): FunctionSpace(new detail::BlockStructuredColumns(grid, distribution, config)), functionspace_(dynamic_cast(get())) {} BlockStructuredColumns::BlockStructuredColumns(const Grid& grid, const Vertical& vertical, const eckit::Configuration& config): FunctionSpace(new detail::BlockStructuredColumns(grid, vertical, config)), functionspace_(dynamic_cast(get())) {} BlockStructuredColumns::BlockStructuredColumns(const Grid& grid, const Vertical& vertical, const grid::Partitioner& partitioner, const eckit::Configuration& config): FunctionSpace(new detail::BlockStructuredColumns(grid, vertical, partitioner, config)), functionspace_(dynamic_cast(get())) {} BlockStructuredColumns::BlockStructuredColumns(const Grid& grid, const grid::Distribution& distribution, const Vertical& vertical, const eckit::Configuration& config): FunctionSpace(new detail::BlockStructuredColumns(grid, distribution, vertical, config)), functionspace_(dynamic_cast(get())) {} std::string BlockStructuredColumns::checksum(const FieldSet& fieldset) const { return functionspace_->checksum(fieldset); } std::string BlockStructuredColumns::checksum(const Field& field) const { return functionspace_->checksum(field); } // ---------------------------------------------------------------------------- } // namespace functionspace } // namespace atlas atlas-0.46.0/src/atlas/functionspace/detail/0000775000175000017500000000000015160212070021061 5ustar alastairalastairatlas-0.46.0/src/atlas/functionspace/detail/StructuredColumns_create_remote_index.cc0000664000175000017500000002612015160212070031163 0ustar alastairalastair/* * (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/functionspace/StructuredColumns.h" #include #include #include // for bad_alloc exception #include #include #include #include "eckit/log/Bytes.h" #include "atlas/array/MakeView.h" #include "atlas/grid/StructuredGrid.h" #include "atlas/library/Library.h" #include "atlas/mesh/Mesh.h" #include "atlas/parallel/GatherScatter.h" #include "atlas/parallel/mpi/Statistics.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/vector.h" namespace atlas { namespace functionspace { namespace detail { void StructuredColumns::create_remote_index() const { field_remote_index_ = Field("remote_idx", array::make_datatype(), array::make_shape(size_halo_)); auto remote_idx = array::make_indexview(field_remote_index_); atlas_omp_parallel_for(idx_t n = 0; n < size_owned_; ++n) { remote_idx(n) = n; } ATLAS_TRACE_SCOPE("Parallelisation ...") { auto build_partition_graph = [this]() -> std::unique_ptr { const eckit::mpi::Comm& comm = mpi::comm(mpi_comm()); const int mpi_size = int(comm.size()); const int mpi_rank = int(comm.rank()); auto part = array::make_view(this->partition()); std::vector others_set(mpi_size, 0); others_set[mpi_rank] = 1; for (idx_t i = size_owned_; i < size_halo_; ++i) { others_set[part(i)] = 1; // present } std::vector others; others.reserve(mpi_size); for (idx_t p = 0; p < mpi_size; ++p) { if (others_set[p]) { others.emplace_back(p); } } eckit::mpi::Buffer recv_others(mpi_size); ATLAS_TRACE_MPI(ALLGATHER) { comm.allGatherv(others.begin(), others.end(), recv_others); } std::vector counts(recv_others.counts.begin(), recv_others.counts.end()); std::vector displs(recv_others.displs.begin(), recv_others.displs.end()); std::vector values(recv_others.buffer.begin(), recv_others.buffer.end()); return std::unique_ptr( new Mesh::PartitionGraph(values.data(), mpi_size, displs.data(), counts.data())); }; std::unique_ptr graph_ptr; ATLAS_TRACE_SCOPE("Building partition graph...") { graph_ptr = build_partition_graph(); } const Mesh::PartitionGraph& graph = *graph_ptr; ATLAS_TRACE_SCOPE("Setup remote_index fields...") { auto p = array::make_view(partition()); auto g = array::make_view(global_index()); const eckit::mpi::Comm& comm = mpi::comm(mpi_comm()); const int mpi_rank = int(comm.rank()); auto neighbours = graph.nearestNeighbours(mpi_rank); const idx_t nb_neighbours = static_cast(neighbours.size()); std::unordered_map part_to_neighbour; ATLAS_TRACE_SCOPE("part_to_neighbour") { part_to_neighbour.reserve(nb_neighbours); for (idx_t j = 0; j < nb_neighbours; ++j) { part_to_neighbour[neighbours[j]] = j; } } std::vector halo_per_neighbour(neighbours.size(), 0); ATLAS_TRACE_SCOPE("set halo_per_neighbour") for (idx_t i = size_owned_; i < size_halo_; ++i) { halo_per_neighbour[part_to_neighbour[p(i)]]++; } std::vector> g_per_neighbour(neighbours.size()); ATLAS_TRACE_SCOPE("assemble g_per_neighbour") { for (idx_t j = 0; j < nb_neighbours; ++j) { g_per_neighbour[j].reserve(halo_per_neighbour[j]); } for (idx_t j = size_owned_; j < size_halo_; ++j) { g_per_neighbour[part_to_neighbour[p(j)]].emplace_back(g(j)); } } std::vector send_requests(neighbours.size()); std::vector recv_requests(neighbours.size()); std::vector recv_size(neighbours.size()); std::vector send_size(neighbours.size()); int tag = 0; ATLAS_TRACE_SCOPE("send-receive g_per_neighbour size") { for (idx_t j = 0; j < nb_neighbours; ++j) { send_size[j] = static_cast(g_per_neighbour[j].size()); send_requests[j] = comm.iSend(send_size[j], neighbours[j], tag); recv_requests[j] = comm.iReceive(recv_size[j], neighbours[j], tag); } ATLAS_TRACE_MPI(WAIT) { for (idx_t j = 0; j < nb_neighbours; ++j) { comm.wait(send_requests[j]); } for (idx_t j = 0; j < nb_neighbours; ++j) { comm.wait(recv_requests[j]); } } } std::vector> recv_g_per_neighbour(neighbours.size()); ATLAS_TRACE_SCOPE("send-receive g_per_neighbour") for (idx_t j = 0; j < nb_neighbours; ++j) { recv_g_per_neighbour[j].resize(recv_size[j]); send_requests[j] = comm.iSend(g_per_neighbour[j].data(), g_per_neighbour[j].size(), neighbours[j], tag); recv_requests[j] = comm.iReceive(recv_g_per_neighbour[j].data(), recv_g_per_neighbour[j].size(), neighbours[j], tag); } std::vector> send_r_per_neighbour(neighbours.size()); // Assemble "send_r_per_neighbour" // Depending if we can afford memory for a globally sized array, we can have a // much faster version of g_to_r map using std::vector. // TODO: using c++14 we can create a polymorphic lambda to avoid duplicated // code in the two branches of following if. size_t max_glb_idx = grid().size(); atlas::vector g_to_r_vector; bool use_unordered_map_fallback = false; try { g_to_r_vector.resize(max_glb_idx + 1); } catch (std::bad_alloc& e) { if (comm.size() > 1) { Log::warning() << "Could not allocate " << eckit::Bytes((max_glb_idx + 1) * sizeof(idx_t)) << Here() << "\n" << "Using slower unordered_map fallback to map global to remote indices" << std::endl; use_unordered_map_fallback = true; } else { throw_Exception( "Could not allocate " + std::string(eckit::Bytes((max_glb_idx + 1) * sizeof(idx_t))), Here()); } } if (not use_unordered_map_fallback) { auto& g_to_r = g_to_r_vector; ATLAS_TRACE_SCOPE("g_to_r (using vector)") { atlas_omp_parallel_for(idx_t j = 0; j < size_owned_; ++j) { // parallel omp possible for ``` g_to_r[g(j)] = j ``` as we only loop over size_owned, // where global_index is such that race-conditions cannot occur #if ATLAS_ARRAYVIEW_BOUNDS_CHECKING if (g(j) >= max_glb_idx + 1) { ATLAS_DEBUG_VAR(g(j)); throw_OutOfRange("g_to_r", g(j), max_glb_idx + 1, Here()); } #endif g_to_r[g(j)] = j; } } for (idx_t j = 0; j < nb_neighbours; ++j) { send_r_per_neighbour[j].reserve(recv_size[j]); comm.wait(recv_requests[j]); // wait for recv_g_per_neighbour[j] for (gidx_t gidx : recv_g_per_neighbour[j]) { send_r_per_neighbour[j].emplace_back(g_to_r[gidx]); } } } else { std::unordered_map g_to_r; g_to_r.reserve(size_owned_); ATLAS_TRACE_SCOPE("g_to_r (using unordered set)") { for (idx_t j = 0; j < size_owned_; ++j) { g_to_r[g(j)] = j; } } ATLAS_TRACE_SCOPE("assemble r_per_neighbour") for (idx_t j = 0; j < nb_neighbours; ++j) { send_r_per_neighbour[j].reserve(recv_size[j]); comm.wait(recv_requests[j]); // wait for recv_g_per_neighbour[j] for (gidx_t gidx : recv_g_per_neighbour[j]) { send_r_per_neighbour[j].emplace_back(g_to_r[gidx]); } } } std::vector> r_per_neighbour(neighbours.size()); ATLAS_TRACE_SCOPE("send-receive r_per_neighbour") { for (idx_t j = 0; j < nb_neighbours; ++j) { r_per_neighbour[j].resize(halo_per_neighbour[j]); } ATLAS_TRACE_MPI(ALLTOALL) { for (idx_t j = 0; j < nb_neighbours; ++j) { comm.wait(send_requests[j]); send_requests[j] = comm.iSend(send_r_per_neighbour[j].data(), send_r_per_neighbour[j].size(), neighbours[j], tag); recv_requests[j] = comm.iReceive(r_per_neighbour[j].data(), r_per_neighbour[j].size(), neighbours[j], tag); } } ATLAS_TRACE_MPI(WAIT) { for (idx_t j = 0; j < nb_neighbours; ++j) { comm.wait(recv_requests[j]); } } } std::vector counters(neighbours.size(), 0); ATLAS_TRACE_SCOPE("set remote_idx") for (idx_t j = size_owned_; j < size_halo_; ++j) { idx_t neighbour = part_to_neighbour[p(j)]; remote_idx(j) = r_per_neighbour[neighbour][counters[neighbour]++]; } ATLAS_TRACE_MPI(WAIT) { for (idx_t j = 0; j < nb_neighbours; ++j) { comm.wait(send_requests[j]); } } } } } } // namespace detail // ---------------------------------------------------------------------------- } // namespace functionspace } // namespace atlas atlas-0.46.0/src/atlas/functionspace/detail/FunctionSpaceImpl.cc0000664000175000017500000001117515160212070024760 0ustar alastairalastair/* * (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 "FunctionSpaceImpl.h" #include "atlas/field/Field.h" #include "atlas/grid.h" #include "atlas/option/Options.h" #include "atlas/runtime/Exception.h" #include "atlas/util/Metadata.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/array/MakeView.h" namespace atlas { namespace functionspace { // ------------------------------------------------------------------ FunctionSpaceImpl::FunctionSpaceImpl(): metadata_(new util::Metadata()) {} FunctionSpaceImpl::~FunctionSpaceImpl() { delete metadata_; } atlas::Field FunctionSpaceImpl::createField(const atlas::Field& field) const { return createField(field, util::NoConfig()); } void FunctionSpaceImpl::haloExchange(const FieldSet&, bool) const { ATLAS_NOTIMPLEMENTED; } void FunctionSpaceImpl::haloExchange(const Field&, bool) const { ATLAS_NOTIMPLEMENTED; } void FunctionSpaceImpl::adjointHaloExchange(const FieldSet&, bool) const { ATLAS_NOTIMPLEMENTED; } void FunctionSpaceImpl::adjointHaloExchange(const Field&, bool) const { ATLAS_NOTIMPLEMENTED; } Field NoFunctionSpace::createField(const eckit::Configuration&) const { ATLAS_NOTIMPLEMENTED; } Field NoFunctionSpace::createField(const Field&, const eckit::Configuration&) const { ATLAS_NOTIMPLEMENTED; } const Grid& FunctionSpaceImpl::grid() const { ATLAS_NOTIMPLEMENTED; } Field FunctionSpaceImpl::lonlat() const { ATLAS_NOTIMPLEMENTED; } Field FunctionSpaceImpl::ghost() const { ATLAS_NOTIMPLEMENTED; } Field FunctionSpaceImpl::remote_index() const { ATLAS_NOTIMPLEMENTED; } Field FunctionSpaceImpl::partition() const { ATLAS_NOTIMPLEMENTED; } Field FunctionSpaceImpl::global_index() const { ATLAS_NOTIMPLEMENTED; } const util::PartitionPolygon& FunctionSpaceImpl::polygon(idx_t /*halo */) const { throw_Exception("polygon() not implemented in derived class", Here()); } const util::PartitionPolygons& FunctionSpaceImpl::polygons() const { throw_Exception("polygons() not implemented in derived class", Here()); } const Projection& FunctionSpaceImpl::projection() const { throw_Exception("projection() not implemented in derived class", Here()); } template Field FunctionSpaceImpl::createField(const eckit::Configuration& options) const { return createField(option::datatypeT() | options); } template Field FunctionSpaceImpl::createField() const { return createField(option::datatypeT()); } idx_t FunctionSpaceImpl::part() const { ATLAS_NOTIMPLEMENTED; } idx_t FunctionSpaceImpl::nb_parts() const { ATLAS_NOTIMPLEMENTED; } void FunctionSpaceImpl::gather(const FieldSet& local, FieldSet& global) const { ATLAS_NOTIMPLEMENTED; } void FunctionSpaceImpl::gather(const Field& local, Field& global) const { ATLAS_NOTIMPLEMENTED; } void FunctionSpaceImpl::scatter(const FieldSet& global, FieldSet& local) const { ATLAS_NOTIMPLEMENTED; } void FunctionSpaceImpl::scatter(const Field& global, Field& local) const { ATLAS_NOTIMPLEMENTED; } const parallel::GatherScatter& FunctionSpaceImpl::gather() const { ATLAS_NOTIMPLEMENTED; } const parallel::GatherScatter& FunctionSpaceImpl::scatter() const { ATLAS_NOTIMPLEMENTED; } std::string FunctionSpaceImpl::mpi_comm() const { return mpi::comm().name(); } const HaloDescription& FunctionSpaceImpl::halo_description() const { if (not halo_description_) { halo_description_.reset(new HaloDescription(array::make_view(ghost()))); } return *halo_description_; } template Field FunctionSpaceImpl::createField() const; template Field FunctionSpaceImpl::createField() const; template Field FunctionSpaceImpl::createField() const; template Field FunctionSpaceImpl::createField() const; template Field FunctionSpaceImpl::createField(const eckit::Configuration&) const; template Field FunctionSpaceImpl::createField(const eckit::Configuration&) const; template Field FunctionSpaceImpl::createField(const eckit::Configuration&) const; template Field FunctionSpaceImpl::createField(const eckit::Configuration&) const; // ------------------------------------------------------------------ } // namespace functionspace // ------------------------------------------------------------------ } // namespace atlas atlas-0.46.0/src/atlas/functionspace/detail/CellColumnsInterface.cc0000664000175000017500000001530515160212070025435 0ustar alastairalastair/* * (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 "CellColumnsInterface.h" #include "atlas/field/FieldSet.h" #include "atlas/field/detail/FieldImpl.h" #include "atlas/runtime/Exception.h" namespace atlas { namespace functionspace { namespace detail { using atlas::FieldSet; using atlas::field::FieldImpl; using atlas::field::FieldSetImpl; // ---------------------------------------------------------------------- extern "C" { const CellColumns* atlas__CellsFunctionSpace__new(Mesh::Implementation* mesh, const eckit::Configuration* config) { ATLAS_ASSERT(mesh); Mesh m(mesh); return new CellColumns(m, *config); } void atlas__CellsFunctionSpace__delete(CellColumns* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_CellColumns"); delete (This); } int atlas__CellsFunctionSpace__nb_cells(const CellColumns* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_CellColumns"); return This->nb_cells(); } const Mesh::Implementation* atlas__CellsFunctionSpace__mesh(const CellColumns* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_CellColumns"); return This->mesh().get(); } const mesh::HybridElements* atlas__CellsFunctionSpace__cells(const CellColumns* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_CellColumns"); return &This->cells(); } void atlas__CellsFunctionSpace__halo_exchange_fieldset(const CellColumns* This, field::FieldSetImpl* fieldset) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_CellColumns"); ATLAS_ASSERT(fieldset != nullptr, "Cannot access uninitialised atlas_FieldSet"); FieldSet f(fieldset); This->haloExchange(f); } void atlas__CellsFunctionSpace__halo_exchange_field(const CellColumns* This, field::FieldImpl* field) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_CellColumns"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialised atlas_Field"); Field f(field); This->haloExchange(f); } const parallel::HaloExchange* atlas__CellsFunctionSpace__get_halo_exchange(const CellColumns* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_CellColumns"); return &This->halo_exchange(); } void atlas__CellsFunctionSpace__gather_fieldset(const CellColumns* This, const field::FieldSetImpl* local, field::FieldSetImpl* global) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_CellColumns"); ATLAS_ASSERT(local != nullptr, "Cannot access uninitialised local atlas_FieldSet"); ATLAS_ASSERT(global != nullptr, "Cannot access uninitialised global atlas_FieldSet"); const FieldSet l(local); FieldSet g(global); This->gather(l, g); } void atlas__CellsFunctionSpace__gather_field(const CellColumns* This, const field::FieldImpl* local, field::FieldImpl* global) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_CellColumns"); ATLAS_ASSERT(local != nullptr, "Cannot access uninitialised local atlas_Field"); ATLAS_ASSERT(global != nullptr, "Cannot access uninitialised global atlas_Field"); const Field l(local); Field g(global); This->gather(l, g); } const parallel::GatherScatter* atlas__CellsFunctionSpace__get_gather(const CellColumns* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_CellColumns"); return &This->gather(); } const parallel::GatherScatter* atlas__CellsFunctionSpace__get_scatter(const CellColumns* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_CellColumns"); return &This->scatter(); } void atlas__CellsFunctionSpace__scatter_fieldset(const CellColumns* This, const field::FieldSetImpl* global, field::FieldSetImpl* local) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_CellColumns"); ATLAS_ASSERT(local != nullptr, "Cannot access uninitialised local atlas_FieldSet"); ATLAS_ASSERT(global != nullptr, "Cannot access uninitialised global atlas_FieldSet"); const FieldSet g(global); FieldSet l(local); This->scatter(g, l); } void atlas__CellsFunctionSpace__scatter_field(const CellColumns* This, const field::FieldImpl* global, field::FieldImpl* local) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_CellColumns"); ATLAS_ASSERT(local != nullptr, "Cannot access uninitialised local atlas_Field"); ATLAS_ASSERT(global != nullptr, "Cannot access uninitialised global atlas_Field"); const Field g(global); Field l(local); This->scatter(g, l); } const parallel::Checksum* atlas__CellsFunctionSpace__get_checksum(const CellColumns* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_CellColumns"); return &This->checksum(); } void atlas__CellsFunctionSpace__checksum_fieldset(const CellColumns* This, const field::FieldSetImpl* fieldset, char*& checksum, int& size, int& allocated) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_CellColumns"); ATLAS_ASSERT(fieldset != nullptr, "Cannot access uninitialised atlas_FieldSet"); std::string checksum_str(This->checksum(fieldset)); size = static_cast(checksum_str.size()); checksum = new char[size + 1]; allocated = true; std::strncpy(checksum, checksum_str.c_str(), size + 1); } void atlas__CellsFunctionSpace__checksum_field(const CellColumns* This, const field::FieldImpl* field, char*& checksum, int& size, int& allocated) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_CellColumns"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialised atlas_Field"); std::string checksum_str(This->checksum(field)); size = static_cast(checksum_str.size()); checksum = new char[size + 1]; allocated = true; std::strncpy(checksum, checksum_str.c_str(), size + 1); } } // extern C } // namespace detail } // namespace functionspace } // namespace atlas atlas-0.46.0/src/atlas/functionspace/detail/PointCloudInterface.cc0000664000175000017500000000364415160212070025300 0ustar alastairalastair/* * (C) Copyright 200 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include #include "PointCloudInterface.h" #include "atlas/field/FieldSet.h" #include "atlas/field/detail/FieldImpl.h" #include "atlas/grid/Grid.h" #include "atlas/runtime/Exception.h" namespace atlas { namespace functionspace { // ---------------------------------------------------------------------------- // Fortran interfaces // ---------------------------------------------------------------------------- extern "C" { const detail::PointCloud* atlas__functionspace__PointCloud__new__lonlat(const Field::Implementation* lonlat) { return new detail::PointCloud(Field(lonlat)); } const detail::PointCloud* atlas__functionspace__PointCloud__new__lonlat_ghost(const field::FieldImpl* lonlat, const field::FieldImpl* ghost) { return new detail::PointCloud(Field(lonlat), Field(ghost)); } const detail::PointCloud* atlas__functionspace__PointCloud__new__fieldset(const field::FieldSetImpl* fset) { return new detail::PointCloud(FieldSet(fset)); } const detail::PointCloud* atlas__functionspace__PointCloud__new__grid(const Grid::Implementation* grid) { return new detail::PointCloud(Grid(grid)); } const field::FieldImpl* atlas__fs__PointCloud__lonlat(const detail::PointCloud* This) { return This->lonlat().get(); } idx_t atlas__fs__PointCloud__size(const detail::PointCloud* This) { return This->size(); } } // ---------------------------------------------------------------------------- } // namespace functionspace } // namespace atlas atlas-0.46.0/src/atlas/functionspace/detail/StructuredColumns_setup.cc0000664000175000017500000006411015160212070026317 0ustar alastairalastair/* * (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/functionspace/StructuredColumns.h" #include #include #include #include #include #include "atlas/array/MakeView.h" #include "atlas/field/FieldSet.h" #include "atlas/grid/Distribution.h" #include "atlas/grid/Partitioner.h" #include "atlas/grid/StructuredGrid.h" #include "atlas/library/Library.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/parallel/omp/omp.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Trace.h" #include "atlas/util/CoordinateEnums.h" namespace atlas { namespace functionspace { namespace detail { namespace { struct GridPoint { public: idx_t i, j; idx_t r; GridPoint() = default; GridPoint(idx_t _i, idx_t _j): i(_i), j(_j) {} GridPoint(idx_t _i, idx_t _j, idx_t _r): i(_i), j(_j), r(_r) {} /* No longer used: bool operator<( const GridPoint& other ) const { if ( j < other.j ) return true; if ( j == other.j ) return i < other.i; return false; } bool operator==( const GridPoint& other ) const { return ( j == other.j && i == other.i ); } */ }; struct GridPointSet { public: GridPointSet() = default; atlas::vector gp_; void reserve(size_t size) { gp_.reserve(size); } void resize(size_t size) { gp_.resize(size); } void set(idx_t i, idx_t j, idx_t r) { gp_[r].i = i; gp_[r].j = j; gp_[r].r = r; } idx_t size() const { return static_cast(gp_.size()); } // idx_t capacity() const { return static_cast(gp_.capacity()); } const GridPoint& operator[](idx_t i) const { return gp_[i]; } // unused: //using const_iterator = decltype( gp_ )::const_iterator; //const_iterator begin() const { return gp_.begin(); } //const_iterator end() const { return gp_.end(); } }; } // namespace void StructuredColumns::setup(const grid::Distribution& distribution, const eckit::Configuration& config) { config.get("periodic_points", periodic_points_); if (not(*grid_)) { throw_Exception("Grid is not a grid::Structured type", Here()); } bool periodic_x = false, periodic_y = false; config.get("periodic_x", periodic_x); config.get("periodic_y", periodic_y); bool regional = (!periodic_x && !periodic_y && !grid_->domain().global()); const double eps = 1.e-12; ny_ = grid_->ny(); north_pole_included_ = 90. - grid_->y(0) == 0.; south_pole_included_ = 90. + grid_->y(ny_ - 1) == 0; distribution_ = distribution.type(); part_ = mpi::comm(mpi_comm()).rank(); nb_partitions_ = distribution.nb_partitions(); // We initialize j_begin_ and j_end_ to large integers, but we avoid using // the max/min (as in numeric_limits::max()/min()) values because we // need the difference `j_end_ - j_begin_` to be representable as an idx_t. // This is to avoid miscompilation of OpenMP for loops below when using the // gcc compiler, as described in https://github.com/ecmwf/atlas/issues/323 const idx_t max_over_two = std::numeric_limits::max() / 2; j_begin_ = max_over_two; j_end_ = -max_over_two; // The logic around i_begin_, i_end_ doesn't miscompile in the same way so // we initialize to the max/min values. i_begin_.resize(grid_->ny(), std::numeric_limits::max()); i_end_.resize(grid_->ny(), std::numeric_limits::min()); idx_t owned(0); ATLAS_TRACE_SCOPE("Compute bounds owned") { if (nb_partitions_ == 1) { j_begin_ = 0; j_end_ = grid_->ny(); for (idx_t j = j_begin_; j < j_end_; ++j) { i_begin_[j] = 0; i_end_[j] = grid_->nx(j); } owned = grid_->size(); } else { size_t num_threads = atlas_omp_get_max_threads(); if (num_threads == 1) { idx_t c(0); for (idx_t j = 0; j < grid_->ny(); ++j) { for (idx_t i = 0; i < grid_->nx(j); ++i, ++c) { if (distribution.partition(c) == part_) { j_begin_ = std::min(j_begin_, j); j_end_ = std::max(j_end_, j + 1); i_begin_[j] = std::min(i_begin_[j], i); i_end_[j] = std::max(i_end_[j], i + 1); ++owned; } } } } else { std::vector thread_reduce_j_begin(num_threads, std::numeric_limits::max()); std::vector thread_reduce_j_end(num_threads, std::numeric_limits::min()); std::vector> thread_reduce_i_begin( grid_->ny(), std::vector(num_threads, std::numeric_limits::max())); std::vector> thread_reduce_i_end( grid_->ny(), std::vector(num_threads, std::numeric_limits::min())); std::vector thread_reduce_owned(num_threads, 0); atlas_omp_parallel { const idx_t thread_num = atlas_omp_get_thread_num(); const idx_t begin = static_cast(size_t(thread_num) * size_t(grid_->size()) / size_t(num_threads)); const idx_t end = static_cast(size_t(thread_num + 1) * size_t(grid_->size()) / size_t(num_threads)); idx_t thread_j_begin = 0; std::vector thread_i_begin(grid_->ny()); std::vector thread_i_end(grid_->ny()); idx_t n = 0; for (idx_t j = 0; j < grid_->ny(); ++j) { if (n + grid_->nx(j) > begin) { thread_j_begin = j; thread_i_begin[j] = begin - n; break; } n += grid_->nx(j); } idx_t thread_j_end{thread_j_begin}; for (idx_t j = thread_j_begin; j < grid_->ny(); ++j) { idx_t i_end = end - n; if (j > thread_j_begin) { thread_i_begin[j] = 0; } if (i_end > grid_->nx(j)) { thread_i_end[j] = grid_->nx(j); n += grid_->nx(j); } else { thread_i_end[j] = i_end; thread_j_end = j + 1; break; } } auto& __j_begin = thread_reduce_j_begin[thread_num]; auto& __j_end = thread_reduce_j_end[thread_num]; auto& __owned = thread_reduce_owned[thread_num]; idx_t c = begin; for (idx_t j = thread_j_begin; j < thread_j_end; ++j) { auto& __i_begin = thread_reduce_i_begin[j][thread_num]; auto& __i_end = thread_reduce_i_end[j][thread_num]; bool j_in_partition{false}; for (idx_t i = thread_i_begin[j]; i < thread_i_end[j]; ++i, ++c) { if (distribution.partition(c) == part_) { j_in_partition = true; __i_begin = std::min(__i_begin, i); __i_end = std::max(__i_end, i + 1); ++__owned; } } if (j_in_partition) { __j_begin = std::min(__j_begin, j); __j_end = std::max(__j_end, j + 1); } } ATLAS_ASSERT(c == end); } owned = std::accumulate(thread_reduce_owned.begin(), thread_reduce_owned.end(), 0); j_begin_ = *std::min_element(thread_reduce_j_begin.begin(), thread_reduce_j_begin.end()); j_end_ = *std::max_element(thread_reduce_j_end.begin(), thread_reduce_j_end.end()); atlas_omp_parallel_for(idx_t j = j_begin_; j < j_end_; ++j) { i_begin_[j] = *std::min_element(thread_reduce_i_begin[j].begin(), thread_reduce_i_begin[j].end()); i_end_[j] = *std::max_element(thread_reduce_i_end[j].begin(), thread_reduce_i_end[j].end()); } } } } size_owned_ = owned; int halo = config.getInt("halo", 0); halo_ = halo; j_begin_halo_ = j_begin_ - halo; j_end_halo_ = j_end_ + halo; i_begin_halo_.resize(-halo, grid_->ny() - 1 + halo); i_end_halo_.resize(-halo, grid_->ny() - 1 + halo); if (regional) { j_begin_halo_ = std::max(j_begin_halo_, idx_t{0}); j_end_halo_ = std::min(j_end_halo_, grid_->ny()); } auto compute_i = [this](idx_t i, idx_t j) -> idx_t { const idx_t nx = grid_->nx(j); while (i >= nx) { i -= nx; } while (i < 0) { i += nx; } return i; }; auto compute_i_fast = [](idx_t i, const idx_t nx) -> idx_t { while (i >= nx) { i -= nx; } while (i < 0) { i += nx; } return i; }; std::function compute_j; compute_j = [this, &compute_j, &periodic_y](idx_t j) -> idx_t { if (periodic_y) { const idx_t ny = grid_->ny(); while (j < 0) { j += ny; } while (j >= ny) { j -= ny; } } else { if (j < 0) { j = (grid_->y(0) == 90.) ? -j : -j - 1; } else if (j >= grid_->ny()) { idx_t jlast = grid_->ny() - 1; j = (grid_->y(jlast) == -90.) ? jlast - 1 - (j - grid_->ny()) : jlast - (j - grid_->ny()); } if (j < 0 or j >= grid_->ny()) { j = compute_j(j); } } return j; }; auto compute_x = [this, &compute_i, &compute_j](idx_t i, idx_t j) -> double { const idx_t jj = compute_j(j); const idx_t ii = compute_i(i, jj); // guaranteed between 0 and nx(jj) const idx_t nx = grid_->nx(jj); const double a = (ii - i) / nx; const double x = grid_->x(ii, jj) - a * grid_->x(nx, jj); return x; }; auto compute_x_fast = [this, &compute_i_fast](idx_t i, idx_t jj, idx_t nx) -> double { const idx_t ii = compute_i_fast(i, nx); // guaranteed between 0 and nx(jj) const double a = (ii - i) / nx; const double x = grid_->x(ii, jj) - a * (grid_->x(nx, jj) - grid_->x(0, jj)); return x; }; auto compute_i_less_equal_x = [this, &eps](const double& x, idx_t j) -> idx_t { const double dx = grid_->dx(j); idx_t i = idx_t(std::floor((x + eps - grid_->xmin(j)) / dx)); return i; }; auto compute_y = [this, &compute_j, &periodic_y](idx_t j) -> double { double y; idx_t jj; jj = compute_j(j); if (periodic_y) { y = grid_->y(jj); } else { y = (j < 0) ? 90. + (90. - grid_->y(jj)) : (j >= grid_->ny()) ? -90. + (-90. - grid_->y(jj)) : grid_->y(jj); } return y; }; std::vector global_offsets(grid_->ny()); idx_t grid_idx = 0; for (idx_t j = 0; j < grid_->ny(); ++j) { global_offsets[j] = grid_idx; grid_idx += grid_->nx(j); } auto compute_g = [this, &global_offsets, &compute_i, &compute_j, &periodic_y](idx_t i, idx_t j) -> gidx_t { idx_t ii, jj; gidx_t g; jj = compute_j(j); ii = compute_i(i, jj); if (!periodic_y) { if (jj != j) { if (grid_->nx(jj) % 2 == 0) { ii = (ii < grid_->nx(jj) / 2) ? ii + grid_->nx(jj) / 2 : (ii >= grid_->nx(jj) / 2) ? ii - grid_->nx(jj) / 2 : ii; } else { if (ii < grid_->nx(jj)/2 + 1) { ii += grid_->nx(jj) / 2 + 1; } else if (ii >= grid_->nx(jj)/2 + 1) { ii -= grid_->nx(jj) / 2 + 1; } } } } g = global_offsets[jj] + ii + 1; return g; }; auto compute_p = [&compute_g, &distribution](idx_t i, idx_t j) -> int { return distribution.partition(compute_g(i, j) - 1); }; GridPointSet gridpoints; ATLAS_TRACE_SCOPE("Compute mapping") { idx_t imin = std::numeric_limits::max(); idx_t imax = -std::numeric_limits::max(); idx_t jmin = std::numeric_limits::max(); idx_t jmax = -std::numeric_limits::max(); ATLAS_TRACE_SCOPE("Compute bounds halo") { for (idx_t j = j_begin_ - halo; j < j_end_ + halo; ++j) { i_begin_halo_(j) = imin; i_end_halo_(j) = imax; } // Following cannot be multithreaded in current form due to race-conditions related to index jj for (idx_t j = j_begin_; j < j_end_; ++j) { for (idx_t i : {i_begin_[j], i_end_[j] - 1}) { // Following line only, increases periodic halo on the east side by 1 if (periodic_points_ && i == grid_->nx(j) - 1) { ++i; } double x = grid_->x(i, j); double x_next = grid_->x(i + idx_t{1}, j); double x_prev = grid_->x(i - idx_t{1}, j); idx_t jj_min = j - halo; idx_t jj_max = j + halo; if (regional) { jj_min = std::max(jj_min, idx_t{0}); jj_max = std::min(jj_max, grid_->ny() - idx_t{1}); } for (idx_t jj = jj_min; jj <= jj_max; ++jj) { idx_t jjj = compute_j(jj); idx_t nx_jjj = grid_->nx(jjj); idx_t last = grid_->nx(jjj) - idx_t{1}; if (i == grid_->nx(j)) { ++last; } jmin = std::min(jmin, jj); jmax = std::max(jmax, jj); // Compute ii as index less-equal of x // // x(i,j) // |-----|-----|-----|-----| // ii-halo ii // // x(i,j) // |-----|-----|--+--|-----| // ii-halo ii // idx_t ii = -halo; // while ( compute_x_fast( ii, jjj, nx_jjj ) < x - eps ) { // ii++; // } // Question: is following implementation reproducible with above original while loop? idx_t ii = compute_i_less_equal_x(x, jjj); // ATLAS-186 workaround // This while should not have to be there, but is here because of // the MatchingMeshDomainDecomposition algorithm. that may lead to points // left of the point ii. while (compute_x_fast(ii - 1, jjj, nx_jjj) > x_prev + eps) { --ii; } idx_t i_minus_halo = ii - halo; // Compute iii as index less-equal of x_next // // x(i,j) x_next(i,j) // |-----|-----|-+---|-+---|-----| // ii-halo iii iii+halo // idx_t iii = ii; while (compute_x_fast(iii + 1, jjj, nx_jjj) < x_next - eps) { ++iii; } iii = std::min(iii, last); idx_t i_plus_halo = iii + halo; if (regional) { i_minus_halo = std::max(i_minus_halo, idx_t{0}); i_plus_halo = std::min(i_plus_halo, grid_->nx(jj) - idx_t{1}); } imin = std::min(imin, i_minus_halo); imax = std::max(imax, i_plus_halo); i_begin_halo_(jj) = std::min(i_begin_halo_(jj), i_minus_halo); i_end_halo_(jj) = std::max(i_end_halo_(jj), i_plus_halo + idx_t{1}); } } } } int extra_halo{0}; for (idx_t j = j_begin_halo_; j < j_begin_; ++j) { extra_halo += i_end_halo_(j) - i_begin_halo_(j); } for (idx_t j = j_begin_; j < j_end_; ++j) { extra_halo += i_begin_[j] - i_begin_halo_(j); extra_halo += i_end_halo_(j) - i_end_[j]; } for (idx_t j = j_end_; j < j_end_halo_; ++j) { extra_halo += i_end_halo_(j) - i_begin_halo_(j); } ATLAS_TRACE_SCOPE("Assemble gridpoints") { gridpoints.reserve(owned + extra_halo); gridpoints.resize(owned); if (atlas_omp_get_max_threads() == 1) { idx_t r = 0; for (idx_t j = j_begin_; j < j_end_; ++j) { for (idx_t i = i_begin_[j]; i < i_end_[j]; ++i, ++r) { gridpoints.set(i, j, r); } } ATLAS_ASSERT(r == owned); } else { atlas_omp_parallel { const size_t num_threads = atlas_omp_get_num_threads(); const size_t thread_num = atlas_omp_get_thread_num(); auto chunksize = size_t(owned) / num_threads; size_t begin = chunksize * thread_num; size_t end = (thread_num == num_threads - 1) ? owned : begin + chunksize; idx_t thread_j_begin = 0; std::vector thread_i_begin(grid_->ny()); std::vector thread_i_end(grid_->ny()); size_t n = 0; for (idx_t j = j_begin_; j < j_end_; ++j) { size_t j_size = static_cast(i_end_[j] - i_begin_[j]); if (n + j_size > begin) { thread_j_begin = j; thread_i_begin[j] = i_begin_[j] + static_cast(begin - n); break; } n += j_size; } idx_t thread_j_end{thread_j_begin}; for (idx_t j = thread_j_begin; j < j_end_; ++j) { if (j > thread_j_begin) { thread_i_begin[j] = i_begin_[j]; } idx_t j_size = (i_end_[j] - i_begin_[j]); idx_t remaining = static_cast(end - n); if (j_size <= remaining) { thread_i_end[j] = i_end_[j]; n += j_size; if (n == end) { goto stop; } } else { thread_i_end[j] = i_begin_[j] + remaining; goto stop; } continue; stop: thread_j_end = j + 1; break; } idx_t r = idx_t(begin); for (idx_t j = thread_j_begin; j < thread_j_end; ++j) { for (idx_t i = thread_i_begin[j]; i < thread_i_end[j]; ++i, ++r) { gridpoints.set(i, j, r); } } if (r != idx_t(end)) { ATLAS_DEBUG_VAR(thread_num); ATLAS_DEBUG_VAR(begin); ATLAS_DEBUG_VAR(end); ATLAS_DEBUG_VAR(r); } ATLAS_ASSERT(r == idx_t(end)); } } ATLAS_ASSERT(gridpoints.size() == owned); gridpoints.resize(owned + extra_halo); idx_t r = owned; for (idx_t j = j_begin_halo_; j < j_begin_; ++j) { for (idx_t i = i_begin_halo_(j); i < i_end_halo_(j); ++i, ++r) { gridpoints.set(i, j, r); } } for (idx_t j = j_begin_; j < j_end_; ++j) { for (idx_t i = i_begin_halo_(j); i < i_begin_[j]; ++i, ++r) { gridpoints.set(i, j, r); } for (idx_t i = i_end_[j]; i < i_end_halo_(j); ++i, ++r) { gridpoints.set(i, j, r); } } for (idx_t j = j_end_; j < j_end_halo_; ++j) { for (idx_t i = i_begin_halo_(j); i < i_end_halo_(j); ++i, ++r) { gridpoints.set(i, j, r); } } ATLAS_ASSERT(gridpoints.size() == owned + extra_halo); } ATLAS_TRACE_SCOPE("Fill in ij2gp ") { ij2gp_.resize({imin, imax}, {jmin, jmax}); atlas_omp_parallel_for(idx_t n = 0; n < gridpoints.size(); ++n) { const GridPoint& gp = gridpoints[n]; ij2gp_.set(gp.i, gp.j, gp.r); } } } ATLAS_TRACE_SCOPE("Create fields") { size_halo_ = gridpoints.size(); field_partition_ = Field("partition", array::make_datatype(), array::make_shape(size_halo_)); field_ghost_ = Field("ghost", array::make_datatype(), array::make_shape(size_halo_)); field_global_index_ = Field("glb_idx", array::make_datatype(), array::make_shape(size_halo_)); field_index_i_ = Field("index_i", array::make_datatype(), array::make_shape(size_halo_)); field_index_j_ = Field("index_j", array::make_datatype(), array::make_shape(size_halo_)); field_xy_ = Field("xy", array::make_datatype(), array::make_shape(size_halo_, 2)); field_z_ = Field("z", array::make_datatype(), array::make_shape(vertical_.size())); { auto z = array::make_view(field_z_); for (idx_t k = 0; k < z.size(); ++k) { z(k) = vertical_[k]; } } auto xy = array::make_view(field_xy_); auto part = array::make_view(field_partition_); auto ghost = array::make_view(field_ghost_); auto global_idx = array::make_view(field_global_index_); auto index_i = array::make_indexview(field_index_i_); auto index_j = array::make_indexview(field_index_j_); atlas_omp_parallel_for(idx_t n = 0; n < gridpoints.size(); ++n) { const GridPoint& gp = gridpoints[n]; if (regional) { std::array lonlatVec; grid_->lonlat(gp.i, gp.j, lonlatVec.data()); xy(gp.r, XX) = lonlatVec[0]; xy(gp.r, YY) = lonlatVec[1]; } else { if (gp.j >= 0 && gp.j < grid_->ny()) { xy(gp.r, XX) = grid_->x(gp.i, gp.j); xy(gp.r, YY) = grid_->y(gp.j); } else { xy(gp.r, XX) = compute_x(gp.i, gp.j); xy(gp.r, YY) = compute_y(gp.j); } } bool in_domain(false); if (gp.j >= 0 && gp.j < grid_->ny()) { if (gp.i >= 0 && gp.i < grid_->nx(gp.j)) { in_domain = true; gidx_t k = global_offsets[gp.j] + gp.i; part(gp.r) = distribution.partition(k); global_idx(gp.r) = k + 1; } } if (not in_domain) { global_idx(gp.r) = compute_g(gp.i, gp.j); part(gp.r) = compute_p(gp.i, gp.j); } index_i(gp.r) = gp.i; index_j(gp.r) = gp.j; ghost(gp.r) = 0; } // Following short loops are not parallelized with OpenMP for (idx_t j = j_begin_halo_; j < j_begin_; ++j) { for (idx_t i = i_begin_halo_(j); i < i_end_halo_(j); ++i) { ghost(index(i, j)) = 1; } } atlas_omp_parallel_for(idx_t j = j_begin_; j < j_end_; ++j) { for (idx_t i = i_begin_halo_(j); i < i_begin_[j]; ++i) { ghost(index(i, j)) = 1; } for (idx_t i = i_end_[j]; i < i_end_halo_(j); ++i) { ghost(index(i, j)) = 1; } } for (idx_t j = j_end_; j < j_end_halo_; ++j) { for (idx_t i = i_begin_halo_(j); i < i_end_halo_(j); ++i) { ghost(index(i, j)) = 1; } } } } } // namespace detail // ---------------------------------------------------------------------------- } // namespace functionspace } // namespace atlas atlas-0.46.0/src/atlas/functionspace/detail/BlockStructuredColumns.h0000664000175000017500000001314215160212070025713 0ustar alastairalastair/* * (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 #include #include "atlas/array/DataType.h" #include "atlas/field/Field.h" #include "atlas/functionspace/detail/FunctionSpaceImpl.h" #include "atlas/functionspace/detail/StructuredColumns.h" #include "atlas/functionspace/StructuredColumns.h" #include "atlas/grid/StructuredGrid.h" #include "atlas/grid/Vertical.h" #include "atlas/library/config.h" #include "atlas/option.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/parallel/omp/omp.h" #include "atlas/runtime/Exception.h" #include "atlas/util/Config.h" #include "atlas/util/ObjectHandle.h" #include "atlas/util/Point.h" #include "atlas/util/Polygon.h" #include "atlas/util/vector.h" namespace atlas { namespace parallel { class GatherScatter; //class HaloExchange; //class Checksum; } // namespace parallel } // namespace atlas namespace atlas { class Field; //class FieldSet; class Grid; class StructuredGrid; } // namespace atlas namespace atlas { namespace grid { class Distribution; class Partitioner; } // namespace grid } // namespace atlas namespace atlas { namespace functionspace { namespace detail { // ------------------------------------------------------------------- class BlockStructuredColumns : public FunctionSpaceImpl { public: BlockStructuredColumns(const Grid&, const eckit::Configuration& = util::NoConfig()); BlockStructuredColumns(const Grid&, const grid::Partitioner&, const eckit::Configuration& = util::NoConfig()); BlockStructuredColumns(const Grid&, const grid::Distribution&, const eckit::Configuration& = util::NoConfig()); BlockStructuredColumns(const Grid&, const grid::Distribution&, const Vertical&, const eckit::Configuration& = util::NoConfig()); BlockStructuredColumns(const Grid&, const Vertical&, const eckit::Configuration& = util::NoConfig()); BlockStructuredColumns(const Grid&, const Vertical&, const grid::Partitioner&, const eckit::Configuration& = util::NoConfig()); static std::string static_type() { return "BlockStructuredColumns"; } std::string type() const override { return static_type(); } std::string distribution() const override { return structuredcolumns_->distribution(); } Field createField(const eckit::Configuration&) const override; Field createField(const Field&, const eckit::Configuration&) const override; using FunctionSpaceImpl::scatter; void scatter(const FieldSet&, FieldSet&) const override; void scatter(const Field&, Field&) const override; using FunctionSpaceImpl::gather; void gather(const FieldSet&, FieldSet&) const override; void gather(const Field&, Field&) const override; idx_t size() const override { return structuredcolumns_->size(); } idx_t index(idx_t jblk, idx_t jrof) const { return jblk * nproma_ + jrof; // local index; } idx_t nproma() const { return nproma_; } idx_t nblks() const { return nblks_; } const Vertical& vertical() const { return structuredcolumns_->vertical(); } const StructuredGrid& grid() const override { return structuredcolumns_->grid(); } idx_t levels() const { return structuredcolumns_->levels(); } Field lonlat() const override { return structuredcolumns_->lonlat(); } Field xy() const { return structuredcolumns_->xy(); } Field z() const { return structuredcolumns_->z(); } Field partition() const override { return structuredcolumns_->partition(); } Field global_index() const override { return structuredcolumns_->global_index(); } Field remote_index() const override { return structuredcolumns_->remote_index(); } Field index_i() const { return structuredcolumns_->index_i(); } Field index_j() const { return structuredcolumns_->index_j(); } Field ghost() const override { return structuredcolumns_->ghost(); } size_t footprint() const override { return structuredcolumns_->footprint(); } idx_t part() const override { return structuredcolumns_->part(); } idx_t nb_parts() const override { return structuredcolumns_->nb_parts(); } const StructuredColumns& structuredcolumns() const { return *structuredcolumns_; } idx_t block_begin(idx_t jblk) const { return jblk * nproma_; } idx_t block_size(idx_t jblk) const { return (jblk != nblks_-1 ? nproma_ : endblk_size_); } idx_t k_begin() const { return vertical().k_begin(); } idx_t k_end() const { return vertical().k_end(); } std::string checksum(const FieldSet&) const; std::string checksum(const Field&) const; const util::PartitionPolygon& polygon(idx_t halo = 0) const override { return structuredcolumns_->polygon(halo); } private: // methods array::ArrayShape config_shape(const eckit::Configuration&) const; array::ArrayAlignment config_alignment(const eckit::Configuration&) const; array::ArraySpec config_spec(const eckit::Configuration&) const; private: // data idx_t nproma_; idx_t endblk_size_; idx_t nblks_; detail::StructuredColumns* structuredcolumns_; functionspace::StructuredColumns structuredcolumns_handle_; void setup(const eckit::Configuration& config); }; // ------------------------------------------------------------------- } // namespace detail } // namespace functionspace } // namespace atlas atlas-0.46.0/src/atlas/functionspace/detail/NodeColumns_FieldStatistics.cc0000664000175000017500000017141415160212070027004 0ustar alastairalastair/* * (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 "atlas/array.h" #include "atlas/field/Field.h" #include "atlas/functionspace/NodeColumns.h" #include "atlas/library/config.h" #include "atlas/mesh/IsGhostNode.h" #include "atlas/mesh/Nodes.h" #include "atlas/parallel/GatherScatter.h" #include "atlas/parallel/omp/omp.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Trace.h" #undef atlas_omp_critical_ordered #define atlas_omp_critical_ordered atlas_omp_critical namespace atlas { namespace functionspace { namespace detail { namespace { template array::LocalView make_leveled_view(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()); } } } template array::LocalView make_leveled_scalar_view(Field& field) { using namespace array; if (field.levels()) { return make_view(field).slice(Range::all(), Range::all()); } else { return make_view(field).slice(Range::all(), Range::dummy()); } } template array::LocalView make_surface_view(Field& field) { using namespace array; if (field.variables()) { return make_view(field).slice(Range::all(), Range::all()); } else { return make_view(field).slice(Range::all(), Range::dummy()); } } template array::LocalView make_per_level_view(Field& field) { using namespace array; if (field.rank() == 2) { return make_view(field).slice(Range::all(), Range::all()); } else { return make_view(field).slice(Range::all(), Range::dummy()); } } } // namespace namespace { inline double sqr(const double& val) { return val * val; } } // namespace namespace detail { // Collectives implementation template void dispatch_sum(const NodeColumns& fs, const Field& field, T& result, idx_t& N) { const mesh::IsGhostNode is_ghost(fs.nodes()); const array::LocalView arr = make_leveled_scalar_view(field); T local_sum = 0; const idx_t npts = std::min(arr.shape(0), fs.nb_nodes()); const idx_t nlev = arr.shape(1); atlas_omp_pragma( omp parallel for default(shared) reduction(+:local_sum) ) for( idx_t n=0; n void sum(const NodeColumns& fs, const Field& field, T& result, idx_t& N) { if (field.datatype() == array::DataType::kind()) { dispatch_sum(fs, field, result, N); } else { switch (field.datatype().kind()) { case array::DataType::KIND_INT32: { int tmp; dispatch_sum(fs, field, tmp, N); result = tmp; return; } case array::DataType::KIND_INT64: { long tmp; dispatch_sum(fs, field, tmp, N); result = tmp; return; } case array::DataType::KIND_REAL32: { float tmp; dispatch_sum(fs, field, tmp, N); result = tmp; return; } case array::DataType::KIND_REAL64: { double tmp; dispatch_sum(fs, field, tmp, N); result = tmp; return; } default: throw_Exception("datatype not supported", Here()); } } } template void dispatch_sum(const NodeColumns& fs, const Field& field, std::vector& result, idx_t& N) { auto arr = make_leveled_view(field); const mesh::IsGhostNode is_ghost(fs.nodes()); const idx_t npts = std::min(arr.shape(0), fs.nb_nodes()); const idx_t nlev = arr.shape(1); const idx_t nvar = arr.shape(2); std::vector local_sum(nvar, 0); result.resize(nvar); atlas_omp_parallel { std::vector local_sum_private(nvar, 0); atlas_omp_for(idx_t n = 0; n < npts; ++n) { if (!is_ghost(n)) { for (idx_t l = 0; l < nlev; ++l) { for (idx_t j = 0; j < nvar; ++j) { local_sum_private[j] += arr(n, l, j); } } } } atlas_omp_critical { for (idx_t j = 0; j < nvar; ++j) { local_sum[j] += local_sum_private[j]; } } } ATLAS_TRACE_MPI(ALLREDUCE) { mpi::comm(fs.mpi_comm()).allReduce(local_sum, result, eckit::mpi::sum()); } N = fs.nb_nodes_global() * nlev; } template void sum(const NodeColumns& fs, const Field& field, std::vector& result, idx_t& N) { if (field.datatype() == array::DataType::kind()) { dispatch_sum(fs, field, result, N); } else { switch (field.datatype().kind()) { case array::DataType::KIND_INT32: { std::vector tmp; dispatch_sum(fs, field, tmp, N); result.assign(tmp.begin(), tmp.end()); return; } case array::DataType::KIND_INT64: { std::vector tmp; dispatch_sum(fs, field, tmp, N); result.assign(tmp.begin(), tmp.end()); return; } case array::DataType::KIND_REAL32: { std::vector tmp; dispatch_sum(fs, field, tmp, N); result.assign(tmp.begin(), tmp.end()); return; } case array::DataType::KIND_REAL64: { std::vector tmp; dispatch_sum(fs, field, tmp, N); result.assign(tmp.begin(), tmp.end()); return; } default: throw_Exception("datatype not supported", Here()); } } } template void dispatch_sum_per_level(const NodeColumns& fs, const Field& field, Field& sum, idx_t& N) { mesh::IsGhostNode is_ghost(fs.nodes()); array::ArrayShape shape; shape.reserve(field.rank() - 1); for (idx_t j = 1; j < field.rank(); ++j) { shape.push_back(field.shape(j)); } sum.resize(shape); auto arr = make_leveled_view(field); const idx_t npts = std::min(arr.shape(0), fs.nb_nodes()); const idx_t nlev = arr.shape(1); const idx_t nvar = arr.shape(2); auto sum_per_level = make_per_level_view(sum); for (idx_t l = 0; l < sum_per_level.shape(0); ++l) { for (idx_t j = 0; j < sum_per_level.shape(1); ++j) { sum_per_level(l, j) = 0; } } atlas_omp_parallel { array::ArrayT sum_per_level_private(sum_per_level.shape(0), sum_per_level.shape(1)); array::ArrayView sum_per_level_private_view = array::make_view(sum_per_level_private); for (idx_t l = 0; l < sum_per_level_private_view.shape(0); ++l) { for (idx_t j = 0; j < sum_per_level_private_view.shape(1); ++j) { sum_per_level_private_view(l, j) = 0; } } atlas_omp_for(idx_t n = 0; n < npts; ++n) { if (!is_ghost(n)) { for (idx_t l = 0; l < nlev; ++l) { for (idx_t j = 0; j < nvar; ++j) { sum_per_level_private_view(l, j) += arr(n, l, j); } } } } atlas_omp_critical { for (idx_t l = 0; l < sum_per_level_private.shape(0); ++l) { for (idx_t j = 0; j < sum_per_level_private.shape(1); ++j) { sum_per_level(l, j) += sum_per_level_private_view(l, j); } } } } ATLAS_TRACE_MPI(ALLREDUCE) { mpi::comm(fs.mpi_comm()).allReduceInPlace(sum_per_level.data(), sum.size(), eckit::mpi::sum()); } N = fs.nb_nodes_global(); } void sum_per_level(const NodeColumns& fs, const Field& field, Field& sum, idx_t& N) { if (field.datatype() != sum.datatype()) { throw_Exception("Field and sum are not of same datatype.", Here()); } switch (field.datatype().kind()) { case array::DataType::KIND_INT32: return dispatch_sum_per_level(fs, field, sum, N); case array::DataType::KIND_INT64: return dispatch_sum_per_level(fs, field, sum, N); case array::DataType::KIND_REAL32: return dispatch_sum_per_level(fs, field, sum, N); case array::DataType::KIND_REAL64: return dispatch_sum_per_level(fs, field, sum, N); default: throw_Exception("datatype not supported", Here()); } } template void dispatch_order_independent_sum_2d(const NodeColumns& fs, const Field& field, DATATYPE& result, idx_t& N) { idx_t root = 0; Field global = fs.createField(field, option::global()); fs.gather(field, global); result = 0; auto glb = array::make_view(global); for (idx_t jnode = 0; jnode < glb.size(); ++jnode) { result += glb(jnode); } ATLAS_TRACE_MPI(BROADCAST) { mpi::comm(fs.mpi_comm()).broadcast(&result, 1, root); } N = fs.nb_nodes_global(); } template void dispatch_order_independent_sum(const NodeColumns& fs, const Field& field, T& result, idx_t& N) { if (field.levels()) { auto arr = make_leveled_scalar_view(field); Field surface_field = fs.createField(option::name("surface") | option::levels(false)); auto surface = array::make_view(surface_field); const idx_t N0 = std::min(surface.shape(0), arr.shape(0)); const idx_t N1 = arr.shape(1); for (idx_t n = 0; n < N0; ++n) { surface(n) = 0; for (idx_t l = 0; l < N1; ++l) { surface(n) += arr(n, l); } } dispatch_order_independent_sum_2d(fs, surface_field, result, N); N *= arr.shape(1); } else { dispatch_order_independent_sum_2d(fs, field, result, N); } } template void order_independent_sum(const NodeColumns& fs, const Field& field, T& result, idx_t& N) { if (field.datatype() == array::DataType::kind()) { dispatch_order_independent_sum(fs, field, result, N); } else { switch (field.datatype().kind()) { case array::DataType::KIND_INT32: { int tmp; dispatch_order_independent_sum(fs, field, tmp, N); result = tmp; return; } case array::DataType::KIND_INT64: { long tmp; dispatch_order_independent_sum(fs, field, tmp, N); result = tmp; return; } case array::DataType::KIND_REAL32: { float tmp; dispatch_order_independent_sum(fs, field, tmp, N); result = tmp; return; } case array::DataType::KIND_REAL64: { double tmp; dispatch_order_independent_sum(fs, field, tmp, N); result = tmp; return; } default: throw_Exception("datatype not supported", Here()); } } } template void dispatch_order_independent_sum_2d(const NodeColumns& fs, const Field& field, std::vector& result, idx_t& N) { idx_t nvar = field.variables(); result.resize(nvar); for (idx_t j = 0; j < nvar; ++j) { result[j] = 0.; } Field global = fs.createField(field, option::name("global") | option::global()); fs.gather(field, global); if (mpi::rank() == 0) { const auto glb = make_surface_view(global); for (idx_t n = 0; n < fs.nb_nodes_global(); ++n) { for (idx_t j = 0; j < nvar; ++j) { result[j] += glb(n, j); } } } idx_t root = global.metadata().get("owner"); ATLAS_TRACE_MPI(BROADCAST) { mpi::comm(fs.mpi_comm()).broadcast(result, root); } N = fs.nb_nodes_global(); } template void dispatch_order_independent_sum(const NodeColumns& fs, const Field& field, std::vector& result, idx_t& N) { if (field.levels()) { const auto arr = make_leveled_view(field); const idx_t npts = std::min(arr.shape(0), fs.nb_nodes()); const idx_t nlev = arr.shape(1); const idx_t nvar = arr.shape(2); Field surface_field = fs.createField(option::name("surface") | option::variables(nvar) | option::levels(false)); auto surface = make_surface_view(surface_field); atlas_omp_for(idx_t n = 0; n < npts; ++n) { for (idx_t j = 0; j < nvar; ++j) { surface(n, j) = 0; } } for (idx_t n = 0; n < npts; ++n) { for (idx_t l = 0; l < nlev; ++l) { for (idx_t j = 0; j < nvar; ++j) { surface(n, j) += arr(n, l, j); } } } dispatch_order_independent_sum_2d(fs, surface_field, result, N); N *= arr.shape(1); } else { dispatch_order_independent_sum_2d(fs, field, result, N); } } template void order_independent_sum(const NodeColumns& fs, const Field& field, std::vector& result, idx_t& N) { if (field.datatype() == array::DataType::kind()) { dispatch_order_independent_sum(fs, field, result, N); } else { switch (field.datatype().kind()) { case array::DataType::KIND_INT32: { std::vector tmp; dispatch_order_independent_sum(fs, field, tmp, N); result.assign(tmp.begin(), tmp.end()); return; } case array::DataType::KIND_INT64: { std::vector tmp; dispatch_order_independent_sum(fs, field, tmp, N); result.assign(tmp.begin(), tmp.end()); return; } case array::DataType::KIND_REAL32: { std::vector tmp; dispatch_order_independent_sum(fs, field, tmp, N); result.assign(tmp.begin(), tmp.end()); return; } case array::DataType::KIND_REAL64: { std::vector tmp; dispatch_order_independent_sum(fs, field, tmp, N); result.assign(tmp.begin(), tmp.end()); return; } default: throw_Exception("datatype not supported", Here()); } } } template void dispatch_order_independent_sum_per_level(const NodeColumns& fs, const Field& field, Field& sumfield, idx_t& N) { array::ArrayShape shape; shape.reserve(field.rank() - 1); for (idx_t j = 1; j < field.rank(); ++j) { shape.push_back(field.shape(j)); } sumfield.resize(shape); auto sum = make_per_level_view(sumfield); for (idx_t l = 0; l < sum.shape(0); ++l) { for (idx_t j = 0; j < sum.shape(1); ++j) { sum(l, j) = 0.; } } idx_t root = 0; Field global = fs.createField(field, option::name("global") | option::global()); fs.gather(field, global); if (mpi::rank() == 0) { const array::LocalView glb = make_leveled_view(global); for (idx_t n = 0; n < glb.shape(0); ++n) { for (idx_t l = 0; l < glb.shape(1); ++l) { for (idx_t j = 0; j < glb.shape(2); ++j) { sum(l, j) += glb(n, l, j); } } } } ATLAS_TRACE_MPI(BROADCAST) { std::vector sum_array(sumfield.size()); if (mpi::rank() == root) { idx_t c(0); for (idx_t l = 0; l < sum.shape(0); ++l) { for (idx_t j = 0; j < sum.shape(1); ++j) { sum_array[c++] = sum(l, j); } } } mpi::comm(fs.mpi_comm()).broadcast(sum_array, root); if (mpi::rank() != root) { idx_t c(0); for (idx_t l = 0; l < sum.shape(0); ++l) { for (idx_t j = 0; j < sum.shape(1); ++j) { sum(l, j) = sum_array[c++]; } } } } N = fs.nb_nodes_global(); } void order_independent_sum_per_level(const NodeColumns& fs, const Field& field, Field& sum, idx_t& N) { if (field.datatype() != sum.datatype()) { throw_Exception("Field and sum are not of same datatype.", Here()); } switch (field.datatype().kind()) { case array::DataType::KIND_INT32: return dispatch_order_independent_sum_per_level(fs, field, sum, N); case array::DataType::KIND_INT64: return dispatch_order_independent_sum_per_level(fs, field, sum, N); case array::DataType::KIND_REAL32: return dispatch_order_independent_sum_per_level(fs, field, sum, N); case array::DataType::KIND_REAL64: return dispatch_order_independent_sum_per_level(fs, field, sum, N); default: throw_Exception("datatype not supported", Here()); } } template void dispatch_minimum(const NodeColumns& fs, const Field& field, std::vector& min) { auto arr = make_leveled_view(field); const idx_t nvar = arr.shape(2); min.resize(nvar); std::vector local_minimum(nvar, std::numeric_limits::max()); atlas_omp_parallel { std::vector local_minimum_private(nvar, std::numeric_limits::max()); const idx_t npts = std::min(arr.shape(0), fs.size()); atlas_omp_for(idx_t n = 0; n < npts; ++n) { for (idx_t l = 0; l < arr.shape(1); ++l) { for (idx_t j = 0; j < arr.shape(2); ++j) { local_minimum_private[j] = std::min(arr(n, l, j), local_minimum_private[j]); } } } atlas_omp_critical { for (idx_t j = 0; j < arr.shape(2); ++j) { local_minimum[j] = std::min(local_minimum_private[j], local_minimum[j]); } } } ATLAS_TRACE_MPI(ALLREDUCE) { mpi::comm(fs.mpi_comm()).allReduce(local_minimum, min, eckit::mpi::min()); } } template void minimum(const NodeColumns& fs, const Field& field, std::vector& min) { if (field.datatype() == array::DataType::kind()) { dispatch_minimum(fs, field, min); } else { switch (field.datatype().kind()) { case array::DataType::KIND_INT32: { std::vector tmp; dispatch_minimum(fs, field, tmp); min.assign(tmp.begin(), tmp.end()); return; } case array::DataType::KIND_INT64: { std::vector tmp; dispatch_minimum(fs, field, tmp); min.assign(tmp.begin(), tmp.end()); return; } case array::DataType::KIND_REAL32: { std::vector tmp; dispatch_minimum(fs, field, tmp); min.assign(tmp.begin(), tmp.end()); return; } case array::DataType::KIND_REAL64: { std::vector tmp; dispatch_minimum(fs, field, tmp); min.assign(tmp.begin(), tmp.end()); return; } default: throw_Exception("datatype not supported", Here()); } } } template void dispatch_maximum(const NodeColumns& fs, const Field& field, std::vector& max) { auto arr = make_leveled_view(field); const idx_t nvar = arr.shape(2); max.resize(nvar); std::vector local_maximum(nvar, -std::numeric_limits::max()); atlas_omp_parallel { std::vector local_maximum_private(nvar, -std::numeric_limits::max()); const idx_t npts = std::min(arr.shape(0), fs.size()); atlas_omp_for(idx_t n = 0; n < npts; ++n) { for (idx_t l = 0; l < arr.shape(1); ++l) { for (idx_t j = 0; j < nvar; ++j) { local_maximum_private[j] = std::max(arr(n, l, j), local_maximum_private[j]); } } } atlas_omp_critical { for (idx_t j = 0; j < nvar; ++j) { local_maximum[j] = std::max(local_maximum_private[j], local_maximum[j]); } } } ATLAS_TRACE_MPI(ALLREDUCE) { mpi::comm(fs.mpi_comm()).allReduce(local_maximum, max, eckit::mpi::max()); } } template void maximum(const NodeColumns& fs, const Field& field, std::vector& max) { if (field.datatype() == array::DataType::kind()) { dispatch_maximum(fs, field, max); } else { switch (field.datatype().kind()) { case array::DataType::KIND_INT32: { std::vector tmp; dispatch_maximum(fs, field, tmp); max.assign(tmp.begin(), tmp.end()); return; } case array::DataType::KIND_INT64: { std::vector tmp; dispatch_maximum(fs, field, tmp); max.assign(tmp.begin(), tmp.end()); return; } case array::DataType::KIND_REAL32: { std::vector tmp; dispatch_maximum(fs, field, tmp); max.assign(tmp.begin(), tmp.end()); return; } case array::DataType::KIND_REAL64: { std::vector tmp; dispatch_maximum(fs, field, tmp); max.assign(tmp.begin(), tmp.end()); return; } default: throw_Exception("datatype not supported", Here()); } } } template void minimum(const NodeColumns& fs, const Field& field, T& min) { std::vector v; minimum(fs, field, v); min = v[0]; } template void maximum(const NodeColumns& fs, const Field& field, T& max) { std::vector v; maximum(fs, field, v); max = v[0]; } template void dispatch_minimum_per_level(const NodeColumns& fs, const Field& field, Field& min_field) { array::ArrayShape shape; shape.reserve(field.rank() - 1); for (idx_t j = 1; j < field.rank(); ++j) { shape.push_back(field.shape(j)); } min_field.resize(shape); auto min = make_per_level_view(min_field); for (idx_t l = 0; l < min.shape(0); ++l) { for (idx_t j = 0; j < min.shape(1); ++j) { min(l, j) = std::numeric_limits::max(); } } auto arr = make_leveled_view(field); atlas_omp_parallel { array::ArrayT min_private(min.shape(0), min.shape(1)); array::ArrayView min_private_view = array::make_view(min_private); for (idx_t l = 0; l < min.shape(0); ++l) { for (idx_t j = 0; j < min.shape(1); ++j) { min_private_view(l, j) = std::numeric_limits::max(); } } const idx_t npts = arr.shape(0); atlas_omp_for(idx_t n = 0; n < npts; ++n) { for (idx_t l = 0; l < arr.shape(1); ++l) { for (idx_t j = 0; j < arr.shape(2); ++j) { min_private_view(l, j) = std::min(arr(n, l, j), min_private_view(l, j)); } } } atlas_omp_critical { for (idx_t l = 0; l < arr.shape(1); ++l) { for (idx_t j = 0; j < arr.shape(2); ++j) { min(l, j) = std::min(min_private_view(l, j), min(l, j)); } } } } ATLAS_TRACE_MPI(ALLREDUCE) { mpi::comm(fs.mpi_comm()).allReduceInPlace(min.data(), min_field.size(), eckit::mpi::min()); } } void minimum_per_level(const NodeColumns& fs, const Field& field, Field& min) { if (field.datatype() != min.datatype()) { throw_Exception("Field and min are not of same datatype.", Here()); } switch (field.datatype().kind()) { case array::DataType::KIND_INT32: return dispatch_minimum_per_level(fs, field, min); case array::DataType::KIND_INT64: return dispatch_minimum_per_level(fs, field, min); case array::DataType::KIND_REAL32: return dispatch_minimum_per_level(fs, field, min); case array::DataType::KIND_REAL64: return dispatch_minimum_per_level(fs, field, min); default: throw_Exception("datatype not supported", Here()); } } template void dispatch_maximum_per_level(const NodeColumns& fs, const Field& field, Field& max_field) { array::ArrayShape shape; shape.reserve(field.rank() - 1); for (idx_t j = 1; j < field.rank(); ++j) { shape.push_back(field.shape(j)); } max_field.resize(shape); auto max = make_per_level_view(max_field); for (idx_t l = 0; l < max.shape(0); ++l) { for (idx_t j = 0; j < max.shape(1); ++j) { max(l, j) = -std::numeric_limits::max(); } } auto arr = make_leveled_view(field); atlas_omp_parallel { array::ArrayT max_private(max.shape(0), max.shape(1)); array::ArrayView max_private_view = array::make_view(max_private); for (idx_t l = 0; l < max_private_view.shape(0); ++l) { for (idx_t j = 0; j < max_private_view.shape(1); ++j) { max_private_view(l, j) = -std::numeric_limits::max(); } } const idx_t npts = arr.shape(0); atlas_omp_for(idx_t n = 0; n < npts; ++n) { for (idx_t l = 0; l < arr.shape(1); ++l) { for (idx_t j = 0; j < arr.shape(2); ++j) { max_private_view(l, j) = std::max(arr(n, l, j), max_private_view(l, j)); } } } atlas_omp_critical { for (idx_t l = 0; l < arr.shape(1); ++l) { for (idx_t j = 0; j < arr.shape(2); ++j) { max(l, j) = std::max(max_private_view(l, j), max(l, j)); } } } } ATLAS_TRACE_MPI(ALLREDUCE) { mpi::comm(fs.mpi_comm()).allReduceInPlace(max.data(), max_field.size(), eckit::mpi::max()); } } void maximum_per_level(const NodeColumns& fs, const Field& field, Field& max) { if (field.datatype() != max.datatype()) { throw_Exception("Field and max are not of same datatype.", Here()); } switch (field.datatype().kind()) { case array::DataType::KIND_INT32: return dispatch_maximum_per_level(fs, field, max); case array::DataType::KIND_INT64: return dispatch_maximum_per_level(fs, field, max); case array::DataType::KIND_REAL32: return dispatch_maximum_per_level(fs, field, max); case array::DataType::KIND_REAL64: return dispatch_maximum_per_level(fs, field, max); default: throw_Exception("datatype not supported", Here()); } } template void dispatch_minimum_and_location(const NodeColumns& fs, const Field& field, std::vector& min, std::vector& glb_idx, std::vector& level) { auto arr = make_leveled_view(field); idx_t nvar = arr.shape(2); min.resize(nvar); glb_idx.resize(nvar); level.resize(nvar); std::vector local_minimum(nvar, std::numeric_limits::max()); std::vector loc_node(nvar); std::vector loc_level(nvar); atlas_omp_parallel { std::vector local_minimum_private(nvar, std::numeric_limits::max()); std::vector loc_node_private(nvar); std::vector loc_level_private(nvar); const idx_t npts = arr.shape(0); atlas_omp_for(idx_t n = 0; n < npts; ++n) { for (idx_t l = 0; l < arr.shape(1); ++l) { for (idx_t j = 0; j < nvar; ++j) { if (arr(n, l, j) < local_minimum_private[j]) { local_minimum_private[j] = arr(n, l, j); loc_node_private[j] = n; loc_level_private[j] = l; } } } } atlas_omp_critical_ordered { for (idx_t l = 0; l < arr.shape(1); ++l) { for (idx_t j = 0; j < nvar; ++j) { if (local_minimum_private[j] < local_minimum[j]) { local_minimum[j] = local_minimum_private[j]; loc_node[j] = loc_node_private[j]; loc_level[j] = loc_level_private[j]; } } } } } std::vector> min_and_gidx_loc(nvar); std::vector> min_and_level_loc(nvar); std::vector> min_and_gidx_glb(nvar); std::vector> min_and_level_glb(nvar); const array::ArrayView global_index = array::make_view(fs.nodes().global_index()); for (idx_t j = 0; j < nvar; ++j) { gidx_t glb_idx = global_index(loc_node[j]); ATLAS_ASSERT(glb_idx < std::numeric_limits::max()); // pairs with 64bit // integer for second not // implemented min_and_gidx_loc[j] = std::make_pair(local_minimum[j], glb_idx); min_and_level_loc[j] = std::make_pair(local_minimum[j], loc_level[j]); } ATLAS_TRACE_MPI(ALLREDUCE) { mpi::comm(fs.mpi_comm()).allReduce(min_and_gidx_loc, min_and_gidx_glb, eckit::mpi::minloc()); mpi::comm(fs.mpi_comm()).allReduce(min_and_level_loc, min_and_level_glb, eckit::mpi::minloc()); } for (idx_t j = 0; j < nvar; ++j) { min[j] = min_and_gidx_glb[j].first; glb_idx[j] = min_and_gidx_glb[j].second; level[j] = min_and_level_glb[j].second; } } template void minimum_and_location(const NodeColumns& fs, const Field& field, std::vector& min, std::vector& glb_idx, std::vector& level) { if (field.datatype() == array::DataType::kind()) { return dispatch_minimum_and_location(fs, field, min, glb_idx, level); } else { switch (field.datatype().kind()) { case array::DataType::KIND_INT32: { std::vector tmp; dispatch_minimum_and_location(fs, field, tmp, glb_idx, level); min.assign(tmp.begin(), tmp.end()); return; } case array::DataType::KIND_INT64: { std::vector tmp; dispatch_minimum_and_location(fs, field, tmp, glb_idx, level); min.assign(tmp.begin(), tmp.end()); return; } case array::DataType::KIND_REAL32: { std::vector tmp; dispatch_minimum_and_location(fs, field, tmp, glb_idx, level); min.assign(tmp.begin(), tmp.end()); return; } case array::DataType::KIND_REAL64: { std::vector tmp; dispatch_minimum_and_location(fs, field, tmp, glb_idx, level); min.assign(tmp.begin(), tmp.end()); return; } default: throw_Exception("datatype not supported", Here()); } } } template void dispatch_maximum_and_location(const NodeColumns& fs, const Field& field, std::vector& max, std::vector& glb_idx, std::vector& level) { auto arr = make_leveled_view(field); idx_t nvar = arr.shape(2); max.resize(nvar); glb_idx.resize(nvar); level.resize(nvar); std::vector local_maximum(nvar, -std::numeric_limits::max()); std::vector loc_node(nvar); std::vector loc_level(nvar); atlas_omp_parallel { std::vector local_maximum_private(nvar, -std::numeric_limits::max()); std::vector loc_node_private(nvar); std::vector loc_level_private(nvar); const idx_t npts = arr.shape(0); atlas_omp_for(idx_t n = 0; n < npts; ++n) { for (idx_t l = 0; l < arr.shape(1); ++l) { for (idx_t j = 0; j < nvar; ++j) { if (arr(n, l, j) > local_maximum_private[j]) { local_maximum_private[j] = arr(n, l, j); loc_node_private[j] = n; loc_level_private[j] = l; } } } } atlas_omp_critical_ordered { for (idx_t l = 0; l < arr.shape(1); ++l) { for (idx_t j = 0; j < nvar; ++j) { if (local_maximum_private[j] > local_maximum[j]) { local_maximum[j] = local_maximum_private[j]; loc_node[j] = loc_node_private[j]; loc_level[j] = loc_level_private[j]; } } } } } std::vector> max_and_gidx_loc(nvar); std::vector> max_and_level_loc(nvar); std::vector> max_and_gidx_glb(nvar); std::vector> max_and_level_glb(nvar); const array::ArrayView global_index = array::make_view(fs.nodes().global_index()); for (idx_t j = 0; j < nvar; ++j) { gidx_t glb_idx = global_index(loc_node[j]); ATLAS_ASSERT(glb_idx < std::numeric_limits::max()); // pairs with 64bit // integer for second not // implemented max_and_gidx_loc[j] = std::make_pair(local_maximum[j], glb_idx); max_and_level_loc[j] = std::make_pair(local_maximum[j], loc_level[j]); } ATLAS_TRACE_MPI(ALLREDUCE) { mpi::comm(fs.mpi_comm()).allReduce(max_and_gidx_loc, max_and_gidx_glb, eckit::mpi::maxloc()); mpi::comm(fs.mpi_comm()).allReduce(max_and_level_loc, max_and_level_glb, eckit::mpi::maxloc()); } for (idx_t j = 0; j < nvar; ++j) { max[j] = max_and_gidx_glb[j].first; glb_idx[j] = max_and_gidx_glb[j].second; level[j] = max_and_level_glb[j].second; } } template void maximum_and_location(const NodeColumns& fs, const Field& field, std::vector& max, std::vector& glb_idx, std::vector& level) { if (field.datatype() == array::DataType::kind()) { return dispatch_maximum_and_location(fs, field, max, glb_idx, level); } else { switch (field.datatype().kind()) { case array::DataType::KIND_INT32: { std::vector tmp; dispatch_maximum_and_location(fs, field, tmp, glb_idx, level); max.assign(tmp.begin(), tmp.end()); return; } case array::DataType::KIND_INT64: { std::vector tmp; dispatch_maximum_and_location(fs, field, tmp, glb_idx, level); max.assign(tmp.begin(), tmp.end()); return; } case array::DataType::KIND_REAL32: { std::vector tmp; dispatch_maximum_and_location(fs, field, tmp, glb_idx, level); max.assign(tmp.begin(), tmp.end()); return; } case array::DataType::KIND_REAL64: { std::vector tmp; dispatch_maximum_and_location(fs, field, tmp, glb_idx, level); max.assign(tmp.begin(), tmp.end()); return; } default: throw_Exception("datatype not supported", Here()); } } } template void minimum_and_location(const NodeColumns& fs, const Field& field, std::vector& min, std::vector& glb_idx) { std::vector level; minimum_and_location(fs, field, min, glb_idx, level); } template void maximum_and_location(const NodeColumns& fs, const Field& field, std::vector& max, std::vector& glb_idx) { std::vector level; maximum_and_location(fs, field, max, glb_idx, level); } template void minimum_and_location(const NodeColumns& fs, const Field& field, T& min, gidx_t& glb_idx, idx_t& level) { std::vector minv; std::vector gidxv; std::vector levelv; minimum_and_location(fs, field, minv, gidxv, levelv); min = minv[0]; glb_idx = gidxv[0]; level = levelv[0]; } template void maximum_and_location(const NodeColumns& fs, const Field& field, T& max, gidx_t& glb_idx, idx_t& level) { std::vector maxv; std::vector gidxv; std::vector levelv; maximum_and_location(fs, field, maxv, gidxv, levelv); max = maxv[0]; glb_idx = gidxv[0]; level = levelv[0]; } template void minimum_and_location(const NodeColumns& fs, const Field& field, T& min, gidx_t& glb_idx) { idx_t level; minimum_and_location(fs, field, min, glb_idx, level); } template void maximum_and_location(const NodeColumns& fs, const Field& field, T& max, gidx_t& glb_idx) { idx_t level; maximum_and_location(fs, field, max, glb_idx, level); } template void dispatch_minimum_and_location_per_level(const NodeColumns& fs, const Field& field, Field& min_field, Field& glb_idx_field) { auto arr = make_leveled_view(field); array::ArrayShape shape; shape.reserve(field.rank() - 1); for (idx_t j = 1; j < field.rank(); ++j) { shape.push_back(field.shape(j)); } min_field.resize(shape); glb_idx_field.resize(shape); const idx_t nvar = arr.shape(2); auto min = make_per_level_view(min_field); auto glb_idx = make_per_level_view(glb_idx_field); for (idx_t l = 0; l < min.shape(0); ++l) { for (idx_t j = 0; j < min.shape(1); ++j) { min(l, j) = std::numeric_limits::max(); } } atlas_omp_parallel { array::ArrayT min_private(min.shape(0), min.shape(1)); array::ArrayView min_private_view = array::make_view(min_private); for (idx_t l = 0; l < min_private_view.shape(0); ++l) { for (idx_t j = 0; j < min_private_view.shape(1); ++j) { min_private_view(l, j) = std::numeric_limits::max(); } } array::ArrayT glb_idx_private(glb_idx.shape(0), glb_idx.shape(1)); array::ArrayView glb_idx_private_view = array::make_view(glb_idx_private); const idx_t npts = arr.shape(0); atlas_omp_for(idx_t n = 0; n < npts; ++n) { for (idx_t l = 0; l < arr.shape(1); ++l) { for (idx_t j = 0; j < nvar; ++j) { if (arr(n, l, j) < min(l, j)) { min_private_view(l, j) = arr(n, l, j); glb_idx_private_view(l, j) = n; } } } } atlas_omp_critical_ordered { for (idx_t l = 0; l < arr.shape(1); ++l) { for (idx_t j = 0; j < nvar; ++j) { if (min_private_view(l, j) < min(l, j)) { min(l, j) = min_private_view(l, j); glb_idx(l, j) = glb_idx_private_view(l, j); } } } } } const idx_t nlev = arr.shape(1); std::vector> min_and_gidx_loc(nlev * nvar); std::vector> min_and_gidx_glb(nlev * nvar); const array::ArrayView global_index = array::make_view(fs.nodes().global_index()); atlas_omp_parallel_for(idx_t l = 0; l < nlev; ++l) { for (idx_t j = 0; j < nvar; ++j) { gidx_t gidx = global_index(glb_idx(l, j)); ATLAS_ASSERT(gidx < std::numeric_limits::max()); // pairs with 64bit // integer for second not // implemented min_and_gidx_loc[j + nvar * l] = std::make_pair(min(l, j), gidx); } } ATLAS_TRACE_MPI(ALLREDUCE) { mpi::comm(fs.mpi_comm()).allReduce(min_and_gidx_loc, min_and_gidx_glb, eckit::mpi::minloc()); } atlas_omp_parallel_for(idx_t l = 0; l < nlev; ++l) { for (idx_t j = 0; j < nvar; ++j) { min(l, j) = min_and_gidx_glb[j + l * nvar].first; glb_idx(l, j) = min_and_gidx_glb[j + l * nvar].second; } } } void minimum_and_location_per_level(const NodeColumns& fs, const Field& field, Field& min, Field& glb_idx) { if (field.datatype() != min.datatype()) { throw_Exception("Field and min are not of same datatype.", Here()); } if (glb_idx.datatype() != array::DataType::kind()) { throw_Exception("glb_idx Field is not of correct datatype", Here()); } switch (field.datatype().kind()) { case array::DataType::KIND_INT32: return dispatch_minimum_and_location_per_level(fs, field, min, glb_idx); case array::DataType::KIND_INT64: return dispatch_minimum_and_location_per_level(fs, field, min, glb_idx); case array::DataType::KIND_REAL32: return dispatch_minimum_and_location_per_level(fs, field, min, glb_idx); case array::DataType::KIND_REAL64: return dispatch_minimum_and_location_per_level(fs, field, min, glb_idx); default: throw_Exception("datatype not supported", Here()); } } template void dispatch_maximum_and_location_per_level(const NodeColumns& fs, const Field& field, Field& max_field, Field& glb_idx_field) { auto arr = make_leveled_view(field); array::ArrayShape shape; shape.reserve(field.rank() - 1); for (idx_t j = 1; j < field.rank(); ++j) { shape.push_back(field.shape(j)); } max_field.resize(shape); glb_idx_field.resize(shape); const idx_t nvar = arr.shape(2); auto max = make_per_level_view(max_field); auto glb_idx = make_per_level_view(glb_idx_field); for (idx_t l = 0; l < max.shape(0); ++l) { for (idx_t j = 0; j < max.shape(1); ++j) { max(l, j) = -std::numeric_limits::max(); } } atlas_omp_parallel { array::ArrayT max_private(max.shape(0), max.shape(1)); array::ArrayView max_private_view = array::make_view(max_private); for (idx_t l = 0; l < max_private_view.shape(0); ++l) { for (idx_t j = 0; j < max_private_view.shape(1); ++j) { max_private_view(l, j) = -std::numeric_limits::max(); } } array::ArrayT glb_idx_private(glb_idx.shape(0), glb_idx.shape(1)); array::ArrayView glb_idx_private_view = array::make_view(glb_idx_private); const idx_t npts = arr.shape(0); atlas_omp_for(idx_t n = 0; n < npts; ++n) { for (idx_t l = 0; l < arr.shape(1); ++l) { for (idx_t j = 0; j < nvar; ++j) { if (arr(n, l, j) > max(l, j)) { max_private_view(l, j) = arr(n, l, j); glb_idx_private_view(l, j) = n; } } } } atlas_omp_critical_ordered { for (idx_t l = 0; l < arr.shape(1); ++l) { for (idx_t j = 0; j < nvar; ++j) { if (max_private_view(l, j) > max(l, j)) { max(l, j) = max_private_view(l, j); glb_idx(l, j) = glb_idx_private_view(l, j); } } } } } const idx_t nlev = arr.shape(1); std::vector> max_and_gidx_loc(nlev * nvar); std::vector> max_and_gidx_glb(nlev * nvar); const array::ArrayView global_index = array::make_view(fs.nodes().global_index()); atlas_omp_parallel_for(idx_t l = 0; l < nlev; ++l) { for (idx_t j = 0; j < nvar; ++j) { gidx_t gidx = global_index(glb_idx(l, j)); ATLAS_ASSERT(gidx < std::numeric_limits::max()); // pairs with 64bit // integer for second not // implemented max_and_gidx_loc[j + nvar * l] = std::make_pair(max(l, j), gidx); } } ATLAS_TRACE_MPI(ALLREDUCE) { mpi::comm(fs.mpi_comm()).allReduce(max_and_gidx_loc, max_and_gidx_glb, eckit::mpi::maxloc()); } atlas_omp_parallel_for(idx_t l = 0; l < nlev; ++l) { for (idx_t j = 0; j < nvar; ++j) { max(l, j) = max_and_gidx_glb[j + l * nvar].first; glb_idx(l, j) = max_and_gidx_glb[j + l * nvar].second; } } } void maximum_and_location_per_level(const NodeColumns& fs, const Field& field, Field& max, Field& glb_idx) { if (field.datatype() != max.datatype()) { throw_Exception("Field and max are not of same datatype.", Here()); } if (glb_idx.datatype() != array::DataType::kind()) { throw_Exception("glb_idx Field is not of correct datatype", Here()); } switch (field.datatype().kind()) { case array::DataType::KIND_INT32: return dispatch_maximum_and_location_per_level(fs, field, max, glb_idx); case array::DataType::KIND_INT64: return dispatch_maximum_and_location_per_level(fs, field, max, glb_idx); case array::DataType::KIND_REAL32: return dispatch_maximum_and_location_per_level(fs, field, max, glb_idx); case array::DataType::KIND_REAL64: return dispatch_maximum_and_location_per_level(fs, field, max, glb_idx); default: throw_Exception("datatype not supported", Here()); } } template void mean(const NodeColumns& fs, const Field& field, T& result, idx_t& N) { sum(fs, field, result, N); result /= static_cast(N); } template void mean(const NodeColumns& fs, const Field& field, std::vector& result, idx_t& N) { sum(fs, field, result, N); for (size_t j = 0; j < result.size(); ++j) { result[j] /= static_cast(N); } } template void dispatch_mean_per_level(const NodeColumns& fs, const Field& field, Field& mean, idx_t& N) { dispatch_sum_per_level(fs, field, mean, N); auto view = make_per_level_view(mean); for (idx_t l = 0; l < view.shape(0); ++l) { for (idx_t j = 0; j < view.shape(1); ++j) { view(l, j) /= static_cast(N); } } } void mean_per_level(const NodeColumns& fs, const Field& field, Field& mean, idx_t& N) { if (field.datatype() != mean.datatype()) { throw_Exception("Field and sum are not of same datatype.", Here()); } switch (field.datatype().kind()) { case array::DataType::KIND_INT32: return dispatch_mean_per_level(fs, field, mean, N); case array::DataType::KIND_INT64: return dispatch_mean_per_level(fs, field, mean, N); case array::DataType::KIND_REAL32: return dispatch_mean_per_level(fs, field, mean, N); case array::DataType::KIND_REAL64: return dispatch_mean_per_level(fs, field, mean, N); default: throw_Exception("datatype not supported", Here()); } } template void mean_and_standard_deviation(const NodeColumns& fs, const Field& field, T& mu, T& sigma, idx_t& N) { mean(fs, field, mu, N); Field squared_diff_field = fs.createField(option::name("sqr_diff") | option::datatype(field.datatype()) | option::levels(field.levels())); auto squared_diff = make_leveled_scalar_view(squared_diff_field); auto values = make_leveled_scalar_view(field); const idx_t npts = std::min(values.shape(0), fs.nb_nodes()); atlas_omp_parallel_for(idx_t n = 0; n < npts; ++n) { for (idx_t l = 0; l < values.shape(1); ++l) { squared_diff(n, l) = sqr(values(n, l) - mu); } } mean(fs, squared_diff_field, sigma, N); sigma = std::sqrt(sigma); } template void mean_and_standard_deviation(const NodeColumns& fs, const Field& field, std::vector& mu, std::vector& sigma, idx_t& N) { mean(fs, field, mu, N); Field squared_diff_field = fs.createField(option::name("sqr_diff") | option::levels(field.levels()) | option::variables(field.variables())); auto squared_diff = make_leveled_view(squared_diff_field); auto values = make_leveled_view(field); const idx_t npts = std::min(values.shape(0), fs.nb_nodes()); atlas_omp_parallel_for(idx_t n = 0; n < npts; ++n) { for (idx_t l = 0; l < values.shape(1); ++l) { for (idx_t j = 0; j < values.shape(2); ++j) { squared_diff(n, l, j) = sqr(values(n, l, j) - mu[j]); } } } mean(fs, squared_diff_field, sigma, N); for (size_t j = 0; j < sigma.size(); ++j) { sigma[j] = std::sqrt(sigma[j]); } } template void dispatch_mean_and_standard_deviation_per_level(const NodeColumns& fs, const Field& field, Field& mean, Field& stddev, idx_t& N) { dispatch_mean_per_level(fs, field, mean, N); Field squared_diff_field = fs.createField(option::name("sqr_diff") | option::levels(field.levels()) | option::variables(field.variables())); auto squared_diff = make_leveled_view(squared_diff_field); auto values = make_leveled_view(field); auto mu = make_per_level_view(mean); const idx_t npts = std::min(values.shape(0), fs.nb_nodes()); atlas_omp_parallel_for(idx_t n = 0; n < npts; ++n) { for (idx_t l = 0; l < values.shape(1); ++l) { for (idx_t j = 0; j < values.shape(2); ++j) { squared_diff(n, l, j) = sqr(values(n, l, j) - mu(l, j)); } } } dispatch_mean_per_level(fs, squared_diff_field, stddev, N); auto sigma = make_per_level_view(stddev); atlas_omp_for(idx_t l = 0; l < sigma.shape(0); ++l) { for (idx_t j = 0; j < sigma.shape(1); ++j) { sigma(l, j) = std::sqrt(sigma(l, j)); } } } void mean_and_standard_deviation_per_level(const NodeColumns& fs, const Field& field, Field& mean, Field& stddev, idx_t& N) { if (field.datatype() != mean.datatype()) { throw_Exception("Field and mean are not of same datatype.", Here()); } if (field.datatype() != stddev.datatype()) { throw_Exception("Field and stddev are not of same datatype.", Here()); } switch (field.datatype().kind()) { case array::DataType::KIND_INT32: return dispatch_mean_and_standard_deviation_per_level(fs, field, mean, stddev, N); case array::DataType::KIND_INT64: return dispatch_mean_and_standard_deviation_per_level(fs, field, mean, stddev, N); case array::DataType::KIND_REAL32: return dispatch_mean_and_standard_deviation_per_level(fs, field, mean, stddev, N); case array::DataType::KIND_REAL64: return dispatch_mean_and_standard_deviation_per_level(fs, field, mean, stddev, N); default: throw_Exception("datatype not supported", Here()); } } } // namespace detail template NodeColumns::FieldStatisticsT::FieldStatisticsT(const NodeColumns* f): functionspace(*f) {} template NodeColumns::FieldStatisticsVectorT::FieldStatisticsVectorT(const NodeColumns* f): functionspace(*f) {} NodeColumns::FieldStatistics::FieldStatistics(const NodeColumns* f): functionspace(*f) {} template void NodeColumns::FieldStatisticsT::sum(const Field& field, Value& result, idx_t& N) const { detail::sum(functionspace, field, result, N); } template void NodeColumns::FieldStatisticsVectorT::sum(const Field& field, Vector& result, idx_t& N) const { detail::sum(functionspace, field, result, N); } void NodeColumns::FieldStatistics::sumPerLevel(const Field& field, Field& result, idx_t& N) const { detail::sum_per_level(functionspace, field, result, N); } template void NodeColumns::FieldStatisticsT::orderIndependentSum(const Field& field, Value& result, idx_t& N) const { detail::order_independent_sum(functionspace, field, result, N); } template void NodeColumns::FieldStatisticsVectorT::orderIndependentSum(const Field& field, Vector& result, idx_t& N) const { detail::order_independent_sum(functionspace, field, result, N); } void NodeColumns::FieldStatistics::orderIndependentSumPerLevel(const Field& field, Field& result, idx_t& N) const { detail::order_independent_sum_per_level(functionspace, field, result, N); } template void NodeColumns::FieldStatisticsT::minimum(const Field& field, Value& minimum) const { detail::minimum(functionspace, field, minimum); } template void NodeColumns::FieldStatisticsT::maximum(const Field& field, Value& maximum) const { detail::maximum(functionspace, field, maximum); } template void NodeColumns::FieldStatisticsVectorT::minimum(const Field& field, Vector& minimum) const { detail::minimum(functionspace, field, minimum); } template void NodeColumns::FieldStatisticsVectorT::maximum(const Field& field, Vector& maximum) const { detail::maximum(functionspace, field, maximum); } void NodeColumns::FieldStatistics::minimumPerLevel(const Field& field, Field& minimum) const { detail::minimum_per_level(functionspace, field, minimum); } void NodeColumns::FieldStatistics::maximumPerLevel(const Field& field, Field& maximum) const { detail::maximum_per_level(functionspace, field, maximum); } template void NodeColumns::FieldStatisticsT::minimumAndLocation(const Field& field, Value& minimum, gidx_t& glb_idx) const { detail::minimum_and_location(functionspace, field, minimum, glb_idx); } template void NodeColumns::FieldStatisticsT::maximumAndLocation(const Field& field, Value& maximum, gidx_t& glb_idx) const { detail::maximum_and_location(functionspace, field, maximum, glb_idx); } template void NodeColumns::FieldStatisticsT::minimumAndLocation(const Field& field, Value& minimum, gidx_t& glb_idx, idx_t& level) const { detail::minimum_and_location(functionspace, field, minimum, glb_idx, level); } template void NodeColumns::FieldStatisticsT::maximumAndLocation(const Field& field, Value& maximum, gidx_t& glb_idx, idx_t& level) const { detail::maximum_and_location(functionspace, field, maximum, glb_idx, level); } template void NodeColumns::FieldStatisticsVectorT::minimumAndLocation(const Field& field, Vector& minimum, std::vector& glb_idx) const { detail::minimum_and_location(functionspace, field, minimum, glb_idx); } template void NodeColumns::FieldStatisticsVectorT::maximumAndLocation(const Field& field, Vector& maximum, std::vector& glb_idx) const { detail::maximum_and_location(functionspace, field, maximum, glb_idx); } template void NodeColumns::FieldStatisticsVectorT::minimumAndLocation(const Field& field, Vector& minimum, std::vector& glb_idx, std::vector& level) const { detail::minimum_and_location(functionspace, field, minimum, glb_idx, level); } template void NodeColumns::FieldStatisticsVectorT::maximumAndLocation(const Field& field, Vector& maximum, std::vector& glb_idx, std::vector& level) const { detail::maximum_and_location(functionspace, field, maximum, glb_idx, level); } void NodeColumns::FieldStatistics::minimumAndLocationPerLevel(const Field& field, Field& column, Field& glb_idx) const { detail::minimum_and_location_per_level(functionspace, field, column, glb_idx); } void NodeColumns::FieldStatistics::maximumAndLocationPerLevel(const Field& field, Field& column, Field& glb_idx) const { detail::maximum_and_location_per_level(functionspace, field, column, glb_idx); } template void NodeColumns::FieldStatisticsT::mean(const Field& field, Value& mean, idx_t& N) const { detail::mean(functionspace, field, mean, N); } template void NodeColumns::FieldStatisticsVectorT::mean(const Field& field, Vector& mean, idx_t& N) const { detail::mean(functionspace, field, mean, N); } void NodeColumns::FieldStatistics::meanPerLevel(const Field& field, Field& mean, idx_t& N) const { detail::mean_per_level(functionspace, field, mean, N); } template void NodeColumns::FieldStatisticsT::meanAndStandardDeviation(const Field& field, Value& mean, Value& stddev, idx_t& N) const { detail::mean_and_standard_deviation(functionspace, field, mean, stddev, N); } template void NodeColumns::FieldStatisticsVectorT::meanAndStandardDeviation(const Field& field, Vector& mean, Vector& stddev, idx_t& N) const { detail::mean_and_standard_deviation(functionspace, field, mean, stddev, N); } void NodeColumns::FieldStatistics::meanAndStandardDeviationPerLevel(const Field& field, Field& mean, Field& stddev, idx_t& N) const { detail::mean_and_standard_deviation_per_level(functionspace, field, mean, stddev, N); } template struct NodeColumns::FieldStatisticsT; template struct NodeColumns::FieldStatisticsT; template struct NodeColumns::FieldStatisticsT; template struct NodeColumns::FieldStatisticsT; // template struct NodeColumns::FieldStatisticsT; template struct NodeColumns::FieldStatisticsVectorT>; template struct NodeColumns::FieldStatisticsVectorT>; template struct NodeColumns::FieldStatisticsVectorT>; template struct NodeColumns::FieldStatisticsVectorT>; // template struct NodeColumns::FieldStatisticsVectorT< std::vector >; } // namespace detail } // namespace functionspace } // namespace atlas atlas-0.46.0/src/atlas/functionspace/detail/FunctionSpaceInterface.h0000664000175000017500000000412415160212070025615 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once namespace eckit { class Configuration; } namespace atlas { namespace field { class FieldImpl; class FieldSetImpl; } // namespace field } // namespace atlas namespace atlas { namespace functionspace { class FunctionSpaceImpl; //------------------------------------------------------------------------------------------------------ // C wrapper interfaces to C++ routines extern "C" { void atlas__FunctionSpace__delete(FunctionSpaceImpl* This); void atlas__FunctionSpace__name(const FunctionSpaceImpl* This, char*& name, int& size); field::FieldImpl* atlas__FunctionSpace__create_field(const FunctionSpaceImpl* This, const eckit::Configuration* options); field::FieldImpl* atlas__FunctionSpace__create_field_template(const FunctionSpaceImpl* This, const field::FieldImpl* field_template, const eckit::Configuration* options); void atlas__FunctionSpace__halo_exchange_field(const FunctionSpaceImpl* This, field::FieldImpl* field); void atlas__FunctionSpace__halo_exchange_fieldset(const FunctionSpaceImpl* This, field::FieldSetImpl* fieldset); void atlas__FunctionSpace__adjoint_halo_exchange_field(const FunctionSpaceImpl* This, field::FieldImpl* field); void atlas__FunctionSpace__adjoint_halo_exchange_fieldset(const FunctionSpaceImpl* This, field::FieldSetImpl* fieldset); } //------------------------------------------------------------------------------------------------------ } // namespace functionspace //------------------------------------------------------------------------------------------------------ } // namespace atlas atlas-0.46.0/src/atlas/functionspace/detail/StructuredColumnsInterface.h0000664000175000017500000001377215160212070026572 0ustar alastairalastair/* * (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/functionspace/StructuredColumns.h" namespace atlas { namespace field { class FieldSetImpl; } namespace grid { class DistributionImpl; } } // namespace atlas namespace atlas { namespace grid { namespace detail { namespace grid { class Grid; } // namespace grid } // namespace detail } // namespace grid using GridImpl = grid::detail::grid::Grid; } // namespace atlas namespace atlas { namespace grid { namespace detail { namespace partitioner { class Partitioner; } // namespace partitioner } // namespace detail } // namespace grid using PartitionerImpl = grid::detail::partitioner::Partitioner; } // namespace atlas namespace atlas { namespace functionspace { // ------------------------------------------------------------------- // C wrapper interfaces to C++ routines extern "C" { const detail::StructuredColumns* atlas__functionspace__StructuredColumns__new__grid(const GridImpl* grid, const eckit::Configuration* config); const detail::StructuredColumns* atlas__functionspace__StructuredColumns__new__grid_dist( const GridImpl* grid, const grid::DistributionImpl* dist, const eckit::Configuration* config); const detail::StructuredColumns* atlas__functionspace__StructuredColumns__new__grid_dist_vert( const GridImpl* grid, const grid::DistributionImpl* dist, const Vertical* vert, const eckit::Configuration* config); const detail::StructuredColumns* atlas__functionspace__StructuredColumns__new__grid_dist_config( const GridImpl* grid, const grid::DistributionImpl* dist, const eckit::Configuration* config); const detail::StructuredColumns* atlas__functionspace__StructuredColumns__new__grid_part( const GridImpl* grid, const PartitionerImpl* dist, const eckit::Configuration* config); const detail::StructuredColumns* atlas__functionspace__StructuredColumns__new__grid_part_vert( const GridImpl* grid, const PartitionerImpl* dist, const Vertical* vert, const eckit::Configuration* config); void atlas__functionspace__StructuredColumns__delete(detail::StructuredColumns* This); field::FieldImpl* atlas__fs__StructuredColumns__create_field(const detail::StructuredColumns* This, const eckit::Configuration* options); void atlas__functionspace__StructuredColumns__gather_field(const detail::StructuredColumns* This, const field::FieldImpl* local, field::FieldImpl* global); void atlas__functionspace__StructuredColumns__scatter_field(const detail::StructuredColumns* This, const field::FieldImpl* global, field::FieldImpl* local); void atlas__functionspace__StructuredColumns__gather_fieldset(const detail::StructuredColumns* This, const field::FieldSetImpl* local, field::FieldSetImpl* global); void atlas__functionspace__StructuredColumns__scatter_fieldset(const detail::StructuredColumns* This, const field::FieldSetImpl* global, field::FieldSetImpl* local); void atlas__fs__StructuredColumns__checksum_fieldset(const detail::StructuredColumns* This, const field::FieldSetImpl* fieldset, char*& checksum, idx_t& size, int& allocated); void atlas__fs__StructuredColumns__checksum_field(const detail::StructuredColumns* This, const field::FieldImpl* field, char*& checksum, idx_t& size, int& allocated); void atlas__fs__StructuredColumns__index_host(const detail::StructuredColumns* This, idx_t*& data, idx_t& i_min, idx_t& i_max, idx_t& j_min, idx_t& j_max); idx_t atlas__fs__StructuredColumns__size(const detail::StructuredColumns* This); idx_t atlas__fs__StructuredColumns__sizeOwned(const detail::StructuredColumns* This); idx_t atlas__fs__StructuredColumns__j_begin(const detail::StructuredColumns* This); idx_t atlas__fs__StructuredColumns__j_end(const detail::StructuredColumns* This); idx_t atlas__fs__StructuredColumns__i_begin(const detail::StructuredColumns* This, idx_t j); idx_t atlas__fs__StructuredColumns__i_end(const detail::StructuredColumns* This, idx_t j); idx_t atlas__fs__StructuredColumns__j_begin_halo(const detail::StructuredColumns* This); idx_t atlas__fs__StructuredColumns__j_end_halo(const detail::StructuredColumns* This); idx_t atlas__fs__StructuredColumns__i_begin_halo(const detail::StructuredColumns* This, idx_t j); idx_t atlas__fs__StructuredColumns__i_end_halo(const detail::StructuredColumns* This, idx_t j); idx_t atlas__fs__StructuredColumns__levels(const detail::StructuredColumns* This); field::FieldImpl* atlas__fs__StructuredColumns__xy(const detail::StructuredColumns* This); field::FieldImpl* atlas__fs__StructuredColumns__z(const detail::StructuredColumns* This); field::FieldImpl* atlas__fs__StructuredColumns__partition(const detail::StructuredColumns* This); field::FieldImpl* atlas__fs__StructuredColumns__global_index(const detail::StructuredColumns* This); field::FieldImpl* atlas__fs__StructuredColumns__index_i(const detail::StructuredColumns* This); field::FieldImpl* atlas__fs__StructuredColumns__index_j(const detail::StructuredColumns* This); const GridImpl* atlas__fs__StructuredColumns__grid(const detail::StructuredColumns* This); } } // namespace functionspace } // namespace atlas atlas-0.46.0/src/atlas/functionspace/detail/CubedSphereStructure.h0000664000175000017500000000464515160212070025355 0ustar alastairalastair/* * (C) Crown Copyright 2021 Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ #pragma once #include "atlas/array.h" #include "atlas/field.h" #include "atlas/meshgenerator/detail/cubedsphere/CubedSphereUtility.h" #include "atlas/parallel/omp/omp.h" #include "atlas/util/Object.h" /// Extra cubed sphere functionality for NodeColumns and CellColumns function /// spaces. namespace atlas { class Field; } namespace atlas { namespace functionspace { namespace detail { class CubedSphereStructure : public util::Object { public: CubedSphereStructure() = default; CubedSphereStructure(const Field& tij, const Field& ghost, idx_t size); /// Invalid index. static constexpr idx_t invalid_index() { return -1; } /// Number of elements. idx_t size() const; /// Number of owned elements. idx_t sizeOwned() const; /// i lower bound for tile t (including halo) idx_t i_begin(idx_t) const; /// i lower bound for tile t (including halo) idx_t i_end(idx_t t) const; /// j lower bound for tile t (including halo) idx_t j_begin(idx_t t) const; /// j lower bound for tile t (including halo) idx_t j_end(idx_t t) const; /// Return array_view index for (t, i, j). idx_t index(idx_t t, idx_t i, idx_t j) const; /// Return true if (t, i, j) is a valid index. bool is_valid_index(idx_t t, idx_t i, idx_t j) const; /// Return ijt field. Field tij() const; /// Return ghost/halo field. Field ghost() const; private: // Bounding box struct. struct BoundingBox { BoundingBox(); idx_t iBegin; idx_t iEnd; idx_t jBegin; idx_t jEnd; }; // Bounds checking. void tBoundsCheck(idx_t t) const; void iBoundsCheck(idx_t i, idx_t t) const; void jBoundsCheck(idx_t j, idx_t t) const; // Row-major index of ijtToidx vectors. size_t vecIndex(idx_t i, idx_t j, idx_t t) const; // Index storage vectors. std::vector> tijToIdx_; // Array of bounding boxes. std::array ijBounds_; // ijt field. Field tij_; // ghost field. Field ghost_; // Number cells/nodes. idx_t nElems_{}; // Number of owned cells/nodes. idx_t nOwnedElems_{}; }; } // namespace detail } // namespace functionspace } // namespace atlas atlas-0.46.0/src/atlas/functionspace/detail/StructuredColumns.cc0000664000175000017500000010476615160212070025113 0ustar alastairalastair/* * (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/functionspace/StructuredColumns.h" #include #include #include #include #include #include "eckit/utils/MD5.h" #include "atlas/array/Array.h" #include "atlas/array/MakeView.h" #include "atlas/domain.h" #include "atlas/field/FieldSet.h" #include "atlas/grid/Distribution.h" #include "atlas/grid/Partitioner.h" #include "atlas/grid/StructuredGrid.h" #include "atlas/grid/StructuredPartitionPolygon.h" #include "atlas/library/Library.h" #include "atlas/library/FloatingPointExceptions.h" #include "atlas/mesh/Mesh.h" #include "atlas/parallel/Checksum.h" #include "atlas/parallel/GatherScatter.h" #include "atlas/parallel/HaloExchange.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/parallel/omp/fill.h" #include "atlas/parallel/omp/omp.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Trace.h" #include "atlas/util/Checksum.h" #include "atlas/util/CoordinateEnums.h" #include "atlas/util/detail/Cache.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(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()); } } } template std::string checksum_3d_field(const parallel::Checksum& checksum, const Field& field) { bool disabled_fpe_overflow = library::disable_floating_point_exception(FE_OVERFLOW); bool disabled_fpe_invalid = library::disable_floating_point_exception(FE_INVALID); auto values = make_leveled_view(field); array::ArrayT surface_field(values.shape(0), values.shape(2)); auto surface = array::make_view(surface_field); const idx_t npts = values.shape(0); atlas_omp_for(idx_t n = 0; n < npts; ++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); } } } if (disabled_fpe_overflow) { library::enable_floating_point_exception(FE_OVERFLOW); } if (disabled_fpe_invalid) { library::enable_floating_point_exception(FE_INVALID); } return checksum.execute(surface.data(), surface_field.stride(0)); } } // namespace class StructuredColumnsHaloExchangeCache : public util::Cache, public grid::detail::grid::GridObserver { private: using Base = util::Cache; StructuredColumnsHaloExchangeCache(): Base("StructuredColumnsHaloExchangeCache") {} public: static StructuredColumnsHaloExchangeCache& instance() { static StructuredColumnsHaloExchangeCache inst; return inst; } util::ObjectHandle get_or_create(const detail::StructuredColumns& funcspace) { registerGrid(*funcspace.grid().get()); creator_type creator = std::bind(&StructuredColumnsHaloExchangeCache::create, &funcspace); return Base::get_or_create(key(funcspace), remove_key(funcspace), creator); } void onGridDestruction(grid::detail::grid::Grid& grid) override { remove(remove_key(grid)); } private: static Base::key_type key(const detail::StructuredColumns& funcspace) { std::ostringstream key; key << "grid[address=" << funcspace.grid().get() << ",halo=" << funcspace.halo() << ",periodic_points=" << std::boolalpha << funcspace.periodic_points_ << ",distribution=" << funcspace.distribution() << "]"; return key.str(); } static Base::key_type remove_key(const detail::StructuredColumns& funcspace) { return remove_key(*funcspace.grid().get()); } static Base::key_type remove_key(const grid::detail::grid::Grid& grid) { std::ostringstream key; key << "grid[address=" << &grid << "]"; return key.str(); } static value_type* create(const detail::StructuredColumns* funcspace) { value_type* value = new value_type(); value->setup(funcspace->mpi_comm(), array::make_view(funcspace->partition()).data(), array::make_view(funcspace->remote_index()).data(), REMOTE_IDX_BASE, funcspace->sizeHalo(), funcspace->sizeOwned()); return value; } ~StructuredColumnsHaloExchangeCache() override = default; }; class StructuredColumnsGatherScatterCache : public util::Cache, public grid::detail::grid::GridObserver { private: using Base = util::Cache; StructuredColumnsGatherScatterCache(): Base("StructuredColumnsGatherScatterCache") {} public: static StructuredColumnsGatherScatterCache& instance() { static StructuredColumnsGatherScatterCache inst; return inst; } util::ObjectHandle get_or_create(const detail::StructuredColumns& funcspace) { registerGrid(*funcspace.grid().get()); creator_type creator = std::bind(&StructuredColumnsGatherScatterCache::create, &funcspace); return Base::get_or_create(key(funcspace), remove_key(funcspace), creator); } void onGridDestruction(grid::detail::grid::Grid& grid) override { remove(remove_key(grid)); } private: static Base::key_type key(const detail::StructuredColumns& funcspace) { std::ostringstream key; key << "grid[address=" << funcspace.grid().get() << ",halo=" << funcspace.halo() << ",periodic_points=" << std::boolalpha << funcspace.periodic_points_ << ",distribution=" << funcspace.distribution() << "]"; return key.str(); } static Base::key_type remove_key(const detail::StructuredColumns& funcspace) { return remove_key(*funcspace.grid().get()); } static Base::key_type remove_key(const grid::detail::grid::Grid& grid) { std::ostringstream key; key << "grid[address=" << &grid << "]"; return key.str(); } static value_type* create(const detail::StructuredColumns* funcspace) { value_type* value = new value_type(); value->setup(funcspace->mpi_comm(), array::make_view(funcspace->partition()).data(), array::make_view(funcspace->remote_index()).data(), REMOTE_IDX_BASE, array::make_view(funcspace->global_index()).data(), funcspace->sizeOwned()); return value; } ~StructuredColumnsGatherScatterCache() override = default; }; class StructuredColumnsChecksumCache : public util::Cache, public grid::detail::grid::GridObserver { private: using Base = util::Cache; StructuredColumnsChecksumCache(): Base("StructuredColumnsChecksumCache") {} public: static StructuredColumnsChecksumCache& instance() { static StructuredColumnsChecksumCache inst; return inst; } util::ObjectHandle get_or_create(const detail::StructuredColumns& funcspace) { registerGrid(*funcspace.grid().get()); creator_type creator = std::bind(&StructuredColumnsChecksumCache::create, &funcspace); return Base::get_or_create(key(funcspace), remove_key(funcspace), creator); } void onGridDestruction(grid::detail::grid::Grid& grid) override { remove(remove_key(grid)); } private: static Base::key_type key(const detail::StructuredColumns& funcspace) { std::ostringstream key; key << "grid[address=" << funcspace.grid().get() << ",halo=" << funcspace.halo() << ",periodic_points=" << std::boolalpha << funcspace.periodic_points_ << ",distribution=" << funcspace.distribution() << "]"; return key.str(); } static Base::key_type remove_key(const detail::StructuredColumns& funcspace) { return remove_key(*funcspace.grid().get()); } static Base::key_type remove_key(const grid::detail::grid::Grid& grid) { std::ostringstream key; key << "grid[address=" << &grid << "]"; return key.str(); } static value_type* create(const detail::StructuredColumns* funcspace) { value_type* value = new value_type(); util::ObjectHandle gather( StructuredColumnsGatherScatterCache::instance().get_or_create(*funcspace)); value->setup(gather); return value; } ~StructuredColumnsChecksumCache() override = default; }; const parallel::GatherScatter& StructuredColumns::gather() const { if (gather_scatter_) { return *gather_scatter_; } gather_scatter_ = StructuredColumnsGatherScatterCache::instance().get_or_create(*this); return *gather_scatter_; } const parallel::GatherScatter& StructuredColumns::scatter() const { if (gather_scatter_) { return *gather_scatter_; } gather_scatter_ = StructuredColumnsGatherScatterCache::instance().get_or_create(*this); return *gather_scatter_; } const parallel::Checksum& StructuredColumns::checksum() const { if (checksum_) { return *checksum_; } checksum_ = StructuredColumnsChecksumCache::instance().get_or_create(*this); return *checksum_; } const parallel::HaloExchange& StructuredColumns::halo_exchange() const { if (halo_exchange_) { return *halo_exchange_; } halo_exchange_ = StructuredColumnsHaloExchangeCache::instance().get_or_create(*this); return *halo_exchange_; } void StructuredColumns::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); if (config.has("type")) { field.metadata().set("type", config.getString("type")); } if (config.has("vector_component")) { field.metadata().set("vector_component", config.getSubConfiguration("vector_component")); } } array::DataType StructuredColumns::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 StructuredColumns::config_name(const eckit::Configuration& config) const { std::string name; config.get("name", name); return name; } idx_t StructuredColumns::config_levels(const eckit::Configuration& config) const { idx_t levels(nb_levels_); config.get("levels", levels); return levels; } array::ArrayShape StructuredColumns::config_shape(const eckit::Configuration& config) const { array::ArrayShape shape; shape.emplace_back(config_size(config)); idx_t levels(nb_levels_); config.get("levels", levels); if (levels > 0) { shape.emplace_back(levels); } idx_t variables(0); config.get("variables", variables); if (variables > 0) { shape.emplace_back(variables); } return shape; } array::ArrayAlignment StructuredColumns::config_alignment(const eckit::Configuration& config) const { int alignment(1); config.get("alignment", alignment); return alignment; } array::ArraySpec StructuredColumns::config_spec(const eckit::Configuration& config) const { return array::ArraySpec(config_shape(config), config_alignment(config)); } size_t StructuredColumns::Map2to1::footprint() const { size_t size = sizeof(*this); size += data_.size() * sizeof(decltype(data_)::value_type); return size; } void StructuredColumns::Map2to1::print(std::ostream& out) const { for (idx_t j = j_min_; j <= j_max_; ++j) { out << std::setw(4) << j << " : "; for (idx_t i = i_min_; i <= i_max_; ++i) { idx_t v = operator()(i, j); if (v == missing()) { out << std::setw(6) << "X"; } else { out << std::setw(6) << v; } } out << '\n'; } } void StructuredColumns::IndexRange::print(std::ostream& out) const { for (idx_t i = min_; i <= max_; ++i) { idx_t v = operator()(i); if (v == missing()) { out << std::setw(4) << "X"; } else { out << std::setw(4) << v; } } out << '\n'; } idx_t StructuredColumns::config_size(const eckit::Configuration& config) const { idx_t size = size_halo_; bool global(false); if (config.get("global", global)) { if (global) { idx_t owner(0); config.get("owner", owner); idx_t rank = mpi::comm(mpi_comm()).rank(); size = (rank == owner ? grid_->size() : 0); } } return size; } std::string StructuredColumns::distribution() const { return distribution_; } void StructuredColumns::throw_outofbounds(idx_t i, idx_t j) const { std::stringstream ss; if (j < j_begin_halo() || j >= j_end_halo()) { ss << "OutofBounds: j out of range! : (i,j) = (" << i << "," << j << ") --- Expected: " << j_begin_halo() << " <= j < " << j_end_halo(); } if (i < i_begin_halo(j) || i >= i_end_halo(j)) { ss << "OutofBounds: i out of range! : (i,j) = (" << i << "," << j << ") --- Expected: " << i_begin_halo(j) << " <= i < " << i_end_halo(j); } throw_Exception(ss.str(), Here()); } namespace { static std::string extract_mpi_comm(const eckit::Configuration& config) { if(config.has("mpi_comm")){ return config.getString("mpi_comm"); } return mpi::comm().name(); } } // ---------------------------------------------------------------------------- // Constructor // ---------------------------------------------------------------------------- StructuredColumns::StructuredColumns(const Grid& grid, const eckit::Configuration& config): StructuredColumns::StructuredColumns(grid, grid::Partitioner(), config) {} StructuredColumns::StructuredColumns(const Grid& grid, const grid::Partitioner& p, const eckit::Configuration& config): StructuredColumns(grid, Vertical(config), p, config) {} StructuredColumns::StructuredColumns(const Grid& grid, const grid::Distribution& distribution, const eckit::Configuration& config): StructuredColumns(grid, distribution, Vertical(config), config) {} StructuredColumns::StructuredColumns(const Grid& grid, const grid::Distribution& distribution, const Vertical& vertical, const eckit::Configuration& config): vertical_(vertical), nb_levels_(vertical_.size()), grid_(new StructuredGrid(grid)), mpi_comm_(extract_mpi_comm(config)) { setup(distribution, config); } StructuredColumns::StructuredColumns(const Grid& grid, const Vertical& vertical, const eckit::Configuration& config): StructuredColumns(grid, vertical, grid::Partitioner(), config) {} StructuredColumns::StructuredColumns(const Grid& grid, const Vertical& vertical, const grid::Partitioner& p, const eckit::Configuration& config): vertical_(vertical), nb_levels_(vertical_.size()), grid_(new StructuredGrid(grid)) { ATLAS_TRACE("StructuredColumns constructor"); grid::Partitioner partitioner(p); if (config.has("mpi_comm")) { mpi_comm_ = config.getString("mpi_comm"); if (partitioner) { ATLAS_ASSERT(partitioner.mpi_comm() == mpi_comm_); } if (config.has("partitioner.mpi_comm")) { ATLAS_ASSERT(config.getString("partitioner.mpi_comm") == mpi_comm_); } } else if (config.has("partitioner.mpi_comm")) { mpi_comm_ = config.getString("partitioner.mpi_comm"); } else if (partitioner) { mpi_comm_ = partitioner.mpi_comm(); } else { mpi_comm_ = mpi::comm().name(); } if (not partitioner) { util::Config partitioner_config; if (config.has("partitioner")) { partitioner_config = config.getSubConfiguration("partitioner"); } else { if (grid_->domain().global()) { partitioner_config.set("type", "equal_regions"); } else { partitioner_config.set("type", "checkerboard"); } } if (not partitioner_config.has("mpi_comm")) { partitioner_config.set("mpi_comm", mpi_comm_); } partitioner = grid::Partitioner(partitioner_config); } grid::Distribution distribution; { mpi::Scope mpi_scope(mpi_comm_); ATLAS_TRACE_SCOPE("Partitioning grid") { distribution = grid::Distribution(grid, partitioner); } } setup(distribution, config); } // ---------------------------------------------------------------------------- void StructuredColumns::compute_xy(idx_t i, idx_t j, PointXY& xy) const { idx_t jj; if (j < 0) { jj = -j - 1 + north_pole_included_; xy[YY] = 180. - grid_->y(jj); } else if (j >= ny_) { jj = 2 * ny_ - j - 1 - south_pole_included_; xy[YY] = -180. - grid_->y(jj); } else { jj = j; xy[YY] = grid_->y(jj); } xy[XX] = grid_->x(i, jj); } void StructuredColumns::Map2to1::resize(std::array i_range, std::array j_range) { i_min_ = i_range[0]; i_max_ = i_range[1]; j_min_ = j_range[0]; j_max_ = j_range[1]; j_stride_ = (i_max_ - i_min_ + 1); data_.resize((i_max_ - i_min_ + 1) * (j_max_ - j_min_ + 1)); atlas::omp::fill(data_.begin(), data_.end(), missing() + 1); } const util::PartitionPolygon& StructuredColumns::polygon(idx_t halo) const { if (halo >= static_cast(polygons_.size())) { polygons_.resize(halo + 1); } if (not polygons_[halo]) { if (halo > this->halo()) { throw_Exception("StructuredColumns does not contain a halo of size " + std::to_string(halo) + ".", Here()); } polygons_[halo].reset(new grid::StructuredPartitionPolygon(*this, halo)); } return *polygons_[halo]; } const util::PartitionPolygons& StructuredColumns::polygons() const { if (all_polygons_.size() == 0) { polygon().allGather(all_polygons_); } return all_polygons_; } // ---------------------------------------------------------------------------- // Destructor // ---------------------------------------------------------------------------- StructuredColumns::~StructuredColumns() { delete grid_; } // ---------------------------------------------------------------------------- // ---------------------------------------------------------------------------- // Create Field // ---------------------------------------------------------------------------- Field StructuredColumns::createField(const eckit::Configuration& options) const { Field field(config_name(options), config_datatype(options), config_spec(options)); set_field_metadata(options, field); return field; } Field StructuredColumns::createField(const Field& other, const eckit::Configuration& config) const { return createField(option::datatype(other.datatype()) | option::levels(other.levels()) | option::variables(other.variables()) | option::type(other.metadata().getString("type", "scalar")) | config); } // ---------------------------------------------------------------------------- // ---------------------------------------------------------------------------- // Gather FieldSet // ---------------------------------------------------------------------------- void StructuredColumns::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()); } } } // ---------------------------------------------------------------------------- // ---------------------------------------------------------------------------- // Gather Field // ---------------------------------------------------------------------------- void StructuredColumns::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); } // ---------------------------------------------------------------------------- // ---------------------------------------------------------------------------- // Scatter FieldSet // ---------------------------------------------------------------------------- void StructuredColumns::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); loc.set_dirty(true); } } // ---------------------------------------------------------------------------- // ---------------------------------------------------------------------------- // Scatter Field // ---------------------------------------------------------------------------- void StructuredColumns::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); } // ---------------------------------------------------------------------------- std::string StructuredColumns::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()) { md5 << checksum_3d_field(checksum(), field); } else if (field.datatype() == array::DataType::kind()) { md5 << checksum_3d_field(checksum(), field); } else if (field.datatype() == array::DataType::kind()) { md5 << checksum_3d_field(checksum(), field); } else if (field.datatype() == array::DataType::kind()) { md5 << checksum_3d_field(checksum(), field); } else { throw_Exception("datatype not supported", Here()); } } return md5; } std::string StructuredColumns::checksum(const Field& field) const { FieldSet fieldset; fieldset.add(field); return checksum(fieldset); } const StructuredGrid& StructuredColumns::grid() const { return *grid_; } namespace { template struct FixupHaloForVectors { FixupHaloForVectors(const StructuredColumns&) {} template void apply(Field& field) { std::string type = field.metadata().getString("type", "scalar"); if (type == "vector") { ATLAS_NOTIMPLEMENTED; } } }; template <> struct FixupHaloForVectors<2> { static constexpr int RANK = 2; const StructuredColumns& fs; FixupHaloForVectors(const StructuredColumns& _fs): fs(_fs) {} template void apply(Field& field) { std::string type = field.metadata().getString("type", "scalar"); if (type == "vector") { auto array = array::make_view(field); for (idx_t j = fs.j_begin_halo(); j < 0; ++j) { for (idx_t i = fs.i_begin_halo(j); i < fs.i_end_halo(j); ++i) { idx_t n = fs.index(i, j); array(n, XX) = -array(n, XX); array(n, YY) = -array(n, YY); } } for (idx_t j = fs.grid().ny(); j < fs.j_end_halo(); ++j) { for (idx_t i = fs.i_begin_halo(j); i < fs.i_end_halo(j); ++i) { idx_t n = fs.index(i, j); array(n, XX) = -array(n, XX); array(n, YY) = -array(n, YY); } } } } }; template <> struct FixupHaloForVectors<3> { static constexpr int RANK = 3; const StructuredColumns& fs; FixupHaloForVectors(const StructuredColumns& _fs): fs(_fs) {} template void apply(Field& field) { std::string type = field.metadata().getString("type", "scalar"); // if levels not setup in functionspace use levels from field idx_t k_end = (fs.k_begin() == 0) && (fs.k_end() == 0) ? field.levels() : fs.k_end(); if (type == "vector") { auto array = array::make_view(field); for (idx_t j = fs.j_begin_halo(); j < 0; ++j) { for (idx_t i = fs.i_begin_halo(j); i < fs.i_end_halo(j); ++i) { idx_t n = fs.index(i, j); for (idx_t k = fs.k_begin(); k < k_end; ++k) { array(n, k, XX) = -array(n, k, XX); array(n, k, YY) = -array(n, k, YY); } } } for (idx_t j = fs.grid().ny(); j < fs.j_end_halo(); ++j) { for (idx_t i = fs.i_begin_halo(j); i < fs.i_end_halo(j); ++i) { idx_t n = fs.index(i, j); for (idx_t k = fs.k_begin(); k < k_end; ++k) { array(n, k, XX) = -array(n, k, XX); array(n, k, YY) = -array(n, k, YY); } } } } } }; template void dispatch_haloExchange(Field& field, bool on_device, const parallel::HaloExchange& halo_exchange, const StructuredColumns& fs) { FixupHaloForVectors fixup_halos(fs); if (field.datatype() == array::DataType::kind()) { halo_exchange.template execute(field.array(), on_device); fixup_halos.template apply(field); } else if (field.datatype() == array::DataType::kind()) { halo_exchange.template execute(field.array(), on_device); fixup_halos.template apply(field); } else if (field.datatype() == array::DataType::kind()) { halo_exchange.template execute(field.array(), on_device); fixup_halos.template apply(field); } else if (field.datatype() == array::DataType::kind()) { halo_exchange.template execute(field.array(), on_device); fixup_halos.template apply(field); } else { throw_Exception("datatype not supported", Here()); } field.set_dirty(false); } template void dispatch_adjointHaloExchange(Field& field, bool on_device, const parallel::HaloExchange& halo_exchange, const StructuredColumns& fs) { FixupHaloForVectors fixup_halos(fs); if (field.datatype() == array::DataType::kind()) { fixup_halos.template apply(field); halo_exchange.template execute_adjoint(field.array(), on_device); } else if (field.datatype() == array::DataType::kind()) { fixup_halos.template apply(field); halo_exchange.template execute_adjoint(field.array(), on_device); } else if (field.datatype() == array::DataType::kind()) { fixup_halos.template apply(field); halo_exchange.template execute_adjoint(field.array(), on_device); } else if (field.datatype() == array::DataType::kind()) { fixup_halos.template apply(field); halo_exchange.template execute_adjoint(field.array(), on_device); } else { throw_Exception("datatype not supported", Here()); } field.set_dirty(false); } } // namespace void StructuredColumns::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, on_device, halo_exchange(), *this); break; case 2: dispatch_haloExchange<2>(field, on_device, halo_exchange(), *this); break; case 3: dispatch_haloExchange<3>(field, on_device, halo_exchange(), *this); break; case 4: dispatch_haloExchange<4>(field, on_device, halo_exchange(), *this); break; default: throw_Exception("Rank not supported", Here()); } } } void StructuredColumns::adjointHaloExchange(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_adjointHaloExchange<1>(field, on_device, halo_exchange(), *this); break; case 2: dispatch_adjointHaloExchange<2>(field, on_device, halo_exchange(), *this); break; case 3: dispatch_adjointHaloExchange<3>(field, on_device, halo_exchange(), *this); break; case 4: dispatch_adjointHaloExchange<4>(field, on_device, halo_exchange(), *this); break; default: throw_Exception("Rank not supported", Here()); } } } void StructuredColumns::haloExchange(const Field& field, bool on_device) const { FieldSet fieldset; fieldset.add(field); haloExchange(fieldset, on_device); } void StructuredColumns::adjointHaloExchange(const Field& field, bool on_device) const { FieldSet fieldset; fieldset.add(field); adjointHaloExchange(fieldset, on_device); } size_t StructuredColumns::footprint() const { size_t size = sizeof(*this); size += ij2gp_.footprint(); if (field_xy_) { size += field_xy_.footprint(); } if (field_partition_) { size += field_partition_.footprint(); } if (field_global_index_) { size += field_global_index_.footprint(); } if (field_remote_index_) { size += field_remote_index_.footprint(); } if (field_index_i_) { size += field_index_i_.footprint(); } if (field_index_j_) { size += field_index_j_.footprint(); } return size; } // ---------------------------------------------------------------------------- } // namespace detail } // namespace functionspace } // namespace atlas atlas-0.46.0/src/atlas/functionspace/detail/BlockStructuredColumnsInterface.cc0000664000175000017500000002532515160212070027700 0ustar alastairalastair/* * (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 "BlockStructuredColumnsInterface.h" #include "atlas/field/FieldSet.h" #include "atlas/field/detail/FieldImpl.h" #include "atlas/grid/Distribution.h" #include "atlas/grid/Grid.h" #include "atlas/grid/Partitioner.h" #include "atlas/grid/detail/distribution/DistributionImpl.h" #include "atlas/runtime/Exception.h" namespace atlas { namespace functionspace { // ---------------------------------------------------------------------------- // Fortran interfaces // ---------------------------------------------------------------------------- namespace detail { struct BlockStructuredColumnsFortranAccess { detail::StructuredColumns::Map2to1& ij2gp_; BlockStructuredColumnsFortranAccess(const detail::BlockStructuredColumns& fs) : ij2gp_(const_cast(fs.structuredcolumns()).ij2gp_) {}; }; } // namespace detail extern "C" { const detail::BlockStructuredColumns* atlas__functionspace__BStructuredColumns__new__grid( const Grid::Implementation* grid, const eckit::Configuration* config) { return new detail::BlockStructuredColumns(Grid(grid), grid::Partitioner(), *config); } const detail::BlockStructuredColumns* atlas__functionspace__BStructuredColumns__new__grid_dist( const Grid::Implementation* grid, const grid::DistributionImpl* dist, const eckit::Configuration* config) { return new detail::BlockStructuredColumns(Grid(grid), grid::Distribution(dist), *config); } const detail::BlockStructuredColumns* atlas__functionspace__BStructuredColumns__new__grid_dist_vert( const Grid::Implementation* grid, const grid::DistributionImpl* dist, const Vertical* vert, const eckit::Configuration* config) { return new detail::BlockStructuredColumns(Grid(grid), grid::Distribution(dist), *vert, *config); } const detail::BlockStructuredColumns* atlas__functionspace__BStructuredColumns__new__grid_dist_config( const Grid::Implementation* grid, const grid::DistributionImpl* dist, const eckit::Configuration* config) { return new detail::BlockStructuredColumns(Grid(grid), grid::Distribution(dist), *config); } const detail::BlockStructuredColumns* atlas__functionspace__BStructuredColumns__new__grid_part( const Grid::Implementation* grid, const PartitionerImpl* partitioner, const eckit::Configuration* config) { return new detail::BlockStructuredColumns(Grid(grid), grid::Partitioner(partitioner), *config); } const detail::BlockStructuredColumns* atlas__functionspace__BStructuredColumns__new__grid_part_vert( const Grid::Implementation* grid, const PartitionerImpl* partitioner, const Vertical* vert, const eckit::Configuration* config) { return new detail::BlockStructuredColumns(Grid(grid), *vert, grid::Partitioner(partitioner), *config); } void atlas__functionspace__BStructuredColumns__gather_field(const detail::BlockStructuredColumns* This, const field::FieldImpl* local, field::FieldImpl* global) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_BlockStructuredColumns"); ATLAS_ASSERT(global != nullptr, "Cannot access uninitialised atlas_Field"); ATLAS_ASSERT(local != nullptr, "Cannot access uninitialised atlas_Field"); const Field l(local); Field g(global); This->gather(l, g); } void atlas__functionspace__BStructuredColumns__scatter_field(const detail::BlockStructuredColumns* This, const field::FieldImpl* global, field::FieldImpl* local) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_BlockStructuredColumns"); ATLAS_ASSERT(global != nullptr, "Cannot access uninitialised atlas_Field"); ATLAS_ASSERT(local != nullptr, "Cannot access uninitialised atlas_Field"); const Field g(global); Field l(local); This->scatter(g, l); } void atlas__functionspace__BStructuredColumns__gather_fieldset(const detail::BlockStructuredColumns* This, const field::FieldSetImpl* local, field::FieldSetImpl* global) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_BlockStructuredColumns"); ATLAS_ASSERT(global != nullptr, "Cannot access uninitialised atlas_FieldSet"); ATLAS_ASSERT(local != nullptr, "Cannot access uninitialised atlas_FieldSet"); const FieldSet l(local); FieldSet g(global); This->gather(l, g); } void atlas__functionspace__BStructuredColumns__scatter_fieldset(const detail::BlockStructuredColumns* This, const field::FieldSetImpl* global, field::FieldSetImpl* local) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_BlockStructuredColumns"); ATLAS_ASSERT(global != nullptr, "Cannot access uninitialised atlas_FieldSet"); ATLAS_ASSERT(local != nullptr, "Cannot access uninitialised atlas_FieldSet"); const FieldSet g(global); FieldSet l(local); This->scatter(g, l); } void atlas__fs__BStructuredColumns__checksum_fieldset(const detail::BlockStructuredColumns* This, const field::FieldSetImpl* fieldset, char*& checksum, idx_t& size, int& allocated) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_BlockStructuredColumns"); ATLAS_ASSERT(fieldset != nullptr, "Cannot access uninitialised atlas_FieldSet"); std::string checksum_str(This->checksum(fieldset)); size = static_cast(checksum_str.size()); checksum = new char[size + 1]; allocated = true; std::strncpy(checksum, checksum_str.c_str(), size + 1); } void atlas__fs__BStructuredColumns__checksum_field(const detail::BlockStructuredColumns* This, const field::FieldImpl* field, char*& checksum, idx_t& size, int& allocated) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_BlockStructuredColumns"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialised atlas_Field"); std::string checksum_str(This->checksum(field)); size = static_cast(checksum_str.size()); checksum = new char[size + 1]; allocated = true; std::strncpy(checksum, checksum_str.c_str(), size + 1); } void atlas__fs__BStructuredColumns__index_host(const detail::BlockStructuredColumns* This, idx_t*& data, idx_t& i_min, idx_t& i_max, idx_t& j_min, idx_t& j_max) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_BlockStructuredColumns"); auto _This = detail::BlockStructuredColumnsFortranAccess{*This}; data = _This.ij2gp_.data_.data(); i_min = _This.ij2gp_.i_min_ + 1; i_max = _This.ij2gp_.i_max_ + 1; j_min = _This.ij2gp_.j_min_ + 1; j_max = _This.ij2gp_.j_max_ + 1; } idx_t atlas__fs__BStructuredColumns__j_begin(const detail::BlockStructuredColumns* This) { return This->structuredcolumns().j_begin() + 1; } idx_t atlas__fs__BStructuredColumns__j_end(const detail::BlockStructuredColumns* This) { return This->structuredcolumns().j_end(); } idx_t atlas__fs__BStructuredColumns__i_begin(const detail::BlockStructuredColumns* This, idx_t j) { return This->structuredcolumns().i_begin(j - 1) + 1; } idx_t atlas__fs__BStructuredColumns__i_end(const detail::BlockStructuredColumns* This, idx_t j) { return This->structuredcolumns().i_end(j - 1); } idx_t atlas__fs__BStructuredColumns__j_begin_halo(const detail::BlockStructuredColumns* This) { return This->structuredcolumns().j_begin_halo() + 1; } idx_t atlas__fs__BStructuredColumns__j_end_halo(const detail::BlockStructuredColumns* This) { return This->structuredcolumns().j_end_halo(); } idx_t atlas__fs__BStructuredColumns__i_begin_halo(const detail::BlockStructuredColumns* This, idx_t j) { return This->structuredcolumns().i_begin_halo(j - 1) + 1; } idx_t atlas__fs__BStructuredColumns__i_end_halo(const detail::BlockStructuredColumns* This, idx_t j) { return This->structuredcolumns().i_end_halo(j - 1); } idx_t atlas__fs__BStructuredColumns__block_begin(const detail::BlockStructuredColumns* This, idx_t jblk) { return This->block_begin(jblk); } idx_t atlas__fs__BStructuredColumns__block_size(const detail::BlockStructuredColumns* This, idx_t jblk) { return This->block_size(jblk); } idx_t atlas__fs__BStructuredColumns__nproma(const detail::BlockStructuredColumns* This) { return This->nproma(); } idx_t atlas__fs__BStructuredColumns__nblks(const detail::BlockStructuredColumns* This) { return This->nblks(); } field::FieldImpl* atlas__fs__BStructuredColumns__xy(const detail::BlockStructuredColumns* This) { return This->xy().get(); } field::FieldImpl* atlas__fs__BStructuredColumns__z(const detail::BlockStructuredColumns* This) { ATLAS_ASSERT(This != nullptr); field::FieldImpl* field; { Field f = This->z(); field = f.get(); field->attach(); } field->detach(); return field; } field::FieldImpl* atlas__fs__BStructuredColumns__partition(const detail::BlockStructuredColumns* This) { return This->partition().get(); } field::FieldImpl* atlas__fs__BStructuredColumns__global_index(const detail::BlockStructuredColumns* This) { return This->global_index().get(); } field::FieldImpl* atlas__fs__BStructuredColumns__index_i(const detail::BlockStructuredColumns* This) { return This->index_i().get(); } field::FieldImpl* atlas__fs__BStructuredColumns__index_j(const detail::BlockStructuredColumns* This) { return This->index_j().get(); } idx_t atlas__fs__BStructuredColumns__size(const detail::BlockStructuredColumns* This) { return This->size(); } idx_t atlas__fs__BStructuredColumns__sizeOwned(const detail::BlockStructuredColumns* This) { return This->structuredcolumns().sizeOwned(); } idx_t atlas__fs__BStructuredColumns__levels(const detail::BlockStructuredColumns* This) { return This->levels(); } const GridImpl* atlas__fs__BStructuredColumns__grid(const detail::BlockStructuredColumns* This) { return This->grid().get(); } } // ---------------------------------------------------------------------------- } // namespace functionspace } // namespace atlas atlas-0.46.0/src/atlas/functionspace/detail/FunctionSpaceInterface.cc0000664000175000017500000001115615160212070025756 0ustar alastairalastair/* * (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 "FunctionSpaceImpl.h" #include "FunctionSpaceInterface.h" #include "atlas/field/Field.h" #include "atlas/field/FieldSet.h" #include "atlas/field/detail/FieldImpl.h" #include "atlas/library/config.h" #include "atlas/runtime/Exception.h" #include "atlas/util/Config.h" namespace atlas { namespace functionspace { //----------------------------------------------------------------------------- // C wrapper interfaces to C++ routines extern "C" { void atlas__FunctionSpace__delete(FunctionSpaceImpl* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_FunctionSpace"); delete This; This = nullptr; } void atlas__FunctionSpace__name(const FunctionSpaceImpl* This, char*& name, int& size) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_FunctionSpace"); std::string s = This->type(); size = static_cast(s.size()); name = new char[size + 1]; std::strncpy(name, s.c_str(), size + 1); } field::FieldImpl* atlas__FunctionSpace__create_field(const FunctionSpaceImpl* This, const eckit::Configuration* options) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_FunctionSpace"); ATLAS_ASSERT(options != nullptr); field::FieldImpl* field; { Field f = This->createField(*options); field = f.get(); field->attach(); } field->detach(); return field; } //------------------------------------------------------------------------------ field::FieldImpl* atlas__FunctionSpace__create_field_template(const FunctionSpaceImpl* This, const field::FieldImpl* field_template, const eckit::Configuration* options) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_FunctionSpace"); ATLAS_ASSERT(field_template != nullptr, "Cannot access uninitialised atlas_Field"); ATLAS_ASSERT(options != nullptr); field::FieldImpl* field; { Field f = This->createField(Field(field_template), *options); field = f.get(); field->attach(); } field->detach(); return field; } //------------------------------------------------------------------------------ void atlas__FunctionSpace__halo_exchange_field(const FunctionSpaceImpl* This, field::FieldImpl* field) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_FunctionSpace"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialised atlas_Field"); Field f(field); This->haloExchange(f); } //------------------------------------------------------------------------------ void atlas__FunctionSpace__halo_exchange_fieldset(const FunctionSpaceImpl* This, field::FieldSetImpl* fieldset) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_FunctionSpace"); ATLAS_ASSERT(fieldset != nullptr, "Cannot access uninitialised atlas_FieldSet"); FieldSet f(fieldset); This->haloExchange(f); } //------------------------------------------------------------------------------ void atlas__FunctionSpace__adjoint_halo_exchange_field(const FunctionSpaceImpl* This, field::FieldImpl* field) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_FunctionSpace"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialised atlas_Field"); Field f(field); This->adjointHaloExchange(f); } //------------------------------------------------------------------------------ void atlas__FunctionSpace__adjoint_halo_exchange_fieldset(const FunctionSpaceImpl* This, field::FieldSetImpl* fieldset) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_FunctionSpace"); ATLAS_ASSERT(fieldset != nullptr, "Cannot access uninitialised atlas_FieldSet"); FieldSet f(fieldset); This->adjointHaloExchange(f); } } // ------------------------------------------------------------------ } // namespace functionspace // ------------------------------------------------------------------ } // namespace atlas atlas-0.46.0/src/atlas/functionspace/detail/NodeColumnsInterface.cc0000664000175000017500000014477715160212070025463 0ustar alastairalastair/* * (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 "NodeColumnsInterface.h" #include "atlas/field/FieldSet.h" #include "atlas/field/detail/FieldImpl.h" #include "atlas/runtime/Exception.h" namespace atlas { namespace functionspace { namespace detail { using atlas::FieldSet; using atlas::field::FieldImpl; using atlas::field::FieldSetImpl; // ---------------------------------------------------------------------- extern "C" { const NodeColumns* atlas__NodesFunctionSpace__new(Mesh::Implementation* mesh, const eckit::Configuration* config) { ATLAS_ASSERT(mesh); Mesh m(mesh); return new NodeColumns(m, *config); } void atlas__NodesFunctionSpace__delete(NodeColumns* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); delete (This); } int atlas__NodesFunctionSpace__nb_nodes(const NodeColumns* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); return This->nb_nodes(); } const Mesh::Implementation* atlas__NodesFunctionSpace__mesh(const NodeColumns* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); return This->mesh().get(); } mesh::Nodes* atlas__NodesFunctionSpace__nodes(const NodeColumns* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); return &This->nodes(); } void atlas__NodesFunctionSpace__halo_exchange_fieldset(const NodeColumns* This, field::FieldSetImpl* fieldset) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); ATLAS_ASSERT(fieldset != nullptr, "Cannot access uninitialised atlas_FieldSet"); FieldSet f(fieldset); This->haloExchange(f); } void atlas__NodesFunctionSpace__halo_exchange_field(const NodeColumns* This, field::FieldImpl* field) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialised atlas_Field"); Field f(field); This->haloExchange(f); } const parallel::HaloExchange* atlas__NodesFunctionSpace__get_halo_exchange(const NodeColumns* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); return &This->halo_exchange(); } void atlas__NodesFunctionSpace__gather_fieldset(const NodeColumns* This, const field::FieldSetImpl* local, field::FieldSetImpl* global) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); ATLAS_ASSERT(local != nullptr, "Cannot access uninitialised local atlas_FieldSet"); ATLAS_ASSERT(global != nullptr, "Cannot access uninitialised global atlas_FieldSet"); const FieldSet l(local); FieldSet g(global); This->gather(l, g); } void atlas__NodesFunctionSpace__gather_field(const NodeColumns* This, const field::FieldImpl* local, field::FieldImpl* global) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); ATLAS_ASSERT(local != nullptr, "Cannot access uninitialised local atlas_Field"); ATLAS_ASSERT(global != nullptr, "Cannot access uninitialised global atlas_Field"); const Field l(local); Field g(global); This->gather(l, g); } const parallel::GatherScatter* atlas__NodesFunctionSpace__get_gather(const NodeColumns* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); return &This->gather(); } const parallel::GatherScatter* atlas__NodesFunctionSpace__get_scatter(const NodeColumns* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); return &This->scatter(); } void atlas__NodesFunctionSpace__scatter_fieldset(const NodeColumns* This, const field::FieldSetImpl* global, field::FieldSetImpl* local) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); ATLAS_ASSERT(local != nullptr, "Cannot access uninitialised local atlas_FieldSet"); ATLAS_ASSERT(global != nullptr, "Cannot access uninitialised global atlas_FieldSet"); const FieldSet g(global); FieldSet l(local); This->scatter(g, l); } void atlas__NodesFunctionSpace__scatter_field(const NodeColumns* This, const field::FieldImpl* global, field::FieldImpl* local) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); ATLAS_ASSERT(local != nullptr, "Cannot access uninitialised local atlas_Field"); ATLAS_ASSERT(global != nullptr, "Cannot access uninitialised global atlas_Field"); const Field g(global); Field l(local); This->scatter(g, l); } const parallel::Checksum* atlas__NodesFunctionSpace__get_checksum(const NodeColumns* This) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); return &This->checksum(); } void atlas__NodesFunctionSpace__checksum_fieldset(const NodeColumns* This, const field::FieldSetImpl* fieldset, char*& checksum, int& size, int& allocated) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); ATLAS_ASSERT(fieldset != nullptr, "Cannot access uninitialised atlas_FieldSet"); std::string checksum_str(This->checksum(fieldset)); size = static_cast(checksum_str.size()); checksum = new char[size + 1]; allocated = true; std::strncpy(checksum, checksum_str.c_str(), size + 1); } void atlas__NodesFunctionSpace__checksum_field(const NodeColumns* This, const field::FieldImpl* field, char*& checksum, int& size, int& allocated) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialised atlas_Field"); std::string checksum_str(This->checksum(field)); size = static_cast(checksum_str.size()); checksum = new char[size + 1]; allocated = true; std::strncpy(checksum, checksum_str.c_str(), size + 1); } void atlas__NodesFunctionSpace__sum_double(const NodeColumns* This, const field::FieldImpl* field, double& sum, int& N) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialised atlas_Field"); idx_t idx_t_N; This->sum(field, sum, idx_t_N); N = idx_t_N; } void atlas__NodesFunctionSpace__sum_float(const NodeColumns* This, const field::FieldImpl* field, float& sum, int& N) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialised atlas_Field"); idx_t idx_t_N; This->sum(field, sum, idx_t_N); N = idx_t_N; } void atlas__NodesFunctionSpace__sum_long(const NodeColumns* This, const field::FieldImpl* field, long& sum, int& N) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialised atlas_Field"); idx_t idx_t_N; This->sum(field, sum, idx_t_N); N = idx_t_N; } void atlas__NodesFunctionSpace__sum_int(const NodeColumns* This, const field::FieldImpl* field, int& sum, int& N) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialised atlas_Field"); idx_t idx_t_N; This->sum(field, sum, idx_t_N); N = idx_t_N; } void atlas__NodesFunctionSpace__sum_arr_double(const NodeColumns* This, const field::FieldImpl* field, double*& sum, int& size, int& N) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialised atlas_Field"); idx_t idx_t_N; std::vector sumvec; This->orderIndependentSum(field, sumvec, idx_t_N); size = sumvec.size(); sum = new double[size]; for (idx_t j = 0; j < (idx_t)size; ++j) { sum[j] = sumvec[j]; } N = idx_t_N; } void atlas__NodesFunctionSpace__sum_arr_float(const NodeColumns* This, const field::FieldImpl* field, float*& sum, int& size, int& N) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialised atlas_Field"); idx_t idx_t_N; std::vector sumvec; This->orderIndependentSum(field, sumvec, idx_t_N); size = sumvec.size(); sum = new float[size]; for (idx_t j = 0; j < (idx_t)size; ++j) { sum[j] = sumvec[j]; } N = idx_t_N; } void atlas__NodesFunctionSpace__sum_arr_long(const NodeColumns* This, const field::FieldImpl* field, long*& sum, int& size, int& N) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialised atlas_Field"); idx_t idx_t_N; std::vector sumvec; This->orderIndependentSum(field, sumvec, idx_t_N); size = sumvec.size(); sum = new long[size]; for (idx_t j = 0; j < (idx_t)size; ++j) { sum[j] = sumvec[j]; } N = idx_t_N; } void atlas__NodesFunctionSpace__sum_arr_int(const NodeColumns* This, const field::FieldImpl* field, int*& sum, int& size, int& N) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialised atlas_Field"); idx_t idx_t_N; std::vector sumvec; This->orderIndependentSum(field, sumvec, idx_t_N); size = sumvec.size(); sum = new int[size]; for (idx_t j = 0; j < (idx_t)size; ++j) { sum[j] = sumvec[j]; } N = idx_t_N; } void atlas__NodesFunctionSpace__oisum_double(const NodeColumns* This, const field::FieldImpl* field, double& sum, int& N) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialised atlas_Field"); idx_t idx_t_N; This->orderIndependentSum(field, sum, idx_t_N); N = idx_t_N; } void atlas__NodesFunctionSpace__oisum_float(const NodeColumns* This, const field::FieldImpl* field, float& sum, int& N) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialised atlas_Field"); idx_t idx_t_N; This->orderIndependentSum(field, sum, idx_t_N); N = idx_t_N; } void atlas__NodesFunctionSpace__oisum_long(const NodeColumns* This, const field::FieldImpl* field, long& sum, int& N) { atlas__NodesFunctionSpace__sum_long(This, field, sum, N); } void atlas__NodesFunctionSpace__oisum_int(const NodeColumns* This, const field::FieldImpl* field, int& sum, int& N) { atlas__NodesFunctionSpace__sum_int(This, field, sum, N); } void atlas__NodesFunctionSpace__oisum_arr_double(const NodeColumns* This, const field::FieldImpl* field, double*& sum, int& size, int& N) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialised atlas_Field"); idx_t idx_t_N; std::vector sumvec; This->orderIndependentSum(field, sumvec, idx_t_N); size = sumvec.size(); sum = new double[size]; for (idx_t j = 0; j < (idx_t)size; ++j) { sum[j] = sumvec[j]; } N = idx_t_N; } void atlas__NodesFunctionSpace__oisum_arr_float(const NodeColumns* This, const field::FieldImpl* field, float*& sum, int& size, int& N) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialised atlas_Field"); idx_t idx_t_N; std::vector sumvec; This->orderIndependentSum(field, sumvec, idx_t_N); size = sumvec.size(); sum = new float[size]; for (idx_t j = 0; j < (idx_t)size; ++j) { sum[j] = sumvec[j]; } N = idx_t_N; } void atlas__NodesFunctionSpace__oisum_arr_int(const NodeColumns* This, const field::FieldImpl* field, int*& sum, int& size, int& N) { atlas__NodesFunctionSpace__sum_arr_int(This, field, sum, size, N); } void atlas__NodesFunctionSpace__oisum_arr_long(const NodeColumns* This, const field::FieldImpl* field, long*& sum, int& size, int& N) { atlas__NodesFunctionSpace__sum_arr_long(This, field, sum, size, N); } void atlas__NodesFunctionSpace__min_double(const NodeColumns* This, const field::FieldImpl* field, double& minimum) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialised atlas_Field"); This->minimum(field, minimum); } void atlas__NodesFunctionSpace__min_float(const NodeColumns* This, const field::FieldImpl* field, float& minimum) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialised atlas_Field"); This->minimum(field, minimum); } void atlas__NodesFunctionSpace__min_long(const NodeColumns* This, const field::FieldImpl* field, long& minimum) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialised atlas_Field"); This->minimum(field, minimum); } void atlas__NodesFunctionSpace__min_int(const NodeColumns* This, const field::FieldImpl* field, int& minimum) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialised atlas_Field"); This->minimum(field, minimum); } void atlas__NodesFunctionSpace__max_double(const NodeColumns* This, const field::FieldImpl* field, double& maximum) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialised atlas_Field"); This->maximum(field, maximum); } void atlas__NodesFunctionSpace__max_float(const NodeColumns* This, const field::FieldImpl* field, float& maximum) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialised atlas_Field"); This->maximum(field, maximum); } void atlas__NodesFunctionSpace__max_long(const NodeColumns* This, const field::FieldImpl* field, long& maximum) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialised atlas_Field"); This->maximum(field, maximum); } void atlas__NodesFunctionSpace__max_int(const NodeColumns* This, const field::FieldImpl* field, int& maximum) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialised atlas_Field"); This->maximum(field, maximum); } void atlas__NodesFunctionSpace__min_arr_double(const NodeColumns* This, const field::FieldImpl* field, double*& minimum, int& size) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialised atlas_Field"); std::vector minvec; This->minimum(field, minvec); size = minvec.size(); minimum = new double[size]; for (idx_t j = 0; j < (idx_t)size; ++j) { minimum[j] = minvec[j]; } } void atlas__NodesFunctionSpace__min_arr_float(const NodeColumns* This, const field::FieldImpl* field, float*& minimum, int& size) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialised atlas_Field"); std::vector minvec; This->minimum(field, minvec); size = minvec.size(); minimum = new float[size]; for (idx_t j = 0; j < (idx_t)size; ++j) { minimum[j] = minvec[j]; } } void atlas__NodesFunctionSpace__min_arr_long(const NodeColumns* This, const field::FieldImpl* field, long*& minimum, int& size) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialised atlas_Field"); std::vector minvec; This->minimum(field, minvec); size = minvec.size(); minimum = new long[size]; for (idx_t j = 0; j < (idx_t)size; ++j) { minimum[j] = minvec[j]; }; } void atlas__NodesFunctionSpace__min_arr_int(const NodeColumns* This, const field::FieldImpl* field, int*& minimum, int& size) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialised atlas_Field"); std::vector minvec; This->minimum(field, minvec); size = minvec.size(); minimum = new int[size]; for (idx_t j = 0; j < (idx_t)size; ++j) { minimum[j] = minvec[j]; } } void atlas__NodesFunctionSpace__max_arr_double(const NodeColumns* This, const field::FieldImpl* field, double*& maximum, int& size) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialised atlas_Field"); std::vector maxvec; This->maximum(field, maxvec); size = maxvec.size(); maximum = new double[size]; for (idx_t j = 0; j < (idx_t)size; ++j) { maximum[j] = maxvec[j]; } } void atlas__NodesFunctionSpace__max_arr_float(const NodeColumns* This, const field::FieldImpl* field, float*& maximum, int& size) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialised atlas_Field"); std::vector maxvec; This->maximum(field, maxvec); size = maxvec.size(); maximum = new float[size]; for (idx_t j = 0; j < (idx_t)size; ++j) { maximum[j] = maxvec[j]; } } void atlas__NodesFunctionSpace__max_arr_long(const NodeColumns* This, const field::FieldImpl* field, long*& maximum, int& size) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialised atlas_Field"); std::vector maxvec; This->maximum(field, maxvec); size = maxvec.size(); maximum = new long[size]; for (idx_t j = 0; j < (idx_t)size; ++j) { maximum[j] = maxvec[j]; } } void atlas__NodesFunctionSpace__max_arr_int(const NodeColumns* This, const field::FieldImpl* field, int*& maximum, int& size) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialised atlas_Field"); std::vector maxvec; This->maximum(field, maxvec); size = maxvec.size(); maximum = new int[size]; for (idx_t j = 0; j < (idx_t)size; ++j) { maximum[j] = maxvec[j]; } } void atlas__NodesFunctionSpace__minloc_double(const NodeColumns* This, const field::FieldImpl* field, double& minimum, long& glb_idx) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialised atlas_Field"); gidx_t gidx; This->minimumAndLocation(field, minimum, gidx); glb_idx = gidx; } void atlas__NodesFunctionSpace__minloc_float(const NodeColumns* This, const field::FieldImpl* field, float& minimum, long& glb_idx) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialised atlas_Field"); gidx_t gidx; This->minimumAndLocation(field, minimum, gidx); glb_idx = gidx; } void atlas__NodesFunctionSpace__minloc_long(const NodeColumns* This, const field::FieldImpl* field, long& minimum, long& glb_idx) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialised atlas_Field"); gidx_t gidx; This->minimumAndLocation(field, minimum, gidx); glb_idx = gidx; } void atlas__NodesFunctionSpace__minloc_int(const NodeColumns* This, const field::FieldImpl* field, int& minimum, long& glb_idx) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialised atlas_Field"); gidx_t gidx; This->minimumAndLocation(field, minimum, gidx); glb_idx = gidx; } void atlas__NodesFunctionSpace__maxloc_double(const NodeColumns* This, const field::FieldImpl* field, double& maximum, long& glb_idx) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialised atlas_Field"); gidx_t gidx; This->maximumAndLocation(field, maximum, gidx); glb_idx = gidx; } void atlas__NodesFunctionSpace__maxloc_float(const NodeColumns* This, const field::FieldImpl* field, float& maximum, long& glb_idx) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialised atlas_Field"); gidx_t gidx; This->maximumAndLocation(field, maximum, gidx); glb_idx = gidx; } void atlas__NodesFunctionSpace__maxloc_long(const NodeColumns* This, const field::FieldImpl* field, long& maximum, long& glb_idx) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialised atlas_Field"); gidx_t gidx; This->maximumAndLocation(field, maximum, gidx); glb_idx = gidx; } void atlas__NodesFunctionSpace__maxloc_int(const NodeColumns* This, const field::FieldImpl* field, int& maximum, long& glb_idx) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialised atlas_Field"); gidx_t gidx; This->maximumAndLocation(field, maximum, gidx); glb_idx = gidx; } void atlas__NodesFunctionSpace__minloc_arr_double(const NodeColumns* This, const field::FieldImpl* field, double*& minimum, long*& glb_idx, int& size) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialised atlas_Field"); std::vector minvec; std::vector gidxvec; This->minimumAndLocation(field, minvec, gidxvec); size = minvec.size(); minimum = new double[size]; glb_idx = new long[size]; for (idx_t j = 0; j < (idx_t)size; ++j) { minimum[j] = minvec[j]; glb_idx[j] = gidxvec[j]; } } void atlas__NodesFunctionSpace__minloc_arr_float(const NodeColumns* This, const field::FieldImpl* field, float*& minimum, long*& glb_idx, int& size) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialised atlas_Field"); std::vector minvec; std::vector gidxvec; This->minimumAndLocation(field, minvec, gidxvec); size = minvec.size(); minimum = new float[size]; glb_idx = new long[size]; for (idx_t j = 0; j < (idx_t)size; ++j) { minimum[j] = minvec[j]; glb_idx[j] = gidxvec[j]; } } void atlas__NodesFunctionSpace__minloc_arr_long(const NodeColumns* This, const field::FieldImpl* field, long*& minimum, long*& glb_idx, int& size) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialised atlas_Field"); std::vector minvec; std::vector gidxvec; This->minimumAndLocation(field, minvec, gidxvec); size = minvec.size(); minimum = new long[size]; glb_idx = new long[size]; for (idx_t j = 0; j < (idx_t)size; ++j) { minimum[j] = minvec[j]; glb_idx[j] = gidxvec[j]; } } void atlas__NodesFunctionSpace__minloc_arr_int(const NodeColumns* This, const field::FieldImpl* field, int*& minimum, long*& glb_idx, int& size) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialised atlas_Field"); std::vector minvec; std::vector gidxvec; This->minimumAndLocation(field, minvec, gidxvec); size = minvec.size(); minimum = new int[size]; glb_idx = new long[size]; for (idx_t j = 0; j < (idx_t)size; ++j) { minimum[j] = minvec[j]; glb_idx[j] = gidxvec[j]; } } void atlas__NodesFunctionSpace__maxloc_arr_double(const NodeColumns* This, const field::FieldImpl* field, double*& maximum, long*& glb_idx, int& size) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialised atlas_Field"); std::vector maxvec; std::vector gidxvec; This->maximumAndLocation(field, maxvec, gidxvec); size = maxvec.size(); maximum = new double[size]; glb_idx = new long[size]; for (idx_t j = 0; j < (idx_t)size; ++j) { maximum[j] = maxvec[j]; glb_idx[j] = gidxvec[j]; } } void atlas__NodesFunctionSpace__maxloc_arr_float(const NodeColumns* This, const field::FieldImpl* field, float*& maximum, long*& glb_idx, int& size) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialised atlas_Field"); std::vector maxvec; std::vector gidxvec; This->maximumAndLocation(field, maxvec, gidxvec); size = maxvec.size(); maximum = new float[size]; glb_idx = new long[size]; for (idx_t j = 0; j < (idx_t)size; ++j) { maximum[j] = maxvec[j]; glb_idx[j] = gidxvec[j]; } } void atlas__NodesFunctionSpace__maxloc_arr_long(const NodeColumns* This, const field::FieldImpl* field, long*& maximum, long*& glb_idx, int& size) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialised atlas_Field"); std::vector maxvec; std::vector gidxvec; This->maximumAndLocation(field, maxvec, gidxvec); size = maxvec.size(); maximum = new long[size]; glb_idx = new long[size]; for (idx_t j = 0; j < (idx_t)size; ++j) { maximum[j] = maxvec[j]; glb_idx[j] = gidxvec[j]; } } void atlas__NodesFunctionSpace__maxloc_arr_int(const NodeColumns* This, const field::FieldImpl* field, int*& maximum, long*& glb_idx, int& size) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialised atlas_Field"); std::vector maxvec; std::vector gidxvec; This->maximumAndLocation(field, maxvec, gidxvec); size = maxvec.size(); maximum = new int[size]; glb_idx = new long[size]; for (idx_t j = 0; j < (idx_t)size; ++j) { maximum[j] = maxvec[j]; glb_idx[j] = gidxvec[j]; } } void atlas__NodesFunctionSpace__mean_double(const NodeColumns* This, const field::FieldImpl* field, double& mean, int& N) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialised atlas_Field"); idx_t idx_t_N; This->mean(field, mean, idx_t_N); N = idx_t_N; } void atlas__NodesFunctionSpace__mean_float(const NodeColumns* This, const field::FieldImpl* field, float& mean, int& N) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialised atlas_Field"); idx_t idx_t_N; This->mean(field, mean, idx_t_N); N = idx_t_N; } void atlas__NodesFunctionSpace__mean_long(const NodeColumns* This, const field::FieldImpl* field, long& mean, int& N) { ATLAS_NOTIMPLEMENTED; } void atlas__NodesFunctionSpace__mean_int(const NodeColumns* This, const field::FieldImpl* field, int& mean, int& N) { ATLAS_NOTIMPLEMENTED; } void atlas__NodesFunctionSpace__mean_arr_double(const NodeColumns* This, const field::FieldImpl* field, double*& mean, int& size, int& N) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialised atlas_Field"); idx_t idx_t_N; std::vector meanvec; This->mean(field, meanvec, idx_t_N); size = meanvec.size(); mean = new double[size]; for (idx_t j = 0; j < (idx_t)size; ++j) { mean[j] = meanvec[j]; } N = idx_t_N; } void atlas__NodesFunctionSpace__mean_arr_float(const NodeColumns* This, const field::FieldImpl* field, float*& mean, int& size, int& N) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialised atlas_Field"); idx_t idx_t_N; std::vector meanvec; This->mean(field, meanvec, idx_t_N); size = meanvec.size(); mean = new float[size]; for (idx_t j = 0; j < (idx_t)size; ++j) { mean[j] = meanvec[j]; } N = idx_t_N; } void atlas__NodesFunctionSpace__mean_arr_long(const NodeColumns* This, const field::FieldImpl* field, long*& mean, int& size, int& N) { ATLAS_NOTIMPLEMENTED; } void atlas__NodesFunctionSpace__mean_arr_int(const NodeColumns* This, const field::FieldImpl* field, int*& mean, int& size, int& N) { ATLAS_NOTIMPLEMENTED; } void atlas__NodesFunctionSpace__mean_and_stddev_double(const NodeColumns* This, const field::FieldImpl* field, double& mean, double& stddev, int& N) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialised atlas_Field"); idx_t idx_t_N; This->meanAndStandardDeviation(field, mean, stddev, idx_t_N); N = idx_t_N; } void atlas__NodesFunctionSpace__mean_and_stddev_float(const NodeColumns* This, const field::FieldImpl* field, float& mean, float& stddev, int& N) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialised atlas_Field"); idx_t idx_t_N; This->meanAndStandardDeviation(field, mean, stddev, idx_t_N); N = idx_t_N; } void atlas__NodesFunctionSpace__mean_and_stddev_long(const NodeColumns* This, const field::FieldImpl* field, long& mean, long& stddev, int& N) { ATLAS_NOTIMPLEMENTED; } void atlas__NodesFunctionSpace__mean_and_stddev_int(const NodeColumns* This, const field::FieldImpl* field, int& mean, int& stddev, int& N) { ATLAS_NOTIMPLEMENTED; } void atlas__NodesFunctionSpace__mean_and_stddev_arr_double(const NodeColumns* This, const field::FieldImpl* field, double*& mean, double*& stddev, int& size, int& N) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialised atlas_Field"); idx_t idx_t_N; std::vector meanvec; std::vector stddevvec; This->meanAndStandardDeviation(field, meanvec, stddevvec, idx_t_N); size = meanvec.size(); mean = new double[size]; stddev = new double[size]; for (idx_t j = 0; j < (idx_t)size; ++j) { mean[j] = meanvec[j]; stddev[j] = stddevvec[j]; } N = idx_t_N; } void atlas__NodesFunctionSpace__mean_and_stddev_arr_float(const NodeColumns* This, const field::FieldImpl* field, float*& mean, float*& stddev, int& size, int& N) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialised atlas_Field"); idx_t idx_t_N; std::vector meanvec; std::vector stddevvec; This->meanAndStandardDeviation(field, meanvec, stddevvec, idx_t_N); size = meanvec.size(); mean = new float[size]; stddev = new float[size]; for (idx_t j = 0; j < (idx_t)size; ++j) { mean[j] = meanvec[j]; stddev[j] = stddevvec[j]; } N = idx_t_N; } void atlas__NodesFunctionSpace__mean_and_stddev_arr_long(const NodeColumns* This, const field::FieldImpl* field, long*& mean, long*& stddev, int& size, int& N) { ATLAS_NOTIMPLEMENTED; } void atlas__NodesFunctionSpace__mean_and_stddev_arr_int(const NodeColumns* This, const field::FieldImpl* field, int*& mean, int*& stddev, int& size, int& N) { ATLAS_NOTIMPLEMENTED; } void atlas__NodesFunctionSpace__minloclev_double(const NodeColumns* This, const field::FieldImpl* field, double& minimum, long& glb_idx, int& level) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialised atlas_Field"); gidx_t gidx; idx_t lev; This->minimumAndLocation(field, minimum, gidx, lev); glb_idx = gidx; level = lev; } void atlas__NodesFunctionSpace__minloclev_float(const NodeColumns* This, const field::FieldImpl* field, float& minimum, long& glb_idx, int& level) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialised atlas_Field"); gidx_t gidx; idx_t lev; This->minimumAndLocation(field, minimum, gidx, lev); glb_idx = gidx; level = lev; } void atlas__NodesFunctionSpace__minloclev_long(const NodeColumns* This, const field::FieldImpl* field, long& minimum, long& glb_idx, int& level) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialised atlas_Field"); gidx_t gidx; idx_t lev; This->minimumAndLocation(field, minimum, gidx, lev); glb_idx = gidx; level = lev; } void atlas__NodesFunctionSpace__minloclev_int(const NodeColumns* This, const field::FieldImpl* field, int& minimum, long& glb_idx, int& level) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialised atlas_Field"); gidx_t gidx; idx_t lev; This->minimumAndLocation(field, minimum, gidx, lev); glb_idx = gidx; level = lev; } void atlas__NodesFunctionSpace__maxloclev_double(const NodeColumns* This, const field::FieldImpl* field, double& maximum, long& glb_idx, int& level) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialised atlas_Field"); gidx_t gidx; idx_t lev; This->maximumAndLocation(field, maximum, gidx, lev); glb_idx = gidx; level = lev; } void atlas__NodesFunctionSpace__maxloclev_float(const NodeColumns* This, const field::FieldImpl* field, float& maximum, long& glb_idx, int& level) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialised atlas_Field"); gidx_t gidx; idx_t lev; This->maximumAndLocation(field, maximum, gidx, lev); glb_idx = gidx; level = lev; } void atlas__NodesFunctionSpace__maxloclev_long(const NodeColumns* This, const field::FieldImpl* field, long& maximum, long& glb_idx, int& level) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialised atlas_Field"); gidx_t gidx; idx_t lev; This->maximumAndLocation(field, maximum, gidx, lev); glb_idx = gidx; level = lev; } void atlas__NodesFunctionSpace__maxloclev_int(const NodeColumns* This, const field::FieldImpl* field, int& maximum, long& glb_idx, int& level) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialised atlas_Field"); gidx_t gidx; idx_t lev; This->maximumAndLocation(field, maximum, gidx, lev); glb_idx = gidx; level = lev; } void atlas__NodesFunctionSpace__minloclev_arr_double(const NodeColumns* This, const field::FieldImpl* field, double*& minimum, long*& glb_idx, int*& level, int& size) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialised atlas_Field"); std::vector minvec; std::vector gidxvec; std::vector levvec; This->minimumAndLocation(field, minvec, gidxvec, levvec); size = minvec.size(); minimum = new double[size]; glb_idx = new long[size]; level = new int[size]; for (idx_t j = 0; j < (idx_t)size; ++j) { minimum[j] = minvec[j]; glb_idx[j] = gidxvec[j]; level[j] = levvec[j]; } } void atlas__NodesFunctionSpace__minloclev_arr_float(const NodeColumns* This, const field::FieldImpl* field, float*& minimum, long*& glb_idx, int*& level, int& size) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialised atlas_Field"); std::vector minvec; std::vector gidxvec; std::vector levvec; This->minimumAndLocation(field, minvec, gidxvec, levvec); size = minvec.size(); minimum = new float[size]; glb_idx = new long[size]; level = new int[size]; for (idx_t j = 0; j < (idx_t)size; ++j) { minimum[j] = minvec[j]; glb_idx[j] = gidxvec[j]; level[j] = levvec[j]; }; } void atlas__NodesFunctionSpace__minloclev_arr_long(const NodeColumns* This, const field::FieldImpl* field, long*& minimum, long*& glb_idx, int*& level, int& size) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialised atlas_Field"); std::vector minvec; std::vector gidxvec; std::vector levvec; This->minimumAndLocation(field, minvec, gidxvec, levvec); size = minvec.size(); minimum = new long[size]; glb_idx = new long[size]; level = new int[size]; for (idx_t j = 0; j < (idx_t)size; ++j) { minimum[j] = minvec[j]; glb_idx[j] = gidxvec[j]; level[j] = levvec[j]; } } void atlas__NodesFunctionSpace__minloclev_arr_int(const NodeColumns* This, const field::FieldImpl* field, int*& minimum, long*& glb_idx, int*& level, int& size) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialised atlas_Field"); std::vector minvec; std::vector gidxvec; std::vector levvec; This->minimumAndLocation(field, minvec, gidxvec, levvec); size = minvec.size(); minimum = new int[size]; glb_idx = new long[size]; level = new int[size]; for (idx_t j = 0; j < (idx_t)size; ++j) { minimum[j] = minvec[j]; glb_idx[j] = gidxvec[j]; level[j] = levvec[j]; }; } void atlas__NodesFunctionSpace__maxloclev_arr_double(const NodeColumns* This, const field::FieldImpl* field, double*& maximum, long*& glb_idx, int*& level, int& size) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialised atlas_Field"); std::vector maxvec; std::vector gidxvec; std::vector levvec; This->maximumAndLocation(field, maxvec, gidxvec, levvec); size = maxvec.size(); maximum = new double[size]; glb_idx = new long[size]; level = new int[size]; for (idx_t j = 0; j < (idx_t)size; ++j) { maximum[j] = maxvec[j]; glb_idx[j] = gidxvec[j]; level[j] = levvec[j]; } } void atlas__NodesFunctionSpace__maxloclev_arr_float(const NodeColumns* This, const field::FieldImpl* field, float*& maximum, long*& glb_idx, int*& level, int& size) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialised atlas_Field"); std::vector maxvec; std::vector gidxvec; std::vector levvec; This->maximumAndLocation(field, maxvec, gidxvec, levvec); size = maxvec.size(); maximum = new float[size]; glb_idx = new long[size]; level = new int[size]; for (idx_t j = 0; j < (idx_t)size; ++j) { maximum[j] = maxvec[j]; glb_idx[j] = gidxvec[j]; level[j] = levvec[j]; } } void atlas__NodesFunctionSpace__maxloclev_arr_long(const NodeColumns* This, const field::FieldImpl* field, long*& maximum, long*& glb_idx, int*& level, int& size) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialised atlas_Field"); std::vector maxvec; std::vector gidxvec; std::vector levvec; This->maximumAndLocation(field, maxvec, gidxvec, levvec); size = maxvec.size(); maximum = new long[size]; glb_idx = new long[size]; level = new int[size]; for (idx_t j = 0; j < (idx_t)size; ++j) { maximum[j] = maxvec[j]; glb_idx[j] = gidxvec[j]; level[j] = levvec[j]; } } void atlas__NodesFunctionSpace__maxloclev_arr_int(const NodeColumns* This, const field::FieldImpl* field, int*& maximum, long*& glb_idx, int*& level, int& size) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialised atlas_Field"); std::vector maxvec; std::vector gidxvec; std::vector levvec; This->maximumAndLocation(field, maxvec, gidxvec, levvec); size = maxvec.size(); maximum = new int[size]; glb_idx = new long[size]; level = new int[size]; for (idx_t j = 0; j < (idx_t)size; ++j) { maximum[j] = maxvec[j]; glb_idx[j] = gidxvec[j]; level[j] = levvec[j]; } } void atlas__NodesFunctionSpace__sum_per_level(const NodeColumns* This, const field::FieldImpl* field, field::FieldImpl* column, int& N) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialised atlas_Field"); ATLAS_ASSERT(column != nullptr, "Cannot access uninitialised atlas_Field"); idx_t idx_t_N; Field sum(column); This->sumPerLevel(field, sum, idx_t_N); N = idx_t_N; } void atlas__NodesFunctionSpace__oisum_per_level(const NodeColumns* This, const field::FieldImpl* field, field::FieldImpl* column, int& N) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialised atlas_Field"); ATLAS_ASSERT(column != nullptr, "Cannot access uninitialised atlas_Field"); idx_t idx_t_N; Field sum(column); This->orderIndependentSumPerLevel(field, sum, idx_t_N); N = idx_t_N; } void atlas__NodesFunctionSpace__min_per_level(const NodeColumns* This, const field::FieldImpl* field, field::FieldImpl* min) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialised atlas_Field"); ATLAS_ASSERT(min != nullptr, "Cannot access uninitialised min atlas_Field"); Field fmin(min); This->minimumPerLevel(field, fmin); } void atlas__NodesFunctionSpace__max_per_level(const NodeColumns* This, const field::FieldImpl* field, field::FieldImpl* max) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialised atlas_Field"); ATLAS_ASSERT(max != nullptr, "Cannot access uninitialised max atlas_Field"); Field fmax(max); This->maximumPerLevel(field, fmax); } void atlas__NodesFunctionSpace__minloc_per_level(const NodeColumns* This, const field::FieldImpl* field, field::FieldImpl* min, field::FieldImpl* glb_idx) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialised atlas_Field"); ATLAS_ASSERT(min != nullptr, "Cannot access uninitialised min atlas_Field"); ATLAS_ASSERT(glb_idx != nullptr, "Cannot access uninitialised glb_idx atlas_Field"); Field fmin(min); Field fglb_idx(glb_idx); This->minimumAndLocationPerLevel(field, fmin, fglb_idx); } void atlas__NodesFunctionSpace__maxloc_per_level(const NodeColumns* This, const field::FieldImpl* field, field::FieldImpl* max, field::FieldImpl* glb_idx) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialised atlas_Field"); ATLAS_ASSERT(max != nullptr, "Cannot access uninitialised max atlas_Field"); ATLAS_ASSERT(glb_idx != nullptr, "Cannot access uninitialised glb_idx atlas_Field"); Field fmax(max); Field fglb_idx(glb_idx); This->maximumAndLocationPerLevel(field, fmax, fglb_idx); } void atlas__NodesFunctionSpace__mean_per_level(const NodeColumns* This, const field::FieldImpl* field, field::FieldImpl* mean, int& N) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialised atlas_Field"); ATLAS_ASSERT(mean != nullptr, "Cannot access uninitialised mean atlas_Field"); idx_t idx_t_N; Field fmean(mean); This->meanPerLevel(field, fmean, idx_t_N); N = idx_t_N; } void atlas__NodesFunctionSpace__mean_and_stddev_per_level(const NodeColumns* This, const field::FieldImpl* field, field::FieldImpl* mean, field::FieldImpl* stddev, int& N) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_NodeColumns"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialised atlas_Field"); ATLAS_ASSERT(mean != nullptr, "Cannot access uninitialised mean atlas_Field"); ATLAS_ASSERT(stddev); idx_t idx_t_N; Field fmean(mean); Field fstddev(stddev); This->meanAndStandardDeviationPerLevel(field, fmean, fstddev, idx_t_N); N = idx_t_N; } } } // namespace detail } // namespace functionspace } // namespace atlas atlas-0.46.0/src/atlas/functionspace/detail/PointCloudInterface.h0000664000175000017500000000356315160212070025142 0ustar alastairalastair/* * (C) Copyright 200 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include "atlas/functionspace/PointCloud.h" namespace atlas { namespace field { class FieldSetImpl; } } // namespace atlas namespace atlas { namespace grid { namespace detail { namespace grid { class Grid; } // namespace grid } // namespace detail } // namespace grid using GridImpl = grid::detail::grid::Grid; } // namespace atlas namespace atlas { namespace functionspace { // ------------------------------------------------------------------- // C wrapper interfaces to C++ routines extern "C" { const detail::PointCloud* atlas__functionspace__PointCloud__new__lonlat(const field::FieldImpl* lonlat); const detail::PointCloud* atlas__functionspace__PointCloud__new__lonlat_ghost(const field::FieldImpl* lonlat, const field::FieldImpl* ghost); const detail::PointCloud* atlas__functionspace__PointCloud__new__fieldset(const field::FieldSetImpl* fset); const detail::PointCloud* atlas__functionspace__PointCloud__new__grid(const GridImpl* grid); void atlas__functionspace__PointCloud__delete(detail::PointCloud* This); field::FieldImpl* atlas__fs__PointCloud__create_field(const detail::PointCloud* This, const eckit::Configuration* options); idx_t atlas__fs__PointCloud__size(const detail::PointCloud* This); const field::FieldImpl* atlas__fs__PointCloud__lonlat(const detail::PointCloud* This); } } // namespace functionspace } // namespace atlas atlas-0.46.0/src/atlas/functionspace/detail/CellColumnsInterface.h0000664000175000017500000000551415160212070025300 0ustar alastairalastair/* * (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/functionspace/CellColumns.h" #include "atlas/mesh/Mesh.h" namespace atlas { namespace field { class FieldSetImpl; class FieldImpl; } // namespace field } // namespace atlas namespace atlas { namespace functionspace { namespace detail { extern "C" { const CellColumns* atlas__CellsFunctionSpace__new(Mesh::Implementation* mesh, const eckit::Configuration* config); void atlas__CellsFunctionSpace__delete(CellColumns* This); int atlas__CellsFunctionSpace__nb_cells(const CellColumns* This); const Mesh::Implementation* atlas__CellsFunctionSpace__mesh(const CellColumns* This); const mesh::HybridElements* atlas__CellsFunctionSpace__cells(const CellColumns* This); void atlas__CellsFunctionSpace__halo_exchange_fieldset(const CellColumns* This, field::FieldSetImpl* fieldset); void atlas__CellsFunctionSpace__halo_exchange_field(const CellColumns* This, field::FieldImpl* field); const parallel::HaloExchange* atlas__CellsFunctionSpace__get_halo_exchange(const CellColumns* This); void atlas__CellsFunctionSpace__gather_fieldset(const CellColumns* This, const field::FieldSetImpl* local, field::FieldSetImpl* global); void atlas__CellsFunctionSpace__gather_field(const CellColumns* This, const field::FieldImpl* local, field::FieldImpl* global); const parallel::GatherScatter* atlas__CellsFunctionSpace__get_gather(const CellColumns* This); void atlas__CellsFunctionSpace__scatter_fieldset(const CellColumns* This, const field::FieldSetImpl* global, field::FieldSetImpl* local); void atlas__CellsFunctionSpace__scatter_field(const CellColumns* This, const field::FieldImpl* global, field::FieldImpl* local); const parallel::GatherScatter* atlas__CellsFunctionSpace__get_scatter(const CellColumns* This); void atlas__CellsFunctionSpace__checksum_fieldset(const CellColumns* This, const field::FieldSetImpl* fieldset, char*& checksum, int& size, int& allocated); void atlas__CellsFunctionSpace__checksum_field(const CellColumns* This, const field::FieldImpl* field, char*& checksum, int& size, int& allocated); const parallel::Checksum* atlas__CellsFunctionSpace__get_checksum(const CellColumns* This); } } // namespace detail } // namespace functionspace } // namespace atlas atlas-0.46.0/src/atlas/functionspace/detail/BlockStructuredColumns.cc0000664000175000017500000003513215160212070026054 0ustar alastairalastair/* * (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/functionspace/BlockStructuredColumns.h" #include #include #include #include #include #include "eckit/utils/MD5.h" #include "atlas/array/Array.h" #include "atlas/array/MakeView.h" #include "atlas/domain.h" #include "atlas/field/FieldSet.h" #include "atlas/grid/Distribution.h" #include "atlas/grid/Partitioner.h" #include "atlas/grid/StructuredGrid.h" #include "atlas/grid/StructuredPartitionPolygon.h" #include "atlas/library/Library.h" #include "atlas/mesh/Mesh.h" #include "atlas/parallel/Checksum.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/parallel/omp/fill.h" #include "atlas/parallel/omp/omp.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Trace.h" #include "atlas/util/Checksum.h" #include "atlas/util/CoordinateEnums.h" #include "atlas/util/detail/Cache.h" namespace atlas { namespace functionspace { namespace detail { namespace { template void block_copy(const Field sloc, Field loc, const functionspace::detail::BlockStructuredColumns& fs) { if (sloc.variables() and sloc.levels()) { auto loc_v = array::make_view(loc); auto sloc_v = array::make_view(sloc); for (idx_t jblk = 0; jblk < fs.nblks(); ++jblk) { const idx_t blk_size = fs.block_size(jblk); const idx_t blk_begin = fs.block_begin(jblk); for (idx_t jvar = 0; jvar < sloc.shape(2); ++jvar) { for (idx_t jlev = 0; jlev < sloc.shape(1); ++jlev) { for (idx_t jrof = 0; jrof < blk_size; ++jrof) { loc_v(jblk, jvar, jlev, jrof) = sloc_v(blk_begin+jrof, jlev, jvar); } } } } } else if (not sloc.variables() and sloc.levels()) { auto loc_v = array::make_view(loc); auto sloc_v = array::make_view(sloc); for (idx_t jblk = 0; jblk < fs.nblks(); ++jblk) { const idx_t blk_size = fs.block_size(jblk); const idx_t blk_begin = fs.block_begin(jblk); for (idx_t jlev = 0; jlev < sloc.shape(1); ++jlev) { for (idx_t jrof = 0; jrof < blk_size; ++jrof) { loc_v(jblk, jlev, jrof) = sloc_v(blk_begin+jrof, jlev); } } } } else if (sloc.variables() and not sloc.levels()) { auto loc_v = array::make_view(loc); auto sloc_v = array::make_view(sloc); for (idx_t jblk = 0; jblk < fs.nblks(); ++jblk) { const idx_t blk_size = fs.block_size(jblk); const idx_t blk_begin = fs.block_begin(jblk); for (idx_t jvar = 0; jvar < sloc.shape(1); ++jvar) { for (idx_t jrof = 0; jrof < blk_size; ++jrof) { loc_v(jblk, jvar, jrof) = sloc_v(blk_begin+jrof, jvar); } } } } else { auto loc_v = array::make_view(loc); auto sloc_v = array::make_view(sloc); for (idx_t jblk = 0; jblk < fs.nblks(); ++jblk) { const idx_t blk_size = fs.block_size(jblk); const idx_t blk_begin = fs.block_begin(jblk); for (idx_t jrof = 0; jrof < blk_size; ++jrof) { loc_v(jblk, jrof) = sloc_v(blk_begin+jrof); } } } } template void rev_block_copy(const Field loc, Field sloc, const functionspace::detail::BlockStructuredColumns& fs) { if (loc.variables() and loc.levels()) { auto loc_v = array::make_view(loc); auto sloc_v = array::make_view(sloc); for (idx_t jblk = 0; jblk < fs.nblks(); ++jblk) { const idx_t blk_size = fs.block_size(jblk); const idx_t blk_begin = fs.block_begin(jblk); for (idx_t jvar = 0; jvar < sloc.shape(2); ++jvar) { for (idx_t jlev = 0; jlev < sloc.shape(1); ++jlev) { for (idx_t jrof = 0; jrof < blk_size; ++jrof) { sloc_v(blk_begin+jrof, jlev, jvar) = loc_v(jblk, jvar, jlev, jrof); } } } } } else if (not loc.variables() and loc.levels()) { auto loc_v = array::make_view(loc); auto sloc_v = array::make_view(sloc); for (idx_t jblk = 0; jblk < fs.nblks(); ++jblk) { const idx_t blk_size = fs.block_size(jblk); const idx_t blk_begin = fs.block_begin(jblk); for (idx_t jlev = 0; jlev < sloc.shape(1); ++jlev) { for (idx_t jrof = 0; jrof < blk_size; ++jrof) { sloc_v(blk_begin+jrof, jlev) = loc_v(jblk, jlev, jrof); } } } } else if (loc.variables() and not loc.levels()) { auto loc_v = array::make_view(loc); auto sloc_v = array::make_view(sloc); for (idx_t jblk = 0; jblk < fs.nblks(); ++jblk) { const idx_t blk_size = fs.block_size(jblk); const idx_t blk_begin = fs.block_begin(jblk); for (idx_t jvar = 0; jvar < sloc.shape(1); ++jvar) { for (idx_t jrof = 0; jrof < blk_size; ++jrof) { sloc_v(blk_begin+jrof, jvar) = loc_v(jblk, jvar, jrof); } } } } else { auto loc_v = array::make_view(loc); auto sloc_v = array::make_view(sloc); for (idx_t jblk = 0; jblk < fs.nblks(); ++jblk) { const idx_t blk_size = fs.block_size(jblk); const idx_t blk_begin = fs.block_begin(jblk); for (idx_t jrof = 0; jrof < blk_size; ++jrof) { sloc_v(blk_begin+jrof) = loc_v(jblk, jrof); } } } } void transpose_nonblocked_to_blocked(const Field& nonblocked, Field& blocked, const functionspace::detail::BlockStructuredColumns& fs) { auto kind = nonblocked.datatype().kind(); if (kind == array::DataType::kind()) { block_copy(nonblocked, blocked, fs); } else if (kind == array::DataType::kind()) { block_copy(nonblocked, blocked, fs); } else if (kind == array::DataType::kind()) { block_copy(nonblocked, blocked, fs); } else if (kind == array::DataType::kind()) { block_copy(nonblocked, blocked, fs); } else { throw_Exception("datatype not supported", Here()); } } void transpose_blocked_to_nonblocked(const Field& blocked, Field& nonblocked, const functionspace::detail::BlockStructuredColumns& fs) { auto kind = blocked.datatype().kind(); if (kind == array::DataType::kind()) { rev_block_copy(blocked, nonblocked, fs); } else if (kind == array::DataType::kind()) { rev_block_copy(blocked, nonblocked, fs); } else if (kind == array::DataType::kind()) { rev_block_copy(blocked, nonblocked, fs); } else if (kind == array::DataType::kind()) { rev_block_copy(blocked, nonblocked, fs); } else { throw_Exception("datatype not supported", Here()); } } }// namespace array::ArrayShape BlockStructuredColumns::config_shape(const eckit::Configuration& config) const { array::ArrayShape shape; bool global = false; config.get("global", global); if (global) { return structuredcolumns_->config_shape(config); } else { shape.emplace_back(nblks_); idx_t variables(0); config.get("variables", variables); if (variables > 0) { shape.emplace_back(variables); } idx_t levels(structuredcolumns_->levels()); config.get("levels", levels); if (levels > 0) { shape.emplace_back(levels); } shape.emplace_back(nproma_); } return shape; } array::ArraySpec BlockStructuredColumns::config_spec(const eckit::Configuration& config) const { return array::ArraySpec(config_shape(config), structuredcolumns_->config_alignment(config)); } // ---------------------------------------------------------------------------- // Constructor // ---------------------------------------------------------------------------- BlockStructuredColumns::BlockStructuredColumns(const Grid& grid, const eckit::Configuration& config): BlockStructuredColumns::BlockStructuredColumns(grid, grid::Partitioner(), config) {} BlockStructuredColumns::BlockStructuredColumns(const Grid& grid, const grid::Partitioner& p, const eckit::Configuration& config): BlockStructuredColumns(grid, Vertical(config), p, config) {} BlockStructuredColumns::BlockStructuredColumns(const Grid& grid, const grid::Distribution& distribution, const eckit::Configuration& config): BlockStructuredColumns(grid, distribution, Vertical(config), config) {} BlockStructuredColumns::BlockStructuredColumns(const Grid& grid, const grid::Distribution& distribution, const Vertical& vertical, const eckit::Configuration& config): structuredcolumns_(new StructuredColumns(grid, distribution, vertical, config)), structuredcolumns_handle_(structuredcolumns_){ setup(config); } BlockStructuredColumns::BlockStructuredColumns(const Grid& grid, const Vertical& vertical, const eckit::Configuration& config): BlockStructuredColumns(grid, vertical, grid::Partitioner(), config) {} BlockStructuredColumns::BlockStructuredColumns(const Grid& grid, const Vertical& vertical, const grid::Partitioner& p, const eckit::Configuration& config): structuredcolumns_(new StructuredColumns(grid, vertical, p, config)), structuredcolumns_handle_(structuredcolumns_){ setup(config); } // ---------------------------------------------------------------------------- // Create Field // ---------------------------------------------------------------------------- Field BlockStructuredColumns::createField(const eckit::Configuration& options) const { Field field(structuredcolumns_->config_name(options), structuredcolumns_->config_datatype(options), config_spec(options)); structuredcolumns_->set_field_metadata(options, field); field.set_functionspace(this); bool global = false; options.get("global", global); if (not global) { field.set_horizontal_dimension({0,field.rank()-1}); } return field; } Field BlockStructuredColumns::createField(const Field& other, const eckit::Configuration& config) const { return createField(option::name(other.name()) | option::datatype(other.datatype()) | option::levels(other.levels()) |option::variables(other.variables()) | option::type(other.metadata().getString("type", "scalar")) | config); } // ---------------------------------------------------------------------------- // ---------------------------------------------------------------------------- // Scatter FieldSet // ---------------------------------------------------------------------------- void BlockStructuredColumns::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]; auto sloc = structuredcolumns_->createField(glb, util::Config("global",false)); structuredcolumns_->scatter(glb, sloc); loc.metadata() = sloc.metadata(); transpose_nonblocked_to_blocked(sloc, loc, *this); } } // ---------------------------------------------------------------------------- // Scatter Field // ---------------------------------------------------------------------------- void BlockStructuredColumns::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); } // ---------------------------------------------------------------------------- // Gather FieldSet // ---------------------------------------------------------------------------- void BlockStructuredColumns::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]; auto sloc = structuredcolumns_->createField(loc, util::Config("global",false)); transpose_blocked_to_nonblocked(loc, sloc, *this); structuredcolumns_->gather(sloc, glb); glb.metadata() = loc.metadata(); glb.metadata().set("global", false); } } // ---------------------------------------------------------------------------- void BlockStructuredColumns::setup(const eckit::Configuration &config) { nproma_ = 1; idx_t tmp_nproma; if (config.get("nproma", tmp_nproma)) { ATLAS_ASSERT(tmp_nproma > 0); nproma_ = tmp_nproma; } nblks_ = std::floor( structuredcolumns_->size() / nproma_ ); endblk_size_ = nproma_; if (structuredcolumns_->size() % nproma_ > 0) { endblk_size_ = structuredcolumns_->size() - nblks_ * nproma_; nblks_++; } } // ---------------------------------------------------------------------------- // Gather Field // ---------------------------------------------------------------------------- void BlockStructuredColumns::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); } // ---------------------------------------------------------------------------- // Checksum Field // ---------------------------------------------------------------------------- std::string BlockStructuredColumns::checksum(const Field&) const { ATLAS_NOTIMPLEMENTED; } std::string BlockStructuredColumns::checksum(const FieldSet&) const { ATLAS_NOTIMPLEMENTED; } // ---------------------------------------------------------------------------- } // namespace detail } // namespace functionspace } // namespace atlas atlas-0.46.0/src/atlas/functionspace/detail/StructuredColumns.h0000664000175000017500000002456515160212070024753 0ustar alastairalastair/* * (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 #include "atlas/array/DataType.h" #include "atlas/field/Field.h" #include "atlas/functionspace/detail/FunctionSpaceImpl.h" #include "atlas/grid/StructuredGrid.h" #include "atlas/grid/Vertical.h" #include "atlas/library/config.h" #include "atlas/option.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/parallel/omp/omp.h" #include "atlas/runtime/Exception.h" #include "atlas/util/Config.h" #include "atlas/util/ObjectHandle.h" #include "atlas/util/Point.h" #include "atlas/util/Polygon.h" #include "atlas/util/vector.h" namespace atlas { namespace parallel { class GatherScatter; class HaloExchange; class Checksum; } // namespace parallel } // namespace atlas namespace atlas { class Field; class FieldSet; class Grid; class StructuredGrid; } // namespace atlas namespace atlas { namespace grid { class Distribution; class Partitioner; } // namespace grid } // namespace atlas namespace atlas { namespace functionspace { namespace detail { class StructuredColumnsHaloExchangeCache; class StructuredColumnsGatherScatterCache; class StructuredColumnsChecksumCache; // ------------------------------------------------------------------- class StructuredColumns : public FunctionSpaceImpl { public: StructuredColumns(const Grid&, const eckit::Configuration& = util::NoConfig()); StructuredColumns(const Grid&, const grid::Partitioner&, const eckit::Configuration& = util::NoConfig()); StructuredColumns(const Grid&, const grid::Distribution&, const eckit::Configuration& = util::NoConfig()); StructuredColumns(const Grid&, const grid::Distribution&, const Vertical&, const eckit::Configuration& = util::NoConfig()); StructuredColumns(const Grid&, const Vertical&, const eckit::Configuration& = util::NoConfig()); StructuredColumns(const Grid&, const Vertical&, const grid::Partitioner&, const eckit::Configuration& = util::NoConfig()); ~StructuredColumns() override; static std::string static_type() { return "StructuredColumns"; } std::string type() const override { return static_type(); } std::string distribution() const override; /// @brief Create a Structured field Field createField(const eckit::Configuration&) const override; Field createField(const Field&, const eckit::Configuration&) const override; void gather(const FieldSet&, FieldSet&) const override; void gather(const Field&, Field&) const override; void scatter(const FieldSet&, FieldSet&) const override; void scatter(const Field&, Field&) const override; void haloExchange(const FieldSet&, bool on_device = false) const override; void haloExchange(const Field&, bool on_device = false) const override; void adjointHaloExchange(const FieldSet&, bool on_device = false) const override; void adjointHaloExchange(const Field&, bool on_device = false) const override; idx_t sizeOwned() const { return size_owned_; } idx_t sizeHalo() const { return size_halo_; } idx_t size() const override { return size_halo_; } idx_t levels() const { return nb_levels_; } idx_t halo() const { return halo_; } std::string checksum(const FieldSet&) const; std::string checksum(const Field&) const; const Vertical& vertical() const { return vertical_; } const StructuredGrid& grid() const override; const Projection& projection() const override { return grid().projection(); } idx_t i_begin(idx_t j) const { return i_begin_[j]; } idx_t i_end(idx_t j) const { return i_end_[j]; } idx_t i_begin_halo(idx_t j) const { return i_begin_halo_[j]; } idx_t i_end_halo(idx_t j) const { return i_end_halo_[j]; } idx_t j_begin() const { return j_begin_; } idx_t j_end() const { return j_end_; } idx_t j_begin_halo() const { return j_begin_halo_; } idx_t j_end_halo() const { return j_end_halo_; } idx_t k_begin() const { return vertical_.k_begin(); } idx_t k_end() const { return vertical_.k_end(); } idx_t index(idx_t i, idx_t j) const { check_bounds(i, j); return ij2gp_(i, j); } Field lonlat() const override { return field_xy_; } Field xy() const { return field_xy_; } Field z() const { return field_z_; } Field partition() const override { return field_partition_; } Field global_index() const override { return field_global_index_; } Field remote_index() const override { if (not field_remote_index_) { create_remote_index(); } return field_remote_index_; } Field index_i() const { return field_index_i_; } Field index_j() const { return field_index_j_; } Field ghost() const override { return field_ghost_; } void compute_xy(idx_t i, idx_t j, PointXY& xy) const; PointXY compute_xy(idx_t i, idx_t j) const { PointXY xy; compute_xy(i, j, xy); return xy; } virtual size_t footprint() const override; const util::PartitionPolygon& polygon(idx_t halo = 0) const override; const util::PartitionPolygons& polygons() const override; idx_t part() const override { return part_; } idx_t nb_parts() const override { return nb_partitions_; } std::string mpi_comm() const override { return mpi_comm_; } 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; array::ArrayAlignment config_alignment(const eckit::Configuration&) const; array::ArraySpec config_spec(const eckit::Configuration&) const; void set_field_metadata(const eckit::Configuration&, Field&) const; void check_bounds(idx_t i, idx_t j) const { #if ATLAS_ARRAYVIEW_BOUNDS_CHECKING if (j < j_begin_halo() || j >= j_end_halo()) { throw_outofbounds(i, j); } if (i < i_begin_halo(j) || i >= i_end_halo(j)) { throw_outofbounds(i, j); } #endif } [[noreturn]] void throw_outofbounds(idx_t i, idx_t j) const; const parallel::GatherScatter& gather() const override; const parallel::GatherScatter& scatter() const override; const parallel::Checksum& checksum() const; const parallel::HaloExchange& halo_exchange() const; void create_remote_index() const; private: // data std::string distribution_; const Vertical vertical_; idx_t nb_levels_; idx_t size_owned_; idx_t size_halo_; idx_t halo_; friend class StructuredColumnsHaloExchangeCache; friend class StructuredColumnsGatherScatterCache; friend class StructuredColumnsChecksumCache; bool periodic_points_{false}; const StructuredGrid* grid_; mutable util::ObjectHandle gather_scatter_; mutable util::ObjectHandle checksum_; mutable util::ObjectHandle halo_exchange_; mutable std::vector> polygons_; mutable util::PartitionPolygons all_polygons_; Field field_xy_; Field field_z_; Field field_partition_; Field field_global_index_; mutable Field field_remote_index_; Field field_index_i_; Field field_index_j_; Field field_ghost_; class Map2to1 { public: Map2to1() { resize({1, 0}, {1, 0}); } Map2to1(std::array i_range, std::array j_range) { resize(i_range, j_range); } void resize(std::array i_range, std::array j_range); atlas::vector data_; idx_t i_min_; idx_t i_max_; idx_t j_min_; idx_t j_max_; idx_t j_stride_; idx_t operator()(idx_t i, idx_t j) const { return data_[(i - i_min_) + (j - j_min_) * j_stride_] - 1; } void set(idx_t i, idx_t j, idx_t n) { data_[(i - i_min_) + (j - j_min_) * j_stride_] = n + 1; } static idx_t missing() { return std::numeric_limits::max() - 1; } size_t footprint() const; private: void print(std::ostream&) const; friend std::ostream& operator<<(std::ostream& s, const Map2to1& p) { p.print(s); return s; } }; class IndexRange { public: IndexRange() { resize(1, 0); } IndexRange(idx_t min, idx_t max) { resize(min, max); } std::vector data_; idx_t min_; idx_t max_; idx_t operator()(idx_t i) const { return data_[i - min_]; } idx_t& operator()(idx_t i) { return data_[i - min_]; } idx_t operator[](idx_t i) const { return data_[i - min_]; } idx_t& operator[](idx_t i) { return data_[i - min_]; } idx_t missing() const { return std::numeric_limits::max() - 1; } idx_t size() const { return idx_t(data_.size()); } void resize(idx_t min, idx_t max) { min_ = min; max_ = max; data_.resize(max_ - min_ + 1, missing() + 1); } private: void print(std::ostream&) const; friend std::ostream& operator<<(std::ostream& s, const IndexRange& p) { p.print(s); return s; } }; idx_t j_begin_; idx_t j_end_; std::vector i_begin_; std::vector i_end_; idx_t j_begin_halo_; idx_t j_end_halo_; IndexRange i_begin_halo_; IndexRange i_end_halo_; idx_t north_pole_included_; idx_t south_pole_included_; idx_t ny_; idx_t part_; idx_t nb_partitions_; std::string mpi_comm_; friend struct StructuredColumnsFortranAccess; friend struct BlockStructuredColumnsFortranAccess; Map2to1 ij2gp_; friend class BlockStructuredColumns; void setup(const grid::Distribution& distribution, const eckit::Configuration& config); friend class BlockStructuredColumns; }; // ------------------------------------------------------------------- } // namespace detail } // namespace functionspace } // namespace atlas atlas-0.46.0/src/atlas/functionspace/detail/NodeColumnsInterface.h0000664000175000017500000004374115160212070025312 0ustar alastairalastair/* * (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/functionspace/NodeColumns.h" #include "atlas/mesh/Mesh.h" namespace atlas { namespace field { class FieldSetImpl; class FieldImpl; } // namespace field } // namespace atlas namespace atlas { namespace functionspace { namespace detail { extern "C" { const NodeColumns* atlas__NodesFunctionSpace__new(Mesh::Implementation* mesh, const eckit::Configuration* config); void atlas__NodesFunctionSpace__delete(NodeColumns* This); int atlas__NodesFunctionSpace__nb_nodes(const NodeColumns* This); const Mesh::Implementation* atlas__NodesFunctionSpace__mesh(const NodeColumns* This); mesh::Nodes* atlas__NodesFunctionSpace__nodes(const NodeColumns* This); void atlas__NodesFunctionSpace__halo_exchange_fieldset(const NodeColumns* This, field::FieldSetImpl* fieldset); void atlas__NodesFunctionSpace__halo_exchange_field(const NodeColumns* This, field::FieldImpl* field); const parallel::HaloExchange* atlas__NodesFunctionSpace__get_halo_exchange(const NodeColumns* This); void atlas__NodesFunctionSpace__gather_fieldset(const NodeColumns* This, const field::FieldSetImpl* local, field::FieldSetImpl* global); void atlas__NodesFunctionSpace__gather_field(const NodeColumns* This, const field::FieldImpl* local, field::FieldImpl* global); const parallel::GatherScatter* atlas__NodesFunctionSpace__get_gather(const NodeColumns* This); void atlas__NodesFunctionSpace__scatter_fieldset(const NodeColumns* This, const field::FieldSetImpl* global, field::FieldSetImpl* local); void atlas__NodesFunctionSpace__scatter_field(const NodeColumns* This, const field::FieldImpl* global, field::FieldImpl* local); const parallel::GatherScatter* atlas__NodesFunctionSpace__get_scatter(const NodeColumns* This); void atlas__NodesFunctionSpace__checksum_fieldset(const NodeColumns* This, const field::FieldSetImpl* fieldset, char*& checksum, int& size, int& allocated); void atlas__NodesFunctionSpace__checksum_field(const NodeColumns* This, const field::FieldImpl* field, char*& checksum, int& size, int& allocated); const parallel::Checksum* atlas__NodesFunctionSpace__get_checksum(const NodeColumns* This); void atlas__NodesFunctionSpace__sum_double(const NodeColumns* This, const field::FieldImpl* field, double& sum, int& N); void atlas__NodesFunctionSpace__sum_float(const NodeColumns* This, const field::FieldImpl* field, float& sum, int& N); void atlas__NodesFunctionSpace__sum_int(const NodeColumns* This, const field::FieldImpl* field, int& sum, int& N); void atlas__NodesFunctionSpace__sum_long(const NodeColumns* This, const field::FieldImpl* field, long& sum, int& N); void atlas__NodesFunctionSpace__sum_arr_double(const NodeColumns* This, const field::FieldImpl* field, double*& sum, int& size, int& N); void atlas__NodesFunctionSpace__sum_arr_float(const NodeColumns* This, const field::FieldImpl* field, float*& sum, int& size, int& N); void atlas__NodesFunctionSpace__sum_arr_int(const NodeColumns* This, const field::FieldImpl* field, int*& sum, int& size, int& N); void atlas__NodesFunctionSpace__sum_arr_long(const NodeColumns* This, const field::FieldImpl* field, long*& sum, int& size, int& N); void atlas__NodesFunctionSpace__oisum_double(const NodeColumns* This, const field::FieldImpl* field, double& sum, int& N); void atlas__NodesFunctionSpace__oisum_float(const NodeColumns* This, const field::FieldImpl* field, float& sum, int& N); void atlas__NodesFunctionSpace__oisum_int(const NodeColumns* This, const field::FieldImpl* field, int& sum, int& N); void atlas__NodesFunctionSpace__oisum_long(const NodeColumns* This, const field::FieldImpl* field, long& sum, int& N); void atlas__NodesFunctionSpace__oisum_arr_double(const NodeColumns* This, const field::FieldImpl* field, double*& sum, int& size, int& N); void atlas__NodesFunctionSpace__oisum_arr_float(const NodeColumns* This, const field::FieldImpl* field, float*& sum, int& size, int& N); void atlas__NodesFunctionSpace__oisum_arr_int(const NodeColumns* This, const field::FieldImpl* field, int*& sum, int& size, int& N); void atlas__NodesFunctionSpace__oisum_arr_long(const NodeColumns* This, const field::FieldImpl* field, long*& sum, int& size, int& N); void atlas__NodesFunctionSpace__min_double(const NodeColumns* This, const field::FieldImpl* field, double& minimum); void atlas__NodesFunctionSpace__min_float(const NodeColumns* This, const field::FieldImpl* field, float& minimum); void atlas__NodesFunctionSpace__min_int(const NodeColumns* This, const field::FieldImpl* field, int& minimum); void atlas__NodesFunctionSpace__min_long(const NodeColumns* This, const field::FieldImpl* field, long& minimum); void atlas__NodesFunctionSpace__max_double(const NodeColumns* This, const field::FieldImpl* field, double& maximum); void atlas__NodesFunctionSpace__max_float(const NodeColumns* This, const field::FieldImpl* field, float& maximum); void atlas__NodesFunctionSpace__max_int(const NodeColumns* This, const field::FieldImpl* field, int& maximum); void atlas__NodesFunctionSpace__max_long(const NodeColumns* This, const field::FieldImpl* field, long& maximum); void atlas__NodesFunctionSpace__min_arr_double(const NodeColumns* This, const field::FieldImpl* field, double*& minimum, int& size); void atlas__NodesFunctionSpace__min_arr_float(const NodeColumns* This, const field::FieldImpl* field, float*& minimum, int& size); void atlas__NodesFunctionSpace__min_arr_int(const NodeColumns* This, const field::FieldImpl* field, int*& minimum, int& size); void atlas__NodesFunctionSpace__min_arr_long(const NodeColumns* This, const field::FieldImpl* field, long*& minimum, int& size); void atlas__NodesFunctionSpace__max_arr_double(const NodeColumns* This, const field::FieldImpl* field, double*& maximum, int& size); void atlas__NodesFunctionSpace__max_arr_float(const NodeColumns* This, const field::FieldImpl* field, float*& maximum, int& size); void atlas__NodesFunctionSpace__max_arr_int(const NodeColumns* This, const field::FieldImpl* field, int*& maximum, int& size); void atlas__NodesFunctionSpace__max_arr_long(const NodeColumns* This, const field::FieldImpl* field, long*& maximum, int& size); void atlas__NodesFunctionSpace__minloc_double(const NodeColumns* This, const field::FieldImpl* field, double& minimum, long& glb_idx); void atlas__NodesFunctionSpace__minloc_float(const NodeColumns* This, const field::FieldImpl* field, float& minimum, long& glb_idx); void atlas__NodesFunctionSpace__minloc_int(const NodeColumns* This, const field::FieldImpl* field, int& minimum, long& glb_idx); void atlas__NodesFunctionSpace__minloc_long(const NodeColumns* This, const field::FieldImpl* field, long& minimum, long& glb_idx); void atlas__NodesFunctionSpace__maxloc_double(const NodeColumns* This, const field::FieldImpl* field, double& maximum, long& glb_idx); void atlas__NodesFunctionSpace__maxloc_float(const NodeColumns* This, const field::FieldImpl* field, float& maximum, long& glb_idx); void atlas__NodesFunctionSpace__maxloc_int(const NodeColumns* This, const field::FieldImpl* field, int& maximum, long& glb_idx); void atlas__NodesFunctionSpace__maxloc_long(const NodeColumns* This, const field::FieldImpl* field, long& maximum, long& glb_idx); void atlas__NodesFunctionSpace__minloc_arr_double(const NodeColumns* This, const field::FieldImpl* field, double*& minimum, long*& glb_idx, int& size); void atlas__NodesFunctionSpace__minloc_arr_float(const NodeColumns* This, const field::FieldImpl* field, float*& minimum, long*& glb_idx, int& size); void atlas__NodesFunctionSpace__minloc_arr_int(const NodeColumns* This, const field::FieldImpl* field, int*& minimum, long*& glb_idx, int& size); void atlas__NodesFunctionSpace__minloc_arr_long(const NodeColumns* This, const field::FieldImpl* field, long*& minimum, long*& glb_idx, int& size); void atlas__NodesFunctionSpace__maxloc_arr_double(const NodeColumns* This, const field::FieldImpl* field, double*& maximum, long*& glb_idx, int& size); void atlas__NodesFunctionSpace__maxloc_arr_float(const NodeColumns* This, const field::FieldImpl* field, float*& maximum, long*& glb_idx, int& size); void atlas__NodesFunctionSpace__maxloc_arr_int(const NodeColumns* This, const field::FieldImpl* field, int*& maximum, long*& glb_idx, int& size); void atlas__NodesFunctionSpace__maxloc_arr_long(const NodeColumns* This, const field::FieldImpl* field, long*& maximum, long*& glb_idx, int& size); void atlas__NodesFunctionSpace__mean_double(const NodeColumns* This, const field::FieldImpl* field, double& mean, int& N); void atlas__NodesFunctionSpace__mean_float(const NodeColumns* This, const field::FieldImpl* field, float& mean, int& N); void atlas__NodesFunctionSpace__mean_int(const NodeColumns* This, const field::FieldImpl* field, int& mean, int& N); void atlas__NodesFunctionSpace__mean_long(const NodeColumns* This, const field::FieldImpl* field, long& mean, int& N); void atlas__NodesFunctionSpace__mean_arr_double(const NodeColumns* This, const field::FieldImpl* field, double*& mean, int& size, int& N); void atlas__NodesFunctionSpace__mean_arr_float(const NodeColumns* This, const field::FieldImpl* field, float*& mean, int& size, int& N); void atlas__NodesFunctionSpace__mean_arr_int(const NodeColumns* This, const field::FieldImpl* field, int*& mean, int& size, int& N); void atlas__NodesFunctionSpace__mean_arr_long(const NodeColumns* This, const field::FieldImpl* field, long*& mean, int& size, int& N); void atlas__NodesFunctionSpace__mean_and_stddev_double(const NodeColumns* This, const field::FieldImpl* field, double& mean, double& stddev, int& N); void atlas__NodesFunctionSpace__mean_and_stddev_float(const NodeColumns* This, const field::FieldImpl* field, float& mean, float& stddev, int& N); void atlas__NodesFunctionSpace__mean_and_stddev_int(const NodeColumns* This, const field::FieldImpl* field, int& mean, int& stddev, int& N); void atlas__NodesFunctionSpace__mean_and_stddev_long(const NodeColumns* This, const field::FieldImpl* field, long& mean, long& stddev, int& N); void atlas__NodesFunctionSpace__mean_and_stddev_arr_double(const NodeColumns* This, const field::FieldImpl* field, double*& mean, double*& stddev, int& size, int& N); void atlas__NodesFunctionSpace__mean_and_stddev_arr_float(const NodeColumns* This, const field::FieldImpl* field, float*& mean, float*& stddev, int& size, int& N); void atlas__NodesFunctionSpace__mean_and_stddev_arr_int(const NodeColumns* This, const field::FieldImpl* field, int*& mean, int*& stddev, int& size, int& N); void atlas__NodesFunctionSpace__mean_and_stddev_arr_long(const NodeColumns* This, const field::FieldImpl* field, long*& mean, long*& stddev, int& size, int& N); void atlas__NodesFunctionSpace__minloclev_double(const NodeColumns* This, const field::FieldImpl* field, double& minimum, long& glb_idx, int& level); void atlas__NodesFunctionSpace__minloclev_float(const NodeColumns* This, const field::FieldImpl* field, float& minimum, long& glb_idx, int& level); void atlas__NodesFunctionSpace__minloclev_int(const NodeColumns* This, const field::FieldImpl* field, int& minimum, long& glb_idx, int& level); void atlas__NodesFunctionSpace__minloclev_long(const NodeColumns* This, const field::FieldImpl* field, long& minimum, long& glb_idx, int& level); void atlas__NodesFunctionSpace__maxloclev_double(const NodeColumns* This, const field::FieldImpl* field, double& maximum, long& glb_idx, int& level); void atlas__NodesFunctionSpace__maxloclev_float(const NodeColumns* This, const field::FieldImpl* field, float& maximum, long& glb_idx, int& level); void atlas__NodesFunctionSpace__maxloclev_int(const NodeColumns* This, const field::FieldImpl* field, int& maximum, long& glb_idx, int& level); void atlas__NodesFunctionSpace__maxloclev_long(const NodeColumns* This, const field::FieldImpl* field, long& maximum, long& glb_idx, int& level); void atlas__NodesFunctionSpace__minloclev_arr_double(const NodeColumns* This, const field::FieldImpl* field, double*& minimum, long*& glb_idx, int*& level, int& size); void atlas__NodesFunctionSpace__minloclev_arr_float(const NodeColumns* This, const field::FieldImpl* field, float*& minimum, long*& glb_idx, int*& level, int& size); void atlas__NodesFunctionSpace__minloclev_arr_int(const NodeColumns* This, const field::FieldImpl* field, int*& minimum, long*& glb_idx, int*& level, int& size); void atlas__NodesFunctionSpace__minloclev_arr_long(const NodeColumns* This, const field::FieldImpl* field, long*& minimum, long*& glb_idx, int*& level, int& size); void atlas__NodesFunctionSpace__maxloclev_arr_double(const NodeColumns* This, const field::FieldImpl* field, double*& maximum, long*& glb_idx, int*& level, int& size); void atlas__NodesFunctionSpace__maxloclev_arr_float(const NodeColumns* This, const field::FieldImpl* field, float*& maximum, long*& glb_idx, int*& level, int& size); void atlas__NodesFunctionSpace__maxloclev_arr_int(const NodeColumns* This, const field::FieldImpl* field, int*& maximum, long*& glb_idx, int*& level, int& size); void atlas__NodesFunctionSpace__maxloclev_arr_long(const NodeColumns* This, const field::FieldImpl* field, long*& maximum, long*& glb_idx, int*& level, int& size); void atlas__NodesFunctionSpace__sum_per_level(const NodeColumns* This, const field::FieldImpl* field, field::FieldImpl* sum, int& N); void atlas__NodesFunctionSpace__oisum_per_level(const NodeColumns* This, const field::FieldImpl* field, field::FieldImpl* sum, int& N); void atlas__NodesFunctionSpace__min_per_level(const NodeColumns* This, const field::FieldImpl* field, field::FieldImpl* min); void atlas__NodesFunctionSpace__max_per_level(const NodeColumns* This, const field::FieldImpl* field, field::FieldImpl* max); void atlas__NodesFunctionSpace__minloc_per_level(const NodeColumns* This, const field::FieldImpl* field, field::FieldImpl* min, field::FieldImpl* glb_idx); void atlas__NodesFunctionSpace__maxloc_per_level(const NodeColumns* This, const field::FieldImpl* field, field::FieldImpl* max, field::FieldImpl* glb_idx); void atlas__NodesFunctionSpace__mean_per_level(const NodeColumns* This, const field::FieldImpl* field, field::FieldImpl* mean, int& N); void atlas__NodesFunctionSpace__mean_and_stddev_per_level(const NodeColumns* This, const field::FieldImpl* field, field::FieldImpl* mean, field::FieldImpl* stddev, int& N); } } // namespace detail } // namespace functionspace } // namespace atlas atlas-0.46.0/src/atlas/functionspace/detail/CubedSphereStructure.cc0000664000175000017500000001175115160212070025507 0ustar alastairalastair/* * (C) Crown Copyright 2021 Met Office * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. */ #include #include "atlas/functionspace/detail/CubedSphereStructure.h" #include "atlas/runtime/Log.h" #include "atlas/runtime/Trace.h" using namespace atlas::meshgenerator::detail::cubedsphere; namespace atlas { namespace functionspace { namespace detail { namespace { #if ATLAS_BUILD_TYPE_DEBUG constexpr bool checkBounds = true; #else constexpr bool checkBounds = false; #endif } // namespace CubedSphereStructure::BoundingBox::BoundingBox() { iBegin = std::numeric_limits::max(); iEnd = std::numeric_limits::min(); jBegin = std::numeric_limits::max(); jEnd = std::numeric_limits::min(); } CubedSphereStructure::CubedSphereStructure(const Field& tij, const Field& ghost, idx_t size): tij_(tij), ghost_(ghost), nElems_(size) { ATLAS_TRACE(); Log::debug() << "CubedSphereStructure bounds checking is set to " + std::to_string(checkBounds) << std::endl; // Make array views. const auto tijView_ = array::make_view(tij_); const auto ghostView_ = array::make_view(ghost_); // loop over tij and find min and max ij bounds. for (idx_t index = 0; index < nElems_; ++index) { const size_t t = static_cast(tijView_(index, Coordinates::T)); const idx_t i = tijView_(index, Coordinates::I); const idx_t j = tijView_(index, Coordinates::J); ijBounds_[t].iBegin = std::min(i, ijBounds_[t].iBegin); ijBounds_[t].jBegin = std::min(j, ijBounds_[t].jBegin); ijBounds_[t].iEnd = std::max(i + 1, ijBounds_[t].iEnd); ijBounds_[t].jEnd = std::max(j + 1, ijBounds_[t].jEnd); // Keep track of highest non-ghost index. if (!ghostView_(index)) { nOwnedElems_ = index + 1; } } // Set tijToIdx vectors for (idx_t t = 0; t < 6; ++t) { // Set data array. size_t vecSize = 0; if( j_end(t) >= j_begin(t) && i_end(t) >= i_begin(t) ) { vecSize = static_cast((j_end(t) - j_begin(t)) * (i_end(t) - i_begin(t))); } tijToIdx_.emplace_back(vecSize, invalid_index()); } // loop over ijt_ and set ijtToIdx for (idx_t index = 0; index < nElems_; ++index) { const idx_t t = tijView_(index, Coordinates::T); const idx_t i = tijView_(index, Coordinates::I); const idx_t j = tijView_(index, Coordinates::J); tijToIdx_[static_cast(t)][vecIndex(t, i, j)] = index; } } idx_t CubedSphereStructure::size() const { return nElems_; } idx_t CubedSphereStructure::sizeOwned() const { return nOwnedElems_; } idx_t CubedSphereStructure::i_begin(idx_t t) const { if (checkBounds) { tBoundsCheck(t); } return ijBounds_[static_cast(t)].iBegin; } idx_t CubedSphereStructure::i_end(idx_t t) const { if (checkBounds) { tBoundsCheck(t); } return ijBounds_[static_cast(t)].iEnd; } idx_t CubedSphereStructure::j_begin(idx_t t) const { if (checkBounds) { tBoundsCheck(t); } return ijBounds_[static_cast(t)].jBegin; } idx_t CubedSphereStructure::j_end(idx_t t) const { if (checkBounds) { tBoundsCheck(t); } return ijBounds_[static_cast(t)].jEnd; } idx_t CubedSphereStructure::index(idx_t t, idx_t i, idx_t j) const { if (checkBounds) { iBoundsCheck(i, t); jBoundsCheck(j, t); } return tijToIdx_[static_cast(t)][vecIndex(t, i, j)]; } bool CubedSphereStructure::is_valid_index(idx_t t, idx_t i, idx_t j) const { // Check if t is in range. if (t < 0 || t > 5) { return false; } // Check if i and j are in range in index method. if (i < i_begin(t) || i >= i_end(t) || j < j_begin(t) || j >= j_end(t)) { return false; } // Check if (t, i, j) is a valid index. if (index(t, i, j) == invalid_index()) { return false; } return true; } Field CubedSphereStructure::tij() const { return tij_; } Field CubedSphereStructure::ghost() const { return ghost_; } void CubedSphereStructure::tBoundsCheck(idx_t t) const { if (t < 0 || t > 5) { throw_OutOfRange("t", t, 6); } } void CubedSphereStructure::jBoundsCheck(idx_t j, idx_t t) const { const idx_t jSize = j_end(t) - j_begin(t); j -= j_begin(t); if (j < 0 || j >= jSize) { throw_OutOfRange("j - jMin", j, jSize); } } void CubedSphereStructure::iBoundsCheck(idx_t i, idx_t t) const { const idx_t iSize = i_end(t) - i_begin(t); i -= i_begin(t); if (i < 0 || i >= iSize) { throw_OutOfRange("i - iMin", i, iSize); } } size_t CubedSphereStructure::vecIndex(idx_t t, idx_t i, idx_t j) const { return static_cast((j - j_begin(t)) * (i_end(t) - i_begin(t)) + i - i_begin(t)); } } // namespace detail } // namespace functionspace } // namespace atlas atlas-0.46.0/src/atlas/functionspace/detail/SpectralInterface.h0000664000175000017500000000525015160212070024632 0ustar alastairalastair/* * (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/functionspace/Spectral.h" // ------------------------------------------------------------------- // C wrapper interfaces to C++ routines namespace atlas { namespace field { class FieldSetImpl; class FieldImpl; } // namespace field namespace trans { class TransImpl; } namespace functionspace { extern "C" { const detail::Spectral* atlas__SpectralFunctionSpace__new__config(const eckit::Configuration* config); void atlas__SpectralFunctionSpace__delete(detail::Spectral* This); field::FieldImpl* atlas__fs__Spectral__create_field(const detail::Spectral* This, const eckit::Configuration* options); void atlas__SpectralFunctionSpace__gather(const detail::Spectral* This, const field::FieldImpl* local, field::FieldImpl* global); void atlas__SpectralFunctionSpace__gather_fieldset(const detail::Spectral* This, const field::FieldSetImpl* local, field::FieldSetImpl* global); void atlas__SpectralFunctionSpace__scatter(const detail::Spectral* This, const field::FieldImpl* global, field::FieldImpl* local); void atlas__SpectralFunctionSpace__scatter_fieldset(const detail::Spectral* This, const field::FieldSetImpl* global, field::FieldSetImpl* local); void atlas__SpectralFunctionSpace__norm(const detail::Spectral* This, const field::FieldImpl* field, double norm[], int rank); void atlas__SpectralFunctionSpace__nspec2(const detail::Spectral* This, int& nspec2); void atlas__SpectralFunctionSpace__nspec2g(const detail::Spectral* This, int& nspec2g); void atlas__SpectralFunctionSpace__truncation(const detail::Spectral* This, int& truncation); void atlas__SpectralFunctionSpace__nump(const detail::Spectral* This, int& nump); void atlas__SpectralFunctionSpace__nmyms(const detail::Spectral* This, const int*& nmyms, int& size); void atlas__SpectralFunctionSpace__nasm0(const detail::Spectral* This, const int*& nasm0, int& size); void atlas__SpectralFunctionSpace__nvalue(const detail::Spectral* This, const int*& nvalue, int& size); void atlas__SpectralFunctionSpace__levels(const detail::Spectral* This, int& levels); } } // namespace functionspace } // namespace atlas atlas-0.46.0/src/atlas/functionspace/detail/StructuredColumnsInterface.cc0000664000175000017500000002335315160212070026724 0ustar alastairalastair/* * (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 "StructuredColumnsInterface.h" #include "atlas/field/FieldSet.h" #include "atlas/field/detail/FieldImpl.h" #include "atlas/grid/Distribution.h" #include "atlas/grid/Grid.h" #include "atlas/grid/Partitioner.h" #include "atlas/grid/detail/distribution/DistributionImpl.h" #include "atlas/runtime/Exception.h" namespace atlas { namespace functionspace { // ---------------------------------------------------------------------------- // Fortran interfaces // ---------------------------------------------------------------------------- namespace detail { struct StructuredColumnsFortranAccess { detail::StructuredColumns::Map2to1& ij2gp_; StructuredColumnsFortranAccess(const detail::StructuredColumns& fs): ij2gp_(const_cast(fs).ij2gp_) {} }; } // namespace detail extern "C" { const detail::StructuredColumns* atlas__functionspace__StructuredColumns__new__grid( const Grid::Implementation* grid, const eckit::Configuration* config) { return new detail::StructuredColumns(Grid(grid), grid::Partitioner(), *config); } const detail::StructuredColumns* atlas__functionspace__StructuredColumns__new__grid_dist( const Grid::Implementation* grid, const grid::DistributionImpl* dist, const eckit::Configuration* config) { return new detail::StructuredColumns(Grid(grid), grid::Distribution(dist), *config); } const detail::StructuredColumns* atlas__functionspace__StructuredColumns__new__grid_dist_vert( const Grid::Implementation* grid, const grid::DistributionImpl* dist, const Vertical* vert, const eckit::Configuration* config) { return new detail::StructuredColumns(Grid(grid), grid::Distribution(dist), *vert, *config); } const detail::StructuredColumns* atlas__functionspace__StructuredColumns__new__grid_dist_config( const Grid::Implementation* grid, const grid::DistributionImpl* dist, const eckit::Configuration* config) { return new detail::StructuredColumns(Grid(grid), grid::Distribution(dist), *config); } const detail::StructuredColumns* atlas__functionspace__StructuredColumns__new__grid_part( const Grid::Implementation* grid, const PartitionerImpl* partitioner, const eckit::Configuration* config) { return new detail::StructuredColumns(Grid(grid), grid::Partitioner(partitioner), *config); } const detail::StructuredColumns* atlas__functionspace__StructuredColumns__new__grid_part_vert( const Grid::Implementation* grid, const PartitionerImpl* partitioner, const Vertical* vert, const eckit::Configuration* config) { return new detail::StructuredColumns(Grid(grid), *vert, grid::Partitioner(partitioner), *config); } void atlas__functionspace__StructuredColumns__gather_field(const detail::StructuredColumns* This, const field::FieldImpl* local, field::FieldImpl* global) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_StructuredColumns"); ATLAS_ASSERT(global != nullptr, "Cannot access uninitialised atlas_Field"); ATLAS_ASSERT(local != nullptr, "Cannot access uninitialised atlas_Field"); const Field l(local); Field g(global); This->gather(l, g); } void atlas__functionspace__StructuredColumns__scatter_field(const detail::StructuredColumns* This, const field::FieldImpl* global, field::FieldImpl* local) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_StructuredColumns"); ATLAS_ASSERT(global != nullptr, "Cannot access uninitialised atlas_Field"); ATLAS_ASSERT(local != nullptr, "Cannot access uninitialised atlas_Field"); const Field g(global); Field l(local); This->scatter(g, l); } void atlas__functionspace__StructuredColumns__gather_fieldset(const detail::StructuredColumns* This, const field::FieldSetImpl* local, field::FieldSetImpl* global) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_StructuredColumns"); ATLAS_ASSERT(global != nullptr, "Cannot access uninitialised atlas_FieldSet"); ATLAS_ASSERT(local != nullptr, "Cannot access uninitialised atlas_FieldSet"); const FieldSet l(local); FieldSet g(global); This->gather(l, g); } void atlas__functionspace__StructuredColumns__scatter_fieldset(const detail::StructuredColumns* This, const field::FieldSetImpl* global, field::FieldSetImpl* local) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_StructuredColumns"); ATLAS_ASSERT(global != nullptr, "Cannot access uninitialised atlas_FieldSet"); ATLAS_ASSERT(local != nullptr, "Cannot access uninitialised atlas_FieldSet"); const FieldSet g(global); FieldSet l(local); This->scatter(g, l); } void atlas__fs__StructuredColumns__checksum_fieldset(const detail::StructuredColumns* This, const field::FieldSetImpl* fieldset, char*& checksum, idx_t& size, int& allocated) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_StructuredColumns"); ATLAS_ASSERT(fieldset != nullptr, "Cannot access uninitialised atlas_FieldSet"); std::string checksum_str(This->checksum(fieldset)); size = static_cast(checksum_str.size()); checksum = new char[size + 1]; allocated = true; std::strncpy(checksum, checksum_str.c_str(), size + 1); } void atlas__fs__StructuredColumns__checksum_field(const detail::StructuredColumns* This, const field::FieldImpl* field, char*& checksum, idx_t& size, int& allocated) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_StructuredColumns"); ATLAS_ASSERT(field != nullptr, "Cannot access uninitialised atlas_Field"); std::string checksum_str(This->checksum(field)); size = static_cast(checksum_str.size()); checksum = new char[size + 1]; allocated = true; std::strncpy(checksum, checksum_str.c_str(), size + 1); } void atlas__fs__StructuredColumns__index_host(const detail::StructuredColumns* This, idx_t*& data, idx_t& i_min, idx_t& i_max, idx_t& j_min, idx_t& j_max) { ATLAS_ASSERT(This != nullptr, "Cannot access uninitialised atlas_functionspace_StructuredColumns"); auto _This = detail::StructuredColumnsFortranAccess{*This}; data = _This.ij2gp_.data_.data(); i_min = _This.ij2gp_.i_min_ + 1; i_max = _This.ij2gp_.i_max_ + 1; j_min = _This.ij2gp_.j_min_ + 1; j_max = _This.ij2gp_.j_max_ + 1; } idx_t atlas__fs__StructuredColumns__j_begin(const detail::StructuredColumns* This) { return This->j_begin() + 1; } idx_t atlas__fs__StructuredColumns__j_end(const detail::StructuredColumns* This) { return This->j_end(); } idx_t atlas__fs__StructuredColumns__i_begin(const detail::StructuredColumns* This, idx_t j) { return This->i_begin(j - 1) + 1; } idx_t atlas__fs__StructuredColumns__i_end(const detail::StructuredColumns* This, idx_t j) { return This->i_end(j - 1); } idx_t atlas__fs__StructuredColumns__j_begin_halo(const detail::StructuredColumns* This) { return This->j_begin_halo() + 1; } idx_t atlas__fs__StructuredColumns__j_end_halo(const detail::StructuredColumns* This) { return This->j_end_halo(); } idx_t atlas__fs__StructuredColumns__i_begin_halo(const detail::StructuredColumns* This, idx_t j) { return This->i_begin_halo(j - 1) + 1; } idx_t atlas__fs__StructuredColumns__i_end_halo(const detail::StructuredColumns* This, idx_t j) { return This->i_end_halo(j - 1); } field::FieldImpl* atlas__fs__StructuredColumns__xy(const detail::StructuredColumns* This) { return This->xy().get(); } field::FieldImpl* atlas__fs__StructuredColumns__z(const detail::StructuredColumns* This) { ATLAS_ASSERT(This != nullptr); field::FieldImpl* field; { Field f = This->z(); field = f.get(); field->attach(); } field->detach(); return field; } field::FieldImpl* atlas__fs__StructuredColumns__partition(const detail::StructuredColumns* This) { return This->partition().get(); } field::FieldImpl* atlas__fs__StructuredColumns__global_index(const detail::StructuredColumns* This) { return This->global_index().get(); } field::FieldImpl* atlas__fs__StructuredColumns__index_i(const detail::StructuredColumns* This) { return This->index_i().get(); } field::FieldImpl* atlas__fs__StructuredColumns__index_j(const detail::StructuredColumns* This) { return This->index_j().get(); } idx_t atlas__fs__StructuredColumns__size(const detail::StructuredColumns* This) { return This->size(); } idx_t atlas__fs__StructuredColumns__sizeOwned(const detail::StructuredColumns* This) { return This->sizeOwned(); } idx_t atlas__fs__StructuredColumns__levels(const detail::StructuredColumns* This) { return This->levels(); } const GridImpl* atlas__fs__StructuredColumns__grid(const detail::StructuredColumns* This) { return This->grid().get(); } } // ---------------------------------------------------------------------------- } // namespace functionspace } // namespace atlas atlas-0.46.0/src/atlas/functionspace/detail/FunctionSpaceImpl.h0000664000175000017500000001166515160212070024626 0ustar alastairalastair/* * (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 #include #include "atlas/functionspace/HaloDescription.h" #include "atlas/library/config.h" #include "atlas/util/Object.h" namespace eckit { class Configuration; } namespace atlas { class FieldSet; class Field; class Grid; class Projection; namespace util { class Metadata; class PartitionPolygon; class PartitionPolygons; } // namespace util namespace parallel { class GatherScatter; } // namespace parallel } // namespace atlas namespace atlas { namespace functionspace { #define FunctionspaceT_nonconst typename std::remove_const::type #define FunctionspaceT_const typename std::add_const::type /// @brief FunctionSpace class helps to interprete Fields. /// @note Abstract base class class FunctionSpaceImpl : public util::Object { public: FunctionSpaceImpl(); virtual ~FunctionSpaceImpl(); virtual std::string type() const = 0; virtual operator bool() const { return true; } virtual size_t footprint() const = 0; virtual atlas::Field createField(const eckit::Configuration&) const = 0; virtual atlas::Field createField(const atlas::Field&, const eckit::Configuration&) const = 0; atlas::Field createField(const atlas::Field&) const; template atlas::Field createField(const eckit::Configuration&) const; template atlas::Field createField() const; const util::Metadata& metadata() const { return *metadata_; } util::Metadata& metadata() { return *metadata_; } template FunctionspaceT_nonconst* cast(); template FunctionspaceT_const* cast() const; virtual std::string distribution() const = 0; virtual void haloExchange(const FieldSet&, bool /*on_device*/ = false) const; virtual void haloExchange(const Field&, bool /* on_device*/ = false) const; virtual void adjointHaloExchange(const FieldSet&, bool /*on_device*/ = false) const; virtual void adjointHaloExchange(const Field&, bool /* on_device*/ = false) const; virtual void gather(const FieldSet&, FieldSet&) const; virtual void gather(const Field&, Field&) const; virtual void scatter(const FieldSet&, FieldSet&) const; virtual void scatter(const Field&, Field&) const; virtual const parallel::GatherScatter& gather() const; virtual const parallel::GatherScatter& scatter() const; virtual idx_t size() const = 0; virtual idx_t part() const; virtual idx_t nb_parts() const; virtual const util::PartitionPolygon& polygon(idx_t halo = 0) const; virtual const atlas::Grid& grid() const; virtual atlas::Field lonlat() const; virtual atlas::Field ghost() const; virtual atlas::Field remote_index() const; virtual atlas::Field partition() const; virtual atlas::Field global_index() const; virtual const util::PartitionPolygons& polygons() const; virtual const Projection& projection() const; virtual std::string mpi_comm() const; virtual const HaloDescription& halo_description() const; private: util::Metadata* metadata_; mutable std::unique_ptr halo_description_; }; template inline FunctionspaceT_nonconst* FunctionSpaceImpl::cast() { return dynamic_cast(this); } template inline FunctionspaceT_const* FunctionSpaceImpl::cast() const { return dynamic_cast(this); } #undef FunctionspaceT_const #undef FunctionspaceT_nonconst //------------------------------------------------------------------------------------------------------ /// @brief Dummy Functionspace class that evaluates to false class NoFunctionSpace : public FunctionSpaceImpl { public: NoFunctionSpace(): FunctionSpaceImpl() {} virtual ~NoFunctionSpace() {} virtual std::string type() const { return "NoFunctionSpace"; } virtual operator bool() const { return false; } virtual size_t footprint() const { return sizeof(*this); } virtual std::string distribution() const { return std::string(); } virtual Field createField(const eckit::Configuration&) const; virtual Field createField(const Field&, const eckit::Configuration&) const; virtual idx_t size() const { return 0; } }; //------------------------------------------------------------------------------------------------------ } // namespace functionspace //------------------------------------------------------------------------------------------------------ } // namespace atlas atlas-0.46.0/src/atlas/functionspace/detail/BlockStructuredColumnsInterface.h0000664000175000017500000001511115160212070027532 0ustar alastairalastair/* * (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/functionspace/BlockStructuredColumns.h" namespace atlas { namespace field { class FieldSetImpl; } namespace grid { class DistributionImpl; } } // namespace atlas namespace atlas { namespace grid { namespace detail { namespace grid { class Grid; } // namespace grid } // namespace detail } // namespace grid using GridImpl = grid::detail::grid::Grid; } // namespace atlas namespace atlas { namespace grid { namespace detail { namespace partitioner { class Partitioner; } // namespace partitioner } // namespace detail } // namespace grid using PartitionerImpl = grid::detail::partitioner::Partitioner; } // namespace atlas namespace atlas { namespace functionspace { // ------------------------------------------------------------------- // C wrapper interfaces to C++ routines extern "C" { const detail::BlockStructuredColumns* atlas__functionspace__BStructuredColumns__new__grid(const GridImpl* grid, const eckit::Configuration* config); const detail::BlockStructuredColumns* atlas__functionspace__BStructuredColumns__new__grid_dist( const GridImpl* grid, const grid::DistributionImpl* dist, const eckit::Configuration* config); const detail::BlockStructuredColumns* atlas__functionspace__BStructuredColumns__new__grid_dist_vert( const GridImpl* grid, const grid::DistributionImpl* dist, const Vertical* vert, const eckit::Configuration* config); const detail::BlockStructuredColumns* atlas__functionspace__BStructuredColumns__new__grid_dist_config( const GridImpl* grid, const grid::DistributionImpl* dist, const eckit::Configuration* config); const detail::BlockStructuredColumns* atlas__functionspace__BStructuredColumns__new__grid_part( const GridImpl* grid, const PartitionerImpl* dist, const eckit::Configuration* config); const detail::BlockStructuredColumns* atlas__functionspace__BStructuredColumns__new__grid_part_vert( const GridImpl* grid, const PartitionerImpl* dist, const Vertical* vert, const eckit::Configuration* config); void atlas__functionspace__BStructuredColumns__delete(detail::BlockStructuredColumns* This); field::FieldImpl* atlas__fs__BStructuredColumns__create_field(const detail::BlockStructuredColumns* This, const eckit::Configuration* options); void atlas__functionspace__BStructuredColumns__gather_field(const detail::BlockStructuredColumns* This, const field::FieldImpl* local, field::FieldImpl* global); void atlas__functionspace__BStructuredColumns__scatter_field(const detail::BlockStructuredColumns* This, const field::FieldImpl* global, field::FieldImpl* local); void atlas__functionspace__BStructuredColumns__gather_fieldset(const detail::BlockStructuredColumns* This, const field::FieldSetImpl* local, field::FieldSetImpl* global); void atlas__functionspace__BStructuredColumns__scatter_fieldset(const detail::BlockStructuredColumns* This, const field::FieldSetImpl* global, field::FieldSetImpl* local); void atlas__fs__BStructuredColumns__checksum_fieldset(const detail::BlockStructuredColumns* This, const field::FieldSetImpl* fieldset, char*& checksum, idx_t& size, int& allocated); void atlas__fs__BStructuredColumns__checksum_field(const detail::BlockStructuredColumns* This, const field::FieldImpl* field, char*& checksum, idx_t& size, int& allocated); void atlas__fs__BStructuredColumns__index_host(const detail::BlockStructuredColumns* This, idx_t*& data, idx_t& i_min, idx_t& i_max, idx_t& j_min, idx_t& j_max); idx_t atlas__fs__BStructuredColumns__size(const detail::BlockStructuredColumns* This); idx_t atlas__fs__BStructuredColumns__sizeOwned(const detail::BlockStructuredColumns* This); idx_t atlas__fs__BStructuredColumns__j_begin(const detail::BlockStructuredColumns* This); idx_t atlas__fs__BStructuredColumns__j_end(const detail::BlockStructuredColumns* This); idx_t atlas__fs__BStructuredColumns__i_begin(const detail::BlockStructuredColumns* This, idx_t j); idx_t atlas__fs__BStructuredColumns__i_end(const detail::BlockStructuredColumns* This, idx_t j); idx_t atlas__fs__BStructuredColumns__j_begin_halo(const detail::BlockStructuredColumns* This); idx_t atlas__fs__BStructuredColumns__j_end_halo(const detail::BlockStructuredColumns* This); idx_t atlas__fs__BStructuredColumns__i_begin_halo(const detail::BlockStructuredColumns* This, idx_t j); idx_t atlas__fs__BStructuredColumns__i_end_halo(const detail::BlockStructuredColumns* This, idx_t j); idx_t atlas__fs__BStructuredColumns__levels(const detail::BlockStructuredColumns* This); idx_t atlas__fs__BStructuredColumns__block_begin(const detail::BlockStructuredColumns* This, idx_t jblk); idx_t atlas__fs__BStructuredColumns__block_size(const detail::BlockStructuredColumns* This, idx_t jblk); idx_t atlas__fs__BStructuredColumns__nproma(const detail::BlockStructuredColumns* This); idx_t atlas__fs__BStructuredColumns__nblks(const detail::BlockStructuredColumns* This); field::FieldImpl* atlas__fs__BStructuredColumns__xy(const detail::BlockStructuredColumns* This); field::FieldImpl* atlas__fs__BStructuredColumns__z(const detail::BlockStructuredColumns* This); field::FieldImpl* atlas__fs__BStructuredColumns__partition(const detail::BlockStructuredColumns* This); field::FieldImpl* atlas__fs__BStructuredColumns__global_index(const detail::BlockStructuredColumns* This); field::FieldImpl* atlas__fs__BStructuredColumns__index_i(const detail::BlockStructuredColumns* This); field::FieldImpl* atlas__fs__BStructuredColumns__index_j(const detail::BlockStructuredColumns* This); const GridImpl* atlas__fs__BStructuredColumns__grid(const detail::BlockStructuredColumns* This); } } // namespace functionspace } // namespace atlas atlas-0.46.0/src/atlas/functionspace/detail/SpectralInterface.cc0000664000175000017500000001206115160212070024766 0ustar alastairalastair/* * (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 "SpectralInterface.h" #include "atlas/array/LocalView.h" #include "atlas/field/FieldSet.h" #include "atlas/field/detail/FieldImpl.h" #include "atlas/runtime/Exception.h" #include "atlas/trans/Trans.h" namespace atlas { namespace functionspace { namespace detail { struct SpectralFortranAccess { const Spectral& fs_; SpectralFortranAccess(const Spectral& fs): fs_(fs) {} int nump() const { return fs_.nump(); } array::LocalView nvalue() const { return fs_.nvalue(); } array::LocalView nmyms() const { return fs_.nmyms(); } array::LocalView nasm0() const { return fs_.nasm0(); } }; } // namespace detail using detail::SpectralFortranAccess; // ---------------------------------------------------------------------- extern "C" { const detail::Spectral* atlas__SpectralFunctionSpace__new__config(const eckit::Configuration* config) { ATLAS_ASSERT(config != nullptr); return new detail::Spectral(*config); } void atlas__SpectralFunctionSpace__delete(detail::Spectral* This) { ATLAS_ASSERT(This != nullptr); delete This; } field::FieldImpl* atlas__fs__Spectral__create_field(const detail::Spectral* This, const eckit::Configuration* options) { ATLAS_ASSERT(This != nullptr); ATLAS_ASSERT(options); field::FieldImpl* field; { Field f = This->createField(*options); field = f.get(); field->attach(); } field->detach(); return field; } void atlas__SpectralFunctionSpace__gather(const detail::Spectral* This, const field::FieldImpl* local, field::FieldImpl* global) { ATLAS_ASSERT(This != nullptr); ATLAS_ASSERT(global != nullptr); ATLAS_ASSERT(local != nullptr); const Field l(local); Field g(global); This->gather(l, g); } void atlas__SpectralFunctionSpace__scatter(const detail::Spectral* This, const field::FieldImpl* global, field::FieldImpl* local) { ATLAS_ASSERT(This != nullptr); ATLAS_ASSERT(global != nullptr); ATLAS_ASSERT(local != nullptr); const Field g(global); Field l(local); This->scatter(g, l); } void atlas__SpectralFunctionSpace__gather_fieldset(const detail::Spectral* This, const field::FieldSetImpl* local, field::FieldSetImpl* global) { ATLAS_ASSERT(This != nullptr); ATLAS_ASSERT(global != nullptr); ATLAS_ASSERT(local != nullptr); const FieldSet l(local); FieldSet g(global); This->gather(l, g); } void atlas__SpectralFunctionSpace__scatter_fieldset(const detail::Spectral* This, const field::FieldSetImpl* global, field::FieldSetImpl* local) { ATLAS_ASSERT(This != nullptr); ATLAS_ASSERT(global != nullptr); ATLAS_ASSERT(local != nullptr); const FieldSet g(global); FieldSet l(local); This->scatter(g, l); } void atlas__SpectralFunctionSpace__norm(const detail::Spectral* This, const field::FieldImpl* field, double norm[], int rank) { ATLAS_ASSERT(This != nullptr); ATLAS_ASSERT(field != nullptr); ATLAS_ASSERT(norm != nullptr); This->norm(field, norm, rank); } void atlas__SpectralFunctionSpace__nspec2(const detail::Spectral* This, int& nspec2) { nspec2 = This->nb_spectral_coefficients(); } void atlas__SpectralFunctionSpace__nspec2g(const detail::Spectral* This, int& nspec2g) { nspec2g = This->nb_spectral_coefficients_global(); } void atlas__SpectralFunctionSpace__truncation(const detail::Spectral* This, int& truncation) { truncation = This->truncation(); } void atlas__SpectralFunctionSpace__nump(const detail::Spectral* This, int& nump) { nump = detail::SpectralFortranAccess(*This).nump(); } void atlas__SpectralFunctionSpace__nmyms(const detail::Spectral* This, const int*& nmyms, int& size) { const auto nmyms_ = SpectralFortranAccess(*This).nmyms(); nmyms = nmyms_.data(); size = nmyms_.size(); } void atlas__SpectralFunctionSpace__nasm0(const detail::Spectral* This, const int*& nasm0, int& size) { const auto nasm0_ = SpectralFortranAccess(*This).nasm0(); nasm0 = nasm0_.data(); size = nasm0_.size(); } void atlas__SpectralFunctionSpace__nvalue(const detail::Spectral* This, const int*& nvalue, int& size) { const auto nvalue_ = SpectralFortranAccess(*This).nvalue(); nvalue = nvalue_.data(); size = nvalue_.size(); } void atlas__SpectralFunctionSpace__levels(const detail::Spectral* This, int& levels) { levels = This->levels(); } } // extern "C" } // namespace functionspace } // namespace atlas atlas-0.46.0/src/atlas/functionspace/StructuredColumns.h0000664000175000017500000002227715160212070023507 0ustar alastairalastair/* * (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/functionspace/FunctionSpace.h" #include "atlas/functionspace/detail/StructuredColumns.h" namespace atlas { namespace functionspace { // ------------------------------------------------------------------- class StructuredColumns : public FunctionSpace { public: StructuredColumns(); StructuredColumns(const FunctionSpace&); StructuredColumns(const Grid&, const eckit::Configuration& = util::NoConfig()); StructuredColumns(const Grid&, const grid::Partitioner&, const eckit::Configuration& = util::NoConfig()); StructuredColumns(const Grid&, const grid::Distribution&, const eckit::Configuration& = util::NoConfig()); StructuredColumns(const Grid&, const Vertical&, const eckit::Configuration& = util::NoConfig()); StructuredColumns(const Grid&, const Vertical&, const grid::Partitioner&, const eckit::Configuration& = util::NoConfig()); StructuredColumns(const Grid&, const grid::Distribution&, const Vertical&, const eckit::Configuration& = util::NoConfig()); static std::string type() { return detail::StructuredColumns::static_type(); } operator bool() const { return valid(); } bool valid() const { return functionspace_; } idx_t size() const { return functionspace_->size(); } idx_t sizeOwned() const { return functionspace_->sizeOwned(); } idx_t sizeHalo() const { return functionspace_->sizeHalo(); } idx_t levels() const { return functionspace_->levels(); } idx_t halo() const { return functionspace_->halo(); } const Vertical& vertical() const { return functionspace_->vertical(); } const StructuredGrid& grid() const { return functionspace_->grid(); } std::string checksum(const FieldSet&) const; std::string checksum(const Field&) const; idx_t index(idx_t i, idx_t j) const { return functionspace_->index(i, j); } idx_t i_begin(idx_t j) const { return functionspace_->i_begin(j); } idx_t i_end(idx_t j) const { return functionspace_->i_end(j); } idx_t i_begin_halo(idx_t j) const { return functionspace_->i_begin_halo(j); } idx_t i_end_halo(idx_t j) const { return functionspace_->i_end_halo(j); } idx_t j_begin() const { return functionspace_->j_begin(); } idx_t j_end() const { return functionspace_->j_end(); } idx_t j_begin_halo() const { return functionspace_->j_begin_halo(); } idx_t j_end_halo() const { return functionspace_->j_end_halo(); } idx_t k_begin() const { return functionspace_->k_begin(); } idx_t k_end() const { return functionspace_->k_end(); } Field xy() const { return functionspace_->xy(); } Field partition() const { return functionspace_->partition(); } Field global_index() const { return functionspace_->global_index(); } Field remote_index() const { return functionspace_->remote_index(); } Field index_i() const { return functionspace_->index_i(); } Field index_j() const { return functionspace_->index_j(); } Field ghost() const { return functionspace_->ghost(); } void compute_xy(idx_t i, idx_t j, PointXY& xy) const { return functionspace_->compute_xy(i, j, xy); } PointXY compute_xy(idx_t i, idx_t j) const { return functionspace_->compute_xy(i, j); } size_t footprint() const { return functionspace_->footprint(); } const util::PartitionPolygon& polygon(idx_t halo = 0) const { return functionspace_->polygon(halo); } class For { public: For(const StructuredColumns& fs, const util::Config& config = util::NoConfig()): fs_{fs}, global{config.getBool("global", false)}, owner{config.getInt("owner", 0)}, levels{config.getInt("levels", fs_.levels())} {} protected: const StructuredColumns& fs_; bool global; idx_t owner; idx_t levels; public: #define FunctorArgs(...) \ typename std::enable_if>::value, Functor>::type* = \ nullptr // Functor: void f(index,i,j,k) template void operator()(const Functor& f) const { ATLAS_ASSERT(levels); if (global) { auto mpi_rank = mpi::comm(fs_.mpi_comm()).rank(); if (owner == mpi_rank) { const idx_t ny = fs_.grid().ny(); std::vector offset(ny); offset[0] = 0; for (idx_t j = 1; j < ny; ++j) { offset[j] = offset[j - 1] + fs_.grid().nx(j - 1); } atlas_omp_parallel_for(idx_t j = 0; j < ny; ++j) { idx_t index = offset[j]; for (auto i = 0; i < fs_.grid().nx(j); ++i, ++index) { for (auto k = 0; k < levels; ++k) { f(index, i, j, k); } } } } } else { for (auto j = fs_.j_begin(); j < fs_.j_end(); ++j) { for (auto i = fs_.i_begin(j); i < fs_.i_end(j); ++i) { for (auto k = 0; k < levels; ++k) { f(fs_.index(i, j), i, j, k); } } } } } // Functor: void f(index,i,j) template void operator()(const Functor& f) const { ATLAS_ASSERT(levels == 0); if (global) { auto mpi_rank = mpi::comm(fs_.mpi_comm()).rank(); if (owner == mpi_rank) { const idx_t ny = fs_.grid().ny(); std::vector offset(ny); offset[0] = 0; for (idx_t j = 1; j < ny; ++j) { offset[j] = offset[j - 1] + fs_.grid().nx(j - 1); } atlas_omp_parallel_for(idx_t j = 0; j < ny; ++j) { idx_t index = offset[j]; for (idx_t i = 0; i < fs_.grid().nx(j); ++i, ++index) { f(index, i, j); } } } } else { for (auto j = fs_.j_begin(); j < fs_.j_end(); ++j) { for (auto i = fs_.i_begin(j); i < fs_.i_end(j); ++i) { f(fs_.index(i, j), i, j); } } } } // Functor: void f(index,k) template void operator()(const Functor& f) const { ATLAS_ASSERT(levels); if (global) { auto mpi_rank = mpi::comm(fs_.mpi_comm()).rank(); if (owner == mpi_rank) { const idx_t size = fs_.grid().size(); atlas_omp_parallel_for(idx_t n = 0; n < size; ++n) { for (idx_t k = 0; k < levels; ++k) { f(n, k); } } } } else { const idx_t size = fs_.sizeOwned(); atlas_omp_parallel_for(idx_t n = 0; n < size; ++n) { for (idx_t k = 0; k < levels; ++k) { f(n, k); } } } } // Functor: void f(index) template void operator()(const Functor& f) const { ATLAS_ASSERT(levels == 0); if (global) { auto mpi_rank = mpi::comm(fs_.mpi_comm()).rank(); if (owner == mpi_rank) { const idx_t size = fs_.grid().size(); atlas_omp_parallel_for(idx_t n = 0; n < size; ++n) { f(n); } } } else { const idx_t size = fs_.sizeOwned(); atlas_omp_parallel_for(idx_t n = 0; n < size; ++n) { f(n); } } } #undef FunctorArgs }; template void parallel_for(const Functor& f) const { For(*this, util::NoConfig())(f); } template void parallel_for(const util::Config& config, const Functor& f) const { For(*this, config)(f); } private: const detail::StructuredColumns* functionspace_; void setup(const Grid& grid, const Vertical& vertical, const grid::Distribution& distribution, const eckit::Configuration& config); }; // ------------------------------------------------------------------- } // namespace functionspace } // namespace atlas atlas-0.46.0/src/atlas/functionspace/Spectral.h0000664000175000017500000002052015160212070021544 0ustar alastairalastair/* * (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/array/LocalView.h" #include "atlas/field/Field.h" #include "atlas/functionspace/FunctionSpace.h" #include "atlas/functionspace/detail/FunctionSpaceImpl.h" #include "atlas/library/config.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/parallel/omp/omp.h" #include "atlas/runtime/Exception.h" #include "atlas/util/Config.h" namespace atlas { class Field; class FieldSet; } // namespace atlas namespace atlas { namespace trans { class Trans; } } // namespace atlas namespace atlas { namespace functionspace { namespace detail { // ------------------------------------------------------------------- class Spectral : public functionspace::FunctionSpaceImpl { /* Spectral data is organised as: m = zonal wavenumber n = total wavenumber const auto zonal_wavenumbers = Spectral::zonal_wavenumbers(); const int truncation = Spectral::truncation(); idx_t jc=0; for( int jm=0; jm& norm_per_level, int rank = 0) const; array::LocalView zonal_wavenumbers() const; // zero-based, OK idx_t levels() const { return nb_levels_; } class For { public: For(const Spectral& fs, const util::Config& config = util::NoConfig()): truncation{fs.truncation()}, zonal_wavenumbers{fs.zonal_wavenumbers()}, global{config.getBool("global", false)}, owner{config.getInt("owner", 0)} {} protected: using View = const array::LocalView; int truncation; View zonal_wavenumbers; bool global; idx_t owner; public: #define FunctorArgs(...) \ typename std::enable_if>::value, Functor>::type* = \ nullptr // Functor: void f(real,imag,n,m) template void operator()(const Functor& f) const { idx_t index = 0; if (global) { if (owner == mpi::rank()) { for(int m = 0; m <= truncation; ++m) { for (int n = m; n <= truncation; ++n) { f(index, index + 1, n, m); index += 2; } } } } else { const int nb_zonal_wavenumbers{static_cast(zonal_wavenumbers.size())}; for(int jm = 0; jm < nb_zonal_wavenumbers; ++jm) { const int m = zonal_wavenumbers(jm); for (int n = m; n <= truncation; ++n) { f(index, index + 1, n, m); index += 2; } } } } // Functor: void f(real,imag,n) template void operator()(const Functor& f) const { idx_t index = 0; if (global) { if (owner == mpi::rank()) { for(int m = 0; m <= truncation; ++m) { for (int n = m; n <= truncation; ++n) { f(index, index + 1, n); index += 2; } } } } else { const int nb_zonal_wavenumbers{static_cast(zonal_wavenumbers.size())}; for(int jm = 0; jm < nb_zonal_wavenumbers; ++jm) { const int m = zonal_wavenumbers(jm); for (int n = m; n <= truncation; ++n) { f(index, index + 1, n); index += 2; } } } } #undef FunctorArgs }; template void parallel_for(const Functor& f) const { For(*this, util::NoConfig())(f); } template void parallel_for(const util::Config& config, const Functor& f) const { For(*this, config)(f); } public: // methods idx_t nb_spectral_coefficients() const; idx_t nb_spectral_coefficients_global() const; int truncation() const { return truncation_; } idx_t size() const override { return nb_spectral_coefficients(); } private: // methods array::DataType config_datatype(const eckit::Configuration&) const; std::string config_name(const eckit::Configuration&) const; idx_t config_size(const eckit::Configuration&) const; idx_t config_levels(const eckit::Configuration&) const; void set_field_metadata(const eckit::Configuration&, Field&) const; size_t footprint() const override; private: // Fortran access friend struct SpectralFortranAccess; int nump() const; // equivalent to nmyms().size() array::LocalView nvalue() const; // Return wave number n for a given index array::LocalView nmyms() const; // Return list of local zonal wavenumbers "m" array::LocalView nasm0() const; private: // data idx_t nb_levels_; int truncation_; class Parallelisation; std::unique_ptr parallelisation_; }; } // namespace detail // ------------------------------------------------------------------- class Spectral : public FunctionSpace { public: Spectral(); Spectral(const FunctionSpace&); Spectral(const eckit::Configuration&); Spectral(const int truncation, const eckit::Configuration& = util::NoConfig()); operator bool() const { return valid(); } bool valid() const { return functionspace_; } std::string checksum(const FieldSet&) const; std::string checksum(const Field&) const; void norm(const Field&, double& norm, int rank = 0) const; void norm(const Field&, double norm_per_level[], int rank = 0) const; void norm(const Field&, std::vector& norm_per_level, int rank = 0) const; array::LocalView zonal_wavenumbers() const; // zero-based, OK idx_t nb_spectral_coefficients() const; idx_t nb_spectral_coefficients_global() const; int truncation() const; idx_t levels() const { return functionspace_->levels(); } template void parallel_for(const Functor& f) const { functionspace_->parallel_for(f); } template void parallel_for(const util::Config& config, const Functor& f) const { functionspace_->parallel_for(config, f); } private: const detail::Spectral* functionspace_; }; } // namespace functionspace } // namespace atlas atlas-0.46.0/src/atlas/functionspace/CellColumns.cc0000664000175000017500000007754315160212070022366 0ustar alastairalastair/* * (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/grid/UnstructuredGrid.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/Build2DCellCentres.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(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) { registerMesh(*mesh.get()); 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) { value_type* value = new value_type(); value->setup(mesh.mpi_comm(), 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) { registerMesh(*mesh.get()); 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) { value_type* value = new value_type(); value->setup(mesh.mpi_comm(), 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) { registerMesh(*mesh.get()); 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) { 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 { 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()); const idx_t rank = mpi::comm(mpi_comm()).rank(); 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_); mesh::actions::build_cells_parallel_fields(mesh_); mesh::actions::build_periodic_boundaries(mesh_); 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_); if (mesh_.grid()) { grid_ = mesh_.grid(); } } 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_; } idx_t CellColumns::levels() const { return nb_levels_; } 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::name(other.name()) | 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()); } auto name = loc.name(); glb.metadata().broadcast(loc.metadata(), root); loc.metadata().set("global", false); if( !name.empty() ) { loc.metadata().set("name", name); } loc.set_dirty(true); } } 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) { auto values = array::make_view(field); array::ArrayT surface_field(field.shape(0), field.shape(2)); auto 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) { auto values = array::make_view(field); return checksum.execute(values.data(), field.stride(0)); } template std::string checksum_1d_field(const parallel::Checksum& checksum, const Field& field) { auto values = array::make_view(field); return checksum.execute(values.data(), 1); } } // 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]; std::string field_checksum; if (field.datatype() == array::DataType::kind()) { if (field.rank()==3) { field_checksum = checksum_3d_field(checksum(), field); } else if (field.rank()==2) { field_checksum = checksum_2d_field(checksum(), field); } else if (field.rank()==1) { field_checksum = checksum_1d_field(checksum(), field); } else { ATLAS_NOTIMPLEMENTED; } } else if (field.datatype() == array::DataType::kind()) { if (field.rank()==3) { field_checksum = checksum_3d_field(checksum(), field); } else if (field.rank()==2) { field_checksum = checksum_2d_field(checksum(), field); } else if (field.rank()==1) { field_checksum = checksum_1d_field(checksum(), field); } else { ATLAS_NOTIMPLEMENTED; } } else if (field.datatype() == array::DataType::kind()) { if (field.rank()==3) { field_checksum = checksum_3d_field(checksum(), field); } else if (field.rank()==2) { field_checksum = checksum_2d_field(checksum(), field); } else if (field.rank()==1) { field_checksum = checksum_1d_field(checksum(), field); } else { ATLAS_NOTIMPLEMENTED; } } else if (field.datatype() == array::DataType::kind()) { if (field.rank()==3) { field_checksum = checksum_3d_field(checksum(), field); } else if (field.rank()==2) { field_checksum = checksum_2d_field(checksum(), field); } else if (field.rank()==1) { field_checksum = checksum_1d_field(checksum(), field); } else { ATLAS_NOTIMPLEMENTED; } } else { throw_Exception("datatype not supported", Here()); } if (fieldset.size() == 1) { return field_checksum; } else { md5 << field_checksum; } } 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_; } const Grid& CellColumns::grid() const { if (grid_) { return grid_; } const auto& comm = mpi::comm(mpi_comm()); std::vector points; if (comm.size() == 1) { const auto xy = atlas::array::make_view(mesh_.nodes().xy()); for (auto i = 0; i < xy.shape(0); i++) { points.push_back({xy(i, 0), xy(i, 1)}); } } else { std::vector gidx; std::vector x, y; const auto gidxView = array::make_view(global_index()); const auto ghostView = array::make_view(ghost()); const auto xy = atlas::array::make_view(mesh_.nodes().xy()); for (auto i = 0; i < xy.shape(0); i++) { if (ghostView(i) == 0) { gidx.push_back(gidxView(i)); x.push_back(xy(i, 0)); y.push_back(xy(i, 1)); } } eckit::mpi::Buffer gidxBuffer(comm.size()); eckit::mpi::Buffer xBuffer(comm.size()); eckit::mpi::Buffer yBuffer(comm.size()); comm.allGatherv(gidx.begin(), gidx.end(), gidxBuffer); comm.allGatherv(x.begin(), x.end(), xBuffer); comm.allGatherv(y.begin(), y.end(), yBuffer); points.reserve(gidxBuffer.buffer.size()); for (auto i : gidxBuffer.buffer) { points[i - 1] = atlas::PointXY{xBuffer.buffer[i - 1], yBuffer.buffer[i - 1]}; } } grid_ = UnstructuredGrid(points); return grid_; } Field CellColumns::lonlat() const { if (!mesh_.cells().has_field("lonlat")) { mesh::actions::Build2DCellCentres("lonlat")(const_cast(mesh_)); } return mesh_.cells().field("lonlat"); } Field CellColumns::remote_index() const { return mesh_.cells().remote_index(); } Field CellColumns::global_index() const { return mesh_.cells().global_index(); } Field CellColumns::ghost() const { if (mesh_.cells().has_field("ghost")) { return mesh_.cells().field("ghost"); } return mesh_.cells().halo(); } Field CellColumns::partition() const { return mesh_.cells().partition(); } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ 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 = static_cast(checksum_str.size()); checksum = new char[size + 1]; allocated = true; std::strncpy(checksum, checksum_str.c_str(), size + 1); } // ----------------------------------------------------------------------------------- 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 = static_cast(checksum_str.size()); checksum = new char[size + 1]; allocated = true; std::strncpy(checksum, checksum_str.c_str(), size + 1); } } // ----------------------------------------------------------------------------------- } // 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(); } idx_t CellColumns::levels() const { return functionspace_->levels(); } 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(); } 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(); } const mesh::Halo& CellColumns::halo() const { return functionspace_->halo(); } } // namespace functionspace } // namespace atlas atlas-0.46.0/src/atlas/functionspace/Locate.h0000664000175000017500000000222615160212070021201 0ustar alastairalastair/* * (C) Copyright 2025 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/parallel/Locate.h" #include "atlas/field/Field.h" namespace atlas::functionspace { class Locator : public ::atlas::parallel::Locator { public: Locator(const FunctionSpace& fs); using ::atlas::parallel::Locator::locate; void locate( // input span global_index, const gidx_t global_index_base, // output span partition, const int partition_base, span remote_index, const idx_t remote_index_base) const override; private: vector distribution_array_; std::function distribution_function_; size_t distribution_size_; const Field fs_global_index_; const Field fs_ghost_; std::string mpi_comm_; }; } // namespace atlas::functionspace atlas-0.46.0/src/atlas/functionspace/Spectral.cc0000664000175000017500000004027415160212070021712 0ustar alastairalastair/* * (C) Copyright 2013 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "eckit/os/BackTrace.h" #include "eckit/utils/MD5.h" #include "atlas/array/Array.h" #include "atlas/array/MakeView.h" #include "atlas/field/FieldSet.h" #include "atlas/functionspace/Spectral.h" #include "atlas/mesh/Mesh.h" #include "atlas/option.h" #include "atlas/parallel/mpi/mpi.h" #include "atlas/runtime/Exception.h" #include "atlas/runtime/Log.h" #include "atlas/trans/Trans.h" #if ATLAS_HAVE_TRANS #include "atlas/trans/ifs/TransIFS.h" #if ATLAS_HAVE_ECTRANS #include "ectrans/transi.h" #else #include "transi/trans.h" #endif namespace { void trans_check(const int code, const char* msg, const eckit::CodeLocation& location) { if (code != TRANS_SUCCESS) { std::stringstream errmsg; errmsg << "atlas::trans ERROR: " << msg << " failed: \n"; errmsg << ::trans_error_msg(code); atlas::throw_Exception(errmsg.str(), location); } } #define TRANS_CHECK(CALL) trans_check(CALL, #CALL, Here()) } // namespace #endif namespace atlas { namespace functionspace { namespace detail { #if ATLAS_HAVE_TRANS class Spectral::Parallelisation { public: Parallelisation(const std::shared_ptr<::Trans_t> other): trans_(other) {} Parallelisation(int truncation) { trans_ = std::shared_ptr<::Trans_t>(new ::Trans_t, [](::Trans_t* p) { TRANS_CHECK(::trans_delete(p)); delete p; }); #if ATLAS_ECTRANS_VERSION_AT_LEAST(1, 8, 0) TRANS_CHECK(::trans_set_mpi_comm(mpi::comm().communicator())); #endif TRANS_CHECK(::trans_new(trans_.get())); TRANS_CHECK(::trans_set_trunc(trans_.get(), truncation)); TRANS_CHECK(::trans_use_mpi(mpi::size() > 1)); TRANS_CHECK(::trans_setup(trans_.get())); } int nb_spectral_coefficients_global() const { return trans_->nspec2g; } int nb_spectral_coefficients() const { return trans_->nspec2; } int nump() const { return trans_->nump; } array::LocalView nvalue() const { if (trans_->nvalue == nullptr) { ::trans_inquire(trans_.get(), "nvalue"); } return array::make_view(trans_->nvalue, array::make_shape(trans_->nspec2)); } array::LocalView nmyms() const { if (trans_->nmyms == nullptr) { ::trans_inquire(trans_.get(), "nmyms"); } return array::make_view(trans_->nmyms, array::make_shape(nump())); } array::LocalView nasm0() const { if (trans_->nasm0 == nullptr) { ::trans_inquire(trans_.get(), "nasm0"); } return array::make_view(trans_->nasm0, array::make_shape(trans_->nsmax + 1)); } std::string distribution() const { return "ectrans"; } operator ::Trans_t*() const { return trans_.get(); } std::shared_ptr<::Trans_t> trans_; }; #else class Spectral::Parallelisation { public: Parallelisation(int truncation): truncation_(truncation) { // Assume serial!!! nmyms_.resize(truncation_ + 1); nasm0_.resize(truncation_ + 1); nvalue_.resize(nb_spectral_coefficients()); idx_t jc{0}; for (idx_t m = 0; m <= truncation_; ++m) { nmyms_[m] = m; nasm0_[m] = jc + 1; // Fortran index for (idx_t n = m; n <= truncation_; ++n) { nvalue_[jc++] = n; nvalue_[jc++] = n; } } ATLAS_ASSERT(jc == nb_spectral_coefficients()); } int nb_spectral_coefficients_global() const { return (truncation_ + 1) * (truncation_ + 2); } int nb_spectral_coefficients() const { return nb_spectral_coefficients_global(); } int truncation_; std::string distribution() const { return "serial"; } int nump() const { return truncation_ + 1; } array::LocalView nmyms() const { return array::make_view(nmyms_.data(), array::make_shape(nump())); } array::LocalView nvalue() const { return array::make_view(nvalue_.data(), array::make_shape(nb_spectral_coefficients())); } array::LocalView nasm0() const { return array::make_view(nasm0_.data(), array::make_shape(truncation_ + 1)); } std::vector nmyms_; std::vector nasm0_; std::vector nvalue_; }; #endif void Spectral::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); field.set_levels(config_levels(config)); field.set_variables(0); } idx_t Spectral::config_size(const eckit::Configuration& config) const { idx_t size = nb_spectral_coefficients(); bool global(false); if (config.get("global", global)) { if (global) { idx_t owner(0); config.get("owner", owner); size = (idx_t(mpi::rank()) == owner ? nb_spectral_coefficients_global() : 0); } } return size; } // ---------------------------------------------------------------------- Spectral::Spectral(const eckit::Configuration& config): Spectral::Spectral(config.getUnsigned("truncation"), config) {} // ---------------------------------------------------------------------- Spectral::Spectral(const int truncation, const eckit::Configuration& config): nb_levels_(0), truncation_(truncation), parallelisation_(new Parallelisation(truncation_)) { config.get("levels", nb_levels_); } Spectral::~Spectral() = default; std::string Spectral::distribution() const { return parallelisation_->distribution(); } idx_t Spectral::part() const { return mpi::rank(); } idx_t Spectral::nb_parts() const { return mpi::size(); } size_t Spectral::footprint() const { size_t size = sizeof(*this); // TODO return size; } idx_t Spectral::nb_spectral_coefficients() const { return parallelisation_->nb_spectral_coefficients(); } idx_t Spectral::nb_spectral_coefficients_global() const { return parallelisation_->nb_spectral_coefficients_global(); } array::DataType Spectral::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 Spectral::config_name(const eckit::Configuration& config) const { std::string name; config.get("name", name); return name; } idx_t Spectral::config_levels(const eckit::Configuration& config) const { idx_t levels(nb_levels_); config.get("levels", levels); return levels; } Field Spectral::createField(const eckit::Configuration& options) const { array::ArrayShape array_shape; idx_t nb_spec_coeffs = config_size(options); array_shape.push_back(nb_spec_coeffs); idx_t levels = config_levels(options); if (levels) { array_shape.push_back(levels); } idx_t variables = 0; if (options.get("variables",variables)) { if (variables) { array_shape.push_back(variables); } } Field field = Field(config_name(options), config_datatype(options), array_shape); set_field_metadata(options, field); return field; } Field Spectral::createField(const Field& other, const eckit::Configuration& config) const { return createField(option::datatype(other.datatype()) | option::levels(other.levels()) | config); } void Spectral::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]; if (loc.datatype() != array::DataType::str()) { std::stringstream err; err << "Cannot gather spectral field " << loc.name() << " of datatype " << loc.datatype().str() << "."; err << "Only " << array::DataType::str() << " supported."; throw_Exception(err.str(), Here()); } #if ATLAS_HAVE_TRANS Field& glb = global_fieldset[f]; idx_t root = 0; idx_t rank = mpi::rank(); glb.metadata().get("owner", root); ATLAS_ASSERT(loc.shape(0) == nb_spectral_coefficients()); 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) { nto[i] = root + 1; } } if (not loc.contiguous()) { throw_Exception("Cannot gather field " + loc.name() + " using IFS trans library as its data is not contiguous"); } struct ::GathSpec_t args = new_gathspec(*parallelisation_); args.nfld = nto.size(); args.rspecg = glb.array().data(); args.nto = nto.data(); args.rspec = loc.array().data(); TRANS_CHECK(::trans_gathspec(&args)); #else throw_Exception( "Cannot gather spectral fields because Atlas has " "not been compiled with TRANS support."); #endif } } void Spectral::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); } void Spectral::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]; if (loc.datatype() != array::DataType::str()) { std::stringstream err; err << "Cannot scatter spectral field " << glb.name() << " of datatype " << glb.datatype().str() << "."; err << "Only " << array::DataType::str() << " supported."; throw_Exception(err.str(), Here()); } #if ATLAS_HAVE_TRANS idx_t root = 0; idx_t rank = mpi::rank(); glb.metadata().get("owner", root); ATLAS_ASSERT(loc.shape(0) == nb_spectral_coefficients()); 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) { nfrom[i] = root + 1; } } if (not loc.contiguous()) { throw_Exception("Cannot scatter field " + glb.name() + " using IFS trans library as its data is not contiguous"); } struct ::DistSpec_t args = new_distspec(*parallelisation_); args.nfld = int(nfrom.size()); args.rspecg = glb.array().data(); args.nfrom = nfrom.data(); args.rspec = loc.array().data(); TRANS_CHECK(::trans_distspec(&args)); glb.metadata().broadcast(loc.metadata(), root); loc.metadata().set("global", false); #else throw_Exception( "Cannot scatter spectral fields because Atlas has " "not been compiled with TRANS support."); #endif } } void Spectral::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); } std::string Spectral::checksum(const FieldSet&) const { eckit::MD5 md5; ATLAS_NOTIMPLEMENTED; } std::string Spectral::checksum(const Field& field) const { FieldSet fieldset; fieldset.add(field); return checksum(fieldset); } void Spectral::norm(const Field& field, double& norm, int rank) const { #if ATLAS_HAVE_TRANS ATLAS_ASSERT(std::max(1, field.levels()) == 1, "Only a single-level field can be used for computing single norm."); struct ::SpecNorm_t args = new_specnorm(*parallelisation_); args.nfld = 1; args.rspec = field.array().data(); args.rnorm = &norm; args.nmaster = rank + 1; TRANS_CHECK(::trans_specnorm(&args)); #else throw_Exception( "Cannot compute spectral norms because Atlas has not " "been compiled with TRANS support."); #endif } void Spectral::norm(const Field& field, double norm_per_level[], int rank) const { #if ATLAS_HAVE_TRANS if (not field.contiguous()) { throw_Exception("Cannot compute spectral norm of field " + field.name() + " using IFS trans library as its data is not contiguous"); } struct ::SpecNorm_t args = new_specnorm(*parallelisation_); args.nfld = std::max(1, field.levels()); args.rspec = field.array().data(); args.rnorm = norm_per_level; args.nmaster = rank + 1; TRANS_CHECK(::trans_specnorm(&args)); #else throw_Exception( "Cannot compute spectral norms because Atlas has not " "been compiled with TRANS support."); #endif } void Spectral::norm(const Field& field, std::vector& norm_per_level, int rank) const { norm(field, norm_per_level.data(), rank); } array::LocalView Spectral::zonal_wavenumbers() const { return parallelisation_->nmyms(); } int Spectral::nump() const { return parallelisation_->nump(); } array::LocalView Spectral::nvalue() const { return parallelisation_->nvalue(); } array::LocalView Spectral::nmyms() const { return parallelisation_->nmyms(); } array::LocalView Spectral::nasm0() const { return parallelisation_->nasm0(); } } // namespace detail // ---------------------------------------------------------------------- Spectral::Spectral(): FunctionSpace(), functionspace_{nullptr} {} Spectral::Spectral(const FunctionSpace& functionspace): FunctionSpace(functionspace), functionspace_(dynamic_cast(get())) {} Spectral::Spectral(const eckit::Configuration& config): FunctionSpace(new detail::Spectral(config)), functionspace_(dynamic_cast(get())) {} Spectral::Spectral(const int truncation, const eckit::Configuration& config): FunctionSpace(new detail::Spectral(truncation, config)), functionspace_(dynamic_cast(get())) {} idx_t Spectral::nb_spectral_coefficients() const { return functionspace_->nb_spectral_coefficients(); } idx_t Spectral::nb_spectral_coefficients_global() const { return functionspace_->nb_spectral_coefficients_global(); } int Spectral::truncation() const { return functionspace_->truncation(); } std::string Spectral::checksum(const FieldSet& fieldset) const { return functionspace_->checksum(fieldset); } std::string Spectral::checksum(const Field& field) const { return functionspace_->checksum(field); } void Spectral::norm(const Field& field, double& norm, int rank) const { functionspace_->norm(field, norm, rank); } void Spectral::norm(const Field& field, double norm_per_level[], int rank) const { functionspace_->norm(field, norm_per_level, rank); } void Spectral::norm(const Field& field, std::vector& norm_per_level, int rank) const { functionspace_->norm(field, norm_per_level, rank); } array::LocalView Spectral::zonal_wavenumbers() const { return functionspace_->zonal_wavenumbers(); } // ---------------------------------------------------------------------- } // namespace functionspace } // namespace atlas atlas-0.46.0/src/atlas/functionspace/Locate.cc0000664000175000017500000000671015160212070021341 0ustar alastairalastair/* * (C) Copyright 2025 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/functionspace/Locate.h" #include "atlas/functionspace/FunctionSpace.h" #include "atlas/array/ArrayView.h" #include "atlas/array/Array.h" #include "atlas/option.h" #include "atlas/parallel/mpi/mpi.h" namespace atlas::functionspace { // Figure out the global distribution using the functionspace partition fields from each rank. static atlas::vector allgather_distribution_array(const FunctionSpace& fs) { auto& comm = atlas::mpi::comm(fs.mpi_comm()); int mpi_rank = comm.rank(); int mpi_root = 0; atlas::vector distribution_array; ATLAS_TRACE_SCOPE("Gather global partition array") { Field field_partition_global = fs.createField(fs.partition(), option::global(mpi_root)); fs.gather(fs.partition(), field_partition_global); std::size_t partition_global_size = field_partition_global.size(); comm.broadcast(partition_global_size, mpi_root); distribution_array.resize(partition_global_size); if (mpi_rank == mpi_root) { int* data = field_partition_global.array().host_data(); distribution_array.assign(data, data+partition_global_size); } comm.broadcast(distribution_array.data(), distribution_array.size(), mpi_root); } return distribution_array; } Locator::Locator(const FunctionSpace& fs) : fs_global_index_{fs.global_index()}, fs_ghost_{fs.ghost()} { distribution_array_ = allgather_distribution_array(fs); distribution_function_ = [this](gidx_t j){ return distribution_array_[j]; }; distribution_size_ = distribution_array_.size(); mpi_comm_ = fs.mpi_comm(); } void Locator::locate( // input span global_index, const gidx_t global_index_base, // output span partition, const int partition_base, span remote_index, const idx_t remote_index_base) const { constexpr gidx_t fs_global_index_base_ = 1; constexpr int distribution_base_ = 0; if (distribution_array_.size()) { ::atlas::parallel::Locator::locate( // context mpi_comm_, array::make_view(fs_global_index_).as_mdspan(), fs_global_index_base_, array::make_view(fs_ghost_).as_mdspan(), span{distribution_array_.data(), distribution_array_.size()}, distribution_base_, // input global_index, global_index_base, // output partition, partition_base, remote_index, remote_index_base); } else { ::atlas::parallel::Locator::locate( // context mpi_comm_, array::make_view(fs_global_index_).as_mdspan(), fs_global_index_base_, array::make_view(fs_ghost_).as_mdspan(), fspan{&distribution_function_, distribution_size_}, distribution_base_, // input global_index, global_index_base, // output partition, partition_base, remote_index, remote_index_base); } } } // namespace atlas::functionspace atlas-0.46.0/src/CMakeLists.txt0000664000175000017500000000616015160212070016415 0ustar alastairalastair# (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. if( atlas_HAVE_OMP_CXX ) set( atlas_HAVE_OMP_CXX 1 ) else() set( atlas_HAVE_OMP_CXX 0 ) endif() if( atlas_HAVE_OMP_Fortran ) set( atlas_HAVE_OMP_Fortran 1 ) else() set( atlas_HAVE_OMP_Fortran 0 ) endif() if( atlas_HAVE_TESSELATION ) set( atlas_HAVE_TESSELATION 1 ) else() set( atlas_HAVE_TESSELATION 0 ) endif() if( atlas_HAVE_QHULL ) set( atlas_HAVE_QHULL 1 ) else() set( atlas_HAVE_QHULL 0 ) endif() if( atlas_HAVE_CGAL ) set( atlas_HAVE_CGAL 1 ) else() set( atlas_HAVE_CGAL 0 ) endif() if( atlas_HAVE_PROJ ) set( atlas_HAVE_PROJ 1 ) else() set( atlas_HAVE_PROJ 0 ) endif() if( atlas_HAVE_FORTRAN ) set( atlas_HAVE_FORTRAN 1 ) else() set( atlas_HAVE_FORTRAN 0 ) endif() if( atlas_HAVE_ECTRANS ) set( atlas_HAVE_ECTRANS 1 ) else() set( atlas_HAVE_ECTRANS 0 ) endif() if( atlas_HAVE_PACKAGE_ECTRANS ) set( atlas_HAVE_PACKAGE_ECTRANS 1 ) else() set( atlas_HAVE_PACKAGE_ECTRANS 0 ) endif() if( atlas_HAVE_FFTW ) set( atlas_HAVE_FFTW 1 ) else() set( atlas_HAVE_FFTW 0 ) endif() if( atlas_HAVE_POCKETFFT ) set( atlas_HAVE_POCKETFFT 1 ) else() set( atlas_HAVE_POCKETFFT 0 ) endif() if( atlas_HAVE_BOUNDSCHECKING ) set( atlas_HAVE_BOUNDSCHECKING 1 ) else() set( atlas_HAVE_BOUNDSCHECKING 0 ) endif() if( atlas_HAVE_INIT_SNAN ) set( atlas_HAVE_INIT_SNAN 1 ) else() set( atlas_HAVE_INIT_SNAN 0 ) endif() if( atlas_HAVE_CUDA ) set( atlas_HAVE_CUDA 1 ) else() set( atlas_HAVE_CUDA 0 ) endif() if( atlas_HAVE_ACC ) set( atlas_HAVE_ACC 1 ) else() set( atlas_HAVE_ACC 0 ) endif() if( atlas_HAVE_GRIDTOOLS_STORAGE ) set( atlas_HAVE_GRIDTOOLS_STORAGE 1 ) else() set( atlas_HAVE_GRIDTOOLS_STORAGE 0 ) endif() set( atlas_BUILD_TYPE_RELEASE 0 ) set( atlas_BUILD_TYPE_DEBUG 0 ) if( CMAKE_BUILD_TYPE MATCHES "Release" ) set( atlas_BUILD_TYPE_RELEASE 1 ) endif() if( CMAKE_BUILD_TYPE MATCHES "Debug" ) set( atlas_BUILD_TYPE_DEBUG 1 ) endif() if( atlas_HAVE_ECKIT_DEVELOP ) set( ATLAS_ECKIT_DEVELOP 1 ) else() set( ATLAS_ECKIT_DEVELOP 0 ) endif() ecbuild_parse_version( ${eckit_VERSION} PREFIX ATLAS_ECKIT ) math( EXPR ATLAS_ECKIT_VERSION_INT "( 10000 * ${ATLAS_ECKIT_VERSION_MAJOR} ) + ( 100 * ${ATLAS_ECKIT_VERSION_MINOR} ) + ${ATLAS_ECKIT_VERSION_PATCH}" ) add_subdirectory( atlas_acc_support ) add_subdirectory( atlas ) if( atlas_HAVE_FORTRAN ) if( atlas_HAVE_FCKIT_DEVELOP ) set( ATLAS_FCKIT_DEVELOP 1 ) else() set( ATLAS_FCKIT_DEVELOP 0 ) endif() ecbuild_parse_version( ${fckit_VERSION} PREFIX ATLAS_FCKIT ) math( EXPR ATLAS_FCKIT_VERSION_INT "( 10000 * ${ATLAS_FCKIT_VERSION_MAJOR} ) + ( 100 * ${ATLAS_FCKIT_VERSION_MINOR} ) + ${ATLAS_FCKIT_VERSION_PATCH}" ) add_subdirectory( atlas_f ) endif() add_subdirectory( apps ) add_subdirectory( tests ) if( HAVE_SANDBOX ) add_subdirectory( sandbox ) endif() atlas-0.46.0/atlas_io/0000775000175000017500000000000015160212070014656 5ustar alastairalastairatlas-0.46.0/atlas_io/tests/0000775000175000017500000000000015160212070016020 5ustar alastairalastairatlas-0.46.0/atlas_io/tests/test_io_stream.cc0000664000175000017500000001505615160212070021357 0ustar alastairalastair/* * (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_io/atlas-io.h" #include "eckit/io/FileHandle.h" #include "eckit/io/PooledHandle.h" #include "TestEnvironment.h" namespace atlas { namespace test { CASE("Stream interoperability with eckit::DataHandle") { SECTION("own pointer") { io::Stream s; { eckit::DataHandle* datahandle = new eckit::FileHandle("test_io_session.data"); datahandle->openForWrite(0); s = io::Stream{datahandle}; } s.datahandle().close(); } SECTION("shared pointer") { io::Stream s; { std::shared_ptr datahandle = std::make_shared("test_io_session.data"); datahandle->openForWrite(0); s = io::Stream{datahandle}; } s.datahandle().close(); } SECTION("reference") { io::Stream s; eckit::FileHandle datahandle("test_io_session.data"); datahandle.openForWrite(0); s = io::Stream{datahandle}; s.datahandle().close(); } } CASE("Test seek-for-write works when opening OutputFileStream for append") { std::string s1("write \n"); std::string s2("append \n"); std::string s3("overwrite\n"); { ATLAS_IO_TRACE("write"); io::Stream f = io::OutputFileStream("append-test"); f.write(s1.c_str(), s1.size()); } { ATLAS_IO_TRACE("append"); io::Stream f = io::OutputFileStream("append-test", io::Mode::append); auto offset = f.position(); f.write(s2.c_str(), s2.size()); // Rewind to beginning of append f.seek(offset); f.write(s3.c_str(), s3.size()); } { ATLAS_IO_TRACE("read"); io::Stream f = io::InputFileStream("append-test"); std::string expected = s1 + s3; std::string read(expected.size(), ' '); f.read(const_cast(read.data()), read.size()); EXPECT_EQ(read, expected); } } CASE("Opening same file in same scope") { // Opening same file within same scope will avoid opening it multiple times, good for perfmance // write a file { io::OutputFileStream out("test_io_session.data"); out.write("line1\n", 6); out.write("line2\n", 6); out.write("line3\n", 6); } std::string l1(5, ' '), l2(5, ' '), l3(5, ' '); io::Stream f1 = io::InputFileStream{"test_io_session.data"}; f1.seek(0 * 6); f1.read(const_cast(l1.data()), 5); io::Stream f2 = io::InputFileStream{"test_io_session.data"}; f2.seek(1 * 6); f2.read(const_cast(l2.data()), 5); io::Stream f3 = io::InputFileStream{"test_io_session.data"}; f3.seek(2 * 6); f3.read(const_cast(l3.data()), 5); EXPECT_EQ(l1, "line1"); EXPECT_EQ(l2, "line2"); EXPECT_EQ(l3, "line3"); auto& pooled_handle = dynamic_cast(f1.datahandle()); EXPECT_EQ(pooled_handle.nbOpens(), 1); EXPECT_EQ(pooled_handle.nbSeeks(), 3); EXPECT_EQ(pooled_handle.nbReads(), 3); } CASE("Opening same file in parallel scopes") { // Files are opened and closed within each scope, bad for performance // write a file { io::OutputFileStream out("test_io_session.data"); out.write("line1\n", 6); out.write("line2\n", 6); out.write("line3\n", 6); } std::string l1(5, ' '), l2(5, ' '), l3(5, ' '); { io::Stream f1 = io::InputFileStream{"test_io_session.data"}; f1.seek(0 * 6); f1.read(const_cast(l1.data()), 5); auto& pooled_handle = dynamic_cast(f1.datahandle()); EXPECT_EQ(pooled_handle.nbOpens(), 1); EXPECT_EQ(pooled_handle.nbSeeks(), 1); EXPECT_EQ(pooled_handle.nbReads(), 1); } { io::Stream f2 = io::InputFileStream{"test_io_session.data"}; f2.seek(1 * 6); f2.read(const_cast(l2.data()), 5); auto& pooled_handle = dynamic_cast(f2.datahandle()); EXPECT_EQ(pooled_handle.nbOpens(), 1); EXPECT_EQ(pooled_handle.nbSeeks(), 1); EXPECT_EQ(pooled_handle.nbReads(), 1); } { io::Stream f3 = io::InputFileStream{"test_io_session.data"}; f3.seek(2 * 6); f3.read(const_cast(l3.data()), 5); auto& pooled_handle = dynamic_cast(f3.datahandle()); EXPECT_EQ(pooled_handle.nbOpens(), 1); EXPECT_EQ(pooled_handle.nbSeeks(), 1); EXPECT_EQ(pooled_handle.nbReads(), 1); } } CASE("Opening same file in parallel scopes with Session") { // Declaring this in an outer scope will keep storage of InputFileStream // within nested scopes, so that files will not be opened/closed repeatedly io::Session session; // write a file { io::OutputFileStream out("test_io_session.data"); out.write("line1\n", 6); out.write("line2\n", 6); out.write("line3\n", 6); } std::string l1(5, ' '), l2(5, ' '), l3(5, ' '); { io::Stream f1 = io::InputFileStream{"test_io_session.data"}; f1.seek(0 * 6); f1.read(const_cast(l1.data()), 5); auto& pooled_handle = dynamic_cast(f1.datahandle()); EXPECT_EQ(pooled_handle.nbOpens(), 1); EXPECT_EQ(pooled_handle.nbSeeks(), 1); EXPECT_EQ(pooled_handle.nbReads(), 1); } { io::Stream f2 = io::InputFileStream{"test_io_session.data"}; f2.seek(1 * 6); f2.read(const_cast(l2.data()), 5); auto& pooled_handle = dynamic_cast(f2.datahandle()); EXPECT_EQ(pooled_handle.nbOpens(), 1); EXPECT_EQ(pooled_handle.nbSeeks(), 2); EXPECT_EQ(pooled_handle.nbReads(), 2); } { io::Stream f3 = io::InputFileStream{"test_io_session.data"}; f3.seek(2 * 6); f3.read(const_cast(l3.data()), 5); auto& pooled_handle = dynamic_cast(f3.datahandle()); EXPECT_EQ(pooled_handle.nbOpens(), 1); EXPECT_EQ(pooled_handle.nbSeeks(), 3); EXPECT_EQ(pooled_handle.nbReads(), 3); } } } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/atlas_io/tests/test_io_encoding.cc0000664000175000017500000005041715160212070021652 0ustar alastairalastair/* * (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 "atlas_io/atlas-io.h" #include "TestEnvironment.h" namespace atlas { namespace test { using io::ArrayReference; // ------------------------------------------------------------------------------------------------------- struct UnencodableType { std::string _; }; // ------------------------------------------------------------------------------------------------------- // Example type that can be encoded / decoded with atlas::io // The "operations" performed on this type are stored for unit-test purposes class EncodableType { public: using Operations = std::vector; EncodableType(std::string s, std::shared_ptr operations = std::make_shared()): str(s), ops(operations) { ATLAS_IO_TRACE("EncodableType[" + str + "] construct"); ops->push_back("constructor"); } EncodableType(): ops(std::make_shared()) {} EncodableType(const EncodableType& other) { // This constructor should not be called. str = other.str; ATLAS_IO_TRACE("EncodableType[" + str + "] copy constructor"); ops = other.ops; ops->push_back("copy constructor"); } EncodableType(EncodableType&& other) { str = std::move(other.str); ATLAS_IO_TRACE("EncodableType[" + str + "] move constructor"); ops = other.ops; ops->push_back("move constructor"); } EncodableType& operator=(const EncodableType& rhs) { // This assignment should not be called. str = rhs.str; ATLAS_IO_TRACE("EncodableType[" + str + "] assignment"); ops = rhs.ops; ops->push_back("assignment"); return *this; } EncodableType& operator=(const EncodableType&& rhs) { // This assignment should not be called. str = std::move(rhs.str); ATLAS_IO_TRACE("EncodableType[" + str + "] move"); ops = rhs.ops; ops->push_back("move"); return *this; } friend void encode_data(const EncodableType& in, atlas::io::Data& out) { in.ops->push_back("encode_data"); out.assign(in.str.data(), in.str.size()); } friend size_t encode_metadata(const EncodableType& in, atlas::io::Metadata& metadata) { in.ops->push_back("encode_metadata"); metadata.set("type", "EncodableType"); metadata.set("bytes", in.str.size()); return in.str.size(); } friend void decode(const atlas::io::Metadata&, const atlas::io::Data& b, EncodableType& out) { out.ops->push_back("decode"); const char* data = static_cast(b.data()); out.str = std::string(data, data + b.size()); } const std::vector& operations() const { return *ops; } private: std::string str; mutable std::shared_ptr> ops; }; // ------------------------------------------------------------------------------------------------------- CASE("test exceptions") { EXPECT(not(io::is_interpretable())); EXPECT(not io::is_encodable()); EXPECT(not io::can_encode_metadata()); EXPECT(not io::can_encode_data()); UnencodableType in; atlas::io::Data data; atlas::io::Metadata metadata; EXPECT_THROWS_AS(io::ref(in, io::tag::disable_static_assert()), io::NotEncodable); EXPECT_THROWS_AS(io::copy(in, io::tag::disable_static_assert()), io::NotEncodable); EXPECT_THROWS_AS(io::encode(in, metadata, data, io::tag::disable_static_assert()), io::NotEncodable); } // ------------------------------------------------------------------------------------------------------- CASE("encoding test::EncodableType") { static_assert(not io::is_interpretable(), ""); static_assert(io::is_encodable(), ""); static_assert(io::is_decodable(), ""); const std::string encoded_string{"encoded string"}; EncodableType in(encoded_string); atlas::io::Data data; atlas::io::Metadata metadata; EXPECT_NO_THROW(encode(in, metadata, data)); EXPECT(metadata.type() == "EncodableType"); EXPECT(data.size() == encoded_string.size()); EXPECT(::memcmp(data, encoded_string.data(), encoded_string.size()) == 0); } // ------------------------------------------------------------------------------------------------------- CASE("encoding atlas::io::types::ArrayView") { static_assert(not io::is_interpretable(), ""); static_assert(io::can_encode_data(), ""); static_assert(io::can_encode_metadata(), ""); static_assert(io::is_encodable(), ""); } // ------------------------------------------------------------------------------------------------------- template void assert_StdVector() { static_assert(io::is_interpretable, ArrayReference>(), ""); static_assert(not io::can_encode_data>(), ""); static_assert(not io::can_encode_metadata>(), ""); static_assert(not io::is_encodable>(), ""); } template void encode_StdVector() { std::vector in{1, 2, 3, 4, 5}; ArrayReference interpreted; interprete(in, interpreted); atlas::io::Data data; atlas::io::Metadata metadata; encode(interpreted, metadata, data); EXPECT(data.size() == in.size() * sizeof(T)); EXPECT(::memcmp(in.data(), data.data(), data.size()) == 0); EXPECT(metadata.type() == "array"); EXPECT(metadata.getString("datatype") == atlas::io::DataType::str()); } CASE("encoding std::vector") { assert_StdVector(); assert_StdVector(); assert_StdVector(); assert_StdVector(); assert_StdVector(); encode_StdVector(); encode_StdVector(); encode_StdVector(); encode_StdVector(); { using T = std::byte; std::bitset<8> bits; std::vector in; in.resize(5); size_t n{0}; for (auto& byte : in) { bits.set(n++, true); byte = *reinterpret_cast(&bits); } ArrayReference interpreted; interprete(in, interpreted); atlas::io::Data data; atlas::io::Metadata metadata; encode(interpreted, metadata, data); EXPECT(data.size() == in.size() * sizeof(T)); EXPECT(::memcmp(in.data(), data.data(), data.size()) == 0); EXPECT(metadata.type() == "array"); EXPECT(metadata.getString("datatype") == atlas::io::DataType::str()); } } // ------------------------------------------------------------------------------------------------------- template void assert_StdArray() { static_assert(io::is_interpretable, ArrayReference>(), ""); static_assert(not io::can_encode_data>(), ""); static_assert(not io::can_encode_metadata>(), ""); static_assert(not io::is_encodable>(), ""); } template void encode_StdArray() { std::array in{1, 2, 3, 4, 5}; ArrayReference interpreted; interprete(in, interpreted); atlas::io::Data data; atlas::io::Metadata metadata; encode(interpreted, metadata, data); EXPECT(data.size() == in.size() * sizeof(T)); EXPECT(::memcmp(in.data(), data.data(), data.size()) == 0); EXPECT(metadata.type() == "array"); EXPECT(metadata.getString("datatype") == atlas::io::DataType::str()); } CASE("encoding std::array") { assert_StdArray(); assert_StdArray(); assert_StdArray(); assert_StdArray(); assert_StdArray(); encode_StdVector(); encode_StdVector(); encode_StdVector(); encode_StdVector(); { using T = std::byte; std::bitset<8> bits; std::vector in; in.resize(5); size_t n{0}; for (auto& byte : in) { bits.set(n++, true); byte = *reinterpret_cast(&bits); } ArrayReference interpreted; interprete(in, interpreted); atlas::io::Data data; atlas::io::Metadata metadata; encode(interpreted, metadata, data); EXPECT(data.size() == in.size() * sizeof(T)); EXPECT(::memcmp(in.data(), data.data(), data.size()) == 0); EXPECT(metadata.type() == "array"); EXPECT(metadata.getString("datatype") == atlas::io::DataType::str()); } } // ------------------------------------------------------------------------------------------------------- CASE("test Encoder") { SECTION("default constructor") { io::Encoder encoder; EXPECT(encoder == false); io::Metadata metadata; io::Data data; EXPECT_THROWS_AS(encode(encoder, metadata, data), eckit::AssertionFailed); } SECTION("Encoder via reference") { io::Encoder encoder; auto ops = std::make_shared(); EncodableType encodable("string", ops); EXPECT_EQ(ops->size(), 1); EXPECT_EQ(ops->back(), "constructor"); io::ref(encodable); EXPECT_EQ(ops->size(), 1); EXPECT_EQ(ops->back(), "constructor"); encoder = io::Encoder{io::ref(encodable)}; EXPECT_EQ(ops->size(), 2); EXPECT_EQ(ops->back(), "encode_metadata"); io::Metadata metadata; io::Data data; encode(encoder, metadata, data); EXPECT_EQ(ops->size(), 3); EXPECT_EQ(ops->back(), "encode_data"); } SECTION("Encoder via copy") { io::Encoder encoder; auto ops = std::make_shared(); EncodableType encodable("string", ops); EXPECT_EQ(ops->size(), 1); EXPECT_EQ(ops->back(), "constructor"); encoder = io::Encoder{io::copy(encodable)}; EXPECT_EQ(ops->size(), 3); EXPECT_EQ(ops->at(1), "encode_metadata"); EXPECT_EQ(ops->at(2), "encode_data"); io::Metadata metadata; io::Data data; encode(encoder, metadata, data); EXPECT_EQ(ops->size(), 3); EXPECT_EQ(ops->at(2), "encode_data"); } SECTION("Encoder via move") { io::Encoder encoder; auto ops = std::make_shared(); EncodableType encodable("string", ops); EXPECT_EQ(ops->size(), 1); EXPECT_EQ(ops->back(), "constructor"); encoder = io::Encoder{std::move(encodable)}; EXPECT_EQ(ops->size(), 3); EXPECT_EQ(ops->at(1), "move constructor"); EXPECT_EQ(ops->at(2), "encode_metadata"); io::Metadata metadata; io::Data data; encode(encoder, metadata, data); EXPECT_EQ(ops->size(), 4); EXPECT_EQ(ops->at(3), "encode_data"); } } // ------------------------------------------------------------------------------------------------------- CASE("Encoder for std::vector") { SECTION("ref") { using T = double; std::vector v{1, 2, 3, 4, 5, 6, 7, 8}; io::Encoder encoder(io::ref(v)); // We can only encode with reference to original vector (no copies were made) io::Metadata metadata; io::Data data; encode(encoder, metadata, data); EXPECT(data.size() == v.size() * sizeof(T)); EXPECT(::memcmp(data, v.data(), data.size()) == 0); } SECTION("copy") { using T = double; std::vector v{1, 2, 3, 4, 5, 6, 7, 8}; io::Encoder encoder; { std::vector scoped = v; encoder = io::Encoder(io::copy(scoped)); scoped.assign(scoped.size(), 0); // zero out before destruction } // We can now encode with scoped vector destroyed io::Metadata metadata; io::Data data; encode(encoder, metadata, data); EXPECT_EQ(data.size(), v.size() * sizeof(T)); EXPECT(::memcmp(data, v.data(), data.size()) == 0); } } // ------------------------------------------------------------------------------------------------------- CASE("Encoder for std::array") { SECTION("ref") { using T = double; std::array v{1, 2, 3, 4, 5, 6, 7, 8}; io::Encoder encoder(io::ref(v)); // We can only encode with reference to original vector (no copies were made) io::Metadata metadata; io::Data data; encode(encoder, metadata, data); EXPECT(data.size() == v.size() * sizeof(T)); EXPECT(::memcmp(data, v.data(), data.size()) == 0); } SECTION("copy") { using T = double; std::array v{1, 2, 3, 4, 5, 6, 7, 8}; io::Encoder encoder; { std::array scoped = v; encoder = io::Encoder(io::copy(scoped)); std::fill(std::begin(scoped), std::end(scoped), 0); // zero out before destruction } // We can now encode with scoped vector destroyed io::Metadata metadata; io::Data data; encode(encoder, metadata, data); EXPECT_EQ(data.size(), v.size() * sizeof(T)); EXPECT(::memcmp(data, v.data(), data.size()) == 0); } } // ------------------------------------------------------------------------------------------------------- CASE("Encoder of encoder") { using T = double; std::vector v{1, 2, 3, 4, 5, 6, 7, 8}; io::Encoder encoder(io::ref(v)); io::Encoder encoder_of_encoder(io::ref(encoder)); io::Metadata metadata; io::Data data; encode(encoder_of_encoder, metadata, data); EXPECT_EQ(data.size(), v.size() * sizeof(T)); EXPECT(::memcmp(data, v.data(), data.size()) == 0); } // ------------------------------------------------------------------------------------------------------- /// Helper class to be used in testing decoding of arrays. template struct EncodedArray { atlas::io::Data data; atlas::io::Metadata metadata; EncodedArray(): in{1, 2, 3, 4, 5, 6, 7, 8} { encode(in, metadata, data); } friend bool operator==(const std::vector& lhs, const EncodedArray& rhs) { if (lhs.size() != rhs.in.size()) { return false; } return ::memcmp(lhs.data(), rhs.in.data(), rhs.in.size() * sizeof(T)) == 0; } friend bool operator==(const std::array& lhs, const EncodedArray& rhs) { if (lhs.size() != rhs.in.size()) { return false; } return ::memcmp(lhs.data(), rhs.in.data(), rhs.in.size() * sizeof(T)) == 0; } private: std::vector in; }; template <> struct EncodedArray { using T = std::byte; atlas::io::Data data; atlas::io::Metadata metadata; EncodedArray() { std::bitset<8> bits; in.resize(5); size_t n{0}; for (auto& byte : in) { bits.set(n++, true); byte = *reinterpret_cast(&bits); } encode(in, metadata, data); } friend bool operator==(const std::vector& lhs, const EncodedArray& rhs) { if (lhs.size() != rhs.in.size()) { return false; } return ::memcmp(lhs.data(), rhs.in.data(), rhs.in.size() * sizeof(T)) == 0; } private: std::vector in; }; // ------------------------------------------------------------------------------------------------------- CASE("Decoding to std::vector") { using T = double; EncodedArray encoded; std::vector out; SECTION("decode std::vector directly") { EXPECT_NO_THROW(decode(encoded.metadata, encoded.data, out)); EXPECT(out == encoded); } SECTION("decode using rvalue io::Decoder (type erasure)") { EXPECT_NO_THROW(decode(encoded.metadata, encoded.data, io::Decoder(out))); EXPECT(out == encoded); } SECTION("decode using lvalue io::Decoder (type erasure)") { io::Decoder decoder(out); EXPECT_NO_THROW(decode(encoded.metadata, encoded.data, io::Decoder(out))); EXPECT(out == encoded); } SECTION("decode using decoder of decoder") { io::Decoder decoder(out); EXPECT_NO_THROW(decode(encoded.metadata, encoded.data, io::Decoder(decoder))); EXPECT(out == encoded); } } // ------------------------------------------------------------------------------------------------------- CASE("Decoding to std::array") { using T = double; EncodedArray encoded; std::array out; SECTION("decode std::vector directly") { EXPECT_NO_THROW(decode(encoded.metadata, encoded.data, out)); EXPECT(out == encoded); } SECTION("decode using rvalue io::Decoder (type erasure)") { EXPECT_NO_THROW(decode(encoded.metadata, encoded.data, io::Decoder(out))); EXPECT(out == encoded); } SECTION("decode using lvalue io::Decoder (type erasure)") { io::Decoder decoder(out); EXPECT_NO_THROW(decode(encoded.metadata, encoded.data, io::Decoder(out))); EXPECT(out == encoded); } SECTION("decode using decoder of decoder") { io::Decoder decoder(out); EXPECT_NO_THROW(decode(encoded.metadata, encoded.data, io::Decoder(decoder))); EXPECT(out == encoded); } } // ------------------------------------------------------------------------------------------------------- CASE("Encode/Decode byte array") { using T = std::byte; EncodedArray encoded; std::vector out; auto validate = [&]() { EXPECT(out == encoded); auto to_byte = []( const char* str) { return std::byte(std::bitset<8>(str).to_ulong()); }; EXPECT(out[0] == to_byte("00000001")); EXPECT(out[1] == to_byte("00000011")); EXPECT(out[2] == to_byte("00000111")); EXPECT(out[3] == to_byte("00001111")); EXPECT(out[4] == to_byte("00011111")); }; SECTION("decode directly") { EXPECT_NO_THROW(decode(encoded.metadata, encoded.data, out)); validate(); } SECTION("decode using rvalue io::Decoder (type erasure)") { EXPECT_NO_THROW(decode(encoded.metadata, encoded.data, io::Decoder(out))); validate(); } SECTION("decode using lvalue io::Decoder (type erasure)") { io::Decoder decoder(out); EXPECT_NO_THROW(decode(encoded.metadata, encoded.data, io::Decoder(out))); validate(); } SECTION("decode using decoder of decoder") { io::Decoder decoder(out); EXPECT_NO_THROW(decode(encoded.metadata, encoded.data, io::Decoder(decoder))); validate(); } } // ------------------------------------------------------------------------------------------------------- CASE("Encode/Decode string") { std::string in{"short string"}; io::Metadata metadata; io::Data data; encode(in, metadata, data); EXPECT_EQ(data.size(), 0); std::string out; decode(metadata, data, out); EXPECT_EQ(out, in); } // ------------------------------------------------------------------------------------------------------- template void test_encode_decode_scalar() { T in{std::numeric_limits::max()}, out; io::Metadata metadata; io::Data data; encode(in, metadata, data); EXPECT_EQ(data.size(), 0); decode(metadata, data, out); EXPECT_EQ(out, in); } CASE("Encode/Decode scalar") { // bit identical encoding via Base64 string within the metadata! SECTION("int32") { test_encode_decode_scalar(); } SECTION("int64") { test_encode_decode_scalar(); } SECTION("real32") { test_encode_decode_scalar(); } SECTION("real64") { test_encode_decode_scalar(); } SECTION("uint64") { test_encode_decode_scalar(); } } // ------------------------------------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/atlas_io/tests/TestEnvironment.h0000664000175000017500000002622515160212070021344 0ustar alastairalastair/* * (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 #include #include #include #include #include "eckit/config/LibEcKit.h" #include "eckit/config/Resource.h" #include "eckit/eckit.h" #include "eckit/log/PrefixTarget.h" #include "eckit/runtime/Main.h" #include "eckit/system/LibraryManager.h" #include "eckit/testing/Test.h" #include "eckit/types/Types.h" #include "atlas_io/atlas-io.h" #include "atlas_io/Trace.h" namespace atlas { namespace test { using eckit::types::is_approximately_equal; class Test; static Test* current_test_{nullptr}; static size_t ATLAS_MAX_FAILED_EXPECTS() { static size_t v = size_t(eckit::Resource("$ATLAS_MAX_FAILED_EXPECTS", 100)); return v; } class Test { struct Failure { std::string message; eckit::CodeLocation location; }; public: Test(const std::string& description, const eckit::CodeLocation& location): description_(description), location_(location) { current_test_ = this; } ~Test() { current_test_ = nullptr; } void expect_failed(const std::string& message, const eckit::CodeLocation& location) { failures_.emplace_back(Failure{message, location}); eckit::Log::error() << message << std::endl; if (failures_.size() == ATLAS_MAX_FAILED_EXPECTS()) { std::stringstream msg; msg << "Maximum number of allowed EXPECTS have failed (${ATLAS_MAX_FAILED_EXPECTS}=" << ATLAS_MAX_FAILED_EXPECTS() << ")."; throw eckit::testing::TestException(msg.str(), location_); } } bool failed() const { return failures_.size() > 0; } void throw_on_failed_expects() { if (failed()) { std::stringstream msg; msg << failures_.size() << " EXPECTS have failed"; throw eckit::testing::TestException(msg.str(), location_); } } const std::string& description() const { return description_; } private: std::vector failures_; std::string description_; eckit::CodeLocation location_; }; Test& current_test() { ATLAS_IO_ASSERT(current_test_); return *current_test_; } //---------------------------------------------------------------------------------------------------------------------- #ifdef MAYBE_UNUSED #define MAYBE_UNUSED [[maybe_unused]] #endif #ifdef EXPECT_EQ #undef EXPECT_EQ #endif #ifdef EXPECT_APPROX_EQ #undef EXPECT_APPROX_EQ #endif #ifdef EXPECT #undef EXPECT #endif #define REQUIRE(expr) \ do { \ if (!(expr)) { \ throw eckit::testing::TestException("EXPECT condition failed: " #expr, Here()); \ } \ } while (false) #define EXPECT(expr) \ do { \ if (!(expr)) { \ current_test().expect_failed("EXPECT condition failed: " #expr, Here()); \ } \ } while (false) template struct Printer { static void print(std::ostream& out, const Value& v) { out << v; } }; template <> struct Printer { static void print(std::ostream& out, const double& v) { out << std::fixed << std::setprecision(12) << v; } }; template <> struct Printer { static void print(std::ostream& out, const eckit::CodeLocation& location) { out << eckit::PathName{location.file()}.baseName() << " +" << location.line(); } }; template struct PrintValue { const Value& value; PrintValue(const Value& v): value(v) {} void print(std::ostream& out) const { Printer::print(out, value); } friend std::ostream& operator<<(std::ostream& out, const PrintValue& v) { v.print(out); return out; } }; template PrintValue print(const Value& v) { return PrintValue(v); } bool approx_eq(const float& v1, const float& v2) { return is_approximately_equal(v1, v2); } bool approx_eq(const float& v1, const float& v2, const float& t) { return is_approximately_equal(v1, v2, t); } bool approx_eq(const double& v1, const double& v2) { return is_approximately_equal(v1, v2); } bool approx_eq(const double& v1, const double& v2, const double& t) { return is_approximately_equal(v1, v2, t); } //bool approx_eq(const Point2& v1, const Point2& v2) { // return approx_eq(v1[0], v2[0]) && approx_eq(v1[1], v2[1]); //} //bool approx_eq(const Point2& v1, const Point2& v2, const double& t) { // return approx_eq(v1[0], v2[0], t) && approx_eq(v1[1], v2[1], t); //} template std::string expect_message(const std::string& condition, const T1& lhs, const T2& rhs, const eckit::CodeLocation& loc) { std::stringstream msg; msg << eckit::Colour::red << condition << " FAILED @ " << print(loc) << eckit::Colour::reset << "\n" << eckit::Colour::red << " --> lhs = " << print(lhs) << eckit::Colour::reset << "\n" << eckit::Colour::red << " --> rhs = " << print(rhs) << eckit::Colour::reset; return msg.str(); } #define EXPECT_EQ(lhs, rhs) \ do { \ if (!(lhs == rhs)) { \ current_test().expect_failed(expect_message("EXPECT_EQ( " #lhs ", " #rhs " )", lhs, rhs, Here()), Here()); \ } \ } while (false) #define __EXPECT_APPROX_EQ(lhs, rhs) \ do { \ if (!(approx_eq(lhs, rhs))) { \ current_test().expect_failed(expect_message("EXPECT_APPROX_EQ( " #lhs ", " #rhs " )", lhs, rhs, Here()), \ Here()); \ } \ } while (false) #define __EXPECT_APPROX_EQ_TOL(lhs, rhs, tol) \ do { \ if (!(approx_eq(lhs, rhs, tol))) { \ current_test().expect_failed( \ expect_message("EXPECT_APPROX_EQ( " #lhs ", " #rhs ", " #tol " )", lhs, rhs, Here()), Here()); \ } \ } while (false) #define EXPECT_APPROX_EQ(...) __ATLAS_IO_SPLICE(__EXPECT_APPROX_EQ__, __ATLAS_IO_NARG(__VA_ARGS__))(__VA_ARGS__) #define __EXPECT_APPROX_EQ__2 __EXPECT_APPROX_EQ #define __EXPECT_APPROX_EQ__3 __EXPECT_APPROX_EQ_TOL //---------------------------------------------------------------------------------------------------------------------- namespace { [[maybe_unused]] int digits(int number) { int d = 0; while (number) { number /= 10; d++; } return d; } [[maybe_unused]] std::string debug_prefix(const std::string& libname) { std::string s = libname; std::transform(s.begin(), s.end(), s.begin(), ::toupper); s += "_DEBUG"; return s; } [[maybe_unused]] void debug_addTarget(eckit::LogTarget* target) { for (std::string libname : eckit::system::LibraryManager::list()) { const eckit::system::Library& lib = eckit::system::LibraryManager::lookup(libname); if (lib.debug()) { lib.debugChannel().addTarget(new eckit::PrefixTarget(debug_prefix(libname), target)); } } if (eckit::Log::debug()) eckit::Log::debug().addTarget(target); } [[maybe_unused]] void debug_setTarget(eckit::LogTarget* target) { for (std::string libname : eckit::system::LibraryManager::list()) { const eckit::system::Library& lib = eckit::system::LibraryManager::lookup(libname); if (lib.debug()) { lib.debugChannel().setTarget(new eckit::PrefixTarget(debug_prefix(libname), target)); } } if (eckit::Log::debug()) eckit::Log::debug().setTarget(target); } [[maybe_unused]] void debug_reset() { for (std::string libname : eckit::system::LibraryManager::list()) { const eckit::system::Library& lib = eckit::system::LibraryManager::lookup(libname); if (lib.debug()) { lib.debugChannel().reset(); } } if (eckit::Log::debug()) eckit::Log::debug().reset(); } [[maybe_unused]] bool getEnv(const std::string& env, bool default_value) { const char* cenv = ::getenv(env.c_str()); if (cenv != nullptr) { return eckit::Translator()(cenv); } return default_value; } [[maybe_unused]] int getEnv(const std::string& env, int default_value) { const char* cenv = ::getenv(env.c_str()); if (cenv != nullptr) { return eckit::Translator()(cenv); } return default_value; } [[maybe_unused]] void setEnv(const std::string& env, bool value) { constexpr int DO_NOT_REPLACE_IF_EXISTS = 0; ::setenv(env.c_str(), eckit::Translator()(value).c_str(), DO_NOT_REPLACE_IF_EXISTS); } } // namespace struct TestEnvironment { TestEnvironment(int argc, char* argv[]) { eckit::Main::initialise(argc, argv); } ~TestEnvironment() {} }; //---------------------------------------------------------------------------------------------------------------------- template int run(int argc, char* argv[]) { Environment env(argc, argv); int errors = eckit::testing::run_tests(argc, argv, false); return errors; } int run(int argc, char* argv[]) { return run(argc, argv); } //---------------------------------------------------------------------------------------------------------------------- } // namespace test } // namespace atlas atlas-0.46.0/atlas_io/tests/test_io_record.cc0000664000175000017500000005320215160212070021335 0ustar alastairalastair/* * (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/io/MemoryHandle.h" #include "TestEnvironment.h" namespace atlas { namespace test { template struct Matrix { std::vector data_; size_t rows_; size_t cols_; size_t size() const { return rows_ * cols_; } T* data() { return data_.data(); } const T* data() const { return data_.data(); } atlas::io::DataType datatype() const { return atlas::io::make_datatype(); } Matrix(size_t rows, size_t cols) { resize(rows, cols); } void resize(size_t rows, size_t cols) { rows_ = rows; cols_ = cols; data_.resize(size()); } void assign(std::initializer_list list) { data_.assign(list); } void assign(T value) { data_.assign(size(), value); } }; template void interprete(const Matrix& in, atlas::io::ArrayReference& out) { out = io::ArrayReference(in.data(), in.datatype(), atlas::io::ArrayShape{in.rows_, in.cols_}); } template void decode(const atlas::io::Metadata& metadata, const atlas::io::Data& data, Matrix& out) { atlas::io::ArrayMetadata array(metadata); out.resize(array.shape(0), array.shape(1)); ::memcpy(out.data(), data, data.size()); } struct Arrays { std::vector v1; std::vector v2; Matrix v3{0, 0}; bool operator==(const Arrays& other) const { return v1 == other.v1 && ::memcmp(v2.data(), other.v2.data(), v2.size() * sizeof(float)) == 0 && ::memcmp(v3.data(), other.v3.data(), v3.size() * v3.datatype().size()) == 0; } bool operator!=(const Arrays& other) const { return not operator==(other); } }; //----------------------------------------------------------------------------- static eckit::LocalConfiguration no_compression = [] { eckit::LocalConfiguration c; c.set("compression", "none"); return c; }(); //----------------------------------------------------------------------------- std::string suffix() { static std::string suffix = eckit::Resource("--suffix", ""); return suffix; } //----------------------------------------------------------------------------- namespace globals { struct TestRecord { Arrays data; TestRecord() = default; TestRecord(const std::function& initializer) { initializer(data); } }; static TestRecord record1{[](Arrays& data) { data.v1 = {0, 1, 2, 3, 4}; data.v2 = {3, 2, 1}; data.v3.resize(3, 2); data.v3.assign({11, 12, 21, 22, 31, 32}); }}; static TestRecord record2{[](Arrays& data) { data.v1 = {0, 10, 20, 30, 40, 50}; data.v2 = {30, 20, 10, 40}; data.v3.resize(2, 3); data.v3.assign({11, 12, 13, 21, 22, 23}); }}; static TestRecord record3{[](Arrays& data) { data.v1.assign(1024 / 8 - 1, 2.); data.v2.assign(1023 * 1024 / 4 + 512 / 4, 1.); data.v3.resize(1024, 1024); data.v3.assign(3); }}; std::vector records; } // namespace globals //----------------------------------------------------------------------------- template void write_length(Length length, const std::string& path) { std::ofstream file(path); file << length; file.close(); } //-----------------------------------------------------------------------------// // // // Writing records // // // //-----------------------------------------------------------------------------// CASE("Write records, each in separate file (offset=0)") { auto write_record = [&](const Arrays& data, const eckit::PathName& path) { io::RecordWriter record; record.set("v1", io::ref(data.v1), no_compression); record.set("v2", io::ref(data.v2), no_compression); record.set("v3", io::ref(data.v3)); auto length = record.write(path); write_length(length, path + ".length"); }; SECTION("record1.atlas" + suffix()) { write_record(globals::record1.data, "record1.atlas" + suffix()); } SECTION("record2.atlas" + suffix()) { write_record(globals::record2.data, "record2.atlas" + suffix()); } SECTION("record3.atlas" + suffix()) { write_record(globals::record3.data, "record3.atlas" + suffix()); } } //----------------------------------------------------------------------------- CASE("Write records to same file using record.write(path,io::Mode)") { // This will reopen files upon every append, bad for performance static std::vector lengths; static std::vector offsets{0}; auto write_record = [&](Arrays& data, const eckit::PathName& path, io::Mode mode) { io::RecordWriter record; record.set("v1", io::ref(data.v1), no_compression); record.set("v2", io::ref(data.v2), no_compression); record.set("v3", io::ref(data.v3)); globals::records.emplace_back(io::Record::URI{path, offsets.back()}); lengths.emplace_back(record.write(path, mode)); offsets.emplace_back(offsets.back() + lengths.back()); }; SECTION("record1 -> records.atlas" + suffix()) { write_record(globals::record1.data, "records.atlas" + suffix(), io::Mode::write); } SECTION("record2 -> records.atlas" + suffix()) { write_record(globals::record2.data, "records.atlas" + suffix(), io::Mode::append); } SECTION("record3 -> records.atlas" + suffix()) { write_record(globals::record3.data, "records.atlas" + suffix(), io::Mode::append); } } //----------------------------------------------------------------------------- CASE("Write records to same file using record.write(Stream) keeping Stream open") { // This should give exactly the same output file as previous, except no auto write_record = [&](const Arrays& data, io::Stream stream) { io::RecordWriter record; record.set("v1", io::ref(data.v1), no_compression); record.set("v2", io::ref(data.v2), no_compression); record.set("v3", io::ref(data.v3)); record.write(stream); }; static io::OutputFileStream stream("records.atlas" + suffix() + ".duplicate"); SECTION("record1 -> records.atlas" + suffix() + ".duplicate") { EXPECT_EQ(stream.position(), globals::records[0].offset); write_record(globals::record1.data, stream); } SECTION("record2 -> records.atlas" + suffix() + ".duplicate") { EXPECT_EQ(stream.position(), globals::records[1].offset); write_record(globals::record2.data, stream); } SECTION("record3 -> records.atlas" + suffix() + ".duplicate") { EXPECT_EQ(stream.position(), globals::records[2].offset); write_record(globals::record3.data, stream); } SECTION("close stream") { stream.close(); // required because stream is a static variable } } //----------------------------------------------------------------------------- CASE("Write master record referencing record1 and record2 and record3") { io::RecordWriter record; record.set("v1", io::link("file:record1.atlas" + suffix() + "?key=v1")); record.set("v2", io::link("file:record1.atlas" + suffix() + "?key=v2")); record.set("v3", io::link("file:record1.atlas" + suffix() + "?key=v3")); record.set("v4", io::link("file:record2.atlas" + suffix() + "?key=v1")); record.set("v5", io::link("file:record2.atlas" + suffix() + "?key=v2")); record.set("v6", io::link("file:record2.atlas" + suffix() + "?key=v3")); record.set("v7", io::link("file:record3.atlas" + suffix() + "?key=v1")); record.set("v8", io::link("file:record3.atlas" + suffix() + "?key=v2")); record.set("v9", io::link("file:record3.atlas" + suffix() + "?key=v3")); record.write("record.atlas" + suffix()); } //----------------------------------------------------------------------------- CASE("Write records in nested subdirectories") { auto reference_path = eckit::PathName{"atlas_test_io_refpath"}; { eckit::PathName{reference_path / "links" / "1"}.mkdir(); io::RecordWriter record; record.set("v1", io::ref(globals::record1.data.v1)); record.set("v2", io::ref(globals::record1.data.v2)); record.set("v3", io::ref(globals::record1.data.v3)); record.set("s1", std::string("short string")); record.set("s2", double(1. / 3.)); record.write(reference_path / "links" / "1" / "record.atlas" + suffix()); } { eckit::PathName{reference_path / "links" / "2"}.mkdir(); io::RecordWriter record; record.set("v1", io::ref(globals::record2.data.v1)); record.set("v2", io::ref(globals::record2.data.v2)); record.set("v3", io::ref(globals::record2.data.v3)); record.set("s1", size_t(10000000000)); record.write(reference_path / "links" / "2" / "record.atlas" + suffix()); } { io::RecordWriter record; record.set("l1", io::link("file:1/record.atlas" + suffix() + "?key=v1")); record.set("l2", io::link("file:1/record.atlas" + suffix() + "?key=v2")); record.set("l3", io::link("file:1/record.atlas" + suffix() + "?key=v3")); record.set("l4", io::link("file:2/record.atlas" + suffix() + "?key=v1")); record.set("l5", io::link("file:2/record.atlas" + suffix() + "?key=v2")); record.set("l6", io::link("file:2/record.atlas" + suffix() + "?key=v3")); record.set("l7", io::link("file:1/record.atlas" + suffix() + "?key=s1")); record.set("l8", io::link("file:1/record.atlas" + suffix() + "?key=s2")); record.set("l9", io::link("file:2/record.atlas" + suffix() + "?key=s1")); record.write(reference_path / "links" / "record.atlas" + suffix()); } { io::RecordWriter record; record.set("l1", io::link("file:links/record.atlas" + suffix() + "?key=l1")); record.set("l2", io::link("file:links/record.atlas" + suffix() + "?key=l2")); record.set("l3", io::link("file:links/record.atlas" + suffix() + "?key=l3")); record.set("l4", io::link("file:links/record.atlas" + suffix() + "?key=l4")); record.set("l5", io::link("file:links/record.atlas" + suffix() + "?key=l5")); record.set("l6", io::link("file:links/record.atlas" + suffix() + "?key=l6")); record.set("l7", io::link("file:links/record.atlas" + suffix() + "?key=l7")); record.set("l8", io::link("file:links/record.atlas" + suffix() + "?key=l8")); record.set("l9", io::link("file:links/record.atlas" + suffix() + "?key=l9")); record.write(reference_path / "record.atlas" + suffix()); } } //-----------------------------------------------------------------------------// // // // Reading tests // // // //-----------------------------------------------------------------------------// CASE("Test RecordItemReader") { SECTION("file:record1.atlas" + suffix() + "?key=v2") { io::RecordItemReader reader{"file:record1.atlas" + suffix() + "?key=v2"}; { // When we only want to read metadata io::Metadata metadata; reader.read(metadata); EXPECT(metadata.link() == false); EXPECT_EQ(metadata.type(), "array"); EXPECT(metadata.data.compressed() == false); EXPECT_EQ(metadata.data.compression(), "none"); EXPECT_EQ(metadata.data.size(), globals::record1.data.v2.size() * sizeof(float)); } { // When we want to read both metadata and data io::Metadata metadata; io::Data data; reader.read(metadata, data); EXPECT(metadata.data.compressed() == false); EXPECT_EQ(metadata.data.compression(), "none"); EXPECT_EQ(metadata.data.size(), globals::record1.data.v2.size() * sizeof(float)); EXPECT_EQ(data.size(), metadata.data.size()); EXPECT_EQ(data.size(), metadata.data.compressed_size()); EXPECT(::memcmp(data, globals::record1.data.v2.data(), data.size()) == 0); } } SECTION("file:record.atlas" + suffix() + "?key=v9") { io::RecordItemReader reader{"file:record.atlas" + suffix() + "?key=v9"}; { // When we only want to read metadata io::Metadata metadata; reader.read(metadata); EXPECT(metadata.link() == true); EXPECT_EQ(metadata.link().str(), "file:record3.atlas" + suffix() + "?key=v3"); EXPECT_EQ(metadata.type(), "array"); } { // When we want to read both metadata and data io::Metadata metadata; io::Data data; reader.read(metadata, data); EXPECT(metadata.data.compressed() == (io::defaults::compression_algorithm() != "none")); EXPECT_EQ(metadata.data.compression(), io::defaults::compression_algorithm()); EXPECT_EQ(metadata.data.size(), globals::record3.data.v3.size() * sizeof(int)); EXPECT_EQ(data.size(), metadata.data.compressed_size()); } } } //----------------------------------------------------------------------------- CASE("Read records from different files") { Arrays data1, data2, data3; auto read_record = [&](const eckit::PathName& path, Arrays& data) { io::RecordReader record(path); record.read("v1", data.v1).wait(); record.read("v2", data.v2).wait(); record.read("v3", data.v3).wait(); }; read_record("record1.atlas" + suffix(), data1); read_record("record2.atlas" + suffix(), data2); read_record("record3.atlas" + suffix(), data3); EXPECT(data1 == globals::record1.data); EXPECT(data2 == globals::record2.data); EXPECT(data3 == globals::record3.data); } //----------------------------------------------------------------------------- CASE("Read multiple records from same file") { Arrays data1, data2; io::RecordReader record1(globals::records[0]); io::RecordReader record2(globals::records[1]); record1.read("v1", data1.v1).wait(); record1.read("v2", data1.v2).wait(); record1.read("v3", data1.v3).wait(); record2.read("v1", data2.v1).wait(); record2.read("v2", data2.v2).wait(); record2.read("v3", data2.v3).wait(); EXPECT(data1 == globals::record1.data); EXPECT(data2 == globals::record2.data); } //----------------------------------------------------------------------------- CASE("Write master record referencing record1 and record2") { io::RecordWriter record; record.set("v1", io::link("file:record1.atlas" + suffix() + "?key=v1")); record.set("v2", io::link("file:record1.atlas" + suffix() + "?key=v2")); record.set("v3", io::link("file:record1.atlas" + suffix() + "?key=v3")); record.set("v4", io::link("file:record2.atlas" + suffix() + "?key=v1")); record.set("v5", io::link("file:record2.atlas" + suffix() + "?key=v2")); record.set("v6", io::link("file:record2.atlas" + suffix() + "?key=v3")); record.write("record.atlas" + suffix()); } //----------------------------------------------------------------------------- CASE("Read master record") { Arrays data1, data2; io::RecordReader record("record.atlas" + suffix()); eckit::Log::info() << "record.metadata(\"v1\"): " << record.metadata("v1") << std::endl; record.read("v1", data1.v1).wait(); record.read("v2", data1.v2).wait(); record.read("v3", data1.v3).wait(); record.read("v4", data2.v1).wait(); record.read("v5", data2.v2).wait(); record.read("v6", data2.v3).wait(); EXPECT(data1 == globals::record1.data); EXPECT(data2 == globals::record2.data); } //----------------------------------------------------------------------------- CASE("Async read") { Arrays data1, data2; io::RecordReader record("record.atlas" + suffix()); // Request reads record.read("v1", data1.v1); record.read("v2", data1.v2); record.read("v3", data1.v3); record.read("v4", data2.v1); record.read("v5", data2.v2); record.read("v6", data2.v3); // Wait for specific requests record.wait("v4"); record.wait("v5"); record.wait("v6"); // Should have completed EXPECT(data2 == globals::record2.data); // Should not be complete yet EXPECT(data1 != globals::record1.data); // Wait for all requests; record.wait(); // Should have completed EXPECT(data1 == globals::record1.data); } //----------------------------------------------------------------------------- CASE("Recursive Write/read records in nested subdirectories") { auto reference_path = eckit::PathName{"atlas_test_io_refpath"}; // Read Arrays data1, data2; io::RecordReader record(reference_path / "record.atlas" + suffix()); record.read("l1", data1.v1).wait(); record.read("l2", data1.v2).wait(); record.read("l3", data1.v3).wait(); record.read("l4", data2.v1).wait(); record.read("l5", data2.v2).wait(); record.read("l6", data2.v3).wait(); std::string l7; double l8; size_t l9; record.read("l7", l7).wait(); record.read("l8", l8).wait(); record.read("l9", l9).wait(); EXPECT(data1 == globals::record1.data); EXPECT(data2 == globals::record2.data); EXPECT_EQ(l7, "short string"); EXPECT_EQ(l8, 1. / 3.); EXPECT_EQ(l9, 10000000000ul); } //----------------------------------------------------------------------------- CASE("Write record to memory") { const auto& data_write = globals::record3.data; const auto& v1 = globals::record3.data.v1; const auto& v2 = globals::record3.data.v2; const auto& v3 = globals::record3.data.v3; eckit::Buffer memory; // write { ATLAS_IO_TRACE("write"); io::RecordWriter record; record.compression(false); record.checksum(false); record.set("v1", io::ref(v1)); record.set("v2", io::ref(v2)); record.set("v3", io::ref(v3)); memory.resize(record.estimateMaximumSize()); eckit::Log::info() << "memory.size() : " << memory.size() << std::endl; ; eckit::MemoryHandle datahandle_out{memory}; datahandle_out.openForWrite(0); auto record_length = record.write(datahandle_out); datahandle_out.close(); // Without compression, this should be exact EXPECT_EQ(memory.size(), record_length); } // read with individual RecordItemReader { ATLAS_IO_TRACE("read with RecordItemReader"); io::Session session; eckit::MemoryHandle datahandle_in{memory}; datahandle_in.openForRead(); { io::RecordItemReader reader(datahandle_in, "v1"); io::Metadata metadata; io::Data data; reader.read(metadata, data); EXPECT(::memcmp(data, data_write.v1.data(), data.size()) == 0); } { io::RecordItemReader reader(datahandle_in, "v2"); io::Metadata metadata; io::Data data; reader.read(metadata, data); EXPECT(::memcmp(data, data_write.v2.data(), data.size()) == 0); } { io::RecordItemReader reader(datahandle_in, "v3"); io::Metadata metadata; io::Data data; reader.read(metadata, data); EXPECT(::memcmp(data, data_write.v3.data(), data.size()) == 0); } datahandle_in.close(); } // read with RecordReader { ATLAS_IO_TRACE("read with RecordReader"); Arrays data_read; eckit::MemoryHandle datahandle_in{memory}; datahandle_in.openForRead(); io::RecordReader reader(datahandle_in); reader.read("v1", data_read.v1); reader.read("v2", data_read.v2); reader.read("v3", data_read.v3); reader.wait(); datahandle_in.close(); EXPECT(data_read == data_write); } } //-----------------------------------------------------------------------------// // // // Reading tests // // // //-----------------------------------------------------------------------------// CASE("RecordPrinter") { SECTION("table") { eckit::LocalConfiguration table_with_details; table_with_details.set("format", "table"); table_with_details.set("details", true); io::RecordPrinter record{eckit::PathName("record1.atlas" + suffix()), table_with_details}; std::stringstream out; EXPECT_NO_THROW(out << record); eckit::Log::debug() << out.str(); } SECTION("yaml") { eckit::LocalConfiguration yaml_with_details; yaml_with_details.set("format", "yaml"); yaml_with_details.set("details", true); io::RecordPrinter record{eckit::PathName("record1.atlas" + suffix()), yaml_with_details}; std::stringstream out; EXPECT_NO_THROW(out << record); eckit::Log::debug() << out.str(); } } //----------------------------------------------------------------------------- } // namespace test } // namespace atlas int main(int argc, char** argv) { return atlas::test::run(argc, argv); } atlas-0.46.0/atlas_io/tests/CMakeLists.txt0000664000175000017500000000227215160212070020563 0ustar alastairalastair# (C) Copyright 2022 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. ecbuild_add_test( TARGET atlas_io_test_encoding SOURCES test_io_encoding.cc LIBS atlas_io ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) ecbuild_add_test( TARGET atlas_io_test_stream SOURCES test_io_stream.cc LIBS atlas_io ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ) ecbuild_add_executable( TARGET atlas_io_test_record SOURCES test_io_record.cc LIBS atlas_io NOINSTALL ) foreach( algorithm none bzip2 aec lz4 snappy ) string( TOUPPER ${algorithm} feature ) if( eckit_HAVE_${feature} OR algorithm MATCHES "none" ) ecbuild_add_test( TARGET atlas_io_test_record_COMPRESSION_${algorithm} COMMAND atlas_io_test_record ARGS --suffix ".${algorithm}" ENVIRONMENT ${ATLAS_TEST_ENVIRONMENT} ATLAS_IO_COMPRESSION=${algorithm} ) endif() endforeach() atlas-0.46.0/atlas_io/src/0000775000175000017500000000000015160212070015445 5ustar alastairalastairatlas-0.46.0/atlas_io/src/atlas_io/0000775000175000017500000000000015160212070017240 5ustar alastairalastairatlas-0.46.0/atlas_io/src/atlas_io/RecordWriter.cc0000664000175000017500000002141515160212070022165 0ustar alastairalastair/* * (C) Copyright 2020 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "atlas_io/RecordWriter.h" #include "atlas_io/Exceptions.h" #include "atlas_io/RecordWriter.h" #include "atlas_io/Trace.h" #include "atlas_io/detail/Checksum.h" #include "atlas_io/detail/Defaults.h" #include "atlas_io/detail/Encoder.h" #include "atlas_io/detail/RecordSections.h" namespace atlas { namespace io { //--------------------------------------------------------------------------------------------------------------------- template inline void write_struct(OStream& out, const Struct& s) { static_assert(Struct::bytes == sizeof(Struct), ""); if (out.write(reinterpret_cast(&s), sizeof(s)) != sizeof(s)) { throw WriteError("Could not write struct to stream"); } } //--------------------------------------------------------------------------------------------------------------------- template inline void write_string(OStream& out, const std::string& s) { if (out.write(s.data(), s.size()) != s.size()) { throw WriteError("Could not write string to stream"); } } //--------------------------------------------------------------------------------------------------------------------- size_t RecordWriter::write(Stream out) const { ATLAS_IO_TRACE("RecordWriter::write"); RecordHead r; auto begin_of_record = out.position(); auto position = [&begin_of_record, &out]() { return out.position() - begin_of_record; }; std::vector index; // Begin Record // ------------ atlas::io::write_struct(out, r); // Metadata section // ---------------- ATLAS_IO_TRACE_SCOPE("metadata section") { r.metadata_offset = position(); atlas::io::write_struct(out, RecordMetadataSection::Begin()); auto metadata_str = metadata(); atlas::io::write_string(out, metadata_str); atlas::io::write_struct(out, RecordMetadataSection::End()); r.metadata_length = position() - r.metadata_offset; r.metadata_checksum = do_checksum_ ? atlas::io::checksum(metadata_str.data(), metadata_str.size()) : std::string("none:"); // Index section // ------------- size_t nb_data_sections = static_cast(nb_data_sections_); r.index_offset = position(); atlas::io::write_struct(out, RecordDataIndexSection::Begin()); index.resize(nb_data_sections); for (size_t i = 0; i < nb_data_sections; ++i) { atlas::io::write_struct(out, index[i]); } atlas::io::write_struct(out, RecordDataIndexSection::End()); r.index_length = position() - r.index_offset; } // Data sections // ------------- ATLAS_IO_TRACE_SCOPE("data sections") { size_t i{0}; for (auto& key : keys_) { auto& encoder = encoders_.at(key); auto& info = info_.at(key); if (info.section() == 0) { continue; } atlas::io::Data data; encode_data(encoder, data); data.compress(info.compression()); auto& data_section = index[i]; data_section.offset = position(); atlas::io::write_struct(out, RecordDataSection::Begin()); if (data.write(out) != data.size()) { throw WriteError("Could not write data for item " + key + " to stream"); } atlas::io::write_struct(out, RecordDataSection::End()); data_section.length = position() - data_section.offset; data_section.checksum = do_checksum_ ? data.checksum() : std::string("none:"); ++i; } } // End Record // ---------- atlas::io::write_struct(out, RecordEnd()); auto end_of_record = out.position(); r.record_length = end_of_record - begin_of_record; r.time = atlas::io::Time::now(); out.seek(begin_of_record); atlas::io::write_struct(out, r); out.seek(begin_of_record + r.index_offset + sizeof(RecordDataIndexSection::Begin)); for (auto& entry : index) { atlas::io::write_struct(out, entry); } out.seek(end_of_record); // So that following writes will not overwrite return r.record_length; } //--------------------------------------------------------------------------------------------------------------------- void RecordWriter::compression(const std::string& c) { compression_ = c; } //--------------------------------------------------------------------------------------------------------------------- void RecordWriter::compression(bool on) { if (on) { compression_ = defaults::compression_algorithm(); // still possible to be "none" } else { compression_ = "none"; } } //--------------------------------------------------------------------------------------------------------------------- void RecordWriter::checksum(bool on) { if (on) { do_checksum_ = defaults::checksum_write(); // still possible to be off via environment } else { do_checksum_ = false; } } //--------------------------------------------------------------------------------------------------------------------- void RecordWriter::set(const RecordWriter::Key& key, Link&& link, const eckit::Configuration&) { keys_.emplace_back(key); encoders_[key] = Encoder{link}; info_.emplace(key, DataInfo{}); } //--------------------------------------------------------------------------------------------------------------------- void RecordWriter::set(const RecordWriter::Key& key, Encoder&& encoder, const eckit::Configuration& config) { DataInfo info; if (encoder.encodes_data()) { ++nb_data_sections_; info.compression(config.getString("compression", compression_)); info.section(nb_data_sections_); } keys_.emplace_back(key); encoders_[key] = std::move(encoder); info_.emplace(key, std::move(info)); } //--------------------------------------------------------------------------------------------------------------------- size_t RecordWriter::write(const eckit::PathName& path, Mode mode) const { return write(OutputFileStream(path, mode)); } //--------------------------------------------------------------------------------------------------------------------- size_t RecordWriter::write(eckit::DataHandle& out) const { return write(Stream(out)); } //--------------------------------------------------------------------------------------------------------------------- size_t RecordWriter::estimateMaximumSize() const { size_t size{0}; size += sizeof(RecordHead); size += sizeof(RecordMetadataSection::Begin); size += metadata().size(); size += sizeof(RecordMetadataSection::End); size += sizeof(RecordDataIndexSection::Begin); size += size_t(nb_data_sections_) * sizeof(RecordDataIndexSection::Entry); size += sizeof(RecordDataIndexSection::End); for (auto& key : keys_) { auto& encoder = encoders_.at(key); auto& info = info_.at(key); if (info.section() == 0) { continue; } size += sizeof(RecordDataSection::Begin); { atlas::io::Metadata m; size_t max_data_size = encode_metadata(encoder, m); if (info.compression() != "none") { max_data_size = size_t(1.2 * max_data_size); max_data_size = std::max(max_data_size, 10 * 1024); // minimum 10KB } size += max_data_size; } size += sizeof(RecordDataSection::End); } size += sizeof(RecordEnd); return size; } //--------------------------------------------------------------------------------------------------------------------- std::string RecordWriter::metadata() const { atlas::io::Metadata metadata; for (auto& key : keys_) { auto& encoder = encoders_.at(key); auto& info = info_.at(key); atlas::io::Metadata m; encode_metadata(encoder, m); if (info.section()) { m.set("data.section", info.section()); if (info.compression() != "none") { m.set("data.compression.type", info.compression()); } } metadata.set(key, m); } std::stringstream ss; atlas::io::write(metadata, ss); return ss.str(); } //--------------------------------------------------------------------------------------------------------------------- } // namespace io } // namespace atlas atlas-0.46.0/atlas_io/src/atlas_io/Record.h0000664000175000017500000000333615160212070020634 0ustar alastairalastair/* * (C) Copyright 2020 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include #include #include #include "atlas_io/detail/Endian.h" #include "atlas_io/detail/Time.h" #include "atlas_io/detail/Version.h" namespace atlas { namespace io { class Stream; class ParsedRecord; class Metadata; //--------------------------------------------------------------------------------------------------------------------- class Record { public: struct URI { std::string str() const; std::string path; std::uint64_t offset; URI() = default; URI(const std::string& _path, std::uint64_t _offset = 0): path(_path), offset(_offset) {} URI(const URI& other): path(other.path), offset(other.offset) {} }; private: std::shared_ptr record_; public: Record(); bool empty() const; Record& read(Stream& in, bool verify_end = false); const Metadata& metadata(const std::string& key) const; Endian endian() const; Version version() const; Time time() const; std::uint64_t size() const; const std::vector& keys() const; bool has(const std::string& key); operator const ParsedRecord&() const; }; //--------------------------------------------------------------------------------------------------------------------- } // namespace io } // namespace atlas atlas-0.46.0/atlas_io/src/atlas_io/RecordReader.h0000664000175000017500000000417315160212070021757 0ustar alastairalastair/* * (C) Copyright 2020 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include #include #include "atlas_io/Metadata.h" #include "atlas_io/ReadRequest.h" #include "atlas_io/Session.h" namespace atlas { namespace io { //--------------------------------------------------------------------------------------------------------------------- class RecordReader { public: RecordReader(const Record::URI& ref); RecordReader(const std::string& path, std::uint64_t offset = 0); RecordReader(Stream stream, std::uint64_t offset = 0); template ReadRequest& read(const std::string& key, T& value) { trace("read(" + key + ")", __FILE__, __LINE__, __func__); if (stream_) { trace("stream", __FILE__, __LINE__, __func__); requests_.emplace(key, ReadRequest{stream_, offset_, key, value}); } else { requests_.emplace(key, ReadRequest{uri(key), value}); } if (do_checksum_ >= 0) { requests_.at(key).checksum(do_checksum_); } return requests_.at(key); } void wait(const std::string& key); void wait(); ReadRequest& request(const std::string& key); Metadata metadata(const std::string& key); void checksum(bool); private: Record::URI uri() const; RecordItem::URI uri(const std::string& key) const; void trace(const std::string&, const char* file, int line, const char* func); private: Session session_; Stream stream_; std::map requests_; std::string path_; std::uint64_t offset_; int do_checksum_{-1}; }; //--------------------------------------------------------------------------------------------------------------------- } // namespace io } // namespace atlas atlas-0.46.0/atlas_io/src/atlas_io/RecordItem.cc0000664000175000017500000001076115160212070021611 0ustar alastairalastair/* * (C) Copyright 2020 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "RecordItem.h" #include "eckit/filesystem/URI.h" #include "atlas_io/atlas_compat.h" #include "atlas_io/detail/Assert.h" namespace atlas { namespace io { //--------------------------------------------------------------------------------------------------------------------- RecordItem::URI::URI(const std::string& _uri) { eckit::URI uri{_uri}; ATLAS_IO_ASSERT(uri.scheme() == "file"); ATLAS_IO_ASSERT(not uri.query("key").empty()); path = uri.path(); offset = 0; if (not uri.query("offset").empty()) { offset = std::stoul(uri.query("offset")); } key = uri.query("key"); } //--------------------------------------------------------------------------------------------------------------------- RecordItem::URI::URI(const std::string& _path, uint64_t _offset, const std::string& _key): path(_path), offset(_offset), key(_key) {} //--------------------------------------------------------------------------------------------------------------------- std::string RecordItem::URI::str() const { eckit::URI uri("file", eckit::PathName(path)); uri.query("offset", std::to_string(offset)); uri.query("key", key); return uri.asRawString(); } //--------------------------------------------------------------------------------------------------------------------- RecordItem::RecordItem(RecordItem&& other): metadata_(std::move(other.metadata_)), data_(std::move(other.data_)) {} //--------------------------------------------------------------------------------------------------------------------- RecordItem::RecordItem(Metadata&& metadata, Data&& data): metadata_(new Metadata(metadata)), data_(std::move(data)) {} //--------------------------------------------------------------------------------------------------------------------- const Data& RecordItem::data() const { return data_; } //--------------------------------------------------------------------------------------------------------------------- const Metadata& RecordItem::metadata() const { return *metadata_; } //--------------------------------------------------------------------------------------------------------------------- void RecordItem::metadata(const Metadata& m) { *metadata_ = m; } //--------------------------------------------------------------------------------------------------------------------- void RecordItem::data(Data&& d) { data_ = std::move(d); } //--------------------------------------------------------------------------------------------------------------------- bool RecordItem::empty() const { return metadata().empty(); } //--------------------------------------------------------------------------------------------------------------------- void RecordItem::clear() { data_.clear(); metadata_.reset(new atlas::io::Metadata()); } //--------------------------------------------------------------------------------------------------------------------- void RecordItem::decompress() { ATLAS_IO_ASSERT(not empty()); if (metadata().data.compressed()) { data_.decompress(metadata().data.compression(), metadata().data.size()); } metadata_->data.compressed(false); } //--------------------------------------------------------------------------------------------------------------------- void RecordItem::compress() { ATLAS_IO_ASSERT(not empty()); if (not metadata().data.compressed() && metadata().data.compression() != "none") { data_.compress(metadata().data.compression()); metadata_->data.compressed(true); } } //--------------------------------------------------------------------------------------------------------------------- size_t encode_metadata(const RecordItem& in, Metadata& metadata) { metadata.set(in.metadata()); return in.data().size(); } //--------------------------------------------------------------------------------------------------------------------- void encode_data(const RecordItem& in, Data& out) { out.assign(in.data()); } //--------------------------------------------------------------------------------------------------------------------- } // namespace io } // namespace atlas atlas-0.46.0/atlas_io/src/atlas_io/ReadRequest.h0000664000175000017500000000361115160212070021636 0ustar alastairalastair/* * (C) Copyright 2020 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include #include #include "atlas_io/RecordItem.h" #include "atlas_io/detail/Decoder.h" namespace atlas { namespace io { //--------------------------------------------------------------------------------------------------------------------- class ReadRequest { public: ReadRequest(ReadRequest&& other); template ReadRequest(Stream stream, size_t offset, const std::string& key, T& value): ReadRequest{stream, offset, key, new Decoder(value)} {} template ReadRequest(const std::string& URI, T& value): ReadRequest{URI, new Decoder(value)} {} template ReadRequest(const RecordItem::URI& URI, T& value): ReadRequest{URI.str(), value} {} ~ReadRequest(); void read(); void checksum(); void decompress(); void decode(); void wait(); void checksum(bool); private: ReadRequest(const std::string& URI, Decoder* decoder); ReadRequest(Stream, size_t offset, const std::string& key, Decoder*); ReadRequest() = delete; ReadRequest(const ReadRequest&) = delete; Stream stream_; size_t offset_; std::string key_; std::string uri_; std::unique_ptr decoder_; std::unique_ptr item_; bool do_checksum_{true}; bool finished_{false}; }; //--------------------------------------------------------------------------------------------------------------------- } // namespace io } // namespace atlas atlas-0.46.0/atlas_io/src/atlas_io/Trace.h0000664000175000017500000000472115160212070020453 0ustar alastairalastair/* * (C) Copyright 2020 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include #include #include #include namespace eckit { class CodeLocation; } namespace atlas { namespace io { struct TraceHook { TraceHook() = default; virtual ~TraceHook() = default; }; struct TraceHookRegistry { using TraceHookBuilder = std::function(const eckit::CodeLocation&, const std::string&)>; std::vector hooks; std::vector enabled_; static TraceHookRegistry& instance() { static TraceHookRegistry instance; return instance; } static size_t add(TraceHookBuilder&& hook) { instance().hooks.emplace_back(hook); instance().enabled_.emplace_back(true); return instance().hooks.size() - 1; } static size_t add(const TraceHookBuilder& hook) { instance().hooks.emplace_back(hook); instance().enabled_.emplace_back(true); return instance().hooks.size() - 1; } static void enable(size_t id) { instance().enabled_[id] = true; } static void disable(size_t id) { instance().enabled_[id] = false; } static bool enabled(size_t id) { return instance().enabled_[id]; } static size_t size() { return instance().hooks.size(); } static TraceHookBuilder& hook(size_t id) { return instance().hooks[id]; } static size_t invalidId() { return std::numeric_limits::max(); } private: TraceHookRegistry() = default; }; struct Trace { using Labels = std::vector; Trace(const eckit::CodeLocation& loc); Trace(const eckit::CodeLocation& loc, const std::string& title); Trace(const eckit::CodeLocation& loc, const std::string& title, const Labels& labels); private: std::vector> hooks_; }; } // namespace io } // namespace atlas #include "atlas_io/detail/BlackMagic.h" #define ATLAS_IO_TRACE(...) __ATLAS_IO_TYPE(::atlas::io::Trace, Here() __ATLAS_IO_COMMA_ARGS(__VA_ARGS__)) #define ATLAS_IO_TRACE_SCOPE(...) __ATLAS_IO_TYPE_SCOPE(::atlas::io::Trace, Here() __ATLAS_IO_COMMA_ARGS(__VA_ARGS__)) atlas-0.46.0/atlas_io/src/atlas_io/RecordReader.cc0000664000175000017500000000576415160212070022124 0ustar alastairalastair/* * (C) Copyright 2020 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "RecordReader.h" #include "atlas_io/Metadata.h" #include "atlas_io/RecordItemReader.h" namespace atlas { namespace io { //--------------------------------------------------------------------------------------------------------------------- RecordReader::RecordReader(const Record::URI& ref): RecordReader(ref.path, ref.offset) {} //--------------------------------------------------------------------------------------------------------------------- RecordReader::RecordReader(const std::string& path, uint64_t offset): session_{}, path_{path}, offset_{offset} {} RecordReader::RecordReader(Stream stream, uint64_t offset): session_{}, stream_{stream}, path_{}, offset_{offset} {} //--------------------------------------------------------------------------------------------------------------------- Record::URI RecordReader::uri() const { Record::URI _uri; _uri.path = path_; _uri.offset = offset_; return _uri; } //--------------------------------------------------------------------------------------------------------------------- RecordItem::URI RecordReader::uri(const std::string& key) const { return RecordItem::URI(path_, offset_, key); } //--------------------------------------------------------------------------------------------------------------------- void RecordReader::trace(const std::string&, const char* file, int line, const char* func) {} //--------------------------------------------------------------------------------------------------------------------- void RecordReader::wait(const std::string& key) { request(key).wait(); } //--------------------------------------------------------------------------------------------------------------------- void RecordReader::wait() { // This can be optimized perhaps to overlap IO with decoding in multithreaded environment for (auto& pair : requests_) { auto& request = pair.second; request.wait(); } } //--------------------------------------------------------------------------------------------------------------------- ReadRequest& RecordReader::request(const std::string& key) { return requests_.at(key); } //--------------------------------------------------------------------------------------------------------------------- Metadata RecordReader::metadata(const std::string& key) { Metadata metadata; RecordItemReader{uri(key)}.read(metadata); return metadata; } void RecordReader::checksum(bool b) { do_checksum_ = b; } //--------------------------------------------------------------------------------------------------------------------- } // namespace io } // namespace atlas atlas-0.46.0/atlas_io/src/atlas_io/Metadata.h0000664000175000017500000000657015160212070021141 0ustar alastairalastair/* * (C) Copyright 2020 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include #include "atlas_io/Stream.h" #include "atlas_io/detail/Checksum.h" #include "atlas_io/detail/DataInfo.h" #include "atlas_io/detail/Endian.h" #include "atlas_io/detail/Link.h" #include "atlas_io/detail/RecordInfo.h" #include "atlas_io/detail/Type.h" #include "eckit/config/LocalConfiguration.h" #include "eckit/value/Value.h" namespace atlas { namespace io { class Metadata; class Stream; //--------------------------------------------------------------------------------------------------------------------- size_t uncompressed_size(const atlas::io::Metadata& m); //--------------------------------------------------------------------------------------------------------------------- class Metadata : public eckit::LocalConfiguration { public: using eckit::LocalConfiguration::LocalConfiguration; Metadata(): eckit::LocalConfiguration() {} Link link() const { return Link{getString("link", "")}; } Type type() const { return Type{getString("type", "")}; } void link(atlas::io::Metadata&&); std::string json() const; DataInfo data; RecordInfo record; // extended LocalConfiguration: using eckit::LocalConfiguration::set; Metadata& set(const eckit::Configuration& other) { #if ATLAS_IO_ECKIT_VERSION_AT_LEAST(1, 26, 0) || ATLAS_IO_ECKIT_DEVELOP LocalConfiguration::set(other); #else eckit::Value& root = const_cast(get()); auto& other_root = other.get(); std::vector other_keys; eckit::fromValue(other_keys, other_root.keys()); for (auto& key : other_keys) { root[key] = other_root[key]; } #endif return *this; } /// @brief Constructor immediately setting a value. template Metadata(const std::string& name, const ValueT& value) { set(name, value); } /// @brief Constructor starting from a Configuration Metadata(const eckit::Configuration& other): eckit::LocalConfiguration(other) {} Metadata& remove(const std::string& name) { #if ATLAS_IO_ECKIT_VERSION_AT_LEAST(1, 26, 0) || ATLAS_IO_ECKIT_DEVELOP LocalConfiguration::remove(name); #else eckit::Value& root = const_cast(get()); root.remove(name); #endif return *this; } std::vector keys() const { #if ATLAS_IO_ECKIT_VERSION_AT_LEAST(1, 26, 0) || ATLAS_IO_ECKIT_DEVELOP return LocalConfiguration::keys(); #else std::vector result; eckit::fromValue(result, get().keys()); return result; #endif } }; //--------------------------------------------------------------------------------------------------------------------- void write(const atlas::io::Metadata&, std::ostream& out); void write(const atlas::io::Metadata&, atlas::io::Stream& out); //--------------------------------------------------------------------------------------------------------------------- } // namespace io } // namespace atlas atlas-0.46.0/atlas_io/src/atlas_io/Stream.cc0000664000175000017500000000343015160212070021002 0ustar alastairalastair/* * (C) Copyright 2020 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "atlas_io/Stream.h" #include "eckit/io/DataHandle.h" #include "atlas_io/atlas_compat.h" #include "atlas_io/detail/Assert.h" namespace atlas { namespace io { //--------------------------------------------------------------------------------------------------------------------- Stream::Stream(eckit::DataHandle& datahandle): ptr_(&datahandle) {} Stream::Stream(eckit::DataHandle* datahandle): shared_(datahandle), ptr_(shared_.get()) {} Stream::Stream(std::shared_ptr datahandle): shared_(datahandle), ptr_(shared_.get()) {} eckit::DataHandle& Stream::datahandle() { ATLAS_IO_ASSERT(ptr_ != nullptr); return *ptr_; } uint64_t Stream::seek(uint64_t offset) { ATLAS_IO_ASSERT(ptr_ != nullptr); return std::uint64_t(ptr_->seek(static_cast(offset))); } uint64_t Stream::position() { ATLAS_IO_ASSERT(ptr_ != nullptr); return std::uint64_t(ptr_->position()); } uint64_t Stream::write(const void* data, size_t length) { ATLAS_IO_ASSERT(ptr_ != nullptr); return std::uint64_t(ptr_->write(data, static_cast(length))); } uint64_t Stream::read(void* data, size_t length) { ATLAS_IO_ASSERT(ptr_ != nullptr); return std::uint64_t(ptr_->read(data, static_cast(length))); } //--------------------------------------------------------------------------------------------------------------------- } // namespace io } // namespace atlas atlas-0.46.0/atlas_io/src/atlas_io/Data.cc0000664000175000017500000000655515160212070020433 0ustar alastairalastair/* * (C) Copyright 2020 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "Data.h" #include #include "eckit/utils/Compressor.h" #include "atlas_io/Stream.h" #include "atlas_io/Trace.h" #include "atlas_io/detail/Assert.h" #include "atlas_io/detail/Checksum.h" namespace atlas { namespace io { //--------------------------------------------------------------------------------------------------------------------- Data::Data(void* p, size_t size): buffer_(p, size), size_(size) {} std::uint64_t Data::write(Stream& out) const { ATLAS_IO_TRACE(); if (size()) { ATLAS_IO_ASSERT(buffer_.size() >= size()); return out.write(buffer_.data(), size()); } return 0; } std::uint64_t Data::read(Stream& in, size_t size) { if (size > size_) { buffer_.resize(size); size_ = size; } return in.read(buffer_, size); } void Data::compress(const std::string& compression) { ATLAS_IO_TRACE("compress(" + compression + ")"); if (size_) { auto compressor = std::unique_ptr(eckit::CompressorFactory::instance().build(compression)); if (dynamic_cast(compressor.get())) { return; } eckit::Buffer compressed(size_t(1.2 * size_)); size_ = compressor->compress(buffer_, size_, compressed); buffer_ = std::move(compressed); } } void Data::decompress(const std::string& compression, size_t uncompressed_size) { ATLAS_IO_TRACE("decompress(" + compression + ")"); auto compressor = std::unique_ptr(eckit::CompressorFactory::instance().build(compression)); if (dynamic_cast(compressor.get())) { return; } eckit::Buffer uncompressed(size_t(1.2 * uncompressed_size)); compressor->uncompress(buffer_, size_, uncompressed, uncompressed_size); size_ = uncompressed_size; buffer_ = std::move(uncompressed); } void Data::clear() { buffer_ = eckit::Buffer{}; size_ = 0; } std::string Data::checksum(const std::string& algorithm) const { return atlas::io::checksum(buffer_, size_, algorithm); } void Data::assign(const Data& other) { if (other.size() > buffer_.size()) { buffer_.resize(other.size()); } size_ = other.size(); buffer_.copy(other.buffer_, size_); } void Data::assign(const void* p, size_t s) { if (s > size()) { buffer_.resize(s); } size_ = s; buffer_.copy(p, size_); } Data& compress(Data& data, const std::string& compression) { data.compress(compression); return data; } Data& decompress(Data& data, const std::string& compression, size_t uncompressed_size) { data.decompress(compression, uncompressed_size); return data; } //--------------------------------------------------------------------------------------------------------------------- void encode(const Data& in, Data& out) { out.assign(in); } //--------------------------------------------------------------------------------------------------------------------- } // namespace io } // namespace atlas atlas-0.46.0/atlas_io/src/atlas_io/Session.h0000664000175000017500000000204715160212070021037 0ustar alastairalastair/* * (C) Copyright 2020 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include #include "atlas_io/Record.h" #include "atlas_io/Stream.h" namespace atlas { namespace io { //--------------------------------------------------------------------------------------------------------------------- class Session { public: Session(); ~Session(); static bool active(); static Record record(const std::string& path, size_t offset); static Record record(Stream, size_t offset); static void store(Stream stream); }; //--------------------------------------------------------------------------------------------------------------------- } // namespace io } // namespace atlas atlas-0.46.0/atlas_io/src/atlas_io/atlas_compat.h0000664000175000017500000000001515160212070022054 0ustar alastairalastair#pragma once atlas-0.46.0/atlas_io/src/atlas_io/FileStream.cc0000664000175000017500000001204715160212070021606 0ustar alastairalastair/* * (C) Copyright 2020 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "atlas_io/FileStream.h" #include "eckit/io/FileHandle.h" #include "eckit/io/PooledHandle.h" #include "atlas_io/Session.h" #include "atlas_io/Trace.h" namespace atlas { namespace io { namespace { //--------------------------------------------------------------------------------------------------------------------- /// DataHandle that implements file IO for reading and writing /// Main differences with eckit::FileHandle: /// - Automatic opening and closing of file /// - Openmode argument: /// * read: for reading /// * write: for writing, will overwrite existing file /// * append: for appending implemented via write and seek to eof. /// - ATLAS_IO_TRACE recording class FileHandle : public eckit::FileHandle { public: FileHandle(const eckit::PathName& path, char openmode): eckit::FileHandle(path, openmode == 'a' /*overwrite*/) { ATLAS_IO_TRACE("FileHandle::open(" + eckit::FileHandle::path() + "," + openmode + ")"); if (openmode == 'r') { openForRead(); } else if (openmode == 'w' || (openmode == 'a' && not path.exists())) { openForWrite(0); } else if (openmode == 'a') { openForWrite(path.size()); seek(eckit::Offset(path.size())); } } void close() override { if (not closed_) { ATLAS_IO_TRACE("FileHandle::close(" + path() + ")"); eckit::FileHandle::close(); closed_ = true; } } FileHandle(const eckit::PathName& path, Mode openmode): FileHandle(path, openmode == Mode::read ? 'r' : openmode == Mode::write ? 'w' : 'a') {} FileHandle(const eckit::PathName& path, const std::string& openmode): FileHandle(path, openmode[0]) {} ~FileHandle() override { close(); } private: bool closed_{false}; }; //--------------------------------------------------------------------------------------------------------------------- /// DataHandle that implements file reading only. /// Internally there is a registry of opened files which avoids /// opening the same file multiple times. /// Note that close() will not actually close the file when there /// is another PooledHandle referencing the same file. /// /// Main difference with eckit::PooledHandle /// - Automatic opening and closing of file /// - ATLAS_IO_TRACE recording class PooledHandle : public eckit::PooledHandle { public: PooledHandle(const eckit::PathName& path): eckit::PooledHandle(path), path_(path) { ATLAS_IO_TRACE("PooledHandle::open(" + path_.baseName() + ")"); openForRead(); } ~PooledHandle() override { ATLAS_IO_TRACE("PooledHandle::close(" + path_.baseName() + ")"); close(); } eckit::PathName path_; }; } // namespace //--------------------------------------------------------------------------------------------------------------------- FileStream::FileStream(const eckit::PathName& path, char openmode): Stream([&path, &openmode]() -> eckit::DataHandle* { eckit::DataHandle* datahandle; if (openmode == 'r') { datahandle = new PooledHandle(path); } else { datahandle = new FileHandle(path, openmode); } return datahandle; }()) { if (openmode == 'r') { // Keep the PooledHandle alive until the end of active session Session::store(*this); } } FileStream::FileStream(const eckit::PathName& path, Mode openmode): FileStream(path, openmode == Mode::read ? 'r' : openmode == Mode::write ? 'w' : 'a') {} FileStream::FileStream(const eckit::PathName& path, const std::string& openmode): FileStream(path, openmode[0]) {} //--------------------------------------------------------------------------------------------------------------------- InputFileStream::InputFileStream(const eckit::PathName& path): FileStream(path, Mode::read) {} //--------------------------------------------------------------------------------------------------------------------- OutputFileStream::OutputFileStream(const eckit::PathName& path, Mode openmode): FileStream(path, openmode) {} OutputFileStream::OutputFileStream(const eckit::PathName& path, const std::string& openmode): FileStream(path, openmode) {} OutputFileStream::OutputFileStream(const eckit::PathName& path, char openmode): FileStream(path, openmode) {} void OutputFileStream::close() { datahandle().close(); } //--------------------------------------------------------------------------------------------------------------------- } // namespace io } // namespace atlas atlas-0.46.0/atlas_io/src/atlas_io/Trace.cc0000664000175000017500000000251515160212070020610 0ustar alastairalastair/* * (C) Copyright 2020 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "Trace.h" #include "eckit/log/CodeLocation.h" namespace atlas { namespace io { atlas::io::Trace::Trace(const eckit::CodeLocation& loc) { for (size_t id = 0; id < TraceHookRegistry::size(); ++id) { if (TraceHookRegistry::enabled(id)) { hooks_.emplace_back(TraceHookRegistry::hook(id)(loc, loc.func())); } } } Trace::Trace(const eckit::CodeLocation& loc, const std::string& title) { for (size_t id = 0; id < TraceHookRegistry::size(); ++id) { if (TraceHookRegistry::enabled(id)) { hooks_.emplace_back(TraceHookRegistry::hook(id)(loc, title)); } } } Trace::Trace(const eckit::CodeLocation& loc, const std::string& title, const Labels&) { for (size_t id = 0; id < TraceHookRegistry::size(); ++id) { if (TraceHookRegistry::enabled(id)) { hooks_.emplace_back(TraceHookRegistry::hook(id)(loc, title)); } } } } // namespace io } // namespace atlas atlas-0.46.0/atlas_io/src/atlas_io/types/0000775000175000017500000000000015160212070020404 5ustar alastairalastairatlas-0.46.0/atlas_io/src/atlas_io/types/array.h0000664000175000017500000000120415160212070021670 0ustar alastairalastair/* * (C) Copyright 2020 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include "atlas_io/types/array/ArrayReference.h" #include "atlas_io/types/array/adaptors/StdArrayAdaptor.h" #include "atlas_io/types/array/adaptors/StdVectorAdaptor.h" #include "atlas_io/types/array/adaptors/StdVectorOfStdArrayAdaptor.h" atlas-0.46.0/atlas_io/src/atlas_io/types/scalar.cc0000664000175000017500000001306015160212070022160 0ustar alastairalastair/* * (C) Copyright 2020 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ // Cray C++ compiler should not try to optimize this code #ifdef _CRAYC #pragma _CRI noopt #endif // GNU C++ compiler (version 11) should not try to optimize this code #if defined(__GNUC__) && !defined(__NVCOMPILER) && !defined(__clang__) #pragma GCC optimize("O0") #endif #include "scalar.h" #include #include #include "eckit/utils/ByteSwap.h" #include "atlas_io/atlas_compat.h" #include "atlas_io/detail/Assert.h" #include "atlas_io/detail/DataType.h" #include "atlas_io/Data.h" #include "atlas_io/Metadata.h" #include "atlas_io/detail/Base64.h" namespace atlas { namespace io { //--------------------------------------------------------------------------------------------------------------------- template void decode_scalar(const atlas::io::Metadata& metadata, T& value) { ATLAS_IO_ASSERT(metadata.getString("type") == "scalar"); ATLAS_IO_ASSERT(metadata.getString("datatype") == DataType::str()); metadata.get("value", value); } template void decode_scalar_b64(const atlas::io::Metadata& metadata, T& value) { ATLAS_IO_ASSERT(metadata.getString("type") == "scalar"); ATLAS_IO_ASSERT(metadata.getString("datatype") == DataType::str()); std::string base64 = metadata.getString("base64"); T value_ns = Base64::decode(base64); if (Endian::native == Endian::little) { T tmp = value_ns; eckit::byteswap(tmp); value_ns = tmp; } value = value_ns; } //--------------------------------------------------------------------------------------------------------------------- template void encode_scalar_metadata(const T& value, atlas::io::Metadata& out) { out.set("type", "scalar"); out.set("datatype", DataType::str()); out.set("value", value); } inline void encode_scalar_metadata(const unsigned long& value, atlas::io::Metadata& out) { out.set("type", "scalar"); out.set("datatype", DataType::str()); out.set("value", size_t(value)); } inline void encode_scalar_metadata(const unsigned long long& value, atlas::io::Metadata& out) { out.set("type", "scalar"); out.set("datatype", DataType::str()); out.set("value", size_t(value)); } template size_t encode_scalar_metadata_b64(const T& value, atlas::io::Metadata& out) { encode_scalar_metadata(value, out); T value_ns = value; if (Endian::native == Endian::little) { eckit::byteswap(value_ns); } out.set("base64", Base64::encode(value_ns)); return 0; } //--------------------------------------------------------------------------------------------------------------------- size_t encode_metadata(const int& value, atlas::io::Metadata& out) { return encode_scalar_metadata_b64(value, out); } size_t encode_metadata(const long& value, atlas::io::Metadata& out) { return encode_scalar_metadata_b64(value, out); } size_t encode_metadata(const long long& value, atlas::io::Metadata& out) { return encode_scalar_metadata_b64(value, out); } size_t encode_metadata(const unsigned long& value, atlas::io::Metadata& out) { return encode_scalar_metadata_b64(value, out); } size_t encode_metadata(const unsigned long long& value, atlas::io::Metadata& out) { return encode_scalar_metadata_b64(value, out); } size_t encode_metadata(const float& value, atlas::io::Metadata& out) { return encode_scalar_metadata_b64(value, out); } size_t encode_metadata(const double& value, atlas::io::Metadata& out) { return encode_scalar_metadata_b64(value, out); } //--------------------------------------------------------------------------------------------------------------------- void encode_data(const int&, atlas::io::Data&) {} void encode_data(const long&, atlas::io::Data&) {} void encode_data(const long long&, atlas::io::Data&) {} void encode_data(const unsigned long&, atlas::io::Data&) {} void encode_data(const unsigned long long&, atlas::io::Data&) {} void encode_data(const float&, atlas::io::Data&) {} void encode_data(const double&, atlas::io::Data&) {} //--------------------------------------------------------------------------------------------------------------------- void decode(const atlas::io::Metadata& metadata, const atlas::io::Data&, int& value) { decode_scalar(metadata, value); } void decode(const atlas::io::Metadata& metadata, const atlas::io::Data&, long& value) { decode_scalar(metadata, value); } void decode(const atlas::io::Metadata& metadata, const atlas::io::Data&, long long& value) { decode_scalar(metadata, value); } void decode(const atlas::io::Metadata& metadata, const atlas::io::Data&, unsigned long& value) { decode_scalar(metadata, value); } void decode(const atlas::io::Metadata& metadata, const atlas::io::Data&, unsigned long long& value) { decode_scalar_b64(metadata, value); } void decode(const atlas::io::Metadata& metadata, const atlas::io::Data&, float& value) { decode_scalar_b64(metadata, value); } void decode(const atlas::io::Metadata& metadata, const atlas::io::Data&, double& value) { decode_scalar_b64(metadata, value); } //--------------------------------------------------------------------------------------------------------------------- } // namespace io } // namespace atlas atlas-0.46.0/atlas_io/src/atlas_io/types/string.h0000664000175000017500000000242315160212070022064 0ustar alastairalastair/* * (C) Copyright 2020 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include "atlas_io/Data.h" #include "atlas_io/Metadata.h" #include "atlas_io/atlas_compat.h" #include "atlas_io/detail/Assert.h" namespace atlas { namespace io { //--------------------------------------------------------------------------------------------------------------------- inline size_t encode_metadata(const std::string& value, atlas::io::Metadata& out) { out.set("type", "string"); out.set("value", value); return 0; } inline void encode_data(const std::string&, atlas::io::Data&) {} inline void decode(const atlas::io::Metadata& metadata, const atlas::io::Data&, std::string& value) { ATLAS_IO_ASSERT(metadata.getString("type") == "string"); metadata.get("value", value); } //--------------------------------------------------------------------------------------------------------------------- } // namespace io } // namespace atlas atlas-0.46.0/atlas_io/src/atlas_io/types/scalar.h0000664000175000017500000000451115160212070022023 0ustar alastairalastair/* * (C) Copyright 2020 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include "atlas_io/Data.h" #include "atlas_io/Metadata.h" namespace atlas { namespace io { //--------------------------------------------------------------------------------------------------------------------- size_t encode_metadata(const int&, atlas::io::Metadata&); size_t encode_metadata(const long&, atlas::io::Metadata&); size_t encode_metadata(const long long&, atlas::io::Metadata&); size_t encode_metadata(const unsigned long&, atlas::io::Metadata&); size_t encode_metadata(const unsigned long long&, atlas::io::Metadata&); size_t encode_metadata(const float&, atlas::io::Metadata&); size_t encode_metadata(const double&, atlas::io::Metadata&); //--------------------------------------------------------------------------------------------------------------------- void encode_data(const int&, atlas::io::Data&); void encode_data(const long&, atlas::io::Data&); void encode_data(const long long&, atlas::io::Data&); void encode_data(const unsigned long&, atlas::io::Data&); void encode_data(const unsigned long long&, atlas::io::Data&); void encode_data(const float&, atlas::io::Data&); void encode_data(const double&, atlas::io::Data&); //--------------------------------------------------------------------------------------------------------------------- void decode(const atlas::io::Metadata&, const atlas::io::Data&, int&); void decode(const atlas::io::Metadata&, const atlas::io::Data&, long&); void decode(const atlas::io::Metadata&, const atlas::io::Data&, long long&); void decode(const atlas::io::Metadata&, const atlas::io::Data&, unsigned long&); void decode(const atlas::io::Metadata&, const atlas::io::Data&, unsigned long long&); void decode(const atlas::io::Metadata&, const atlas::io::Data&, float&); void decode(const atlas::io::Metadata&, const atlas::io::Data&, double&); //--------------------------------------------------------------------------------------------------------------------- } // namespace io } // namespace atlas atlas-0.46.0/atlas_io/src/atlas_io/types/array/0000775000175000017500000000000015160212070021522 5ustar alastairalastairatlas-0.46.0/atlas_io/src/atlas_io/types/array/ArrayMetadata.h0000664000175000017500000000733015160212070024415 0ustar alastairalastair/* * (C) Copyright 2020 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include #include #include "atlas_io/Metadata.h" #include "atlas_io/detail/DataType.h" namespace atlas { namespace io { //--------------------------------------------------------------------------------------------------------------------- class ArrayShape : public std::vector { private: using Base = std::vector; public: ArrayShape() {} ArrayShape(Base&& base): Base(std::forward(base)) {} template ArrayShape(std::initializer_list list): Base(list.begin(), list.end()) {} template ArrayShape(idx_t data[], size_t size): Base(data, data + size) {} template ArrayShape(const std::array& list): Base(list.begin(), list.end()) {} template ArrayShape(const std::vector& list): Base(list.begin(), list.end()) {} template >> ArrayShape(Int1 i) { resize(1); operator[](0) = i; } template && std::is_integral_v>> ArrayShape(Int1 i, Int2 j) { resize(2); operator[](0) = i; operator[](1) = j; } template && std::is_integral_v && std::is_integral_v>> ArrayShape(Int1 i, Int2 j, Int3 k) { resize(3); operator[](0) = i; operator[](1) = j; operator[](2) = k; } template && std::is_integral_v && std::is_integral_v && std::is_integral_v>> ArrayShape(Int1 i, Int2 j, Int3 k, Int4 l) { resize(4); operator[](0) = i; operator[](1) = j; operator[](2) = k; operator[](3) = l; } }; //--------------------------------------------------------------------------------------------------------------------- class ArrayMetadata { public: using ArrayShape = io::ArrayShape; using DataType = io::DataType; static std::string type() { return "array"; } public: ArrayMetadata(); explicit ArrayMetadata(const Metadata&); explicit ArrayMetadata(const DataType&, const ArrayShape&); explicit ArrayMetadata(const ArrayMetadata&); ArrayMetadata(ArrayMetadata&&); ArrayMetadata& operator=(ArrayMetadata&&); int rank() const { return int(shape_.size()); } int shape(int i) const; const ArrayShape& shape() const { return shape_; } DataType datatype() const { return datatype_; } size_t size() const; size_t bytes() const { return size() * datatype_.size(); } friend size_t encode_metadata(const ArrayMetadata& value, atlas::io::Metadata& out); private: ArrayShape shape_; DataType datatype_; }; //--------------------------------------------------------------------------------------------------------------------- size_t encode_metadata(const ArrayMetadata& value, atlas::io::Metadata& out); //--------------------------------------------------------------------------------------------------------------------- } // namespace io } // namespace atlas atlas-0.46.0/atlas_io/src/atlas_io/types/array/ArrayReference.cc0000664000175000017500000000631715160212070024735 0ustar alastairalastair/* * (C) Copyright 2020 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "ArrayReference.h" #include "atlas_io/atlas_compat.h" #include "atlas_io/detail/Assert.h" namespace atlas { namespace io { //--------------------------------------------------------------------------------------------------------------------- void encode_data(const ArrayReference& value, atlas::io::Data& out) { out = atlas::io::Data(value.data(), value.bytes()); } //--------------------------------------------------------------------------------------------------------------------- namespace { template void encode_metadata_value(const ArrayReference& value, atlas::io::Metadata& out) { ATLAS_IO_ASSERT(value.datatype() == make_datatype()); const T* array = reinterpret_cast(value.data()); std::vector vector(value.size()); std::copy(array, array + vector.size(), vector.begin()); out.set("value", vector); } } // namespace size_t encode_metadata(const ArrayReference& value, atlas::io::Metadata& out) { auto bytes = encode_metadata(static_cast(value), out); if (value.rank() == 1 && value.size() <= 4) { auto kind = value.datatype().kind(); using DataType = ArrayReference::DataType; if (kind == DataType::kind()) { encode_metadata_value(value, out); } else if (kind == DataType::kind()) { encode_metadata_value(value, out); } else if (kind == DataType::kind()) { encode_metadata_value(value, out); } else if (kind == DataType::kind()) { encode_metadata_value(value, out); } else if (kind == DataType::kind()) { encode_metadata_value(value, out); } } return bytes; } //--------------------------------------------------------------------------------------------------------------------- ArrayReference::ArrayReference(const void* data, ArrayMetadata::DataType datatype, const ArrayMetadata::ArrayShape& shape): ArrayMetadata(datatype, shape), data_(const_cast(data)) {} //--------------------------------------------------------------------------------------------------------------------- ArrayReference::ArrayReference(ArrayReference&& other): ArrayMetadata(std::move(other)), data_(other.data_) { other.data_ = nullptr; } //--------------------------------------------------------------------------------------------------------------------- ArrayReference& ArrayReference::operator=(ArrayReference&& rhs) { ArrayMetadata::operator=(std::move(rhs)); data_ = rhs.data_; rhs.data_ = nullptr; return *this; } //--------------------------------------------------------------------------------------------------------------------- } // namespace io } // namespace atlas atlas-0.46.0/atlas_io/src/atlas_io/types/array/adaptors/0000775000175000017500000000000015160212070023337 5ustar alastairalastairatlas-0.46.0/atlas_io/src/atlas_io/types/array/adaptors/StdArrayAdaptor.h0000664000175000017500000000405715160212070026562 0ustar alastairalastair/* * (C) Copyright 2020 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include "atlas_io/Data.h" #include "atlas_io/Exceptions.h" #include "atlas_io/Metadata.h" #include "atlas_io/types/array/ArrayMetadata.h" #include "atlas_io/types/array/ArrayReference.h" namespace std { //--------------------------------------------------------------------------------------------------------------------- template void interprete(const std::array& vector, atlas::io::ArrayReference& out) { using atlas::io::ArrayReference; out = ArrayReference{vector.data(), {N}}; } //--------------------------------------------------------------------------------------------------------------------- template void decode(const atlas::io::Metadata& m, const atlas::io::Data& encoded, std::array& out) { atlas::io::ArrayMetadata array(m); if (array.datatype().kind() != atlas::io::ArrayMetadata::DataType::kind()) { std::stringstream err; err << "Could not decode " << m.json() << " into std::vector<" << atlas::io::demangle() << ">. " << "Incompatible datatype!"; throw atlas::io::Exception(err.str(), Here()); } if (array.size() != N) { std::stringstream err; err << "Could not decode " << m.json() << " into std::array<" << atlas::io::demangle() << "," << N << ">. " << "Incompatible size!"; throw atlas::io::Exception(err.str(), Here()); } const T* data = static_cast(encoded.data()); std::copy(data, data + N, out.begin()); } //--------------------------------------------------------------------------------------------------------------------- } // end namespace std atlas-0.46.0/atlas_io/src/atlas_io/types/array/adaptors/StdVectorAdaptor.h0000664000175000017500000000376115160212070026747 0ustar alastairalastair/* * (C) Copyright 2020 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include "atlas_io/Data.h" #include "atlas_io/Exceptions.h" #include "atlas_io/Metadata.h" #include "atlas_io/detail/TypeTraits.h" #include "atlas_io/types/array/ArrayMetadata.h" #include "atlas_io/types/array/ArrayReference.h" namespace std { //--------------------------------------------------------------------------------------------------------------------- template = 0> void interprete(const std::vector& vector, atlas::io::ArrayReference& out) { using atlas::io::ArrayReference; out = ArrayReference{vector.data(), {int(vector.size())}}; } //--------------------------------------------------------------------------------------------------------------------- template = 0> void decode(const atlas::io::Metadata& m, const atlas::io::Data& encoded, std::vector& out) { atlas::io::ArrayMetadata array(m); if (array.datatype().kind() != atlas::io::ArrayMetadata::DataType::kind()) { std::stringstream err; err << "Could not decode " << m.json() << " into std::vector<" << atlas::io::demangle() << ">. " << "Incompatible datatypes: " << array.datatype().str() << " and " << atlas::io::ArrayMetadata::DataType::str() << "."; throw atlas::io::Exception(err.str(), Here()); } const T* data = static_cast(encoded.data()); out.assign(data, data + array.size()); } //--------------------------------------------------------------------------------------------------------------------- } // end namespace std atlas-0.46.0/atlas_io/src/atlas_io/types/array/adaptors/StdVectorOfStdArrayAdaptor.h0000664000175000017500000000503615160212070030703 0ustar alastairalastair/* * (C) Copyright 2020 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include #include "atlas_io/Data.h" #include "atlas_io/Exceptions.h" #include "atlas_io/Metadata.h" #include "atlas_io/types/array/ArrayMetadata.h" #include "atlas_io/types/array/ArrayReference.h" namespace std { //--------------------------------------------------------------------------------------------------------------------- template void interprete(const std::vector>& vector_of_array, atlas::io::ArrayReference& out) { using atlas::io::ArrayReference; out = ArrayReference{vector_of_array.front().data(), {vector_of_array.size(),N}}; } //--------------------------------------------------------------------------------------------------------------------- template void decode(const atlas::io::Metadata& m, const atlas::io::Data& encoded, std::vector>& out) { atlas::io::ArrayMetadata array(m); if (array.datatype().kind() != atlas::io::ArrayMetadata::DataType::kind()) { std::stringstream err; err << "Could not decode " << m.json() << " into std::vector<" << atlas::io::demangle() << ">. " << "Incompatible datatype!"; throw atlas::io::Exception(err.str(), Here()); } if (array.rank() != 2) { std::stringstream err; err << "Could not decode " << m.json() << " into std::vector() << "," << N << ">>. " << "Incompatible rank!"; throw atlas::io::Exception(err.str(), Here()); } if (array.shape(1) != N) { std::stringstream err; err << "Could not decode " << m.json() << " into std::vector() << "," << N << ">>. " << "Incompatible size!"; throw atlas::io::Exception(err.str(), Here()); } const std::array* data = static_cast*>(encoded.data()); // std::copy(data, data + array.shape(0), out.begin()); out.assign(data, data + array.shape(0)); } //--------------------------------------------------------------------------------------------------------------------- } // end namespace std atlas-0.46.0/atlas_io/src/atlas_io/types/array/ArrayReference.h0000664000175000017500000000323315160212070024571 0ustar alastairalastair/* * (C) Copyright 2020 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include "atlas_io/Data.h" #include "atlas_io/Metadata.h" #include "atlas_io/types/array/ArrayMetadata.h" namespace atlas { namespace io { //--------------------------------------------------------------------------------------------------------------------- class ArrayReference : public ArrayMetadata { public: ArrayReference() = default; ArrayReference(const void* data, DataType, const ArrayShape&); template ArrayReference(const T* data, const ArrayShape& shape): ArrayMetadata(DataType::create(), shape), data_(const_cast(data)) {} ArrayReference(ArrayReference&&); ArrayReference& operator=(ArrayReference&&); void* data() const { return data_; } friend void decode(const atlas::io::Metadata&, const atlas::io::Data&, ArrayReference&); private: void* data_{nullptr}; }; //--------------------------------------------------------------------------------------------------------------------- size_t encode_metadata(const ArrayReference& value, atlas::io::Metadata& out); void encode_data(const ArrayReference& value, atlas::io::Data& out); //--------------------------------------------------------------------------------------------------------------------- } // namespace io } // namespace atlas atlas-0.46.0/atlas_io/src/atlas_io/types/array/ArrayMetadata.cc0000664000175000017500000000725115160212070024555 0ustar alastairalastair/* * (C) Copyright 2020 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "ArrayMetadata.h" #include #include #include #include "atlas_io/Exceptions.h" #include "atlas_io/atlas_compat.h" #include "atlas_io/detail/Assert.h" namespace atlas { namespace io { //--------------------------------------------------------------------------------------------------------------------- size_t encode_metadata(const ArrayMetadata& value, atlas::io::Metadata& out) { out.set("type", value.type()); out.set("shape", value.shape_); out.set("datatype", value.datatype_.str()); return value.bytes(); } //--------------------------------------------------------------------------------------------------------------------- int ArrayMetadata::shape(int i) const { if (i >= rank()) { throw Exception( "ArrayMetadata::shape(i=" + std::to_string(i) + ") goes out of bounds. rank=" + std::to_string(rank()), Here()); } return shape_[size_t(i)]; } //--------------------------------------------------------------------------------------------------------------------- ArrayMetadata::ArrayMetadata(const Metadata& metadata): datatype_(DataType::KIND_REAL64) /* circumvent absense of default constructor */ { std::string encoded_type; ATLAS_IO_ASSERT_MSG(metadata.get("type", encoded_type), "metadata is missing 'type'"); ATLAS_IO_ASSERT_MSG(encoded_type == type(), "metadata has unexpected type '" + encoded_type + "'"); ATLAS_IO_ASSERT_MSG(metadata.get("shape", shape_), "metadata is missing 'shape'"); std::string datatype_str; ATLAS_IO_ASSERT_MSG(metadata.get("datatype", datatype_str), "metadata is missing 'datatype'"); datatype_ = DataType(datatype_str); } //--------------------------------------------------------------------------------------------------------------------- size_t ArrayMetadata::size() const { return size_t(std::accumulate(shape_.begin(), shape_.end(), 1, std::multiplies())); } //--------------------------------------------------------------------------------------------------------------------- ArrayMetadata::ArrayMetadata(): shape_(), datatype_(DataType::KIND_REAL64) /* circumvent absense of default constructor */ {} //--------------------------------------------------------------------------------------------------------------------- ArrayMetadata::ArrayMetadata(const DataType& datatype, const ArrayShape& shape): shape_(shape), datatype_(datatype) {} //--------------------------------------------------------------------------------------------------------------------- ArrayMetadata::ArrayMetadata(const ArrayMetadata& other): ArrayMetadata{other.datatype_, other.shape_} {} //--------------------------------------------------------------------------------------------------------------------- ArrayMetadata::ArrayMetadata(ArrayMetadata&& other): shape_(std::move(other.shape_)), datatype_{other.datatype_} {} //--------------------------------------------------------------------------------------------------------------------- ArrayMetadata& ArrayMetadata::operator=(ArrayMetadata&& rhs) { shape_ = std::move(rhs.shape_); datatype_ = rhs.datatype_; return *this; } //--------------------------------------------------------------------------------------------------------------------- } // namespace io } // namespace atlas atlas-0.46.0/atlas_io/src/atlas_io/RecordWriter.h0000664000175000017500000001063615160212070022032 0ustar alastairalastair/* * (C) Copyright 2020 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include #include #include "atlas_io/FileStream.h" #include "atlas_io/RecordItem.h" #include "atlas_io/Stream.h" #include "atlas_io/detail/Encoder.h" #include "atlas_io/detail/NoConfig.h" #include "atlas_io/detail/Reference.h" #include "atlas_io/detail/TypeTraits.h" #include "atlas_io/types/array/ArrayReference.h" #include "atlas_io/types/scalar.h" #include "atlas_io/types/string.h" #include "atlas_io/detail/Defaults.h" namespace atlas { namespace io { template Interpreted interprete(T& in) { Interpreted interpreted; interprete(in, interpreted); return interpreted; } //--------------------------------------------------------------------------------------------------------------------- /// @class RecordWriter /// @brief Write record class RecordWriter { public: using Key = std::string; public: /// @brief Set compression void compression(const std::string&); /// @brief Set compression off or to default void compression(bool); /// @brief Set checksum off or to default void checksum(bool); // -- set( Key, Value ) where Value can be a variety of things /// @brief Add link to other record item (RecordItem::URI) void set(const Key&, Link&&, const eckit::Configuration& = NoConfig()); /// @brief Add item to record void set(const Key&, Encoder&&, const eckit::Configuration& = NoConfig()); /// @brief Add item to record template = 0> void set(const Key& key, Value&& value, const eckit::Configuration& config = NoConfig()) { set(key, Encoder{std::move(value)}, config); } /// @brief Add item to record template void set(const Key& key, const Reference& value, const eckit::Configuration& config = NoConfig()) { set(key, std::move(value), config); } /// @brief Add item to record template = 0> void set(const Key& key, const Value& value, const eckit::Configuration& config = NoConfig()) { set(key, RecordItem(interprete(value)), config); } /// @brief Add item to record template = 0> void set(const Key& key, const Value& value, const eckit::Configuration& config = NoConfig()) { set(key, Encoder{value}, config); } void set(const Key& key, const char* value, const eckit::Configuration& config = NoConfig()) { set(key, Encoder{std::string(value)}, config); } void set(const Key& key, const std::string& value, const eckit::Configuration& config = NoConfig()) { set(key, Encoder{std::string(value)}, config); } /// @brief Write new record to path size_t write(const eckit::PathName&, Mode = Mode::write) const; /// @brief Write new record to a DataHandle /// @pre The DataHandle must be opened for Write access. size_t write(eckit::DataHandle&) const; /// @brief Write new record to a Stream /// @pre The Stream must be opened for Write access. size_t write(Stream) const; /// @brief estimate maximum size of record /// /// This could be useful to write a record to a fixed size MemoryHandle /// /// @note Without compression this matches exactly the required record size. /// With compression active, the data sizes are assumed to be 120% of uncompressed sizes for robustness, /// which may seem contradictory. size_t estimateMaximumSize() const; private: std::vector keys_; std::map encoders_; std::map info_; std::string compression_{defaults::compression_algorithm()}; int do_checksum_{defaults::checksum_write()}; int nb_data_sections_{0}; std::string metadata() const; }; //--------------------------------------------------------------------------------------------------------------------- } // namespace io } // namespace atlas atlas-0.46.0/atlas_io/src/atlas_io/atlas-io.h0000664000175000017500000001100515160212070021117 0ustar alastairalastair/* * (C) Copyright 2020 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include #include #include "atlas_io/detail/Link.h" #include "atlas_io/detail/Reference.h" #include "atlas_io/detail/StaticAssert.h" #include "atlas_io/detail/sfinae.h" #include "atlas_io/Exceptions.h" #include "atlas_io/FileStream.h" #include "atlas_io/Record.h" #include "atlas_io/RecordItemReader.h" #include "atlas_io/RecordPrinter.h" #include "atlas_io/RecordReader.h" #include "atlas_io/RecordWriter.h" #include "atlas_io/Session.h" #include "atlas_io/Stream.h" #include "atlas_io/Trace.h" #include "atlas_io/types/array.h" #include "atlas_io/types/scalar.h" #include "atlas_io/types/string.h" namespace atlas { namespace io { //--------------------------------------------------------------------------------------------------------------------- inline Link link(const std::string& uri) { return Link{uri}; } //--------------------------------------------------------------------------------------------------------------------- template = 0> Reference ref(const T& x, tag::enable_static_assert = tag::enable_static_assert()) { static_assert(is_encodable(), "in atlas::io::ref(const Value&)" "\n" "\n Static assertion failed" "\n -----------------------" "\n" "\n Cannot encode values of referenced type." "\n" "\n Implement the functions" "\n" "\n void encode_data(const Value& in, atlas::io::Data& out);" "\n size_t encode_metadata(const Value& value, atlas::io::Metadata& metadata);" "\n" "\n or alternatively a conversion function to atlas::io::types::ArrayView" "\n" "\n void interprete(const Value& in, atlas::io::types::ArrayView& out)" "\n" "\n Rules of argument-dependent-lookup apply." "\n --> Functions need to be declared in namespace of any of the arguments." "\n" "\n Note, turn this into a runtime exception by calling this function instead:" "\n" "\n atlas::io::ref(const T&, atlas::io::no_static_assert() )" "\n"); return Reference(x); } template = 0> Reference ref(const T& x, tag::disable_static_assert) { if (not is_encodable()) { throw NotEncodable(x); } return Reference(x); } template = 0> ArrayReference ref(const T& x, tag::enable_static_assert = tag::enable_static_assert()) { ArrayReference w; interprete(x, w); return w; } //--------------------------------------------------------------------------------------------------------------------- template RecordItem copy(T&& value, tag::disable_static_assert) { return RecordItem(std::forward(value), tag::disable_static_assert()); } template RecordItem copy(T&& value) { return RecordItem(std::forward(value)); } //--------------------------------------------------------------------------------------------------------------------- template void encode(const T& in, atlas::io::Metadata& metadata, atlas::io::Data& data, tag::enable_static_assert = tag::enable_static_assert()) { auto referenced = ref(in, tag::enable_static_assert()); sfinae::encode_metadata(referenced, metadata); sfinae::encode_data(referenced, data); } template void encode(const T& in, atlas::io::Metadata& metadata, atlas::io::Data& data, tag::disable_static_assert) { auto referenced = ref(in, tag::disable_static_assert()); sfinae::encode_metadata(referenced, metadata); sfinae::encode_data(referenced, data); } //--------------------------------------------------------------------------------------------------------------------- } // namespace io } // namespace atlas atlas-0.46.0/atlas_io/src/atlas_io/Record.cc0000664000175000017500000002624615160212070020777 0ustar alastairalastair/* * (C) Copyright 2020 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "atlas_io/Record.h" #include "eckit/config/YAMLConfiguration.h" #include "eckit/filesystem/URI.h" #include "atlas_io/Exceptions.h" #include "atlas_io/Trace.h" #include "atlas_io/detail/Assert.h" #include "atlas_io/detail/ParsedRecord.h" #include "atlas_io/detail/Version.h" namespace atlas { namespace io { namespace { //--------------------------------------------------------------------------------------------------------------------- template inline size_t read_struct(IStream& in, Struct& s) { static_assert(Struct::bytes == sizeof(Struct), ""); return in.read(reinterpret_cast(&s), sizeof(Struct)); } //--------------------------------------------------------------------------------------------------------------------- template inline Struct read_struct(IStream& in) { Struct s; if (read_struct(in, s) != sizeof(Struct)) { throw InvalidRecord("Unexpected EOF reached"); } return s; } //--------------------------------------------------------------------------------------------------------------------- } // namespace //--------------------------------------------------------------------------------------------------------------------- Endian RecordHead::endian() const { if (magic_number == 1234) { return Endian::native; } else if (magic_number == 3523477504) { return Endian::swapped; } throw Exception("Mixed endianness is not supported", Here()); } //--------------------------------------------------------------------------------------------------------------------- std::string Record::URI::str() const { eckit::URI uri("file", eckit::PathName(path)); uri.query("offset", std::to_string(offset)); return uri.asRawString(); } //--------------------------------------------------------------------------------------------------------------------- Record::Record(): record_(new ParsedRecord()) {} //--------------------------------------------------------------------------------------------------------------------- bool Record::empty() const { return record_->head.record_length == 0; } //--------------------------------------------------------------------------------------------------------------------- const Metadata& Record::metadata(const std::string& key) const { if (record_->items.find(key) == record_->items.end()) { throw Exception("Record does not contain key \"" + key + "\"", Here()); } return record_->items.at(key); } //--------------------------------------------------------------------------------------------------------------------- Endian Record::endian() const { return record_->head.endian(); } //--------------------------------------------------------------------------------------------------------------------- Version Record::version() const { return record_->head.version; } //--------------------------------------------------------------------------------------------------------------------- Time Record::time() const { return record_->head.time; } //--------------------------------------------------------------------------------------------------------------------- uint64_t Record::size() const { return record_->head.record_length; } //--------------------------------------------------------------------------------------------------------------------- const std::vector& Record::keys() const { return record_->keys; } //--------------------------------------------------------------------------------------------------------------------- bool Record::has(const std::string& key) { return record_->items.find(key) != record_->items.end(); } //--------------------------------------------------------------------------------------------------------------------- Record::operator const ParsedRecord&() const { return *record_; } //--------------------------------------------------------------------------------------------------------------------- static void parse_record(ParsedRecord& record, const std::string& key, const Metadata& metadata) { if (metadata.type() || metadata.link()) { record.items.emplace(key, metadata); record.keys.emplace_back(key); } else { for (auto& next_key : metadata.keys()) { parse_record(record, key + "." + next_key, metadata.getSubConfiguration(next_key)); } } } //--------------------------------------------------------------------------------------------------------------------- Record& Record::read(Stream& in, bool read_to_end) { if (not empty()) { return *this; } ATLAS_IO_TRACE("read_metadata"); auto& r = record_->head; auto rbegin = in.position(); // Begin Record // ------------ if (atlas::io::read_struct(in, r) != sizeof(r)) { if (in.position() > sizeof(r.begin.string)) { if (not r.begin.valid()) { std::stringstream err; err << "Format is not recognized. Received: " << r.begin.string; throw InvalidRecord(err.str()); } } throw InvalidRecord("Unexpected EOF reached"); } if (not r.valid()) { std::stringstream err; err << "Format is not recognized. Received: " << r.begin.string; throw InvalidRecord(err.str()); } if (r.version < RecordHead{}.version) { throw InvalidRecord("Version of record (" + r.version.str() + ") is too old."); } if (r.metadata_length < sizeof(RecordMetadataSection::Begin) + sizeof(RecordMetadataSection::End)) { throw InvalidRecord("Unexpected metadata section length: " + std::to_string(r.metadata_length) + " < " + std::to_string(sizeof(RecordMetadataSection::Begin) + sizeof(RecordMetadataSection::End))); } if (r.index_length < sizeof(RecordDataIndexSection::Begin) + sizeof(RecordDataIndexSection::End)) { throw InvalidRecord("Unexpected data index section length."); } r.metadata_offset += rbegin; r.index_offset += rbegin; // Metadata section // ---------------- in.seek(r.metadata_offset); auto metadata_begin = atlas::io::read_struct(in); if (not metadata_begin.valid()) { throw InvalidRecord("Metadata section is not valid. Invalid section begin marker: [" + metadata_begin.str() + "]"); } std::string metadata_str; metadata_str.resize(size_t(r.metadata_length) - sizeof(RecordMetadataSection::Begin) - sizeof(RecordMetadataSection::End)); if (in.read(const_cast(metadata_str.data()), metadata_str.size()) != metadata_str.size()) { throw InvalidRecord("Unexpected EOF reached"); } auto metadata_end = atlas::io::read_struct(in); if (not metadata_end.valid()) { throw InvalidRecord("Metadata section is not valid. Invalid section end marker: [" + metadata_end.str() + "]"); } Checksum encoded_metadata_checksum(r.metadata_checksum); Checksum computed_metadata_checksum( atlas::io::checksum(metadata_str.data(), metadata_str.size(), encoded_metadata_checksum.algorithm())); if (computed_metadata_checksum.available() && encoded_metadata_checksum.str() != computed_metadata_checksum.str()) { std::stringstream err; err << "Mismatch in metadata checksum.\n"; err << " Encoded: [" << encoded_metadata_checksum.str() << "].\n"; err << " Computed: [" << computed_metadata_checksum.str() << "]."; throw DataCorruption(err.str()); } ATLAS_IO_ASSERT(r.metadata_format == "yaml"); Metadata metadata = eckit::YAMLConfiguration(metadata_str); for (auto& key : metadata.keys()) { parse_record(*record_, key, metadata.getSubConfiguration(key)); } // DataIndex section // ----------------- in.seek(r.index_offset); auto index_begin = atlas::io::read_struct(in); if (not index_begin.valid()) { throw InvalidRecord("Data index section is not valid. Invalid section begin marker: [" + index_begin.str() + "]"); } const auto index_length = (size_t(r.index_length) - sizeof(RecordDataIndexSection::Begin) - sizeof(RecordDataIndexSection::End)); const auto index_size = index_length / sizeof(RecordDataIndexSection::Entry); auto& data_sections = record_->data_sections; data_sections.resize(index_size); if (in.read(data_sections.data(), index_length) != index_length) { throw InvalidRecord("Unexpected EOF reached"); } auto index_end = atlas::io::read_struct(in); if (not index_end.valid()) { throw InvalidRecord("Data index section is not valid. Invalid section end marker: [" + index_end.str() + "]"); } for (auto& data_section : data_sections) { data_section.offset += rbegin; if (data_section.length < sizeof(RecordDataSection::Begin) + sizeof(RecordDataSection::End)) { throw InvalidRecord("Unexpected data section length: [" + std::to_string(data_section.length) + "]"); } } record_->parse(); if (read_to_end) { in.seek(rbegin + r.record_length - sizeof(RecordEnd)); RecordEnd record_end; read_struct(in, record_end); if (not record_end.valid()) { throw InvalidRecord("RecordEnd has unexpected format: [" + record_end.str() + "]"); } if (in.position() != rbegin + r.record_length) { throw InvalidRecord("RecordEnd has unexpected format"); } } return *this; } //--------------------------------------------------------------------------------------------------------------------- void ParsedRecord::parse() { for (auto& key : keys) { auto& item = items.at(key); item.record.version_ = head.version; item.record.created_ = head.time; item.data.section(item.getInt("data.section", 0)); item.data.endian(head.endian()); item.data.compression(item.getString("data.compression.type", "none")); if (item.data.section()) { auto& data_section = data_sections.at(size_t(item.data.section() - 1)); item.data.checksum(data_section.checksum); item.data.compressed_size(data_section.length - sizeof(RecordDataSection::Begin) - sizeof(RecordDataSection::End)); if (item.data.compressed()) { item.data.size(atlas::io::uncompressed_size(item)); } else { item.data.size(item.data.compressed_size()); } } } } //--------------------------------------------------------------------------------------------------------------------- } // namespace io } // namespace atlas atlas-0.46.0/atlas_io/src/atlas_io/RecordPrinter.h0000664000175000017500000000334315160212070022176 0ustar alastairalastair/* * (C) Copyright 2020 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include #include #include "eckit/config/Configuration.h" #include "eckit/filesystem/PathName.h" #include "atlas_io/Record.h" #include "atlas_io/Session.h" #include "atlas_io/detail/NoConfig.h" namespace atlas { namespace io { //--------------------------------------------------------------------------------------------------------------------- class RecordPrinter { public: RecordPrinter(const Record::URI&, const eckit::Configuration& = NoConfig()); RecordPrinter(const eckit::PathName&, const eckit::Configuration& = NoConfig()); RecordPrinter(const eckit::PathName&, std::uint64_t offset, const eckit::Configuration& = NoConfig()); Record record() const { return record_; } size_t size() const { return record_.size(); } Version version() const { return record_.version(); } Time time() const { return record_.time(); } void print(std::ostream& out) const; friend std::ostream& operator<<(std::ostream&, const RecordPrinter&); private: Session session_; Record::URI uri_; struct { std::string format{"table"}; bool details{false}; } options_; Record record_; }; //--------------------------------------------------------------------------------------------------------------------- } // namespace io } // namespace atlas atlas-0.46.0/atlas_io/src/atlas_io/Stream.h0000664000175000017500000000462115160212070020647 0ustar alastairalastair/* * (C) Copyright 2020 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include namespace eckit { class DataHandle; } namespace atlas { namespace io { //--------------------------------------------------------------------------------------------------------------------- /// @class Stream /// @brief Handle to a shared eckit::DataHandle /// /// Note, a Stream is not intended to be opened and closed within atlas::io context /// The derived classes InputFileStream and OutputFileStream automatically open and close /// on construction an destruction. class Stream { public: /// Default constructor /// @post Stream is not usable but can be assigned to become valid Stream() = default; /// Constructor taking ownership of datahandle Stream(eckit::DataHandle*); /// Constructor to share datahandle with a shared_ptr Stream(std::shared_ptr); /// Constructor referencing datahandle, no ownership is taken /// @note The usability depends on the usable lifetime of /// the referenced datahandle Stream(eckit::DataHandle&); /// Access internal eckit::DataHandle eckit::DataHandle& datahandle(); /// Move position to given offset std::uint64_t seek(std::uint64_t offset); /// Return offset of current position std::uint64_t position(); /// Write data of given length (bytes) /// @return number of bytes written /// @post The position is increased with number of bytes written std::uint64_t write(const void* data, size_t length); /// Read data of given length (bytes) /// @return number of bytes read /// @post The position is increased with number of bytes read std::uint64_t read(void* data, size_t length); /// Return true if pointer is valid; operator bool() const { return ptr_; } private: std::shared_ptr shared_; eckit::DataHandle* ptr_{nullptr}; }; //--------------------------------------------------------------------------------------------------------------------- } // namespace io } // namespace atlas atlas-0.46.0/atlas_io/src/atlas_io/Metadata.cc0000664000175000017500000000471515160212070021276 0ustar alastairalastair/* * (C) Copyright 2020 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "Metadata.h" #include #include #include #include "eckit/log/JSON.h" #include "atlas_io/atlas_compat.h" #include "atlas_io/detail/Assert.h" #include "atlas_io/Exceptions.h" #include "atlas_io/types/array/ArrayReference.h" namespace atlas { namespace io { //--------------------------------------------------------------------------------------------------------------------- size_t uncompressed_size(const atlas::io::Metadata& m) { if (m.has("data.size")) { return m.getUnsigned("data.size"); } else if (m.has("type")) { if (m.getString("type") == "array") { atlas::io::ArrayMetadata array(m); return array.bytes(); } } std::stringstream err; err << "Could not compute uncompressed data size from metadata \n"; write(m, err); throw Exception(err.str()); } //--------------------------------------------------------------------------------------------------------------------- void write(const atlas::io::Metadata& metadata, std::ostream& out) { eckit::JSON js(out, eckit::JSON::Formatting::indent(4)); js << metadata; } void write(const atlas::io::Metadata& metadata, atlas::io::Stream& out) { std::stringstream ss; write(metadata, ss); std::string s = ss.str(); out.write(s.data(), s.size()); } //--------------------------------------------------------------------------------------------------------------------- void Metadata::link(Metadata&& linked) { std::string initial_link = link(); ATLAS_IO_ASSERT(initial_link.size()); data = std::move(linked.data); record = std::move(linked.record); set(linked); // Set link to initial_link, in case the link is itself another link set("link", initial_link); } std::string Metadata::json() const { std::stringstream s; eckit::JSON js(s, eckit::JSON::Formatting::compact()); js << *this; return s.str(); } //--------------------------------------------------------------------------------------------------------------------- } // namespace io } // namespace atlas atlas-0.46.0/atlas_io/src/atlas_io/detail/0000775000175000017500000000000015160212070020502 5ustar alastairalastairatlas-0.46.0/atlas_io/src/atlas_io/detail/Encoder.cc0000664000175000017500000000151715160212070022374 0ustar alastairalastair/* * (C) Copyright 2020 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "Encoder.h" #include "atlas_io/Trace.h" namespace atlas { namespace io { size_t encode_metadata(const Encoder& encoder, atlas::io::Metadata& metadata) { ATLAS_IO_TRACE(); ASSERT(encoder); return encoder.self_->encode_metadata_(metadata); } void encode_data(const Encoder& encoder, atlas::io::Data& out) { ATLAS_IO_TRACE(); ASSERT(encoder); encoder.self_->encode_data_(out); } } // namespace io } // namespace atlas atlas-0.46.0/atlas_io/src/atlas_io/detail/RecordInfo.h0000664000175000017500000000133015160212070022702 0ustar alastairalastair/* * (C) Copyright 2020 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include "atlas_io/detail/Time.h" #include "atlas_io/detail/Version.h" namespace atlas { namespace io { struct RecordInfo { Version version_; Time created_; const Version& version() const { return version_; } const Time& created() const { return created_; } }; } // namespace io } // namespace atlas atlas-0.46.0/atlas_io/src/atlas_io/detail/Time.cc0000664000175000017500000000722215160212070021712 0ustar alastairalastair/* * (C) Copyright 2020 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "Time.h" // ------------------------------------------------------------------------------------------------------- // Some older compilers, e.g. intel 17 rely on GCC 4, which does not have std::put_time implemented yet, even though it is in C++11. // So implement it here. #include // std::ios_base #include // std::ostreambuf_iterator #include // std::use_facet, std::time_put #include // std::basic_ostream namespace atlas_std { namespace { template struct _put_time { const std::tm* time; const char* fmt; }; template inline _put_time put_time(const std::tm* time, const CharT* fmt) { return {time, fmt}; } template std::basic_ostream& operator<<(std::basic_ostream& os, _put_time f) { using Iter = typename std::ostreambuf_iterator; using TimePut = std::time_put; const CharT* const fmt_end = f.fmt + Traits::length(f.fmt); const TimePut& mp = std::use_facet(os.getloc()); std::ios_base::iostate err = std::ios_base::goodbit; try { if (mp.put(Iter(os.rdbuf()), os, os.fill(), f.time, f.fmt, fmt_end).failed()) { err |= std::ios_base::badbit; } } catch (...) { err |= std::ios_base::badbit; } if (err) { os.setstate(err); } return os; } } // namespace } // namespace atlas_std // ------------------------------------------------------------------------------------------------------- #include #include #include #include #include #include "eckit/log/JSON.h" namespace atlas { namespace io { namespace { static std::time_t to_time_t(Time time) { return std::time_t(time.tv_sec); } static Time from_time_point(std::chrono::time_point t) { using namespace std::chrono; auto since_epoch = t.time_since_epoch(); auto sec_since_epoch = duration_cast(since_epoch); auto nsec_since_epoch = duration_cast(since_epoch); auto extra_nsec = duration_cast(nsec_since_epoch - sec_since_epoch); Time time; time.tv_sec = static_cast(sec_since_epoch.count()); time.tv_nsec = static_cast(extra_nsec.count()); return time; } } // namespace Time Time::now() { return from_time_point(std::chrono::system_clock::now()); } void Time::print(std::ostream& out) const { // Will print time-date in ISO 8601 format: 1970-01-01T00:00:00.123456789Z auto time = to_time_t(*this); out << atlas_std::put_time(::gmtime(&time), "%FT%T") << "." << tv_nsec << "Z"; // Note, normally we should be using std::put_time instead of above implemented // atlas_std::put_time but some installations that we support don't implement it. } std::ostream& operator<<(std::ostream& out, const Time& time) { time.print(out); return out; } eckit::JSON& operator<<(eckit::JSON& out, const Time& time) { std::stringstream s; s << time; out << s.str(); return out; } std::string Time::str() const { std::stringstream s; print(s); return s.str(); } } // namespace io } // namespace atlas atlas-0.46.0/atlas_io/src/atlas_io/detail/Time.h0000664000175000017500000000227215160212070021554 0ustar alastairalastair/* * (C) Copyright 2020 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include #include namespace eckit { class JSON; } namespace atlas { namespace io { /// Store UTC time up to nanosecond precision struct Time { std::uint64_t tv_sec{0}; ///< seconds since Epoch (1970-01-01T00:00:00Z) std::uint64_t tv_nsec{0}; ///< additional nanoseconds /// Create current time using system clock static Time now(); /// print UTC time in ISO 8601 format: "1970-01-01T00:00:00.123456789Z" void print(std::ostream&) const; friend std::ostream& operator<<(std::ostream&, const Time&); friend eckit::JSON& operator<<(eckit::JSON&, const Time&); /// @return string of UTC time in ISO 8601 format: "1970-01-01T00:00:00.123456789Z" std::string str() const; }; } // namespace io } // namespace atlas atlas-0.46.0/atlas_io/src/atlas_io/detail/Link.h0000664000175000017500000000152415160212070021552 0ustar alastairalastair/* * (C) Copyright 2020 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include namespace eckit { class PathName; } namespace atlas { namespace io { struct Link { std::string uri; const std::string& str() const { return uri; } operator bool() const { return uri.size(); } operator const std::string&() const { return str(); } // bool relative() const; // friend Link operator/( const eckit::PathName& dir, const Link& link ); }; } // namespace io } // namespace atlas atlas-0.46.0/atlas_io/src/atlas_io/detail/tag.h0000664000175000017500000000110715160212070021425 0ustar alastairalastair/* * (C) Copyright 2020 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once namespace atlas { namespace io { namespace tag { struct disable_static_assert {}; struct enable_static_assert {}; } // namespace tag } // namespace io } // namespace atlas atlas-0.46.0/atlas_io/src/atlas_io/detail/DataType.h0000664000175000017500000002616615160212070022401 0ustar alastairalastair/* * (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 //------------------------------------------------------------------------------------------------------ // For type safety we want to use std::byte for the DataType "BYTE", but it is a C++17 feature. // Backport std::byte here without any operations #if __cplusplus >= 201703L #include #else #ifndef STD_BYTE_DEFINED #define STD_BYTE_DEFINED namespace std { #ifdef _CRAYC struct byte { unsigned char byte_; }; #else enum class byte : unsigned char { }; #endif } // namespace std #endif #endif //------------------------------------------------------------------------------------------------------ namespace atlas { namespace io { class DataType { public: typedef long kind_t; static const kind_t KIND_BYTE = 1; static const kind_t KIND_INT32 = -4; static const kind_t KIND_INT64 = -8; static const kind_t KIND_REAL32 = 4; static const kind_t KIND_REAL64 = 8; static const kind_t KIND_UINT64 = -16; template static DataType create(); static DataType byte() { return DataType(KIND_BYTE); } static DataType int32() { return DataType(KIND_INT32); } static DataType int64() { return DataType(KIND_INT64); } static DataType real32() { return DataType(KIND_REAL32); } static DataType real64() { return DataType(KIND_REAL64); } static DataType uint64() { return DataType(KIND_UINT64); } template static kind_t kind(); template static kind_t kind(const DATATYPE&); template static std::string str(); template static std::string str(const DATATYPE); static kind_t str_to_kind(const std::string&); static std::string kind_to_str(kind_t); static bool kind_valid(kind_t); private: static std::string byte_str() { return "byte"; } static std::string int32_str() { return "int32"; } static std::string int64_str() { return "int64"; } static std::string real32_str() { return "real32"; } static std::string real64_str() { return "real64"; } static std::string uint64_str() { return "uint64"; } [[noreturn]] static void throw_not_recognised(kind_t); [[noreturn]] static void throw_not_recognised(std::string datatype); public: DataType(const std::string&); DataType(long); DataType(const DataType&); DataType& operator=(const DataType&); std::string str() const { return kind_to_str(kind_); } kind_t kind() const { return kind_; } size_t size() const { return (kind_ == KIND_UINT64) ? 8 : std::abs(kind_); } friend bool operator==(DataType dt1, DataType dt2); friend bool operator!=(DataType dt1, DataType dt2); friend bool operator==(DataType dt, kind_t kind); friend bool operator!=(DataType dt, kind_t kind); friend bool operator==(kind_t kind, DataType dt); friend bool operator!=(kind_t kind, DataType dt2); private: kind_t kind_; }; template <> inline std::string DataType::str() { return byte_str(); } template <> inline std::string DataType::str() { return byte_str(); } template <> inline std::string DataType::str() { static_assert(sizeof(int) == 4, ""); return int32_str(); } template <> inline std::string DataType::str() { static_assert(sizeof(int) == 4, ""); return int32_str(); } template <> inline std::string DataType::str() { static_assert(sizeof(long) == 8, ""); return int64_str(); } template <> inline std::string DataType::str() { static_assert(sizeof(long) == 8, ""); return int64_str(); } template <> inline std::string DataType::str() { static_assert(sizeof(long long) == 8, ""); return int64_str(); } template <> inline std::string DataType::str() { static_assert(sizeof(long long) == 8, ""); return int64_str(); } template <> inline std::string DataType::str() { static_assert(sizeof(float) == 4, ""); return real32_str(); } template <> inline std::string DataType::str() { static_assert(sizeof(float) == 4, ""); return real32_str(); } template <> inline std::string DataType::str() { static_assert(sizeof(double) == 8, ""); return real64_str(); } template <> inline std::string DataType::str() { static_assert(sizeof(double) == 8, ""); return real64_str(); } template <> inline std::string DataType::str() { static_assert(sizeof(unsigned long) == 8, ""); return uint64_str(); } template <> inline std::string DataType::str() { static_assert(sizeof(unsigned long) == 8, ""); return uint64_str(); } template <> inline std::string DataType::str() { static_assert(sizeof(unsigned long long) == 8, ""); return uint64_str(); } template <> inline std::string DataType::str() { static_assert(sizeof(unsigned long long) == 8, ""); return uint64_str(); } template <> inline std::string DataType::str(const int&) { return str(); } template <> inline std::string DataType::str(const long&) { return str(); } template <> inline std::string DataType::str(const long long&) { return str(); } template <> inline std::string DataType::str(const unsigned long&) { return str(); } template <> inline std::string DataType::str(const unsigned long long&) { return str(); } template <> inline std::string DataType::str(const float&) { return str(); } template <> inline std::string DataType::str(const double&) { return str(); } template <> inline DataType::kind_t DataType::kind() { static_assert(sizeof(std::byte) == 1, ""); return KIND_BYTE; } template <> inline DataType::kind_t DataType::kind() { static_assert(sizeof(std::byte) == 1, ""); return KIND_BYTE; } template <> inline DataType::kind_t DataType::kind() { static_assert(sizeof(int) == 4, ""); return KIND_INT32; } template <> inline DataType::kind_t DataType::kind() { static_assert(sizeof(int) == 4, ""); return KIND_INT32; } template <> inline DataType::kind_t DataType::kind() { static_assert(sizeof(long) == 8, ""); return KIND_INT64; } template <> inline DataType::kind_t DataType::kind() { static_assert(sizeof(long) == 8, ""); return KIND_INT64; } template <> inline DataType::kind_t DataType::kind() { static_assert(sizeof(long long) == 8, ""); return KIND_INT64; } template <> inline DataType::kind_t DataType::kind() { static_assert(sizeof(long long) == 8, ""); return KIND_INT64; } template <> inline DataType::kind_t DataType::kind() { static_assert(sizeof(unsigned long) == 8, ""); return KIND_UINT64; } template <> inline DataType::kind_t DataType::kind() { static_assert(sizeof(unsigned long) == 8, ""); return KIND_UINT64; } template <> inline DataType::kind_t DataType::kind() { static_assert(sizeof(unsigned long long) == 8, ""); return KIND_UINT64; } template <> inline DataType::kind_t DataType::kind() { static_assert(sizeof(unsigned long long) == 8, ""); return KIND_UINT64; } template <> inline DataType::kind_t DataType::kind() { static_assert(sizeof(float) == 4, ""); return KIND_REAL32; } template <> inline DataType::kind_t DataType::kind() { static_assert(sizeof(float) == 4, ""); return KIND_REAL32; } template <> inline DataType::kind_t DataType::kind() { static_assert(sizeof(double) == 8, ""); return KIND_REAL64; } template <> inline DataType::kind_t DataType::kind() { static_assert(sizeof(double) == 8, ""); return KIND_REAL64; } template <> inline DataType::kind_t DataType::kind(const int&) { return kind(); } template <> inline DataType::kind_t DataType::kind(const long&) { return kind(); } template <> inline DataType::kind_t DataType::kind(const unsigned long&) { return kind(); } template <> inline DataType::kind_t DataType::kind(const float&) { return kind(); } template <> inline DataType::kind_t DataType::kind(const double&) { return kind(); } inline DataType::kind_t DataType::str_to_kind(const std::string& datatype) { if (datatype == "int32") return KIND_INT32; else if (datatype == "int64") return KIND_INT64; else if (datatype == "uint64") return KIND_UINT64; else if (datatype == "real32") return KIND_REAL32; else if (datatype == "real64") return KIND_REAL64; else if (datatype == "byte") { return KIND_BYTE; } else { throw_not_recognised(datatype); } } inline std::string DataType::kind_to_str(kind_t kind) { switch (kind) { case KIND_INT32: return int32_str(); case KIND_INT64: return int64_str(); case KIND_UINT64: return uint64_str(); case KIND_REAL32: return real32_str(); case KIND_REAL64: return real64_str(); case KIND_BYTE: return byte_str(); default: throw_not_recognised(kind); } } inline bool DataType::kind_valid(kind_t kind) { switch (kind) { case KIND_BYTE: case KIND_INT32: case KIND_INT64: case KIND_UINT64: case KIND_REAL32: case KIND_REAL64: return true; default: return false; } } inline DataType::DataType(const DataType& other): kind_(other.kind_) {} inline DataType& DataType::operator=(const DataType& other) { kind_ = other.kind_; return *this; } inline DataType::DataType(const std::string& datatype): kind_(str_to_kind(datatype)) {} inline DataType::DataType(long kind): kind_(kind) {} inline bool operator==(DataType dt1, DataType dt2) { return dt1.kind_ == dt2.kind_; } inline bool operator!=(DataType dt1, DataType dt2) { return dt1.kind_ != dt2.kind_; } inline bool operator==(DataType dt, DataType::kind_t kind) { return dt.kind_ == kind; } inline bool operator!=(DataType dt, DataType::kind_t kind) { return dt.kind_ != kind; } inline bool operator==(DataType::kind_t kind, DataType dt) { return dt.kind_ == kind; } inline bool operator!=(DataType::kind_t kind, DataType dt) { return dt.kind_ != kind; } template inline DataType DataType::create() { return DataType(DataType::kind()); } template inline DataType make_datatype() { return DataType(DataType::kind()); } //------------------------------------------------------------------------------------------------------ } // namespace io } // namespace atlas atlas-0.46.0/atlas_io/src/atlas_io/detail/Decoder.cc0000664000175000017500000000155415160212070022363 0ustar alastairalastair/* * (C) Copyright 2020 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "Decoder.h" #include "atlas_io/Trace.h" namespace atlas { namespace io { void decode(const atlas::io::Metadata& metadata, const atlas::io::Data& data, Decoder& decoder) { ATLAS_IO_TRACE("decode"); decoder.self_->decode_(metadata, data); } void decode(const atlas::io::Metadata& metadata, const atlas::io::Data& data, Decoder&& decoder) { ATLAS_IO_TRACE_SCOPE("decode"); decoder.self_->decode_(metadata, data); } } // namespace io } // namespace atlas atlas-0.46.0/atlas_io/src/atlas_io/detail/Checksum.h0000664000175000017500000000164015160212070022416 0ustar alastairalastair/* * (C) Copyright 2020 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include namespace atlas { namespace io { class Checksum { public: Checksum() = default; Checksum(const std::string& checksum); bool available() const; std::string str() const; std::string str(size_t size) const; std::string algorithm() const { return algorithm_; } private: std::string algorithm_; std::string checksum_; }; std::string checksum(const void* buffer, size_t size, const std::string& algorithm = ""); } // namespace io } // namespace atlas atlas-0.46.0/atlas_io/src/atlas_io/detail/defines.h.in0000664000175000017500000000153015160212070022674 0ustar alastairalastair#if 0 /* * (C) Copyright 2022 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. */ // clang-format off #endif #ifndef atlas_io_defines_h #define atlas_io_defines_h #cmakedefine01 ATLAS_IO_HAVE_CXXABI_H #cmakedefine01 ATLAS_IO_LITTLE_ENDIAN #cmakedefine01 ATLAS_IO_BIG_ENDIAN #define ATLAS_IO_ECKIT_VERSION_INT @ATLAS_IO_ECKIT_VERSION_INT@ #define ATLAS_IO_ECKIT_DEVELOP @ATLAS_IO_ECKIT_DEVELOP@ #define ATLAS_IO_ECKIT_VERSION_AT_LEAST(x, y, z) (ATLAS_IO_ECKIT_VERSION_INT >= x * 10000 + y * 100 + z) #endif atlas-0.46.0/atlas_io/src/atlas_io/detail/Base64.h0000664000175000017500000000232315160212070021677 0ustar alastairalastair/* * (C) Copyright 2020 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include namespace atlas { namespace io { //--------------------------------------------------------------------------------------------------------------------- class Base64 { public: static std::string encode(const void* data, size_t len); static std::string decode(const void* data, size_t len); template static std::string encode(const T& value) { return encode(&value, sizeof(value)); } template static T decode(const std::string& in) { std::string decoded = decode(in.data(), in.size()); return *reinterpret_cast(decoded.data()); } }; //--------------------------------------------------------------------------------------------------------------------- } // namespace io } // namespace atlas atlas-0.46.0/atlas_io/src/atlas_io/detail/Defaults.h0000664000175000017500000000253315160212070022425 0ustar alastairalastair/* * (C) Copyright 2020 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include "eckit/config/Resource.h" namespace atlas { namespace io { namespace defaults { [[maybe_unused]] static std::string checksum_algorithm() { static std::string checksum = eckit::Resource("atlas.io.checksum.algorithm;$ATLAS_IO_CHECKSUM", "xxh64"); return checksum; } [[maybe_unused]] static bool checksum_read() { static bool checksum = eckit::Resource("atlas.io.checksum.read;$ATLAS_IO_CHECKSUM_READ", true); return checksum; } [[maybe_unused]] static bool checksum_write() { static bool checksum = eckit::Resource("atlas.io.checksum.write;$ATLAS_IO_CHECKSUM_WRITE", true); return checksum; } [[maybe_unused]] static const std::string& compression_algorithm() { static std::string compression = eckit::Resource("atlas.io.compression;$ATLAS_IO_COMPRESSION", "none"); return compression; } } // namespace defaults } // namespace io } // namespace atlas atlas-0.46.0/atlas_io/src/atlas_io/detail/Assert.h0000664000175000017500000000102615160212070022113 0ustar alastairalastair/* * (C) Copyright 2020 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include "eckit/exception/Exceptions.h" #define ATLAS_IO_ASSERT(X) ASSERT(X) #define ATLAS_IO_ASSERT_MSG(X, M) ASSERT_MSG(X, M) atlas-0.46.0/atlas_io/src/atlas_io/detail/Reference.h0000664000175000017500000000221115160212070022545 0ustar alastairalastair/* * (C) Copyright 2020 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include "atlas_io/Data.h" #include "atlas_io/Metadata.h" #include "atlas_io/detail/sfinae.h" #include "atlas_io/Exceptions.h" namespace atlas { namespace io { template struct Reference { const T* ref; Reference(const T& r): ref(&r) {} friend size_t encode_metadata(const Reference& in, atlas::io::Metadata& metadata) { size_t size{0}; if (not sfinae::encode_metadata(*in.ref, metadata, size)) { throw NotEncodable(*in.ref); } return size; } friend void encode_data(const Reference& in, atlas::io::Data& out) { if (not sfinae::encode_data(*in.ref, out)) { throw NotEncodable(*in.ref); } } }; } // namespace io } // namespace atlas atlas-0.46.0/atlas_io/src/atlas_io/detail/RecordSections.h0000664000175000017500000001375715160212070023616 0ustar alastairalastair/* * (C) Copyright 2020 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include #include "eckit/types/FixedString.h" #include "atlas_io/detail/Endian.h" #include "atlas_io/detail/Time.h" #include "atlas_io/detail/Version.h" namespace atlas { namespace io { // ------------------------------------------------------------------------------------------------------------------------------------ struct RecordBegin { eckit::FixedString<8> string{"ATLAS-IO"}; ///< 16 ATLAS-IO string eckit::FixedString<8> spare{"\n"}; ///< bool valid() const { return string == "ATLAS-IO"; } std::string str() const { return string; } }; struct RecordEnd { // 32 bytes static constexpr size_t bytes = 32; eckit::FixedString<1> eol{"\n"}; eckit::FixedString<12> string{"ATLAS-IO-END"}; eckit::FixedString<19> padding{" \n\n\n\n"}; bool valid() const { return string == "ATLAS-IO-END"; } std::string str() const { return string; } }; // ------------------------------------------------------------------------------------------------------------------------------------ struct RecordHead { static constexpr size_t bytes = 256; static constexpr size_t padding_ = bytes - 16 - 8 - 16 - 8 * 4 - 64 - 8 * 2 - 4 - 1; RecordBegin begin; ///< 16 beginning of record Version version; ///< 8 version of this record Time time; ///< 16 time since system_clock epoch (1970-1-1 00:00) uint64_t record_length{0}; ///< 8 length of entire record eckit::FixedString<8> metadata_format{"yaml"}; ///< 8 format of metadata section in this record std::uint64_t metadata_offset{bytes}; ///< 8 offset where metadata section starts std::uint64_t metadata_length{0}; ///< 8 length of metadata section eckit::FixedString<64> metadata_checksum; ///< 64 checksum of metadata std::uint64_t index_offset{0}; ///< 8 offset where data section starts std::uint64_t index_length; ///< 8 length of data section std::uint32_t magic_number{1234}; ///< 4 number 1234 encoded in binary, used to detect encoded endianness eckit::FixedString padding__; ///< Extra padding to get to eckit::FixedString<1> eol{"\n"}; static constexpr size_t size() { return bytes; } ///< Size in bytes of this section Endian endian() const; ///< Endianness determined from magic_number bool valid() const { return begin.valid(); } ///< Check if this is a valid RecordRoot std::string str() const { return begin.str(); } }; // ------------------------------------------------------------------------------------------------------------------------------------ struct RecordMetadataSection { struct Begin { // 32 bytes static constexpr size_t bytes = 32; eckit::FixedString<1> eol{"\n"}; eckit::FixedString<14> string{"METADATA-BEGIN"}; eckit::FixedString<17> padding{" \n"}; bool valid() const { return string == "METADATA-BEGIN"; } std::string str() const { return string; } }; struct End { // 32 bytes static constexpr size_t bytes = 32; eckit::FixedString<1> eol{"\n"}; eckit::FixedString<12> string{"METADATA-END"}; eckit::FixedString<19> padding{" \n"}; bool valid() const { return string == "METADATA-END"; } std::string str() const { return string; } }; }; // ------------------------------------------------------------------------------------------------------------------------------------ struct RecordDataIndexSection { struct Begin { // 32 bytes static constexpr size_t bytes = 32; eckit::FixedString<1> eol{"\n"}; eckit::FixedString<11> string{"INDEX-BEGIN"}; eckit::FixedString<20> padding{" \n"}; bool valid() const { return string == "INDEX-BEGIN"; } std::string str() const { return string; } }; struct End { // 32 bytes static constexpr size_t bytes = 32; eckit::FixedString<1> eol{"\n"}; eckit::FixedString<9> string{"INDEX-END"}; eckit::FixedString<22> padding{" \n"}; bool valid() const { return string == "INDEX-END"; } std::string str() const { return string; } }; struct Entry { static constexpr size_t bytes = 80; std::uint64_t offset; std::uint64_t length; eckit::FixedString<64> checksum; }; }; // ------------------------------------------------------------------------------------------------------------------------------------ struct RecordDataSection { struct Begin { // 32 bytes static constexpr size_t bytes = 32; eckit::FixedString<1> eol{"\n"}; eckit::FixedString<10> string{"DATA-BEGIN"}; eckit::FixedString<21> padding{" \n"}; bool valid() const { return string == "DATA-BEGIN"; } std::string str() const { return string; } }; struct End { // 32 bytes static constexpr size_t bytes = 32; eckit::FixedString<1> eol{"\n"}; eckit::FixedString<8> string{"DATA-END"}; eckit::FixedString<23> padding{" \n"}; bool valid() const { return string == "DATA-END"; } std::string str() const { return string; } }; }; // ------------------------------------------------------------------------------------------------------------------------------------ } // namespace io } // namespace atlas atlas-0.46.0/atlas_io/src/atlas_io/detail/TypeTraits.h0000664000175000017500000001200615160212070022762 0ustar alastairalastair/* * (C) Copyright 2020 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include "atlas_io/detail/DataType.h" namespace atlas { namespace io { class Metadata; class Data; class Stream; namespace adl_tests { using std::declval; using std::is_pod; template std::false_type can_interprete(...) noexcept(false); template (), declval()))> std::true_type can_interprete(int) noexcept(noexcept(interprete(declval(), declval()))); template std::false_type can_encode_data(...) noexcept(false); template (), declval()))> std::true_type can_encode_data(int) noexcept(noexcept(encode_data(declval(), declval()))); template std::false_type can_encode_metadata(...) noexcept(false); template (), declval()))> std::true_type can_encode_metadata(int) noexcept(noexcept(encode_metadata(declval(), declval()))); template std::false_type can_decode(...) noexcept(false); template (), declval(), declval()))> std::true_type can_decode(int) noexcept(noexcept(decode(declval(), declval(), declval()))); } // namespace adl_tests template static constexpr bool is_interpretable() { return decltype(adl_tests::can_interprete::type, A>(0))::value; } template static constexpr bool can_encode_data() { return decltype(adl_tests::can_encode_data::type>(0))::value; } template static constexpr bool can_encode_metadata() { return decltype(adl_tests::can_encode_metadata::type>(0))::value; } template static constexpr bool is_encodable() { return can_encode_data() && can_encode_metadata(); } template static constexpr bool is_decodable() { return decltype(adl_tests::can_decode::type>(0))::value; } template static constexpr bool is_encodable_rvalue() { return is_encodable() && std::is_rvalue_reference::value; }; template using enable_if_t = typename std::enable_if::type; template using disable_if_t = typename std::enable_if::type; template using enable_if_encodable_t = enable_if_t()>; template using disable_if_encodable_t = disable_if_t()>; template using enable_if_decodable_t = enable_if_t()>; template using disable_if_decodable_t = disable_if_t()>; template using enable_if_can_encode_metadata_t = enable_if_t()>; template using disable_if_can_encode_metadata_t = disable_if_t()>; template using enable_if_can_encode_data_t = enable_if_t()>; template using disable_if_can_encode_data_t = disable_if_t()>; template using enable_if_interpretable_t = enable_if_t()>; template using disable_if_interpretable_t = disable_if_t()>; template using enable_if_rvalue_t = enable_if_t::value>; template using enable_if_move_constructible_encodable_rvalue_t = enable_if_t() && std::is_rvalue_reference() && std::is_move_constructible()>; template using enable_if_move_constructible_decodable_rvalue_t = enable_if_t() && std::is_rvalue_reference() && std::is_move_constructible()>; template using enable_if_scalar_t = enable_if_t::value && EnableBool>; template constexpr bool is_array_datatype() { return std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v || std::is_same_v; } template using enable_if_array_datatype = typename std::enable_if(), int>::type; } // namespace io } // namespace atlas atlas-0.46.0/atlas_io/src/atlas_io/detail/DataInfo.h0000664000175000017500000000326115160212070022342 0ustar alastairalastair/* * (C) Copyright 2020 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include #include "atlas_io/detail/Checksum.h" #include "atlas_io/detail/Endian.h" namespace atlas { namespace io { class DataInfo { public: int section() const { return section_; } const std::string& compression() const { return compression_; } Endian endian() const { return endian_; } void endian(Endian e) { endian_ = e; } void compression(const std::string& c) { compression_ = c; } void size(size_t s) { uncompressed_size_ = s; } size_t size() const { return uncompressed_size_; } void compressed_size(size_t s) { compressed_size_ = s; } size_t compressed_size() const { return compressed_size_; } void compressed(bool f) { if (f == false) { compression("none"); } } bool compressed() const { return compression_ != "none"; } operator bool() const { return section_ > 0; } const Checksum& checksum() const { return checksum_; } void checksum(const std::string& c) { checksum_ = Checksum(c); } void section(int s) { section_ = s; } private: int section_{0}; std::string compression_{"none"}; Checksum checksum_; Endian endian_{Endian::native}; size_t uncompressed_size_{0}; size_t compressed_size_{0}; }; } // namespace io } // namespace atlas atlas-0.46.0/atlas_io/src/atlas_io/detail/Link.cc0000664000175000017500000000215715160212070021713 0ustar alastairalastair/* * (C) Copyright 2020 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "Link.h" #include "eckit/filesystem/PathName.h" #include "atlas_io/RecordItem.h" namespace atlas { namespace io { //bool Link::relative() const { // std::string path = RecordItem::URI{uri}.path; // if( path.size() == 0 ) { // return true; // } // if( path[0] == '/' ) { // return false; // } // if( path[0] == '~' ) { // return false; // } // return true; //} //Link operator/( const eckit::PathName& dir, const Link& link ) { // auto relative_path = eckit::PathName{ RecordItem::URI{link}.path }; // auto absolute_uri = RecordItem::URI(link); // absolute_uri.path = dir / relative_path; // return Link{absolute_uri.str()}; //}; } // namespace io } // namespace atlas atlas-0.46.0/atlas_io/src/atlas_io/detail/Version.h0000664000175000017500000000320515160212070022300 0ustar alastairalastair/* * (C) Copyright 2020 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include "eckit/types/SemanticVersion.h" namespace atlas { namespace io { struct Version { // 8 bytes std::uint32_t major{0}; ///< Major version std::uint32_t minor{2}; ///< Minor version std::string str() const { return std::to_string(major) + "." + std::to_string(minor); } operator std::string() const { return str(); } operator eckit::SemanticVersion() const { return eckit::SemanticVersion{major, minor, 0}; } bool operator<(const Version& v) const { return eckit::SemanticVersion{major, minor, 0} < eckit::SemanticVersion{v.major, v.minor, 0}; } bool operator==(const Version& v) const { return eckit::SemanticVersion{major, minor, 0} == eckit::SemanticVersion{v.major, v.minor, 0}; } bool operator!=(const Version& v) const { return !(*this == v); } bool operator<=(const Version& v) const { return (*this < v) or (*this == v); } bool operator>(const Version& v) const { return !(*this <= v); } bool operator>=(const Version& v) const { return (*this > v) or (*this == v); } friend std::ostream& operator<<(std::ostream& out, const Version& v) { out << v.str(); return out; } }; } // namespace io } // namespace atlas atlas-0.46.0/atlas_io/src/atlas_io/detail/Endian.h0000664000175000017500000000167015160212070022055 0ustar alastairalastair/* * (C) Copyright 2020 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include "atlas_io/detail/defines.h" #ifndef ATLAS_IO_BIG_ENDIAN #error ATLAS_IO_BIG_ENDIAN not defined #endif #ifndef ATLAS_IO_LITTLE_ENDIAN #error ATLAS_IO_LITTLE_ENDIAN not defined #endif namespace atlas { namespace io { enum class Endian { little = 0, big = 1, #if ATLAS_IO_BIG_ENDIAN native = big, swapped = little #elif ATLAS_IO_LITTLE_ENDIAN native = little, swapped = big #else #error Neither ATLAS_IO_BIG_ENDIAN nor ATLAS_IO_LITTLE_ENDIAN equals true #endif }; } // namespace io } // namespace atlas atlas-0.46.0/atlas_io/src/atlas_io/detail/Type.h0000664000175000017500000000146615160212070021603 0ustar alastairalastair/* * (C) Copyright 2020 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include namespace atlas { namespace io { struct Type { const std::string name_; operator const std::string&() { return name_; } operator bool() const { return name_.size(); } Type(const char* name): name_(name) {} Type(const std::string& name): name_(name) {} bool operator==(const Type& other) const { return name_ == other.name_; } }; } // namespace io } // namespace atlas atlas-0.46.0/atlas_io/src/atlas_io/detail/ParsedRecord.h0000664000175000017500000000271215160212070023232 0ustar alastairalastair/* * (C) Copyright 2020 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include #include #include "atlas_io/Metadata.h" #include "atlas_io/detail/RecordSections.h" namespace atlas { namespace io { /// Low-level Record information container. /// /// No big data is kept here, only metadata, and information /// on how to retrieve data at a later stage class ParsedRecord { public: RecordHead head; ///< head section of parsed record std::vector keys; ///< Keys of items encoded in parsed record std::map items; ///< Items encoded in parsed record std::vector data_sections; ///< Description of data sections in parsed record /// The parse() function needs to be called during the reading of the record and /// completes the "items" through introspection of the "data_sections". /// It also computes uncompressed data size using available metadata in the "items" void parse(); }; } // namespace io } // namespace atlas atlas-0.46.0/atlas_io/src/atlas_io/detail/sfinae.h0000664000175000017500000000601015160212070022115 0ustar alastairalastair/* * (C) Copyright 2020 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include "atlas_io/detail/TypeTraits.h" namespace atlas { namespace io { // ------------------------------------------------------------------------------------------------------- namespace { // These anonymous namespace functions are to avoid recursive behaviour in // following sfinae namespace template = 0> inline void do_interprete(const T& in, A& interpreted) { interprete(in, interpreted); } template = 0> inline size_t do_encode_metadata(const T& in, Metadata& out) { size_t size = encode_metadata(in, out); return size; } template = 0> inline void do_encode_data(const T& in, Data& out) { encode_data(in, out); } } // namespace // ------------------------------------------------------------------------------------------------------- namespace sfinae { // ------------------------------------------------------------------------------------------------------- template = 0> bool interprete(const T& in, A& interpreted) { do_interprete(in, interpreted); return true; } template = 0> bool interprete(const T& /*in*/, A& /*interpreted*/) { return false; } // ------------------------------------------------------------------------------------------------------- template = 0> bool encode_data(const T& in, Data& out) { do_encode_data(in, out); return true; } template = 0> bool encode_data(const T&, Data&) { return false; } // ------------------------------------------------------------------------------------------------------- template = 0> bool encode_metadata(const T& in, Metadata& out) { do_encode_metadata(in, out); return true; } template = 0> bool encode_metadata(const T&, Metadata&) { return false; } template = 0> bool encode_metadata(const T& in, Metadata& out, size_t& data_size) { data_size = do_encode_metadata(in, out); return true; } template = 0> bool encode_metadata(const T&, Metadata&, size_t& data_size) { data_size = 0; return false; } // ------------------------------------------------------------------------------------------------------- } // namespace sfinae } // namespace io } // namespace atlas atlas-0.46.0/atlas_io/src/atlas_io/detail/Checksum.cc0000664000175000017500000000404115160212070022552 0ustar alastairalastair/* * (C) Copyright 2020 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "Checksum.h" #include #include "eckit/utils/Hash.h" #include "eckit/utils/Tokenizer.h" #include "atlas_io/Trace.h" #include "atlas_io/detail/Defaults.h" namespace atlas { namespace io { Checksum::Checksum(const std::string& checksum) { std::vector tokens; eckit::Tokenizer tokenizer(':'); eckit::Tokenizer{':'}(checksum, tokens); if (tokens.size() == 1) { algorithm_ = "none"; checksum_ = ""; } else { algorithm_ = tokens[0]; checksum_ = tokens[1]; } } bool Checksum::available() const { return checksum_.size() && algorithm_ != "none"; } std::string Checksum::str() const { if (algorithm_.empty()) { return ""; } return algorithm_ + ":" + checksum_; } std::string Checksum::str(size_t size) const { if (algorithm_.empty()) { return ""; } return algorithm_ + ":" + checksum_.substr(0, std::min(size, checksum_.size())); } std::string checksum(const void* buffer, size_t size, const std::string& algorithm) { auto is_available = [](const std::string& alg) -> bool { return eckit::HashFactory::instance().has(alg); }; auto hash = [&](const std::string& alg) -> std::string { std::unique_ptr hasher(eckit::HashFactory::instance().build(alg)); ATLAS_IO_TRACE("checksum(" + alg + ")"); return std::string(alg) + ":" + hasher->compute(buffer, long(size)); }; std::string alg = algorithm.empty() ? defaults::checksum_algorithm() : algorithm; if (is_available(alg)) { return hash(alg); } else { return hash("none"); } } } // namespace io } // namespace atlas atlas-0.46.0/atlas_io/src/atlas_io/detail/Base64.cc0000664000175000017500000001102315160212070022032 0ustar alastairalastair/* * (C) Copyright 2020 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "Base64.h" #include namespace atlas { namespace io { //--------------------------------------------------------------------------------------------------------------------- namespace { static std::array b64_decode_table{ /* ASCII table */ 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 62, 64, 64, 64, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 64, 64, 64, 64, 64, 64, 64, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 64, 64, 64, 64, 64, 64, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64, 64}; static std::array b64_encode_table{ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'}; } // namespace //--------------------------------------------------------------------------------------------------------------------- std::string Base64::encode(const void* data, size_t len) { const auto& table = b64_encode_table; const unsigned char* src = reinterpret_cast(data); unsigned char *out, *pos; const unsigned char *end, *in; size_t out_len = 4 * ((len + 2) / 3); /* 3-byte blocks to 4-byte */ if (out_len < len) { return std::string(); /* integer overflow */ } std::string str; str.resize(out_len); out = reinterpret_cast(const_cast(str.data())); end = src + len; in = src; pos = out; while (end - in >= 3) { *pos++ = table[in[0] >> 2]; *pos++ = table[((in[0] & 0x03) << 4) | (in[1] >> 4)]; *pos++ = table[((in[1] & 0x0f) << 2) | (in[2] >> 6)]; *pos++ = table[in[2] & 0x3f]; in += 3; } if (end - in) { *pos++ = table[in[0] >> 2]; if (end - in == 1) { *pos++ = table[(in[0] & 0x03) << 4]; *pos++ = '='; } else { *pos++ = table[((in[0] & 0x03) << 4) | (in[1] >> 4)]; *pos++ = table[(in[1] & 0x0f) << 2]; } *pos++ = '='; } return str; } //--------------------------------------------------------------------------------------------------------------------- std::string Base64::decode(const void* data, size_t len) { const auto& table = b64_decode_table; const unsigned char* p = reinterpret_cast(data); int pad = len > 0 && (len % 4 || p[len - 1] == '='); const size_t L = ((len + 3) / 4 - pad) * 4; std::string str(L / 4 * 3 + pad, '\0'); for (size_t i = 0, j = 0; i < L; i += 4) { int n = table[p[i]] << 18 | table[p[i + 1]] << 12 | table[p[i + 2]] << 6 | table[p[i + 3]]; str[j++] = n >> 16; str[j++] = n >> 8 & 0xFF; str[j++] = n & 0xFF; } if (pad) { int n = table[p[L]] << 18 | table[p[L + 1]] << 12; str[str.size() - 1] = n >> 16; if (len > L + 2 && p[L + 2] != '=') { n |= table[p[L + 2]] << 6; str.push_back(n >> 8 & 0xFF); } } return str; } //--------------------------------------------------------------------------------------------------------------------- } // namespace io } // namespace atlas atlas-0.46.0/atlas_io/src/atlas_io/detail/Decoder.h0000664000175000017500000000313415160212070022221 0ustar alastairalastair/* * (C) Copyright 2020 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include "atlas_io/Data.h" #include "atlas_io/Metadata.h" #include "atlas_io/detail/TypeTraits.h" namespace atlas { namespace io { class Decoder { public: template = 0> explicit Decoder(T& value): self_(new DecodableItem(value)) {} friend void decode(const atlas::io::Metadata& metadata, const atlas::io::Data& data, Decoder&); friend void decode(const atlas::io::Metadata& metadata, const atlas::io::Data& data, Decoder&&); private: struct Decodable { virtual ~Decodable() = default; virtual void decode_(const atlas::io::Metadata&, const atlas::io::Data&) = 0; }; template struct DecodableItem : Decodable { explicit DecodableItem(T& value): data_(value) {} void decode_(const atlas::io::Metadata& metadata, const atlas::io::Data& encoded) override { decode(metadata, encoded, data_); } T& data_; }; std::shared_ptr self_; }; void decode(const Metadata&, const Data&, Decoder&); void decode(const Metadata&, const Data&, Decoder&&); } // namespace io } // namespace atlas atlas-0.46.0/atlas_io/src/atlas_io/detail/DataType.cc0000664000175000017500000000214015160212070022521 0ustar alastairalastair/* * (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 "DataType.h" #include #include "atlas_io/Exceptions.h" //------------------------------------------------------------------------------------------------------ namespace atlas { namespace io { void DataType::throw_not_recognised(kind_t kind) { std::stringstream msg; msg << "kind [" << kind << "] not recognised."; throw Exception(msg.str(), Here()); } void DataType::throw_not_recognised(std::string datatype) { std::stringstream msg; msg << "datatype [" << datatype << "] not recognised."; throw Exception(msg.str(), Here()); } //------------------------------------------------------------------------------------------------------ } // namespace io } // namespace atlas atlas-0.46.0/atlas_io/src/atlas_io/detail/NoConfig.h0000664000175000017500000000160715160212070022361 0ustar alastairalastair/* * (C) Copyright 2020 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include "eckit/config/LocalConfiguration.h" namespace atlas { namespace io { //--------------------------------------------------------------------------------------------------------------------- class NoConfig : public eckit::LocalConfiguration { public: NoConfig() = default; virtual ~NoConfig() = default; }; //--------------------------------------------------------------------------------------------------------------------- } // namespace io } // namespace atlas atlas-0.46.0/atlas_io/src/atlas_io/detail/BlackMagic.h0000664000175000017500000001256515160212070022641 0ustar alastairalastair/* * (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 // This file contains preprocessor black magic. It contains macros that // can return the number of arguments passed //----------------------------------------------------------------------------------------------------------- // Public /// Returns the number of passed arguments #define __ATLAS_IO_NARG(...) /// Splice a and b together #define __ATLAS_IO_SPLICE(a, b) #define __ATLAS_IO_STRINGIFY(a) a #define __ATLAS_IO_TYPE(Type, ...) #define __ATLAS_IO_TYPE_SCOPE(Type, ...) //----------------------------------------------------------------------------------------------------------- // Details // Undefine these, to be redefined further down. #undef __ATLAS_IO_NARG #undef __ATLAS_IO_SPLICE #undef __ATLAS_IO_TYPE #undef __ATLAS_IO_TYPE_SCOPE #define __ATLAS_IO_REVERSE 5, 4, 3, 2, 1, 0 #define __ATLAS_IO_ARGN(_1, _2, _3, _4, _5, N, ...) N #define __ATLAS_IO_NARG__(dummy, ...) __ATLAS_IO_ARGN(__VA_ARGS__) #define __ATLAS_IO_NARG_(...) __ATLAS_IO_NARG__(dummy, ##__VA_ARGS__, __ATLAS_IO_REVERSE) #define __ATLAS_IO_SPLICE(a, b) __ATLAS_IO_SPLICE_1(a, b) #define __ATLAS_IO_SPLICE_1(a, b) __ATLAS_IO_SPLICE_2(a, b) #define __ATLAS_IO_SPLICE_2(a, b) a##b #define __ATLAS_IO_ARG16(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, ...) _15 #define __ATLAS_IO_HAS_COMMA(...) __ATLAS_IO_ARG16(__VA_ARGS__, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0) #define __ATLAS_IO_TRIGGER_PARENTHESIS(...) , #define __ATLAS_IO_ISEMPTY(...) \ __ATLAS_IO_ISEMPTY_(/* test if there is just one argument, eventually an empty \ one */ \ __ATLAS_IO_HAS_COMMA(__VA_ARGS__), /* test if \ _TRIGGER_PARENTHESIS_ \ together with the \ argument adds a comma */ \ __ATLAS_IO_HAS_COMMA( \ __ATLAS_IO_TRIGGER_PARENTHESIS __VA_ARGS__), /* test if the argument together with \ a parenthesis adds a comma */ \ __ATLAS_IO_HAS_COMMA(__VA_ARGS__(/*empty*/)), /* test if placing it between \ __ATLAS_IO_TRIGGER_PARENTHESIS and the \ parenthesis adds a comma */ \ __ATLAS_IO_HAS_COMMA(__ATLAS_IO_TRIGGER_PARENTHESIS __VA_ARGS__(/*empty*/))) #define __ATLAS_IO_PASTE5(_0, _1, _2, _3, _4) _0##_1##_2##_3##_4 #define __ATLAS_IO_ISEMPTY_(_0, _1, _2, _3) \ __ATLAS_IO_HAS_COMMA(__ATLAS_IO_PASTE5(__ATLAS_IO_IS_EMPTY_CASE_, _0, _1, _2, _3)) #define __ATLAS_IO_IS_EMPTY_CASE_0001 , #define __ATLAS_IO_NARG(...) __ATLAS_IO_SPLICE(__ATLAS_IO_CALL_NARG_, __ATLAS_IO_ISEMPTY(__VA_ARGS__))(__VA_ARGS__) #define __ATLAS_IO_CALL_NARG_1(...) 0 #define __ATLAS_IO_CALL_NARG_0 __ATLAS_IO_NARG_ #define __ATLAS_IO_COMMA_ARGS(...) \ __ATLAS_IO_SPLICE(__ATLAS_IO_COMMA_ARGS_, __ATLAS_IO_ISEMPTY(__VA_ARGS__))(__VA_ARGS__) #define __ATLAS_IO_COMMA_ARGS_1(...) #define __ATLAS_IO_COMMA_ARGS_0(...) , __VA_ARGS__ #define __ATLAS_IO_ARGS_OR_DUMMY(...) \ __ATLAS_IO_SPLICE(__ATLAS_IO_ARGS_OR_DUMMY_, __ATLAS_IO_ISEMPTY(__VA_ARGS__)) \ (__VA_ARGS__) #define __ATLAS_IO_ARGS_OR_DUMMY_0(...) __VA_ARGS__ #define __ATLAS_IO_ARGS_OR_DUMMY_1(...) 0 #define __ATLAS_IO_TYPE(Type, ...) \ __ATLAS_IO_SPLICE(__ATLAS_IO_TYPE_, __ATLAS_IO_ISEMPTY(__VA_ARGS__)) \ (Type, __ATLAS_IO_ARGS_OR_DUMMY(__VA_ARGS__)) #define __ATLAS_IO_TYPE_1(Type, dummy) Type __ATLAS_IO_SPLICE(__variable_, __LINE__) #define __ATLAS_IO_TYPE_0(Type, ...) Type __ATLAS_IO_SPLICE(__variable_, __LINE__)(__VA_ARGS__) #define __ATLAS_IO_TYPE_SCOPE(Type, ...) \ __ATLAS_IO_SPLICE(__ATLAS_IO_TYPE_SCOPE_, __ATLAS_IO_ISEMPTY(__VA_ARGS__)) \ (Type, __ATLAS_IO_ARGS_OR_DUMMY(__VA_ARGS__)) #define __ATLAS_IO_TYPE_SCOPE_1(Type, ...) \ for (bool __ATLAS_IO_SPLICE(__done_, __LINE__) = false; __ATLAS_IO_SPLICE(__done_, __LINE__) != true;) \ for (Type __ATLAS_IO_SPLICE(__variable_, __LINE__); __ATLAS_IO_SPLICE(__done_, __LINE__) != true; \ __ATLAS_IO_SPLICE(__done_, __LINE__) = true) #define __ATLAS_IO_TYPE_SCOPE_0(Type, ...) \ for (bool __ATLAS_IO_SPLICE(__done_, __LINE__) = false; __ATLAS_IO_SPLICE(__done_, __LINE__) != true;) \ for (Type __ATLAS_IO_SPLICE(__variable_, __LINE__)(__VA_ARGS__); __ATLAS_IO_SPLICE(__done_, __LINE__) != true; \ __ATLAS_IO_SPLICE(__done_, __LINE__) = true) atlas-0.46.0/atlas_io/src/atlas_io/detail/StaticAssert.h0000664000175000017500000000555715160212070023300 0ustar alastairalastair/* * (C) Copyright 2020 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #ifndef ATLAS_IO_STATIC_ASSERT #if defined(__clang_analyzer__) #define ATLAS_IO_STATIC_ASSERT 0 #elif defined(__clang__) && (__clang_major__ < 4) #define ATLAS_IO_STATIC_ASSERT 0 #else #define ATLAS_IO_STATIC_ASSERT 1 #endif #endif #if ATLAS_IO_STATIC_ASSERT #include #include "atlas_io/detail/TypeTraits.h" namespace atlas { namespace io { template = 0> void encode_data(const T& in, atlas::io::Data& out) { static_assert( can_encode_data(), "\n\n" "\n Static assertion failed" "\n -----------------------" "\n" "\n Values of template type T cannot be encoded into Data because following function is not defined:" "\n" "\n void encode_data(const T& value, atlas::io::Data& out);" "\n" "\n Note that argument-dependent-lookup rules apply." "\n --> The function must be declared in the namespace of type T." "\n\n"); } template = 0> size_t encode_metadata(const T&, atlas::io::Metadata&) { static_assert( can_encode_metadata(), "\n\n" "\n Static assertion failed" "\n -----------------------" "\n" "\n Values of template type T cannot be incoded into Metadata because following function is not defined:" "\n" "\n size_t encode_metadata(const T& value, atlas::io::Metadata& metadata);" "\n" "\n Note that argument-dependent-lookup rules apply." "\n --> The function must be declared in the namespace of type T" "\n\n"); return 0; } template = 0> void decode(const atlas::io::Metadata&, const atlas::io::Data&, T&) { static_assert(is_decodable(), "\n\n" "\n Static assertion failed" "\n -----------------------" "\n" "\n Values of template type T cannot be decoded because following function is not defined:" "\n" "\n void decode(const atlas::io::Metadata&, const atlas::io::Data&, T& out);" "\n" "\n Note that argument-dependent-lookup rules apply." "\n The function must be declared in the namespace of type T" "\n\n"); } } // namespace io } // namespace atlas #endif atlas-0.46.0/atlas_io/src/atlas_io/detail/Encoder.h0000664000175000017500000000646615160212070022246 0ustar alastairalastair/* * (C) Copyright 2020 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include #include #include #include "atlas_io/Data.h" #include "atlas_io/RecordItem.h" #include "atlas_io/detail/DataInfo.h" #include "atlas_io/detail/Link.h" #include "atlas_io/detail/Reference.h" #include "atlas_io/detail/TypeTraits.h" namespace atlas { namespace io { class Encoder { public: Encoder() = default; operator bool() const { return bool(self_); } template = 0> explicit Encoder(T&& x): self_(new EncodableValue(std::move(x))) {} Encoder(const Link& link): self_(new EncodableLink(link)) {} Encoder(Encoder&& other): self_(std::move(other.self_)) {} template = 0> explicit Encoder(const T& x): self_(new EncodableValue(x)) {} Encoder& operator=(Encoder&& rhs) { self_ = std::move(rhs.self_); return *this; } friend size_t encode_metadata(const Encoder&, atlas::io::Metadata&); friend void encode_data(const Encoder&, atlas::io::Data&); bool encodes_data() const { return self_->encodes_data_(); } private: struct Encodable { virtual ~Encodable() = default; virtual size_t encode_metadata_(atlas::io::Metadata&) const = 0; virtual void encode_data_(atlas::io::Data&) const = 0; virtual bool encodes_data_() const = 0; }; template struct EncodableValue : Encodable { EncodableValue(Value&& v): value_{std::move(v)} { sfinae::encode_metadata(value_, metadata_, data_size_); } template = 0> EncodableValue(const Value& v): value_{v} { sfinae::encode_metadata(value_, metadata_, data_size_); } size_t encode_metadata_(atlas::io::Metadata& metadata) const override { metadata.set(metadata_); return data_size_; } void encode_data_(atlas::io::Data& out) const override { sfinae::encode_data(value_, out); } bool encodes_data_() const override { return data_size_ > 0; } const Value value_; Metadata metadata_; size_t data_size_{0}; }; struct EncodableLink : Encodable { EncodableLink(const Link& link): link_(link) {} size_t encode_metadata_(atlas::io::Metadata& metadata) const override { metadata.set(atlas::io::Metadata("link", link_.uri)); return 0; } void encode_data_(atlas::io::Data& /*out*/) const override {} bool encodes_data_() const override { return false; } Link link_; }; std::unique_ptr self_; }; size_t encode_metadata(const Encoder& encoder, atlas::io::Metadata& metadata); void encode_data(const Encoder& encoder, atlas::io::Data& out); } // namespace io } // namespace atlas atlas-0.46.0/atlas_io/src/atlas_io/Exceptions.h0000664000175000017500000000541315160212070021535 0ustar alastairalastair/* * (C) Copyright 2020 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include #include #include "eckit/exception/Exceptions.h" namespace atlas { namespace io { //--------------------------------------------------------------------------------------------------------------------- std::string demangle(const char*); template std::string demangle() { return demangle(typeid(T).name()); } //--------------------------------------------------------------------------------------------------------------------- class Exception : public eckit::Exception { public: using eckit::Exception::Exception; ~Exception() override; }; //--------------------------------------------------------------------------------------------------------------------- class NotEncodable : Exception { public: NotEncodable(const std::string& type_name); template NotEncodable(const T&): NotEncodable{demangle::type>()} {} ~NotEncodable() override; }; //--------------------------------------------------------------------------------------------------------------------- class NotDecodable : public Exception { public: NotDecodable(const std::string& type_name); template NotDecodable(const T&): NotDecodable{demangle::type>()} {} ~NotDecodable() override; }; //--------------------------------------------------------------------------------------------------------------------- class InvalidRecord : public Exception { public: InvalidRecord(const std::string& message): Exception("atlas::io::InvalidRecord: " + message) {} ~InvalidRecord() override; }; //--------------------------------------------------------------------------------------------------------------------- class DataCorruption : public Exception { public: DataCorruption(const std::string& message): Exception("atlas::io::DataCorruption: " + message) {} ~DataCorruption() override; }; //--------------------------------------------------------------------------------------------------------------------- class WriteError : public Exception { public: WriteError(const std::string& message): Exception("atlas::io::WriteError: " + message) {} ~WriteError() override; }; //--------------------------------------------------------------------------------------------------------------------- } // namespace io } // namespace atlas atlas-0.46.0/atlas_io/src/atlas_io/RecordItemReader.cc0000664000175000017500000001752315160212070022737 0ustar alastairalastair/* * (C) Copyright 2020 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "RecordItemReader.h" #include "atlas_io/Exceptions.h" #include "atlas_io/FileStream.h" #include "atlas_io/Record.h" #include "atlas_io/Session.h" #include "atlas_io/Trace.h" #include "atlas_io/detail/Assert.h" #include "atlas_io/detail/ParsedRecord.h" #include "atlas_io/detail/RecordSections.h" namespace atlas { namespace io { namespace { //--------------------------------------------------------------------------------------------------------------------- template inline size_t read_struct(IStream& in, Struct& s) { static_assert(Struct::bytes == sizeof(Struct), ""); return in.read(reinterpret_cast(&s), sizeof(Struct)); } //--------------------------------------------------------------------------------------------------------------------- template inline Struct read_struct(IStream& in) { Struct s; if (read_struct(in, s) != sizeof(Struct)) { throw InvalidRecord("Unexpected EOF reached"); } return s; } //--------------------------------------------------------------------------------------------------------------------- static Data read_data(const Record& record, int data_section_index, Stream in) { ATLAS_IO_TRACE("read_data(data_section=" + std::to_string(data_section_index) + ")"); if (data_section_index == 0) { return atlas::io::Data(); } const auto& parsed = static_cast(record); const auto& data_section = parsed.data_sections.at(size_t(data_section_index) - 1); atlas::io::Data data; auto offset = data_section.offset; in.seek(offset); auto data_begin = atlas::io::read_struct(in); if (not data_begin.valid()) { throw InvalidRecord("Data section is not valid"); } auto data_size = size_t(data_section.length) - sizeof(RecordDataSection::Begin) - sizeof(RecordDataSection::End); if (data_size) { if (data.read(in, data_size) != data_size) { throw InvalidRecord("Data section is not valid"); } ATLAS_IO_ASSERT(data.size() == data_size); } auto data_end = atlas::io::read_struct(in); if (not data_end.valid()) { throw InvalidRecord("Data section is not valid"); } return data; } //--------------------------------------------------------------------------------------------------------------------- static eckit::PathName make_absolute_path(const std::string& reference_path, RecordItem::URI& uri) { eckit::PathName absolute_path = uri.path; if (reference_path.size() && uri.path[0] != '/' && uri.path[0] != '~') { absolute_path = eckit::PathName{reference_path} / absolute_path; } return absolute_path.fullName(); } //--------------------------------------------------------------------------------------------------------------------- static Record read_record(const std::string& path, size_t offset) { auto record = Session::record(path, offset); if (record.empty()) { auto in = InputFileStream(path); in.seek(offset); record.read(in); } return record; } //--------------------------------------------------------------------------------------------------------------------- static Record read_record(Stream in, size_t offset) { auto record = Session::record(in, offset); if (record.empty()) { in.seek(offset); record.read(in); } return record; } //--------------------------------------------------------------------------------------------------------------------- } // anonymous namespace //--------------------------------------------------------------------------------------------------------------------- RecordItemReader::RecordItemReader(Stream in, size_t offset, const std::string& key): in_(in), uri_{"", offset, key} { ATLAS_IO_TRACE("RecordItemReader(Stream,offset,key"); record_ = read_record(in, uri_.offset); if (not record_.has(uri_.key)) { throw InvalidRecord(uri_.key + " not found in record " + uri_.path); } } RecordItemReader::RecordItemReader(Stream in, const std::string& key): in_(in), uri_{"", 0, key} { record_ = read_record(in, uri_.offset); if (not record_.has(uri_.key)) { throw InvalidRecord(uri_.key + " not found in record " + uri_.path); } } //--------------------------------------------------------------------------------------------------------------------- RecordItemReader::RecordItemReader(const std::string& uri): RecordItemReader("", uri) {} //--------------------------------------------------------------------------------------------------------------------- RecordItemReader::RecordItemReader(const std::string& ref, const std::string& uri): ref_{ref}, uri_{uri} { auto absolute_path = make_absolute_path(ref_, uri_); if (not absolute_path.exists()) { throw InvalidRecord("Item " + uri_.str() + " refers to non existing file: " + absolute_path); } record_ = read_record(absolute_path, uri_.offset); if (not record_.has(uri_.key)) { throw InvalidRecord(uri_.key + " not found in record " + uri_.path); } } //--------------------------------------------------------------------------------------------------------------------- void RecordItemReader::read(RecordItem& item) { io::Metadata metadata; io::Data data; read(metadata, data); item.metadata(metadata); item.data(std::move(data)); }; //--------------------------------------------------------------------------------------------------------------------- void RecordItemReader::read(Metadata& metadata, bool follow_links) { ATLAS_IO_TRACE("RecordItemReader::read_metadata(" + uri_.path + ":" + uri_.key + ")"); metadata = record_.metadata(uri_.key); if (follow_links && metadata.link()) { auto absolute_path = make_absolute_path(ref_, uri_); Metadata linked; RecordItemReader{absolute_path.dirName(), metadata.link()}.read(linked); metadata.link(std::move(linked)); } }; //--------------------------------------------------------------------------------------------------------------------- static void read_from_stream(Record record, Stream in, const std::string& key, io::Metadata& metadata, io::Data& data) { ATLAS_IO_TRACE("RecordItemReader::read( Stream, " + key + ")"); metadata = record.metadata(key); if (metadata.link()) { throw atlas::io::Exception("Cannot follow links in records that are not file based"); } else { if (metadata.data.section()) { data = atlas::io::read_data(record, metadata.data.section(), in); } } } void RecordItemReader::read(io::Metadata& metadata, io::Data& data) { if (in_) { read_from_stream(record_, in_, uri_.key, metadata, data); return; } ATLAS_IO_TRACE("RecordItemReader::read(" + uri_.path + ":" + uri_.key + ")"); metadata = record_.metadata(uri_.key); auto absolute_path = make_absolute_path(ref_, uri_); if (metadata.link()) { Metadata linked; RecordItemReader{absolute_path.dirName(), metadata.link()}.read(linked, data); metadata.link(std::move(linked)); } else { if (metadata.data.section()) { data = atlas::io::read_data(record_, metadata.data.section(), InputFileStream(absolute_path)); } } }; //--------------------------------------------------------------------------------------------------------------------- } // namespace io } // namespace atlas atlas-0.46.0/atlas_io/src/atlas_io/RecordItem.h0000664000175000017500000000526415160212070021455 0ustar alastairalastair/* * (C) Copyright 2020 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include #include #include "atlas_io/Data.h" #include "atlas_io/Metadata.h" #include "atlas_io/detail/tag.h" namespace atlas { namespace io { //--------------------------------------------------------------------------------------------------------------------- struct RecordItem { public: struct URI { URI(const std::string& uri); URI(const std::string& _path, std::uint64_t _offset, const std::string& _key); std::string str() const; operator std::string() { return str(); } std::string path; std::uint64_t offset; std::string key; }; public: RecordItem() = default; template explicit RecordItem(T&& x, tag::enable_static_assert = tag::enable_static_assert()); template explicit RecordItem(T&& x, tag::disable_static_assert); RecordItem(Metadata&&, Data&&); RecordItem(RecordItem&& other); const Data& data() const; const Metadata& metadata() const; void metadata(const Metadata& m); void data(atlas::io::Data&& d); bool empty() const; void clear(); void decompress(); void compress(); private: std::unique_ptr metadata_{new Metadata()}; Data data_; }; //--------------------------------------------------------------------------------------------------------------------- template RecordItem::RecordItem(T&& x, tag::enable_static_assert) { encode(x, *metadata_, data_); } //--------------------------------------------------------------------------------------------------------------------- template RecordItem::RecordItem(T&& x, tag::disable_static_assert) { encode(x, *metadata_, data_, tag::disable_static_assert()); } //--------------------------------------------------------------------------------------------------------------------- size_t encode_metadata(const RecordItem& in, atlas::io::Metadata& metadata); //--------------------------------------------------------------------------------------------------------------------- void encode_data(const RecordItem& in, atlas::io::Data& out); //--------------------------------------------------------------------------------------------------------------------- } // namespace io } // namespace atlas atlas-0.46.0/atlas_io/src/atlas_io/Session.cc0000664000175000017500000001240615160212070021175 0ustar alastairalastair/* * (C) Copyright 2020 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "Session.h" #include #include #include #include #include #include #include "eckit/filesystem/PathName.h" #include "atlas_io/Exceptions.h" #include "atlas_io/atlas_compat.h" #include "atlas_io/detail/Assert.h" namespace atlas { namespace io { using lock_guard = std::lock_guard; class SessionImpl { public: void store(Stream stream); Record record(const std::string& path, size_t offset); private: std::recursive_mutex mutex_; std::vector handles_; std::map records_; }; //--------------------------------------------------------------------------------------------------------------------- class ActiveSession { public: static ActiveSession& instance(); SessionImpl& current(); void push(); void pop(); Record record(const std::string& path, size_t offset); void store(Stream stream); private: friend class Session; std::recursive_mutex mutex_; std::unique_ptr session_; std::atomic count_{0}; }; //--------------------------------------------------------------------------------------------------------------------- ActiveSession& ActiveSession::instance() { static ActiveSession instance; return instance; } //--------------------------------------------------------------------------------------------------------------------- Record ActiveSession::record(const std::string& path, size_t offset) { if (count_) { return current().record(path, offset); } else { return Record(); } } //--------------------------------------------------------------------------------------------------------------------- void ActiveSession::store(Stream stream) { if (count_) { return current().store(stream); } } //--------------------------------------------------------------------------------------------------------------------- SessionImpl& ActiveSession::current() { lock_guard lock(mutex_); if (count_ == 0) { throw Exception("No atlas::io session is currently active", Here()); } return *session_; } //--------------------------------------------------------------------------------------------------------------------- void ActiveSession::push() { lock_guard lock(mutex_); if (count_ == 0) { ATLAS_IO_ASSERT(session_ == nullptr); session_.reset(new SessionImpl()); } ++count_; } //--------------------------------------------------------------------------------------------------------------------- void ActiveSession::pop() { lock_guard lock(mutex_); if (count_ == 0) { throw Exception("No atlas::io session is currently active", Here()); } --count_; if (count_ == 0) { session_.reset(); } } //--------------------------------------------------------------------------------------------------------------------- void SessionImpl::store(Stream stream) { lock_guard lock(mutex_); handles_.emplace_back(stream); } //--------------------------------------------------------------------------------------------------------------------- Record SessionImpl::record(const std::string& path, size_t offset) { lock_guard lock(mutex_); auto key = Record::URI{eckit::PathName(path).fullName(), offset}.str(); if (records_.find(key) == records_.end()) { records_.emplace(key, Record{}); } return records_.at(key); } //--------------------------------------------------------------------------------------------------------------------- Session::Session() { ActiveSession::instance().push(); } //--------------------------------------------------------------------------------------------------------------------- Session::~Session() { ActiveSession::instance().pop(); } //--------------------------------------------------------------------------------------------------------------------- bool Session::active() { return ActiveSession::instance().count_ > 0; } //--------------------------------------------------------------------------------------------------------------------- Record Session::record(const std::string& path, size_t offset) { return ActiveSession::instance().record(path, offset); } //--------------------------------------------------------------------------------------------------------------------- Record Session::record(Stream stream, size_t offset) { std::stringstream id; id << &stream.datahandle(); return ActiveSession::instance().record(id.str(), offset); } //--------------------------------------------------------------------------------------------------------------------- void Session::store(Stream stream) { ActiveSession::instance().store(stream); } //--------------------------------------------------------------------------------------------------------------------- } // namespace io } // namespace atlas atlas-0.46.0/atlas_io/src/atlas_io/FileStream.h0000664000175000017500000000357515160212070021456 0ustar alastairalastair/* * (C) Copyright 2020 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include "eckit/filesystem/PathName.h" #include "atlas_io/Stream.h" namespace eckit { class DataHandle; } namespace atlas { namespace io { //--------------------------------------------------------------------------------------------------------------------- enum class Mode { read, append, write, }; //--------------------------------------------------------------------------------------------------------------------- class FileStream : public Stream { public: FileStream(const eckit::PathName& path, Mode openmode); FileStream(const eckit::PathName& path, char openmode); FileStream(const eckit::PathName& path, const std::string& openmode); }; //--------------------------------------------------------------------------------------------------------------------- class InputFileStream : public FileStream { public: InputFileStream(const eckit::PathName& path); }; //--------------------------------------------------------------------------------------------------------------------- class OutputFileStream : public FileStream { public: OutputFileStream(const eckit::PathName& path, Mode openmode = Mode::write); OutputFileStream(const eckit::PathName& path, const std::string& openmode); OutputFileStream(const eckit::PathName& path, char openmode); void close(); }; //--------------------------------------------------------------------------------------------------------------------- } // namespace io } // namespace atlas atlas-0.46.0/atlas_io/src/atlas_io/print/0000775000175000017500000000000015160212070020374 5ustar alastairalastairatlas-0.46.0/atlas_io/src/atlas_io/print/TableFormat.h0000664000175000017500000000273015160212070022747 0ustar alastairalastair/* * (C) Copyright 2020 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include #include #include #include #include "atlas_io/Metadata.h" #include "atlas_io/Record.h" #include "atlas_io/RecordItemReader.h" #include "atlas_io/Session.h" namespace atlas { namespace io { class MetadataPrettyPrintBase { public: virtual ~MetadataPrettyPrintBase() = default; virtual void print(std::ostream&) const = 0; friend std::ostream& operator<<(std::ostream&, const MetadataPrettyPrintBase&); std::string str() const; }; class MetadataPrettyPrint { public: MetadataPrettyPrint(const atlas::io::Metadata&); friend std::ostream& operator<<(std::ostream& out, const MetadataPrettyPrint& p); std::string str() const; private: std::unique_ptr impl_; }; class TableFormat { public: TableFormat(const Record::URI& record, const eckit::Parametrisation& config); void print(std::ostream&) const; private: const Record record_; std::map items_; bool print_details_{false}; }; } // namespace io } // namespace atlas atlas-0.46.0/atlas_io/src/atlas_io/print/TableFormat.cc0000664000175000017500000002306415160212070023110 0ustar alastairalastair/* * (C) Copyright 2020 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "TableFormat.h" #include #include "atlas_io/Exceptions.h" #include "atlas_io/Metadata.h" #include "atlas_io/Record.h" #include "atlas_io/RecordItemReader.h" #include "atlas_io/Session.h" #include "atlas_io/detail/Assert.h" #include "atlas_io/print/Bytes.h" #include "atlas_io/types/array/ArrayReference.h" #include "atlas_io/types/scalar.h" #include "atlas_io/atlas_compat.h" namespace atlas { namespace io { std::ostream& operator<<(std::ostream& out, const MetadataPrettyPrintBase& p) { p.print(out); return out; } std::string MetadataPrettyPrintBase::str() const { std::stringstream s; print(s); return s.str(); } class DefaultMetadataPrettyPrint : public MetadataPrettyPrintBase { public: DefaultMetadataPrettyPrint(const Metadata&) {} void print(std::ostream&) const override {} }; class ArrayMetadataPrettyPrint : public MetadataPrettyPrintBase { template void print_value(std::ostream& out) const { std::vector value; metadata_.get("value", value); out << "{"; for (size_t i = 0; i < value.size(); ++i) { out << value[i]; if (i < value.size() - 1) { out << ","; } } out << "}"; } public: ArrayMetadataPrettyPrint(const Metadata& m): metadata_(m) {} void print(std::ostream& out) const override { std::string type = metadata_.getString("type"); ATLAS_IO_ASSERT(type == "array"); ArrayMetadata array(metadata_); out << std::setw(7) << std::left << array.datatype().str(); if (metadata_.has("value")) { out << ": "; std::string datatype = metadata_.getString("datatype"); if (datatype == DataType::str()) { print_value(out); } else if (datatype == DataType::str()) { print_value(out); } else if (datatype == DataType::str()) { print_value(out); } else if (datatype == DataType::str()) { print_value(out); } else if (datatype == DataType::str()) { print_value(out); } } else { out << "["; for (int i = 0; i < array.rank(); ++i) { out << array.shape(i); if (i < array.rank() - 1) { out << ","; } } out << "]"; } } private: Metadata metadata_; }; class StringMetadataPrettyPrint : public MetadataPrettyPrintBase { public: StringMetadataPrettyPrint(const Metadata& m): metadata_(m) {} void print(std::ostream& out) const override { std::string type = metadata_.getString("type"); ATLAS_IO_ASSERT(type == "string"); std::string value = metadata_.getString("value"); if (value.size() <= 32) { out << value; } else { out << value.substr(0, 32) << "..."; } } private: Metadata metadata_; }; class ScalarMetadataPrettyPrint : public MetadataPrettyPrintBase { public: ScalarMetadataPrettyPrint(const Metadata& m): metadata_(m) {} template T decode() const { T value; Data dummy; atlas::io::decode(metadata_, dummy, value); return value; } void print(std::ostream& out) const override { std::string type = metadata_.getString("type"); ATLAS_IO_ASSERT(type == "scalar"); std::string datatype = metadata_.getString("datatype"); out << std::setw(7) << std::left << datatype << ": "; if (datatype == DataType::str()) { out << decode(); } else if (datatype == DataType::str()) { out << decode(); } else if (datatype == DataType::str()) { out << decode(); } else if (datatype == DataType::str()) { out << decode(); } else if (datatype == DataType::str()) { out << decode(); } } private: Metadata metadata_; }; MetadataPrettyPrint::MetadataPrettyPrint(const atlas::io::Metadata& m) { std::string type = m.getString("type"); // Poor man factory builder if (type == "array") { impl_.reset(new ArrayMetadataPrettyPrint(m)); } else if (type == "scalar") { impl_.reset(new ScalarMetadataPrettyPrint(m)); } else if (type == "string") { impl_.reset(new StringMetadataPrettyPrint(m)); } else { impl_.reset(new DefaultMetadataPrettyPrint(m)); } } std::ostream& operator<<(std::ostream& out, const MetadataPrettyPrint& p) { out << *p.impl_; return out; } std::string MetadataPrettyPrint::str() const { return impl_->str(); } struct TablePrinter { std::vector > columns; std::vector widths; std::string indent{" "}; std::string sep{" "}; int col{0}; int row{1}; std::vector optional; std::vector underline; TablePrinter() = default; TablePrinter& column(const std::string& title, size_t width = 0) { columns.emplace_back(std::vector{title}); widths.emplace_back(std::max(title.size(), width)); optional.push_back(false); underline.push_back(true); return *this; } TablePrinter& column() { columns.emplace_back(std::vector{""}); widths.emplace_back(0); optional.push_back(true); underline.push_back(false); return *this; } TablePrinter& operator<<(const MetadataPrettyPrint p) { *this << p.str(); return *this; } TablePrinter& operator<<(const std::string& s) { columns[col].emplace_back(s); widths[col] = std::max(widths[col], s.size()); if (optional[col] && widths[col] > 0) { optional[col] = false; widths[col] = std::max(widths[col], columns[col][0].size()); } ++col; if (col == static_cast(columns.size())) { ++row; col = 0; } return *this; } void print(std::ostream& out) const { out << " "; for (size_t c = 0; c < columns.size(); ++c) { out << " " << sep << " " << std::setw(widths[c]) << std::left << columns[c][0]; } out << " " << sep << std::endl; out << " "; for (size_t c = 0; c < columns.size(); ++c) { const char u = underline[c] ? '-' : ' '; out << " " << sep << " " << std::string(widths[c], u); // underline } out << " " << sep << std::endl; for (int r = 1; r < row; ++r) { out << " "; for (size_t c = 0; c < columns.size(); ++c) { out << " " << sep << " " << std::setw(widths[c]) << std::left << columns[c][r]; } out << " " << sep << std::endl; } } }; TableFormat::TableFormat(const Record::URI& record, const eckit::Parametrisation& config): record_(Session::record(record.path, record.offset)) { for (const auto& key : record_.keys()) { items_.emplace(key, Metadata()); RecordItemReader{RecordItem::URI{record.path, record.offset, key}}.read(items_.at(key)); } config.get("details", print_details_); } void TableFormat::print(std::ostream& out) const { ATLAS_IO_ASSERT(not record_.empty()); TablePrinter table; table.column("name"); table.column("type"); table.column("description"); if (print_details_) { table.column("ver."); table.column("comp."); table.column("size"); table.column("checksum[:8]"); table.column("endian"); table.column("created"); } table.column(); for (auto& key : record_.keys()) { auto& item = items_.at(key); size_t cbytes = item.data.compressed_size(); size_t ubytes = item.data.size(); std::string endian = (item.data.endian() == Endian::little) ? "little" : "big"; std::string created = item.record.created().str().substr(0, 16); created[10] = ' '; table << key; table << item.getString("type"); table << MetadataPrettyPrint{item}; if (print_details_) { table << item.record.version(); if (item.data.section()) { table << item.data.compression(); table << Bytes{cbytes}.str() + (item.data.compressed() ? " < " + Bytes{ubytes}.str() : ""); table << Checksum{item.data.checksum()}.str(8); table << endian; } else { table << "" << "" << "" << ""; } table << created; } if (record_.metadata(key).link()) { table << "-> " + record_.metadata(key).link().str(); } else { table << ""; } } table.print(out); } } // namespace io } // namespace atlas atlas-0.46.0/atlas_io/src/atlas_io/print/JSONFormat.cc0000664000175000017500000000430015160212070022622 0ustar alastairalastair/* * (C) Copyright 2020 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "JSONFormat.h" #include "eckit/log/JSON.h" #include "atlas_io/Record.h" #include "atlas_io/RecordItemReader.h" #include "atlas_io/Session.h" namespace atlas { namespace io { JSONFormat::JSONFormat(const Record::URI& record, const eckit::Configuration& config): record_(Session::record(record.path, record.offset)) { for (const auto& key : record_.keys()) { items_.emplace(key, Metadata()); RecordItemReader{RecordItem::URI{record.path, record.offset, key}}.read(items_.at(key)); } config.get("details", print_details_); } void JSONFormat::print(std::ostream& out) const { eckit::JSON js(out, eckit::JSON::Formatting::indent(4)); Metadata metadata; for (const auto& key : record_.keys()) { const auto& item = items_.at(key); Metadata m = record_.metadata(key); if (record_.metadata(key).link()) { m.set(item); if (m.has("data")) { // removes data.section m.remove("data"); } } if (not print_details_) { if (m.has("data")) { m.remove("data"); } } if (print_details_) { if (item.data.size()) { m.set("data.compression.type", item.data.compression()); m.set("data.compression.size", item.data.compressed_size()); m.set("data.size", item.data.size()); m.set("data.byte_order", (item.data.endian() == Endian::little) ? "little endian" : "big endian"); m.set("data.checksum", item.data.checksum().str()); } m.set("version", item.record.version().str()); m.set("created", item.record.created().str()); } metadata.set(key, m); } js << metadata; } } // namespace io } // namespace atlas atlas-0.46.0/atlas_io/src/atlas_io/print/Bytes.h0000664000175000017500000000156615160212070021643 0ustar alastairalastair/* * (C) Copyright 2020 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include #include namespace atlas { namespace io { struct Bytes { public: Bytes(size_t bytes): bytes_(bytes) {} operator size_t() const { return bytes_; } std::string str(int decimals = 2, int width = 7) const; void print(std::ostream& out, int decimals = 2, int width = 7) const; friend std::ostream& operator<<(std::ostream&, const Bytes&); private: size_t bytes_; }; } // namespace io } // namespace atlas atlas-0.46.0/atlas_io/src/atlas_io/print/JSONFormat.h0000664000175000017500000000157315160212070022475 0ustar alastairalastair/* * (C) Copyright 2020 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include #include #include "eckit/config/Configuration.h" #include "atlas_io/Metadata.h" #include "atlas_io/Record.h" namespace atlas { namespace io { class JSONFormat { public: JSONFormat(const Record::URI& record, const eckit::Configuration&); void print(std::ostream&) const; private: const Record record_; std::map items_; bool print_details_{false}; }; } // namespace io } // namespace atlas atlas-0.46.0/atlas_io/src/atlas_io/print/Bytes.cc0000664000175000017500000000564515160212070022003 0ustar alastairalastair/* * (C) Copyright 2020 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "Bytes.h" #include #include #include #include #include namespace atlas { namespace io { namespace { template class FixedFormat { public: using value_type = Value; FixedFormat(value_type x, long precision): x_(x), precision_(precision > 0 ? precision : 20) {} void print(std::ostream& out) const { for (long precision = 0; precision <= precision_; ++precision) { if (is_precision(precision) || precision == precision_) { out << std::setprecision(precision); out << std::fixed << x_; break; } } } bool is_precision(long precision) const { std::stringstream ss; ss << std::setprecision(precision); ss << std::fixed << x_; value_type _x; ss >> _x; return std::abs(x_ - _x) < 1.e-20; } friend std::ostream& operator<<(std::ostream& out, const FixedFormat& This) { This.print(out); return out; } private: value_type x_; long precision_; }; inline FixedFormat fixed_format(double x, long precision) { return FixedFormat(x, precision); } /* static std::pair reduce_to_10( size_t bytes ) { static const std::vector magnitudes{"B", "K", "M", "G", "T", "P", "E", "Z", "Y"}; double x = bytes; size_t n = 0; while ( x >= 10 && n < magnitudes.size() ) { x /= 1024.; n++; } return std::make_pair( x, magnitudes[n] ); } */ static std::pair reduce_to_1000(size_t bytes) { static const std::vector magnitudes{"B", "K", "M", "G", "T", "P", "E", "Z", "Y"}; double x = bytes; size_t n = 0; while (x >= 1000 && n < magnitudes.size()) { x /= 1024.; n++; } return std::make_pair(x, magnitudes[n]); } } // namespace void Bytes::print(std::ostream& out, int decimals, int width) const { if (bytes_ < 1000 && width >= 4) { out << std::setw(width - 1) << std::right << bytes_ << 'B'; } else { auto pair = reduce_to_1000(bytes_); out << std::setw(width - 1) << std::right << fixed_format(pair.first, decimals); out << pair.second; } } std::ostream& operator<<(std::ostream& out, const Bytes& bytes) { bytes.print(out); return out; } std::string Bytes::str(int decimals, int width) const { std::stringstream s; print(s, decimals, width); return s.str(); } } // namespace io } // namespace atlas atlas-0.46.0/atlas_io/src/atlas_io/RecordPrinter.cc0000664000175000017500000000700415160212070022332 0ustar alastairalastair/* * (C) Copyright 2020 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "RecordPrinter.h" #include #include "atlas_io/Exceptions.h" #include "atlas_io/FileStream.h" #include "atlas_io/detail/Assert.h" #include "atlas_io/print/JSONFormat.h" #include "atlas_io/print/TableFormat.h" #include "atlas_io/atlas_compat.h" namespace atlas { namespace io { //--------------------------------------------------------------------------------------------------------------------- RecordPrinter::RecordPrinter(const eckit::PathName& path, const eckit::Configuration& config): RecordPrinter(path, 0, config) {} //--------------------------------------------------------------------------------------------------------------------- RecordPrinter::RecordPrinter(const eckit::PathName& path, const std::uint64_t offset, const eckit::Configuration& config): RecordPrinter(Record::URI{path, offset}, config) {} //--------------------------------------------------------------------------------------------------------------------- RecordPrinter::RecordPrinter(const Record::URI& ref, const eckit::Configuration& config): uri_(ref), record_(Session::record(ref.path, ref.offset)) { if (record_.empty()) { auto in = InputFileStream(uri_.path); in.seek(uri_.offset); record_.read(in, true); ATLAS_IO_ASSERT(not record_.empty()); } config.get("format", options_.format); config.get("details", options_.details); // Check if format is supported { std::vector supported_formats{"json", "yaml", "table"}; bool format_supported{false}; for (auto& supported_format : supported_formats) { if (options_.format == supported_format) { format_supported = true; break; } } if (not format_supported) { std::stringstream s; s << "Format '" + options_.format + "' not supported. Supported formats:"; for (auto& supported_format : supported_formats) { s << "\n - " << supported_format; } throw Exception(s.str(), Here()); } } } //--------------------------------------------------------------------------------------------------------------------- void RecordPrinter::print(std::ostream& out) const { eckit::LocalConfiguration config; config.set("details", options_.details); if (options_.format == "json") { JSONFormat{uri_, config}.print(out); } else if (options_.format == "yaml") { JSONFormat{uri_, config}.print(out); } else if (options_.format == "table") { TableFormat{uri_, config}.print(out); } else { throw Exception("Cannot print record: Unrecognized format " + options_.format + ".", Here()); } } //--------------------------------------------------------------------------------------------------------------------- std::ostream& operator<<(std::ostream& out, const RecordPrinter& info) { info.print(out); return out; } //--------------------------------------------------------------------------------------------------------------------- } // namespace io } // namespace atlas atlas-0.46.0/atlas_io/src/atlas_io/Data.h0000664000175000017500000000323715160212070020267 0ustar alastairalastair/* * (C) Copyright 2020 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include "eckit/io/Buffer.h" namespace atlas { namespace io { class Stream; //--------------------------------------------------------------------------------------------------------------------- class Data { public: Data() = default; Data(void*, size_t); Data(Data&&) = default; Data& operator=(Data&&) = default; operator const void*() const { return data(); } const void* data() const { return buffer_.data(); } size_t size() const { return size_; } void assign(const Data& other); void assign(const void*, size_t); void clear(); std::uint64_t write(Stream& out) const; std::uint64_t read(Stream& in, size_t size); void compress(const std::string& compression); void decompress(const std::string& compression, size_t uncompressed_size); std::string checksum(const std::string& algorithm = "") const; private: eckit::Buffer buffer_; size_t size_{0}; }; //--------------------------------------------------------------------------------------------------------------------- void encode_data(const Data&, Data& out); //--------------------------------------------------------------------------------------------------------------------- } // namespace io } // namespace atlas atlas-0.46.0/atlas_io/src/atlas_io/RecordItemReader.h0000664000175000017500000000254715160212070022601 0ustar alastairalastair/* * (C) Copyright 2020 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include "atlas_io/Record.h" #include "atlas_io/RecordItem.h" #include "atlas_io/Stream.h" namespace atlas { namespace io { //--------------------------------------------------------------------------------------------------------------------- class RecordItemReader { public: RecordItemReader(Stream, size_t offset, const std::string& key); RecordItemReader(Stream, const std::string& key); RecordItemReader(const std::string& uri); void read(RecordItem& item); void read(Metadata&, bool follow_links = true); void read(Metadata&, Data&); private: RecordItemReader(const std::string& ref, const std::string& uri); Stream in_; Record record_; std::string ref_{}; // directory to which relative URI's are evaluated RecordItem::URI uri_; }; //--------------------------------------------------------------------------------------------------------------------- } // namespace io } // namespace atlas atlas-0.46.0/atlas_io/src/atlas_io/Exceptions.cc0000664000175000017500000000673215160212070021700 0ustar alastairalastair/* * (C) Copyright 2020 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "Exceptions.h" #include "atlas_io/detail/defines.h" #if ATLAS_HAVE_CXXABI_H #include #endif #include #include namespace atlas { namespace io { //--------------------------------------------------------------------------------------------------------------------- std::string demangle(const char* name) { #if ATLAS_HAVE_CXXABI_H int status = -4; std::unique_ptr res{abi::__cxa_demangle(name, nullptr, nullptr, &status), std::free}; return (status == 0) ? res.get() : name; #else return name; #endif } //--------------------------------------------------------------------------------------------------------------------- NotEncodable::NotEncodable(const std::string& type_name): Exception{[&type_name] { std::stringstream message; message << "atlas::io::NotEncodable: Cannot encode values of type " << type_name << "."; message << "\n Implement the functions" "\n" "\n void encode_data(const " << type_name << "&, atlas::io::Data& );" "\n size_t encode_metadata(const " << type_name << "&, atlas::io::Metadata& );" "\n" "\n or alternatively a conversion function to atlas::io::types::ArrayView" "\n" "\n void interprete(const " << type_name << "&, atlas::io::types::ArrayView& )" "\n" "\n Rules of argument-dependent-lookup apply." "\n --> Functions need to be declared in namespace of any of the arguments."; return message.str(); }()} {} //--------------------------------------------------------------------------------------------------------------------- NotDecodable::NotDecodable(const std::string& type_name): Exception{[&type_name] { std::stringstream message; message << "atlas::io::NotDecodable: Cannot decode values of type " << type_name << "."; message << "\n Implement the functions" "\n" "\n void decode( const atlas::io::Metadata&, const atlas::io::Data&, " << type_name << "& );" "\n" "\n Rules of argument-dependent-lookup apply." "\n --> Functions need to be declared in namespace of any of the arguments."; return message.str(); }()} {} //--------------------------------------------------------------------------------------------------------------------- Exception::~Exception() = default; NotEncodable::~NotEncodable() = default; NotDecodable::~NotDecodable() = default; InvalidRecord::~InvalidRecord() = default; DataCorruption::~DataCorruption() = default; WriteError::~WriteError() = default; //--------------------------------------------------------------------------------------------------------------------- } // namespace io } // namespace atlas atlas-0.46.0/atlas_io/src/atlas_io/CMakeLists.txt0000664000175000017500000000531315160212070022002 0ustar alastairalastair if( HAVE_ECKIT_DEVELOP ) set( ATLAS_IO_ECKIT_DEVELOP 1 ) else() set( ATLAS_IO_ECKIT_DEVELOP 0 ) endif() ecbuild_parse_version( ${eckit_VERSION} PREFIX ATLAS_IO_ECKIT ) math( EXPR ATLAS_IO_ECKIT_VERSION_INT "( 10000 * ${ATLAS_IO_ECKIT_VERSION_MAJOR} ) + ( 100 * ${ATLAS_IO_ECKIT_VERSION_MINOR} ) + ${ATLAS_IO_ECKIT_VERSION_PATCH}" ) configure_file( detail/defines.h.in detail/defines.h ) install( FILES ${CMAKE_CURRENT_BINARY_DIR}/detail/defines.h DESTINATION ${INSTALL_INCLUDE_DIR}/atlas_io/detail ) ecbuild_add_library( TARGET atlas_io INSTALL_HEADERS ALL HEADER_DESTINATION include/atlas_io PUBLIC_LIBS eckit PUBLIC_INCLUDES $ $ SOURCES atlas-io.h Data.cc Data.h detail/Assert.h detail/Base64.cc detail/Base64.h detail/Checksum.h detail/Checksum.cc detail/DataInfo.h detail/DataType.cc detail/DataType.h detail/Decoder.cc detail/Decoder.h detail/Defaults.h detail/Encoder.cc detail/Encoder.h detail/Endian.h detail/Link.cc detail/Link.h detail/ParsedRecord.h detail/RecordInfo.h detail/RecordSections.h detail/Reference.h detail/sfinae.h detail/StaticAssert.h detail/tag.h detail/Time.cc detail/Time.h detail/Type.h detail/TypeTraits.h detail/Version.h Exceptions.cc Exceptions.h FileStream.cc FileStream.h Metadata.cc Metadata.h print/TableFormat.cc print/TableFormat.h print/JSONFormat.cc print/JSONFormat.h print/Bytes.cc print/Bytes.h ReadRequest.cc ReadRequest.h Record.cc Record.h RecordItem.cc RecordItem.h RecordItemReader.cc RecordItemReader.h RecordPrinter.cc RecordPrinter.h RecordReader.cc RecordReader.h RecordWriter.cc RecordWriter.h Session.cc Session.h Stream.cc Stream.h Trace.cc Trace.h types/array.h types/array/ArrayMetadata.cc types/array/ArrayMetadata.h types/array/ArrayReference.cc types/array/ArrayReference.h types/array/adaptors/StdArrayAdaptor.h types/array/adaptors/StdVectorAdaptor.h types/array/adaptors/StdVectorOfStdArrayAdaptor.h types/string.h types/scalar.h types/scalar.cc ${CMAKE_CURRENT_BINARY_DIR}/detail/defines.h ) target_compile_features( atlas_io PUBLIC cxx_std_17 ) atlas-0.46.0/atlas_io/src/atlas_io/ReadRequest.cc0000664000175000017500000001113115160212070021770 0ustar alastairalastair/* * (C) Copyright 2020 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "ReadRequest.h" #include "eckit/log/Log.h" #include "atlas_io/Exceptions.h" #include "atlas_io/RecordItemReader.h" #include "atlas_io/Trace.h" #include "atlas_io/detail/Assert.h" #include "atlas_io/detail/Checksum.h" #include "atlas_io/detail/Defaults.h" namespace atlas { namespace io { static std::string stream_path(Stream stream) { std::stringstream s; s << &stream.datahandle(); return s.str(); } //--------------------------------------------------------------------------------------------------------------------- ReadRequest::ReadRequest(const std::string& URI, atlas::io::Decoder* decoder): uri_(URI), decoder_(decoder), item_(new RecordItem()) { do_checksum_ = defaults::checksum_read(); ATLAS_IO_ASSERT(uri_.size()); } ReadRequest::ReadRequest(Stream stream, size_t offset, const std::string& key, Decoder* decoder): stream_{stream}, offset_{offset}, key_{key}, uri_{"stream:" + stream_path(stream) + "?offset=key=" + key_}, decoder_(decoder), item_(new RecordItem()) { do_checksum_ = defaults::checksum_read(); ATLAS_IO_ASSERT(stream_); } //--------------------------------------------------------------------------------------------------------------------- ReadRequest::ReadRequest(ReadRequest&& other): stream_{other.stream_}, offset_{other.offset_}, key_{other.key_}, uri_(std::move(other.uri_)), decoder_(std::move(other.decoder_)), item_(std::move(other.item_)), do_checksum_{other.do_checksum_}, finished_{other.finished_} { other.do_checksum_ = true; other.finished_ = true; } //--------------------------------------------------------------------------------------------------------------------- ReadRequest::~ReadRequest() { if (item_) { if (not finished_) { eckit::Log::error() << "Request for " << uri_ << " was not completed." << std::endl; } } } //--------------------------------------------------------------------------------------------------------------------- void ReadRequest::read() { if (item_->empty()) { if (stream_) { RecordItemReader{stream_, offset_, key_}.read(*item_); } else { RecordItemReader(uri_).read(*item_); } } } //--------------------------------------------------------------------------------------------------------------------- void ReadRequest::checksum(bool b) { do_checksum_ = b; } void ReadRequest::checksum() { if (not do_checksum_) { return; } Checksum encoded_checksum{item_->metadata().data.checksum()}; if (not encoded_checksum.available()) { return; } Checksum computed_checksum{item_->data().checksum(encoded_checksum.algorithm())}; if (computed_checksum.available() && (computed_checksum.str() != encoded_checksum.str())) { std::stringstream err; err << "Mismatch in checksums for " << uri_ << ".\n"; err << " Encoded: [" << encoded_checksum.str() << "].\n"; err << " Computed: [" << computed_checksum.str() << "]."; throw DataCorruption(err.str()); } do_checksum_ = false; } //--------------------------------------------------------------------------------------------------------------------- void ReadRequest::decompress() { read(); item_->decompress(); } //--------------------------------------------------------------------------------------------------------------------- void ReadRequest::decode() { decompress(); io::decode(item_->metadata(), item_->data(), *decoder_); if (item_->data().size()) { ATLAS_IO_TRACE_SCOPE("deallocate"); item_->clear(); } else { item_->clear(); } } //--------------------------------------------------------------------------------------------------------------------- void ReadRequest::wait() { ATLAS_IO_TRACE("ReadRequest::wait(" + uri_ + ")"); if (item_) { if (not finished_) { read(); checksum(); decompress(); decode(); } finished_ = true; } } //--------------------------------------------------------------------------------------------------------------------- } // namespace io } // namespace atlas atlas-0.46.0/atlas_io/src/tools/0000775000175000017500000000000015160212070016605 5ustar alastairalastairatlas-0.46.0/atlas_io/src/tools/atlas-io-list.cc0000664000175000017500000001627215160212070021606 0ustar alastairalastair/* * (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/option/CmdArgs.h" #include "eckit/option/Separator.h" #include "eckit/option/SimpleOption.h" #include "eckit/option/VectorOption.h" #include "eckit/runtime/Tool.h" #include "eckit/filesystem/PathName.h" #include "atlas_io/Exceptions.h" #include "atlas_io/RecordPrinter.h" #include "atlas_io/print/Bytes.h" //-------------------------------------------------------------------------------- using eckit::Log; class AtlasIOTool : public eckit::Tool { public: using Options = std::vector; using Args = eckit::option::CmdArgs; protected: virtual std::string indent() { return " "; } virtual std::string briefDescription() { return ""; } virtual std::string longDescription() { return ""; } virtual std::string usage() { return name() + " [OPTION]... [--help,-h]"; } void add_option(eckit::option::Option* option) { options_.push_back(option); } virtual void help(std::ostream& out = Log::info()) { auto indented = [&](const std::string& s) -> std::string { std::string str = indent() + s; size_t pos = 0; while ((pos = str.find('\n', pos)) != std::string::npos) { str.replace(pos, 1, '\n' + indent()); ++pos; } return str; }; out << "NAME\n" << indented(name()); std::string brief = briefDescription(); if (brief.size()) { out << " - " << brief << '\n'; } std::string usg = usage(); if (usg.size()) { out << '\n'; out << "SYNOPSIS\n" << indented(usg) << '\n'; } std::string desc = longDescription(); if (desc.size()) { out << '\n'; out << "DESCRIPTION\n" << indented(desc) << '\n'; } out << '\n'; out << "OPTIONS\n"; for (Options::const_iterator it = options_.begin(); it != options_.end(); ++it) { std::stringstream s; s << **it; out << indented(s.str()) << "\n\n"; } out << std::flush; } virtual int numberOfPositionalArguments() { return -1; } virtual int minimumPositionalArguments() { return 0; } bool handle_help() { for (int i = 1; i < argc(); ++i) { if (argv(i) == "--help" || argv(i) == "-h") { help(std::cout); return true; } } return false; } public: AtlasIOTool(int argc, char** argv): eckit::Tool(argc, argv) { add_option(new eckit::option::SimpleOption("help", "Print this help")); } int start() { try { if (handle_help()) { return success(); } if (argc() - 1 < minimumPositionalArguments()) { Log::error() << "Usage: " << usage() << std::endl; return failed(); } Options opts = options_; std::function dummy = [](const std::string&) {}; Args args(dummy, opts, numberOfPositionalArguments(), minimumPositionalArguments() > 0); int err_code = execute(args); return err_code; } catch (eckit::Exception& e) { Log::error() << "** " << e.what() << " Caught in " << Here() << std::endl; Log::error() << "** Exception terminates " << name() << std::endl; } catch (std::exception& e) { Log::error() << "** " << e.what() << " Caught in " << Here() << std::endl; Log::error() << "** Exception terminates " << name() << std::endl; } return failed(); } void run() final {} // unused virtual int execute(const Args&) = 0; static constexpr int success() { return 0; } static constexpr int failed() { return 1; } private: Options options_; }; //---------------------------------------------------------------------------------------------------------------------- struct AtlasIOList : public AtlasIOTool { std::string briefDescription() override { return "Inspection of atlas-io files"; } std::string usage() override { return name() + " [OPTION]... [--help,-h]"; } std::string longDescription() override { return "Inspection of atlas-io files\n" "\n" " : path to atlas-io file"; } AtlasIOList(int argc, char** argv): AtlasIOTool(argc, argv) { add_option(new eckit::option::SimpleOption("format", "Output format")); add_option(new eckit::option::SimpleOption("version", "Print version of records")); add_option(new eckit::option::SimpleOption("details", "Print detailed information")); } int execute(const Args& args) override { auto return_code = success(); using namespace atlas; // User sanity checks if (args.count() == 0) { Log::error() << "No file specified." << std::endl; help(Log::error()); return failed(); } // Configuration eckit::LocalConfiguration config; config.set("format", args.getString("format", "table")); config.set("details", args.getBool("details", false)); // Loop over files for (size_t f = 0; f < args.count(); ++f) { eckit::PathName file(args(f)); if (!file.exists()) { Log::error() << "File does not exist: " << file << std::endl; return failed(); } auto filesize = size_t(file.size()); io::Session session; std::uint64_t pos = 0; try { while (pos < filesize) { auto uri = io::Record::URI{file, pos}; auto record = io::RecordPrinter{uri, config}; std::stringstream out; out << "\n# " << uri.path << " [" << uri.offset << "] " << "{ size: " << atlas::io::Bytes{record.size()}.str(0) << ", version: " << record.version() << ", created: " << record.time() << " }"; out << '\n' << (config.getString("format") == "table" ? "" : "---") << '\n'; out << record << std::endl; std::cout << out.str(); pos += record.size(); } } catch (const io::Exception& e) { Log::error() << " ATLAS-IO-ERROR: " << e.what() << std::endl; return_code = failed(); } } return return_code; } }; //------------------------------------------------------------------------------------------------------ int main(int argc, char** argv) { return AtlasIOList{argc, argv}.start(); } atlas-0.46.0/atlas_io/src/tools/CMakeLists.txt0000664000175000017500000000076315160212070021353 0ustar alastairalastair# (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. ecbuild_add_executable( TARGET atlas-io-list SOURCES atlas-io-list.cc LIBS atlas_io eckit_option ) atlas-0.46.0/atlas_io/src/CMakeLists.txt0000664000175000017500000000006315160212070020204 0ustar alastairalastairadd_subdirectory(atlas_io) add_subdirectory(tools) atlas-0.46.0/atlas_io/README.md0000664000175000017500000000030515160212070016133 0ustar alastairalastairatlas-io ======== An IO API for serializing arbitrary data, supporting compression. The API contains a message format (like GRIB). Multiple messages can be written to file or sent over network. atlas-0.46.0/atlas_io/CHANGELOG.md0000664000175000017500000000000015160212070016455 0ustar alastairalastairatlas-0.46.0/atlas_io/eckit_codec_adaptor/0000775000175000017500000000000015160212070020624 5ustar alastairalastairatlas-0.46.0/atlas_io/eckit_codec_adaptor/src/0000775000175000017500000000000015160212070021413 5ustar alastairalastairatlas-0.46.0/atlas_io/eckit_codec_adaptor/src/atlas_io/0000775000175000017500000000000015160212070023206 5ustar alastairalastairatlas-0.46.0/atlas_io/eckit_codec_adaptor/src/atlas_io/Trace.h0000664000175000017500000000472115160212070024421 0ustar alastairalastair/* * (C) Copyright 2020 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include #include #include #include #include namespace eckit { class CodeLocation; } namespace atlas { namespace io { struct TraceHook { TraceHook() = default; virtual ~TraceHook() = default; }; struct TraceHookRegistry { using TraceHookBuilder = std::function(const eckit::CodeLocation&, const std::string&)>; std::vector hooks; std::vector enabled_; static TraceHookRegistry& instance() { static TraceHookRegistry instance; return instance; } static size_t add(TraceHookBuilder&& hook) { instance().hooks.emplace_back(hook); instance().enabled_.emplace_back(true); return instance().hooks.size() - 1; } static size_t add(const TraceHookBuilder& hook) { instance().hooks.emplace_back(hook); instance().enabled_.emplace_back(true); return instance().hooks.size() - 1; } static void enable(size_t id) { instance().enabled_[id] = true; } static void disable(size_t id) { instance().enabled_[id] = false; } static bool enabled(size_t id) { return instance().enabled_[id]; } static size_t size() { return instance().hooks.size(); } static TraceHookBuilder& hook(size_t id) { return instance().hooks[id]; } static size_t invalidId() { return std::numeric_limits::max(); } private: TraceHookRegistry() = default; }; struct Trace { using Labels = std::vector; Trace(const eckit::CodeLocation& loc); Trace(const eckit::CodeLocation& loc, const std::string& title); Trace(const eckit::CodeLocation& loc, const std::string& title, const Labels& labels); private: std::vector> hooks_; }; } // namespace io } // namespace atlas #include "atlas_io/detail/BlackMagic.h" #define ATLAS_IO_TRACE(...) __ATLAS_IO_TYPE(::atlas::io::Trace, Here() __ATLAS_IO_COMMA_ARGS(__VA_ARGS__)) #define ATLAS_IO_TRACE_SCOPE(...) __ATLAS_IO_TYPE_SCOPE(::atlas::io::Trace, Here() __ATLAS_IO_COMMA_ARGS(__VA_ARGS__)) atlas-0.46.0/atlas_io/eckit_codec_adaptor/src/atlas_io/Trace.cc0000664000175000017500000000252415160212070024556 0ustar alastairalastair/* * (C) Copyright 2020 ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #include "Trace.h" #include "eckit/log/CodeLocation.h" namespace atlas { namespace io { atlas::io::Trace::Trace(const eckit::CodeLocation& loc) { for (size_t id = 0; id < TraceHookRegistry::size(); ++id) { if (TraceHookRegistry::enabled(id)) { hooks_.emplace_back(TraceHookRegistry::hook(id)(loc, loc.func())); } } } Trace::Trace(const eckit::CodeLocation& loc, const std::string& title) { for (size_t id = 0; id < TraceHookRegistry::size(); ++id) { if (TraceHookRegistry::enabled(id)) { hooks_.emplace_back(TraceHookRegistry::hook(id)(loc, title)); } } } Trace::Trace(const eckit::CodeLocation& loc, const std::string& title, const Labels& labels) { for (size_t id = 0; id < TraceHookRegistry::size(); ++id) { if (TraceHookRegistry::enabled(id)) { hooks_.emplace_back(TraceHookRegistry::hook(id)(loc, title)); } } } } // namespace io } // namespace atlas atlas-0.46.0/atlas_io/eckit_codec_adaptor/src/atlas_io/atlas-io.h0000664000175000017500000000667615160212070025107 0ustar alastairalastair/* * (C) Copyright 2023- ECMWF. * * This software is licensed under the terms of the Apache Licence Version 2.0 * which can be obtained at http://www.apache.org/licenses/LICENSE-2.0. * In applying this licence, ECMWF does not waive the privileges and immunities * granted to it by virtue of its status as an intergovernmental organisation * nor does it submit to any jurisdiction. */ #pragma once #include "eckit/codec/codec.h" namespace atlas::io { // Encoding/Decoding using Metadata = ::eckit::codec::Metadata; using DataType = ::eckit::codec::DataType; using Data = ::eckit::codec::Data; using Encoder = ::eckit::codec::Encoder; using Decoder = ::eckit::codec::Decoder; // Record using Record = ::eckit::codec::Record; using RecordWriter = ::eckit::codec::RecordWriter; using RecordPrinter = ::eckit::codec::RecordPrinter; using RecordItemReader = ::eckit::codec::RecordItemReader; using RecordReader = ::eckit::codec::RecordReader; // I/O using Session = ::eckit::codec::Session; using Mode = ::eckit::codec::Mode; using Stream = ::eckit::codec::Stream; using FileStream = ::eckit::codec::FileStream; using InputFileStream = ::eckit::codec::InputFileStream; using OutputFileStream = ::eckit::codec::OutputFileStream; // Array using ArrayReference = ::eckit::codec::ArrayReference; using ArrayMetadata = ::eckit::codec::ArrayMetadata; using ArrayShape = ::eckit::codec::ArrayShape; // Exceptions using Exception = ::eckit::codec::Exception; using NotEncodable = ::eckit::codec::NotEncodable; using NotDecodable = ::eckit::codec::NotDecodable; using InvalidRecord = ::eckit::codec::InvalidRecord; using DataCorruption = ::eckit::codec::DataCorruption; using WriteError = ::eckit::codec::WriteError; // Type traits template static constexpr bool is_interpretable() { return ::eckit::codec::is_interpretable(); } template static constexpr bool is_encodable() { return ::eckit::codec::is_encodable(); } template static constexpr bool is_decodable() { return ::eckit::codec::is_decodable(); } template static constexpr bool can_encode_metadata() { return ::eckit::codec::can_encode_metadata(); } template static constexpr bool can_encode_data() { return ::eckit::codec::can_encode_metadata(); } namespace tag { using disable_static_assert = ::eckit::codec::tag::disable_static_assert; using enable_static_assert = ::eckit::codec::tag::enable_static_assert; } // Functions using ::eckit::codec::ref; using ::eckit::codec::copy; using ::eckit::codec::encode; using ::eckit::codec::decode; using ::eckit::codec::interprete; using ::eckit::codec::link; using ::eckit::codec::make_datatype; // template // using make_datatype = eckit::codec::make_datatype; namespace defaults { using ::eckit::codec::defaults::compression_algorithm; using ::eckit::codec::defaults::checksum_algorithm; using ::eckit::codec::defaults::checksum_read; using ::eckit::codec::defaults::checksum_write; } } #define ATLAS_IO_ASSERT(X) ASSERT(X) #define ATLAS_IO_ASSERT_MSG(X, M) ASSERT_MSG(X, M) atlas-0.46.0/atlas_io/eckit_codec_adaptor/src/atlas_io/detail/0000775000175000017500000000000015160212070024450 5ustar alastairalastairatlas-0.46.0/atlas_io/eckit_codec_adaptor/src/atlas_io/detail/BlackMagic.h0000664000175000017500000001256515160212070026607 0ustar alastairalastair/* * (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 // This file contains preprocessor black magic. It contains macros that // can return the number of arguments passed //----------------------------------------------------------------------------------------------------------- // Public /// Returns the number of passed arguments #define __ATLAS_IO_NARG(...) /// Splice a and b together #define __ATLAS_IO_SPLICE(a, b) #define __ATLAS_IO_STRINGIFY(a) a #define __ATLAS_IO_TYPE(Type, ...) #define __ATLAS_IO_TYPE_SCOPE(Type, ...) //----------------------------------------------------------------------------------------------------------- // Details // Undefine these, to be redefined further down. #undef __ATLAS_IO_NARG #undef __ATLAS_IO_SPLICE #undef __ATLAS_IO_TYPE #undef __ATLAS_IO_TYPE_SCOPE #define __ATLAS_IO_REVERSE 5, 4, 3, 2, 1, 0 #define __ATLAS_IO_ARGN(_1, _2, _3, _4, _5, N, ...) N #define __ATLAS_IO_NARG__(dummy, ...) __ATLAS_IO_ARGN(__VA_ARGS__) #define __ATLAS_IO_NARG_(...) __ATLAS_IO_NARG__(dummy, ##__VA_ARGS__, __ATLAS_IO_REVERSE) #define __ATLAS_IO_SPLICE(a, b) __ATLAS_IO_SPLICE_1(a, b) #define __ATLAS_IO_SPLICE_1(a, b) __ATLAS_IO_SPLICE_2(a, b) #define __ATLAS_IO_SPLICE_2(a, b) a##b #define __ATLAS_IO_ARG16(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, _11, _12, _13, _14, _15, ...) _15 #define __ATLAS_IO_HAS_COMMA(...) __ATLAS_IO_ARG16(__VA_ARGS__, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0) #define __ATLAS_IO_TRIGGER_PARENTHESIS(...) , #define __ATLAS_IO_ISEMPTY(...) \ __ATLAS_IO_ISEMPTY_(/* test if there is just one argument, eventually an empty \ one */ \ __ATLAS_IO_HAS_COMMA(__VA_ARGS__), /* test if \ _TRIGGER_PARENTHESIS_ \ together with the \ argument adds a comma */ \ __ATLAS_IO_HAS_COMMA( \ __ATLAS_IO_TRIGGER_PARENTHESIS __VA_ARGS__), /* test if the argument together with \ a parenthesis adds a comma */ \ __ATLAS_IO_HAS_COMMA(__VA_ARGS__(/*empty*/)), /* test if placing it between \ __ATLAS_IO_TRIGGER_PARENTHESIS and the \ parenthesis adds a comma */ \ __ATLAS_IO_HAS_COMMA(__ATLAS_IO_TRIGGER_PARENTHESIS __VA_ARGS__(/*empty*/))) #define __ATLAS_IO_PASTE5(_0, _1, _2, _3, _4) _0##_1##_2##_3##_4 #define __ATLAS_IO_ISEMPTY_(_0, _1, _2, _3) \ __ATLAS_IO_HAS_COMMA(__ATLAS_IO_PASTE5(__ATLAS_IO_IS_EMPTY_CASE_, _0, _1, _2, _3)) #define __ATLAS_IO_IS_EMPTY_CASE_0001 , #define __ATLAS_IO_NARG(...) __ATLAS_IO_SPLICE(__ATLAS_IO_CALL_NARG_, __ATLAS_IO_ISEMPTY(__VA_ARGS__))(__VA_ARGS__) #define __ATLAS_IO_CALL_NARG_1(...) 0 #define __ATLAS_IO_CALL_NARG_0 __ATLAS_IO_NARG_ #define __ATLAS_IO_COMMA_ARGS(...) \ __ATLAS_IO_SPLICE(__ATLAS_IO_COMMA_ARGS_, __ATLAS_IO_ISEMPTY(__VA_ARGS__))(__VA_ARGS__) #define __ATLAS_IO_COMMA_ARGS_1(...) #define __ATLAS_IO_COMMA_ARGS_0(...) , __VA_ARGS__ #define __ATLAS_IO_ARGS_OR_DUMMY(...) \ __ATLAS_IO_SPLICE(__ATLAS_IO_ARGS_OR_DUMMY_, __ATLAS_IO_ISEMPTY(__VA_ARGS__)) \ (__VA_ARGS__) #define __ATLAS_IO_ARGS_OR_DUMMY_0(...) __VA_ARGS__ #define __ATLAS_IO_ARGS_OR_DUMMY_1(...) 0 #define __ATLAS_IO_TYPE(Type, ...) \ __ATLAS_IO_SPLICE(__ATLAS_IO_TYPE_, __ATLAS_IO_ISEMPTY(__VA_ARGS__)) \ (Type, __ATLAS_IO_ARGS_OR_DUMMY(__VA_ARGS__)) #define __ATLAS_IO_TYPE_1(Type, dummy) Type __ATLAS_IO_SPLICE(__variable_, __LINE__) #define __ATLAS_IO_TYPE_0(Type, ...) Type __ATLAS_IO_SPLICE(__variable_, __LINE__)(__VA_ARGS__) #define __ATLAS_IO_TYPE_SCOPE(Type, ...) \ __ATLAS_IO_SPLICE(__ATLAS_IO_TYPE_SCOPE_, __ATLAS_IO_ISEMPTY(__VA_ARGS__)) \ (Type, __ATLAS_IO_ARGS_OR_DUMMY(__VA_ARGS__)) #define __ATLAS_IO_TYPE_SCOPE_1(Type, ...) \ for (bool __ATLAS_IO_SPLICE(__done_, __LINE__) = false; __ATLAS_IO_SPLICE(__done_, __LINE__) != true;) \ for (Type __ATLAS_IO_SPLICE(__variable_, __LINE__); __ATLAS_IO_SPLICE(__done_, __LINE__) != true; \ __ATLAS_IO_SPLICE(__done_, __LINE__) = true) #define __ATLAS_IO_TYPE_SCOPE_0(Type, ...) \ for (bool __ATLAS_IO_SPLICE(__done_, __LINE__) = false; __ATLAS_IO_SPLICE(__done_, __LINE__) != true;) \ for (Type __ATLAS_IO_SPLICE(__variable_, __LINE__)(__VA_ARGS__); __ATLAS_IO_SPLICE(__done_, __LINE__) != true; \ __ATLAS_IO_SPLICE(__done_, __LINE__) = true) atlas-0.46.0/atlas_io/eckit_codec_adaptor/src/atlas_io/CMakeLists.txt0000664000175000017500000000062715160212070025753 0ustar alastairalastair ecbuild_add_library( TARGET atlas_io INSTALL_HEADERS ALL HEADER_DESTINATION include/atlas_io PUBLIC_LIBS eckit_codec PUBLIC_INCLUDES $ SOURCES atlas-io.h Trace.cc Trace.h detail/BlackMagic.h LINKER_LANGUAGE CXX ) target_compile_features( atlas_io PUBLIC cxx_std_17 ) atlas-0.46.0/atlas_io/eckit_codec_adaptor/src/CMakeLists.txt0000664000175000017500000000003415160212070024150 0ustar alastairalastairadd_subdirectory(atlas_io) atlas-0.46.0/atlas_io/eckit_codec_adaptor/CMakeLists.txt0000664000175000017500000000002615160212070023362 0ustar alastairalastairadd_subdirectory(src) atlas-0.46.0/atlas_io/LICENSE0000664000175000017500000002500315160212070015663 0ustar alastairalastair Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS Copyright 1996-2018 ECMWF Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. atlas-0.46.0/atlas_io/cmake/0000775000175000017500000000000015160212070015736 5ustar alastairalastairatlas-0.46.0/atlas_io/cmake/atlas-io-import.cmake.in0000664000175000017500000000022015160212070022360 0ustar alastairalastair include( CMakeFindDependencyMacro ) ## eckit find_dependency( eckit HINTS ${CMAKE_CURRENT_LIST_DIR}/../eckit @eckit_DIR@ @eckit_BINARY_DIR@ ) atlas-0.46.0/atlas_io/AUTHORS0000664000175000017500000000014215160212070015723 0ustar alastairalastairAuthors ======= - Willem Deconinck Thanks for contributions from ============================= atlas-0.46.0/atlas_io/CMakeLists.txt0000664000175000017500000000654415160212070017427 0ustar alastairalastair# (C) Copyright 2021 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. ############################################################################################ # Atlas-IO cmake_minimum_required( VERSION 3.12 FATAL_ERROR ) find_package( ecbuild 3.4 REQUIRED HINTS ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_SOURCE_DIR}/../ecbuild ) ################################################################################ # Initialise project Atlas project( atlas_io VERSION ${atlas_VERSION} LANGUAGES CXX ) ################################################################################ # Required packages ecbuild_find_package( NAME eckit REQUIRED ) ecbuild_debug( " eckit_FEATURES : [${eckit_FEATURES}]" ) ################################################################################ # Features that can be enabled / disabled with -DENABLE_ ecbuild_add_option( FEATURE ECKIT_DEVELOP DESCRIPTION "Used to enable new features or API depending on eckit develop branch, not yet in a tagged release" DEFAULT OFF ) set( eckit_HAVE_ECKIT_CODEC 0 ) if( TARGET eckit_codec ) set( eckit_HAVE_ECKIT_CODEC 1 ) endif() ecbuild_add_option( FEATURE ECKIT_CODEC DEFAULT ON DESCRIPTION "Use eckit::codec with adaptor" CONDITION eckit_HAVE_ECKIT_CODEC ) ################################################################################ # sources set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) ecbuild_add_option( FEATURE WARNING_AS_ERROR DEFAULT OFF DESCRIPTION "Treat compile warning as error" ) if(HAVE_WARNING_AS_ERROR) ecbuild_add_cxx_flags("-Werror" NO_FAIL NAME atlas_io_cxx_warning_as_error) endif() ecbuild_add_option( FEATURE WARNINGS DEFAULT ON DESCRIPTION "Add warnings to compiler" ) if(HAVE_WARNINGS) ecbuild_add_cxx_flags("-Wall" NO_FAIL) ecbuild_add_cxx_flags("-Wextra" NO_FAIL) ecbuild_add_cxx_flags("-Wno-unused-parameter" NO_FAIL) endif() if( CMAKE_CXX_COMPILER_ID STREQUAL Intel ) ecbuild_add_cxx_flags("-diag-disable=10441" NO_FAIL) # Deprecated classic compiler endif() check_cxx_source_compiles( "#include \n int main() { char * type; int status; char * r = abi::__cxa_demangle(type, 0, 0, &status); }" ATLAS_IO_HAVE_CXXABI_H ) test_big_endian( _BIG_ENDIAN ) if( _BIG_ENDIAN ) set( ATLAS_IO_BIG_ENDIAN 1 ) set( ATLAS_IO_LITTLE_ENDIAN 0 ) else() set( ATLAS_IO_BIG_ENDIAN 0 ) set( ATLAS_IO_LITTLE_ENDIAN 1 ) endif() if( HAVE_ECKIT_CODEC ) ecbuild_info("atlas_io is configured to be an adaptor library which delegates calls to eckit_codec") add_subdirectory(eckit_codec_adaptor) else() add_subdirectory( src ) endif() add_subdirectory( tests ) ################################################################################ # Export and summarize ecbuild_add_resources( TARGET atlas_io-others SOURCES_PACK README.md CHANGELOG.md LICENSE ) ecbuild_install_project( NAME Atlas-IO ) ecbuild_print_summary() atlas-0.46.0/bamboo/0000775000175000017500000000000015160212070014322 5ustar alastairalastairatlas-0.46.0/bamboo/env.sh0000664000175000017500000000006315160212070015445 0ustar alastairalastair#!/usr/bin/env bash # export ctest_parallel="no" atlas-0.46.0/bamboo/MACOSX-env.sh0000664000175000017500000000007515160212070016440 0ustar alastairalastairexport PATH=$HOME/Applications/CMake.app/Contents/bin:$PATH atlas-0.46.0/bamboo/INTEL-env.sh0000664000175000017500000000066215160212070016323 0ustar alastairalastair#!/bin/bash [[ $(uname) == "Darwin" ]] && return # no module environment on the Mac # initialise module environment if it is not if [[ ! $(command -v module > /dev/null 2>&1) ]]; then . /usr/local/apps/module/init/bash fi module unload grib_api module unload eccodes module unload emos module unload fftw module unload libemos module unload metview module unload netcdf4 module load cmake/3.16.5 module switch gnu intel/17.0.3 atlas-0.46.0/bamboo/leap42-env.sh0000775000175000017500000000010415160212070016531 0ustar alastairalastair#!/bin/bash # CC=mpicc # CXX=mpicxx # FC=mpif90 ulimit -s unlimitedatlas-0.46.0/bamboo/CLANG-env.sh0000664000175000017500000000104415160212070016267 0ustar alastairalastair#!/bin/bash if [[ $(uname) == "Darwin" ]]; then # Up to date CMake version required export PATH=${HOME}/Applications/CMake.app/Contents/bin:${PATH} # No module environment on the Mac return fi # initialise module environment if it is not if [[ ! $(command -v module > /dev/null 2>&1) ]]; then . /usr/local/apps/module/init/bash fi module unload grib_api module unload eccodes module unload emos module unload fftw module unload libemos module unload metview module unload netcdf4 module load cmake/3.16.5 module switch gnu clang atlas-0.46.0/bamboo/GCC-env.sh0000664000175000017500000000065115160212070016042 0ustar alastairalastair#!/bin/bash [[ $(uname) == "Darwin" ]] && return # no module environment on the Mac # initialise module environment if it is not if [[ ! $(command -v module > /dev/null 2>&1) ]]; then . /usr/local/apps/module/init/bash fi module unload grib_api module unload eccodes module unload emos module unload fftw module unload libemos module unload metview module unload netcdf4 module load cmake/3.16.5 module load proj/8.2.1 atlas-0.46.0/bamboo/CLANG-flags.cmake0000664000175000017500000000052315160212070017242 0ustar alastairalastairset( ENABLE_TRANS ON CACHE BOOL "Enable TRANS" ) set( ENABLE_BOUNDSCHECKING ON CACHE BOOL "Enable bounds checking") set( ENABLE_TESSELATION OFF CACHE BOOL "Disable CGAL" ) # cgal is old in leap42 set( ENABLE_OMP_CXX OFF CACHE BOOL "Disable OpenMP for C++" ) # because of problems with clang OpenMP atlas-0.46.0/bamboo/GCC-flags.cmake0000664000175000017500000000073415160212070017016 0ustar alastairalastairset( ENABLE_MPI OFF CACHE BOOL "Disable MPI under Intel compilation" ) set( ENABLE_TRANS ON CACHE BOOL "Enable TRANS" ) set( ENABLE_BOUNDSCHECKING ON CACHE BOOL "Enable bounds checking") set( ENABLE_PROJ ON CACHE BOOL "Enable proj" ) set( ENABLE_TESSELATION OFF CACHE BOOL "Disable CGAL" ) # cgal is old in leap42 #set( ECBUILD_2_COMPAT OFF CACHE BOOL "Disable ecbuild 2 compat mode for bamboo testing" ) atlas-0.46.0/bamboo/flags.cmake0000664000175000017500000000147015160212070016422 0ustar alastairalastair # the following line does not work (CMAKE_SOURCE_DIR seems to point to the build dir) #include(${CMAKE_SOURCE_DIR}/cmake/atlas_compiler_flags.cmake) #include(../../git/atlas/cmake/atlas_compiler_flags.cmake) # on openSUSE 11.3 machines, we need to point to a newer compiler #SET(CMAKE_Fortran_COMPILER /usr/local/apps/gcc/4.8.1/LP64/bin/gfortran CACHE STRING "Fortran compiler") #SET(CMAKE_C_COMPILER /usr/local/apps/gcc/4.8.1/LP64/bin/gcc CACHE STRING "C compiler") #SET(CMAKE_CXX_COMPILER /usr/local/apps/gcc/4.8.1/LP64/bin/g++ CACHE STRING "C++ compiler") #set(CMAKE_Fortran_LINK_FLAGS "-L/usr/local/apps/gcc/4.8.1/LP64/lib/gcc/x86_64-suse-linux/4.8.1/") #link_directories("/usr/local/apps/gcc/4.8.1/LP64/lib/gcc/x86_64-suse-linux/4.8.1/") SET(ENABLE_SANDBOX OFF CACHE BOOL "Disable sandbox") atlas-0.46.0/bamboo/CMakeLists.txt0000664000175000017500000000025415160212070017063 0ustar alastairalastairfile( GLOB_RECURSE bamboo_files RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "*" ) ecbuild_add_resources( TARGET ${PROJECT_NAME}_bamboo SOURCES_DONT_PACK ${bamboo_files} ) atlas-0.46.0/bamboo/INTEL-flags.cmake0000664000175000017500000000047015160212070017272 0ustar alastairalastairset( ENABLE_MPI OFF CACHE BOOL "Disable MPI under Intel compilation" ) set( ENABLE_TRANS ON CACHE BOOL "Enable TRANS" ) set( ENABLE_BOUNDSCHECKING ON CACHE BOOL "Enable bounds checking") set( ENABLE_TESSELATION OFF CACHE BOOL "Disable CGAL" ) # cgal is old in leap42 atlas-0.46.0/doc/0000775000175000017500000000000015160212070013630 5ustar alastairalastairatlas-0.46.0/doc/Doxyfile-default0000664000175000017500000033030415160212070016763 0ustar alastairalastair# Doxyfile 1.8.17 # This file describes the settings to be used by the documentation system # doxygen (www.doxygen.org) for a project. # # All text after a double hash (##) is considered a comment and is placed in # front of the TAG it is preceding. # # All text after a single hash (#) is considered a comment and will be ignored. # The format is: # TAG = value [value, ...] # For lists, items can also be appended using: # TAG += value [value, ...] # Values that contain spaces should be placed between quotes (\" \"). #--------------------------------------------------------------------------- # Project related configuration options #--------------------------------------------------------------------------- # This tag specifies the encoding used for all characters in the configuration # file that follow. The default is UTF-8 which is also the encoding used for all # text before the first occurrence of this tag. Doxygen uses libiconv (or the # iconv built into libc) for the transcoding. See # https://www.gnu.org/software/libiconv/ for the list of possible encodings. # The default value is: UTF-8. DOXYFILE_ENCODING = UTF-8 # The PROJECT_NAME tag is a single word (or a sequence of words surrounded by # double-quotes, unless you are using Doxywizard) that should identify the # project for which the documentation is generated. This name is used in the # title of most generated pages and in a few other places. # The default value is: My Project. PROJECT_NAME = "My Project" # The PROJECT_NUMBER tag can be used to enter a project or revision number. This # could be handy for archiving the generated documentation or if some version # control system is used. PROJECT_NUMBER = # Using the PROJECT_BRIEF tag one can provide an optional one line description # for a project that appears at the top of each page and should give viewer a # quick idea about the purpose of the project. Keep the description short. PROJECT_BRIEF = # With the PROJECT_LOGO tag one can specify a logo or an icon that is included # in the documentation. The maximum height of the logo should not exceed 55 # pixels and the maximum width should not exceed 200 pixels. Doxygen will copy # the logo to the output directory. PROJECT_LOGO = # The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path # into which the generated documentation will be written. If a relative path is # entered, it will be relative to the location where doxygen was started. If # left blank the current directory will be used. OUTPUT_DIRECTORY = # If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- # directories (in 2 levels) under the output directory of each output format and # will distribute the generated files over these directories. Enabling this # option can be useful when feeding doxygen a huge amount of source files, where # putting all generated files in the same directory would otherwise causes # performance problems for the file system. # The default value is: NO. CREATE_SUBDIRS = NO # If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII # characters to appear in the names of generated files. If set to NO, non-ASCII # characters will be escaped, for example _xE3_x81_x84 will be used for Unicode # U+3044. # The default value is: NO. ALLOW_UNICODE_NAMES = NO # The OUTPUT_LANGUAGE tag is used to specify the language in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all constant output in the proper language. # Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Catalan, Chinese, # Chinese-Traditional, Croatian, Czech, Danish, Dutch, English (United States), # Esperanto, Farsi (Persian), Finnish, French, German, Greek, Hungarian, # Indonesian, Italian, Japanese, Japanese-en (Japanese with English messages), # Korean, Korean-en (Korean with English messages), Latvian, Lithuanian, # Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, Romanian, Russian, # Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, Swedish, Turkish, # Ukrainian and Vietnamese. # The default value is: English. OUTPUT_LANGUAGE = English # The OUTPUT_TEXT_DIRECTION tag is used to specify the direction in which all # documentation generated by doxygen is written. Doxygen will use this # information to generate all generated output in the proper direction. # Possible values are: None, LTR, RTL and Context. # The default value is: None. OUTPUT_TEXT_DIRECTION = None # If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member # descriptions after the members that are listed in the file and class # documentation (similar to Javadoc). Set to NO to disable this. # The default value is: YES. BRIEF_MEMBER_DESC = YES # If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief # description of a member or function before the detailed description # # Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the # brief descriptions will be completely suppressed. # The default value is: YES. REPEAT_BRIEF = YES # This tag implements a quasi-intelligent brief description abbreviator that is # used to form the text in various listings. Each string in this list, if found # as the leading text of the brief description, will be stripped from the text # and the result, after processing the whole list, is used as the annotated # text. Otherwise, the brief description is used as-is. If left blank, the # following values are used ($name is automatically replaced with the name of # the entity):The $name class, The $name widget, The $name file, is, provides, # specifies, contains, represents, a, an and the. ABBREVIATE_BRIEF = "The $name class" \ "The $name widget" \ "The $name file" \ is \ provides \ specifies \ contains \ represents \ a \ an \ the # If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then # doxygen will generate a detailed section even if there is only a brief # description. # The default value is: NO. ALWAYS_DETAILED_SEC = NO # If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all # inherited members of a class in the documentation of that class as if those # members were ordinary class members. Constructors, destructors and assignment # operators of the base classes will not be shown. # The default value is: NO. INLINE_INHERITED_MEMB = NO # If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path # before files name in the file list and in the header files. If set to NO the # shortest path that makes the file name unique will be used # The default value is: YES. FULL_PATH_NAMES = YES # The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. # Stripping is only done if one of the specified strings matches the left-hand # part of the path. The tag can be used to show relative paths in the file list. # If left blank the directory from which doxygen is run is used as the path to # strip. # # Note that you can specify absolute paths here, but also relative paths, which # will be relative from the directory where doxygen is started. # This tag requires that the tag FULL_PATH_NAMES is set to YES. STRIP_FROM_PATH = # The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the # path mentioned in the documentation of a class, which tells the reader which # header file to include in order to use a class. If left blank only the name of # the header file containing the class definition is used. Otherwise one should # specify the list of include paths that are normally passed to the compiler # using the -I flag. STRIP_FROM_INC_PATH = # If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but # less readable) file names. This can be useful is your file systems doesn't # support long names like on DOS, Mac, or CD-ROM. # The default value is: NO. SHORT_NAMES = NO # If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the # first line (until the first dot) of a Javadoc-style comment as the brief # description. If set to NO, the Javadoc-style will behave just like regular Qt- # style comments (thus requiring an explicit @brief command for a brief # description.) # The default value is: NO. JAVADOC_AUTOBRIEF = NO # If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line # such as # /*************** # as being the beginning of a Javadoc-style comment "banner". If set to NO, the # Javadoc-style will behave just like regular comments and it will not be # interpreted by doxygen. # The default value is: NO. JAVADOC_BANNER = NO # If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first # line (until the first dot) of a Qt-style comment as the brief description. If # set to NO, the Qt-style will behave just like regular Qt-style comments (thus # requiring an explicit \brief command for a brief description.) # The default value is: NO. QT_AUTOBRIEF = NO # The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a # multi-line C++ special comment block (i.e. a block of //! or /// comments) as # a brief description. This used to be the default behavior. The new default is # to treat a multi-line C++ comment block as a detailed description. Set this # tag to YES if you prefer the old behavior instead. # # Note that setting this tag to YES also means that rational rose comments are # not recognized any more. # The default value is: NO. MULTILINE_CPP_IS_BRIEF = NO # If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the # documentation from any documented member that it re-implements. # The default value is: YES. INHERIT_DOCS = YES # If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new # page for each member. If set to NO, the documentation of a member will be part # of the file/class/namespace that contains it. # The default value is: NO. SEPARATE_MEMBER_PAGES = NO # The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen # uses this value to replace tabs by spaces in code fragments. # Minimum value: 1, maximum value: 16, default value: 4. TAB_SIZE = 4 # This tag can be used to specify a number of aliases that act as commands in # the documentation. An alias has the form: # name=value # For example adding # "sideeffect=@par Side Effects:\n" # will allow you to put the command \sideeffect (or @sideeffect) in the # documentation, which will result in a user-defined paragraph with heading # "Side Effects:". You can put \n's in the value part of an alias to insert # newlines (in the resulting output). You can put ^^ in the value part of an # alias to insert a newline as if a physical newline was in the original file. # When you need a literal { or } or , in the value part of an alias you have to # escape them by means of a backslash (\), this can lead to conflicts with the # commands \{ and \} for these it is advised to use the version @{ and @} or use # a double escape (\\{ and \\}) ALIASES = # This tag can be used to specify a number of word-keyword mappings (TCL only). # A mapping has the form "name=value". For example adding "class=itcl::class" # will allow you to use the command class in the itcl::class meaning. TCL_SUBST = # Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources # only. Doxygen will then generate output that is more tailored for C. For # instance, some of the names that are used will be different. The list of all # members will be omitted, etc. # The default value is: NO. OPTIMIZE_OUTPUT_FOR_C = NO # Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or # Python sources only. Doxygen will then generate output that is more tailored # for that language. For instance, namespaces will be presented as packages, # qualified scopes will look different, etc. # The default value is: NO. OPTIMIZE_OUTPUT_JAVA = NO # Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran # sources. Doxygen will then generate output that is tailored for Fortran. # The default value is: NO. OPTIMIZE_FOR_FORTRAN = NO # Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL # sources. Doxygen will then generate output that is tailored for VHDL. # The default value is: NO. OPTIMIZE_OUTPUT_VHDL = NO # Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice # sources only. Doxygen will then generate output that is more tailored for that # language. For instance, namespaces will be presented as modules, types will be # separated into more groups, etc. # The default value is: NO. OPTIMIZE_OUTPUT_SLICE = NO # Doxygen selects the parser to use depending on the extension of the files it # parses. With this tag you can assign which parser to use for a given # extension. Doxygen has a built-in mapping, but you can override or extend it # using this tag. The format is ext=language, where ext is a file extension, and # language is one of the parsers supported by doxygen: IDL, Java, Javascript, # Csharp (C#), C, C++, D, PHP, md (Markdown), Objective-C, Python, Slice, # Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: # FortranFree, unknown formatted Fortran: Fortran. In the later case the parser # tries to guess whether the code is fixed or free formatted code, this is the # default for Fortran type files), VHDL, tcl. For instance to make doxygen treat # .inc files as Fortran files (default is PHP), and .f files as C (default is # Fortran), use: inc=Fortran f=C. # # Note: For files without extension you can use no_extension as a placeholder. # # Note that for custom extensions you also need to set FILE_PATTERNS otherwise # the files are not read by doxygen. EXTENSION_MAPPING = # If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments # according to the Markdown format, which allows for more readable # documentation. See https://daringfireball.net/projects/markdown/ for details. # The output of markdown processing is further processed by doxygen, so you can # mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in # case of backward compatibilities issues. # The default value is: YES. MARKDOWN_SUPPORT = YES # When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up # to that level are automatically included in the table of contents, even if # they do not have an id attribute. # Note: This feature currently applies only to Markdown headings. # Minimum value: 0, maximum value: 99, default value: 5. # This tag requires that the tag MARKDOWN_SUPPORT is set to YES. TOC_INCLUDE_HEADINGS = 5 # When enabled doxygen tries to link words that correspond to documented # classes, or namespaces to their corresponding documentation. Such a link can # be prevented in individual cases by putting a % sign in front of the word or # globally by setting AUTOLINK_SUPPORT to NO. # The default value is: YES. AUTOLINK_SUPPORT = YES # If you use STL classes (i.e. std::string, std::vector, etc.) but do not want # to include (a tag file for) the STL sources as input, then you should set this # tag to YES in order to let doxygen match functions declarations and # definitions whose arguments contain STL classes (e.g. func(std::string); # versus func(std::string) {}). This also make the inheritance and collaboration # diagrams that involve STL classes more complete and accurate. # The default value is: NO. BUILTIN_STL_SUPPORT = NO # If you use Microsoft's C++/CLI language, you should set this option to YES to # enable parsing support. # The default value is: NO. CPP_CLI_SUPPORT = NO # Set the SIP_SUPPORT tag to YES if your project consists of sip (see: # https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen # will parse them like normal C++ but will assume all classes use public instead # of private inheritance when no explicit protection keyword is present. # The default value is: NO. SIP_SUPPORT = NO # For Microsoft's IDL there are propget and propput attributes to indicate # getter and setter methods for a property. Setting this option to YES will make # doxygen to replace the get and set methods by a property in the documentation. # This will only work if the methods are indeed getting or setting a simple # type. If this is not the case, or you want to show the methods anyway, you # should set this option to NO. # The default value is: YES. IDL_PROPERTY_SUPPORT = YES # If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC # tag is set to YES then doxygen will reuse the documentation of the first # member in the group (if any) for the other members of the group. By default # all members of a group must be documented explicitly. # The default value is: NO. DISTRIBUTE_GROUP_DOC = NO # If one adds a struct or class to a group and this option is enabled, then also # any nested class or struct is added to the same group. By default this option # is disabled and one has to add nested compounds explicitly via \ingroup. # The default value is: NO. GROUP_NESTED_COMPOUNDS = NO # Set the SUBGROUPING tag to YES to allow class member groups of the same type # (for instance a group of public functions) to be put as a subgroup of that # type (e.g. under the Public Functions section). Set it to NO to prevent # subgrouping. Alternatively, this can be done per class using the # \nosubgrouping command. # The default value is: YES. SUBGROUPING = YES # When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions # are shown inside the group in which they are included (e.g. using \ingroup) # instead of on a separate page (for HTML and Man pages) or section (for LaTeX # and RTF). # # Note that this feature does not work in combination with # SEPARATE_MEMBER_PAGES. # The default value is: NO. INLINE_GROUPED_CLASSES = NO # When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions # with only public data fields or simple typedef fields will be shown inline in # the documentation of the scope in which they are defined (i.e. file, # namespace, or group documentation), provided this scope is documented. If set # to NO, structs, classes, and unions are shown on a separate page (for HTML and # Man pages) or section (for LaTeX and RTF). # The default value is: NO. INLINE_SIMPLE_STRUCTS = NO # When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or # enum is documented as struct, union, or enum with the name of the typedef. So # typedef struct TypeS {} TypeT, will appear in the documentation as a struct # with name TypeT. When disabled the typedef will appear as a member of a file, # namespace, or class. And the struct will be named TypeS. This can typically be # useful for C code in case the coding convention dictates that all compound # types are typedef'ed and only the typedef is referenced, never the tag name. # The default value is: NO. TYPEDEF_HIDES_STRUCT = NO # The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This # cache is used to resolve symbols given their name and scope. Since this can be # an expensive process and often the same symbol appears multiple times in the # code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small # doxygen will become slower. If the cache is too large, memory is wasted. The # cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range # is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 # symbols. At the end of a run doxygen will report the cache usage and suggest # the optimal cache size from a speed point of view. # Minimum value: 0, maximum value: 9, default value: 0. LOOKUP_CACHE_SIZE = 0 #--------------------------------------------------------------------------- # Build related configuration options #--------------------------------------------------------------------------- # If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in # documentation are documented, even if no documentation was available. Private # class members and static file members will be hidden unless the # EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. # Note: This will also disable the warnings about undocumented members that are # normally produced when WARNINGS is set to YES. # The default value is: NO. EXTRACT_ALL = NO # If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will # be included in the documentation. # The default value is: NO. EXTRACT_PRIVATE = NO # If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual # methods of a class will be included in the documentation. # The default value is: NO. EXTRACT_PRIV_VIRTUAL = NO # If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal # scope will be included in the documentation. # The default value is: NO. EXTRACT_PACKAGE = NO # If the EXTRACT_STATIC tag is set to YES, all static members of a file will be # included in the documentation. # The default value is: NO. EXTRACT_STATIC = NO # If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined # locally in source files will be included in the documentation. If set to NO, # only classes defined in header files are included. Does not have any effect # for Java sources. # The default value is: YES. EXTRACT_LOCAL_CLASSES = YES # This flag is only useful for Objective-C code. If set to YES, local methods, # which are defined in the implementation section but not in the interface are # included in the documentation. If set to NO, only methods in the interface are # included. # The default value is: NO. EXTRACT_LOCAL_METHODS = NO # If this flag is set to YES, the members of anonymous namespaces will be # extracted and appear in the documentation as a namespace called # 'anonymous_namespace{file}', where file will be replaced with the base name of # the file that contains the anonymous namespace. By default anonymous namespace # are hidden. # The default value is: NO. EXTRACT_ANON_NSPACES = NO # If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all # undocumented members inside documented classes or files. If set to NO these # members will be included in the various overviews, but no documentation # section is generated. This option has no effect if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_MEMBERS = NO # If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all # undocumented classes that are normally visible in the class hierarchy. If set # to NO, these classes will be included in the various overviews. This option # has no effect if EXTRACT_ALL is enabled. # The default value is: NO. HIDE_UNDOC_CLASSES = NO # If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend # (class|struct|union) declarations. If set to NO, these declarations will be # included in the documentation. # The default value is: NO. HIDE_FRIEND_COMPOUNDS = NO # If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any # documentation blocks found inside the body of a function. If set to NO, these # blocks will be appended to the function's detailed documentation block. # The default value is: NO. HIDE_IN_BODY_DOCS = NO # The INTERNAL_DOCS tag determines if documentation that is typed after a # \internal command is included. If the tag is set to NO then the documentation # will be excluded. Set it to YES to include the internal documentation. # The default value is: NO. INTERNAL_DOCS = NO # If the CASE_SENSE_NAMES tag is set to NO then doxygen will only generate file # names in lower-case letters. If set to YES, upper-case letters are also # allowed. This is useful if you have classes or files whose names only differ # in case and if your file system supports case sensitive file names. Windows # (including Cygwin) ands Mac users are advised to set this option to NO. # The default value is: system dependent. CASE_SENSE_NAMES = YES # If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with # their full class and namespace scopes in the documentation. If set to YES, the # scope will be hidden. # The default value is: NO. HIDE_SCOPE_NAMES = NO # If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will # append additional text to a page's title, such as Class Reference. If set to # YES the compound reference will be hidden. # The default value is: NO. HIDE_COMPOUND_REFERENCE= NO # If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of # the files that are included by a file in the documentation of that file. # The default value is: YES. SHOW_INCLUDE_FILES = YES # If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each # grouped member an include statement to the documentation, telling the reader # which file to include in order to use the member. # The default value is: NO. SHOW_GROUPED_MEMB_INC = NO # If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include # files with double quotes in the documentation rather than with sharp brackets. # The default value is: NO. FORCE_LOCAL_INCLUDES = NO # If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the # documentation for inline members. # The default value is: YES. INLINE_INFO = YES # If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the # (detailed) documentation of file and class members alphabetically by member # name. If set to NO, the members will appear in declaration order. # The default value is: YES. SORT_MEMBER_DOCS = YES # If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief # descriptions of file, namespace and class members alphabetically by member # name. If set to NO, the members will appear in declaration order. Note that # this will also influence the order of the classes in the class list. # The default value is: NO. SORT_BRIEF_DOCS = NO # If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the # (brief and detailed) documentation of class members so that constructors and # destructors are listed first. If set to NO the constructors will appear in the # respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. # Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief # member documentation. # Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting # detailed member documentation. # The default value is: NO. SORT_MEMBERS_CTORS_1ST = NO # If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy # of group names into alphabetical order. If set to NO the group names will # appear in their defined order. # The default value is: NO. SORT_GROUP_NAMES = NO # If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by # fully-qualified names, including namespaces. If set to NO, the class list will # be sorted only by class name, not including the namespace part. # Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. # Note: This option applies only to the class list, not to the alphabetical # list. # The default value is: NO. SORT_BY_SCOPE_NAME = NO # If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper # type resolution of all parameters of a function it will reject a match between # the prototype and the implementation of a member function even if there is # only one candidate or it is obvious which candidate to choose by doing a # simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still # accept a match between prototype and implementation in such cases. # The default value is: NO. STRICT_PROTO_MATCHING = NO # The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo # list. This list is created by putting \todo commands in the documentation. # The default value is: YES. GENERATE_TODOLIST = YES # The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test # list. This list is created by putting \test commands in the documentation. # The default value is: YES. GENERATE_TESTLIST = YES # The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug # list. This list is created by putting \bug commands in the documentation. # The default value is: YES. GENERATE_BUGLIST = YES # The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) # the deprecated list. This list is created by putting \deprecated commands in # the documentation. # The default value is: YES. GENERATE_DEPRECATEDLIST= YES # The ENABLED_SECTIONS tag can be used to enable conditional documentation # sections, marked by \if ... \endif and \cond # ... \endcond blocks. ENABLED_SECTIONS = # The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the # initial value of a variable or macro / define can have for it to appear in the # documentation. If the initializer consists of more lines than specified here # it will be hidden. Use a value of 0 to hide initializers completely. The # appearance of the value of individual variables and macros / defines can be # controlled using \showinitializer or \hideinitializer command in the # documentation regardless of this setting. # Minimum value: 0, maximum value: 10000, default value: 30. MAX_INITIALIZER_LINES = 30 # Set the SHOW_USED_FILES tag to NO to disable the list of files generated at # the bottom of the documentation of classes and structs. If set to YES, the # list will mention the files that were used to generate the documentation. # The default value is: YES. SHOW_USED_FILES = YES # Set the SHOW_FILES tag to NO to disable the generation of the Files page. This # will remove the Files entry from the Quick Index and from the Folder Tree View # (if specified). # The default value is: YES. SHOW_FILES = YES # Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces # page. This will remove the Namespaces entry from the Quick Index and from the # Folder Tree View (if specified). # The default value is: YES. SHOW_NAMESPACES = YES # The FILE_VERSION_FILTER tag can be used to specify a program or script that # doxygen should invoke to get the current version for each file (typically from # the version control system). Doxygen will invoke the program by executing (via # popen()) the command command input-file, where command is the value of the # FILE_VERSION_FILTER tag, and input-file is the name of an input file provided # by doxygen. Whatever the program writes to standard output is used as the file # version. For an example see the documentation. FILE_VERSION_FILTER = # The LAYOUT_FILE tag can be used to specify a layout file which will be parsed # by doxygen. The layout file controls the global structure of the generated # output files in an output format independent way. To create the layout file # that represents doxygen's defaults, run doxygen with the -l option. You can # optionally specify a file name after the option, if omitted DoxygenLayout.xml # will be used as the name of the layout file. # # Note that if you run doxygen from a directory containing a file called # DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE # tag is left empty. LAYOUT_FILE = # The CITE_BIB_FILES tag can be used to specify one or more bib files containing # the reference definitions. This must be a list of .bib files. The .bib # extension is automatically appended if omitted. This requires the bibtex tool # to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info. # For LaTeX the style of the bibliography can be controlled using # LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the # search path. See also \cite for info how to create references. CITE_BIB_FILES = #--------------------------------------------------------------------------- # Configuration options related to warning and progress messages #--------------------------------------------------------------------------- # The QUIET tag can be used to turn on/off the messages that are generated to # standard output by doxygen. If QUIET is set to YES this implies that the # messages are off. # The default value is: NO. QUIET = NO # The WARNINGS tag can be used to turn on/off the warning messages that are # generated to standard error (stderr) by doxygen. If WARNINGS is set to YES # this implies that the warnings are on. # # Tip: Turn warnings on while writing the documentation. # The default value is: YES. WARNINGS = YES # If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate # warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag # will automatically be disabled. # The default value is: YES. WARN_IF_UNDOCUMENTED = YES # If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for # potential errors in the documentation, such as not documenting some parameters # in a documented function, or documenting parameters that don't exist or using # markup commands wrongly. # The default value is: YES. WARN_IF_DOC_ERROR = YES # This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that # are documented, but have no documentation for their parameters or return # value. If set to NO, doxygen will only warn about wrong or incomplete # parameter documentation, but not about the absence of documentation. If # EXTRACT_ALL is set to YES then this flag will automatically be disabled. # The default value is: NO. WARN_NO_PARAMDOC = NO # If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when # a warning is encountered. # The default value is: NO. WARN_AS_ERROR = NO # The WARN_FORMAT tag determines the format of the warning messages that doxygen # can produce. The string should contain the $file, $line, and $text tags, which # will be replaced by the file and line number from which the warning originated # and the warning text. Optionally the format may contain $version, which will # be replaced by the version of the file (if it could be obtained via # FILE_VERSION_FILTER) # The default value is: $file:$line: $text. WARN_FORMAT = "$file:$line: $text" # The WARN_LOGFILE tag can be used to specify a file to which warning and error # messages should be written. If left blank the output is written to standard # error (stderr). WARN_LOGFILE = #--------------------------------------------------------------------------- # Configuration options related to the input files #--------------------------------------------------------------------------- # The INPUT tag is used to specify the files and/or directories that contain # documented source files. You may enter file names like myfile.cpp or # directories like /usr/src/myproject. Separate the files or directories with # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. INPUT = # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses # libiconv (or the iconv built into libc) for the transcoding. See the libiconv # documentation (see: https://www.gnu.org/software/libiconv/) for the list of # possible encodings. # The default value is: UTF-8. INPUT_ENCODING = UTF-8 # If the value of the INPUT tag contains directories, you can use the # FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and # *.h) to filter out the source-files in the directories. # # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not # read by doxygen. # # If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cpp, # *.c++, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, *.idl, *.ddl, *.odl, *.h, # *.hh, *.hxx, *.hpp, *.h++, *.cs, *.d, *.php, *.php4, *.php5, *.phtml, *.inc, # *.m, *.markdown, *.md, *.mm, *.dox (to be provided as doxygen C comment), # *.doc (to be provided as doxygen C comment), *.txt (to be provided as doxygen # C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f, *.for, *.tcl, *.vhd, # *.vhdl, *.ucf, *.qsf and *.ice. FILE_PATTERNS = *.c \ *.cc \ *.cxx \ *.cpp \ *.c++ \ *.java \ *.ii \ *.ixx \ *.ipp \ *.i++ \ *.inl \ *.idl \ *.ddl \ *.odl \ *.h \ *.hh \ *.hxx \ *.hpp \ *.h++ \ *.cs \ *.d \ *.php \ *.php4 \ *.php5 \ *.phtml \ *.inc \ *.m \ *.markdown \ *.md \ *.mm \ *.dox \ *.doc \ *.txt \ *.py \ *.pyw \ *.f90 \ *.f95 \ *.f03 \ *.f08 \ *.f \ *.for \ *.tcl \ *.vhd \ *.vhdl \ *.ucf \ *.qsf \ *.ice # The RECURSIVE tag can be used to specify whether or not subdirectories should # be searched for input files as well. # The default value is: NO. RECURSIVE = NO # The EXCLUDE tag can be used to specify files and/or directories that should be # excluded from the INPUT source files. This way you can easily exclude a # subdirectory from a directory tree whose root is specified with the INPUT tag. # # Note that relative paths are relative to the directory from which doxygen is # run. EXCLUDE = # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded # from the input. # The default value is: NO. EXCLUDE_SYMLINKS = NO # If the value of the INPUT tag contains directories, you can use the # EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude # certain files from those directories. # # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories for example use the pattern */test/* EXCLUDE_PATTERNS = # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the # output. The symbol name can be a fully qualified name, a word, or if the # wildcard * is used, a substring. Examples: ANamespace, AClass, # AClass::ANamespace, ANamespace::*Test # # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories use the pattern */test/* EXCLUDE_SYMBOLS = # The EXAMPLE_PATH tag can be used to specify one or more files or directories # that contain example code fragments that are included (see the \include # command). EXAMPLE_PATH = # If the value of the EXAMPLE_PATH tag contains directories, you can use the # EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and # *.h) to filter out the source-files in the directories. If left blank all # files are included. EXAMPLE_PATTERNS = * # If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be # searched for input files to be used with the \include or \dontinclude commands # irrespective of the value of the RECURSIVE tag. # The default value is: NO. EXAMPLE_RECURSIVE = NO # The IMAGE_PATH tag can be used to specify one or more files or directories # that contain images that are to be included in the documentation (see the # \image command). IMAGE_PATH = # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program # by executing (via popen()) the command: # # # # where is the value of the INPUT_FILTER tag, and is the # name of an input file. Doxygen will then use the output that the filter # program writes to standard output. If FILTER_PATTERNS is specified, this tag # will be ignored. # # Note that the filter must not add or remove lines; it is applied before the # code is scanned, but not when the output code is generated. If lines are added # or removed, the anchors will not be placed correctly. # # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not # properly processed by doxygen. INPUT_FILTER = # The FILTER_PATTERNS tag can be used to specify filters on a per file pattern # basis. Doxygen will compare the file name with each pattern and apply the # filter if there is a match. The filters are a list of the form: pattern=filter # (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how # filters are used. If the FILTER_PATTERNS tag is empty or if none of the # patterns match the file name, INPUT_FILTER is applied. # # Note that for custom extensions or not directly supported extensions you also # need to set EXTENSION_MAPPING for the extension otherwise the files are not # properly processed by doxygen. FILTER_PATTERNS = # If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using # INPUT_FILTER) will also be used to filter the input files that are used for # producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). # The default value is: NO. FILTER_SOURCE_FILES = NO # The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file # pattern. A pattern will override the setting for FILTER_PATTERN (if any) and # it is also possible to disable source filtering for a specific pattern using # *.ext= (so without naming a filter). # This tag requires that the tag FILTER_SOURCE_FILES is set to YES. FILTER_SOURCE_PATTERNS = # If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that # is part of the input, its contents will be placed on the main page # (index.html). This can be useful if you have a project on for instance GitHub # and want to reuse the introduction page also for the doxygen output. USE_MDFILE_AS_MAINPAGE = #--------------------------------------------------------------------------- # Configuration options related to source browsing #--------------------------------------------------------------------------- # If the SOURCE_BROWSER tag is set to YES then a list of source files will be # generated. Documented entities will be cross-referenced with these sources. # # Note: To get rid of all source code in the generated output, make sure that # also VERBATIM_HEADERS is set to NO. # The default value is: NO. SOURCE_BROWSER = NO # Setting the INLINE_SOURCES tag to YES will include the body of functions, # classes and enums directly into the documentation. # The default value is: NO. INLINE_SOURCES = NO # Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any # special comment blocks from generated source code fragments. Normal C, C++ and # Fortran comments will always remain visible. # The default value is: YES. STRIP_CODE_COMMENTS = YES # If the REFERENCED_BY_RELATION tag is set to YES then for each documented # entity all documented functions referencing it will be listed. # The default value is: NO. REFERENCED_BY_RELATION = NO # If the REFERENCES_RELATION tag is set to YES then for each documented function # all documented entities called/used by that function will be listed. # The default value is: NO. REFERENCES_RELATION = NO # If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set # to YES then the hyperlinks from functions in REFERENCES_RELATION and # REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will # link to the documentation. # The default value is: YES. REFERENCES_LINK_SOURCE = YES # If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the # source code will show a tooltip with additional information such as prototype, # brief description and links to the definition and documentation. Since this # will make the HTML file larger and loading of large files a bit slower, you # can opt to disable this feature. # The default value is: YES. # This tag requires that the tag SOURCE_BROWSER is set to YES. SOURCE_TOOLTIPS = YES # If the USE_HTAGS tag is set to YES then the references to source code will # point to the HTML generated by the htags(1) tool instead of doxygen built-in # source browser. The htags tool is part of GNU's global source tagging system # (see https://www.gnu.org/software/global/global.html). You will need version # 4.8.6 or higher. # # To use it do the following: # - Install the latest version of global # - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file # - Make sure the INPUT points to the root of the source tree # - Run doxygen as normal # # Doxygen will invoke htags (and that will in turn invoke gtags), so these # tools must be available from the command line (i.e. in the search path). # # The result: instead of the source browser generated by doxygen, the links to # source code will now point to the output of htags. # The default value is: NO. # This tag requires that the tag SOURCE_BROWSER is set to YES. USE_HTAGS = NO # If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a # verbatim copy of the header file for each class for which an include is # specified. Set to NO to disable this. # See also: Section \class. # The default value is: YES. VERBATIM_HEADERS = YES #--------------------------------------------------------------------------- # Configuration options related to the alphabetical class index #--------------------------------------------------------------------------- # If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all # compounds will be generated. Enable this if the project contains a lot of # classes, structs, unions or interfaces. # The default value is: YES. ALPHABETICAL_INDEX = YES # The COLS_IN_ALPHA_INDEX tag can be used to specify the number of columns in # which the alphabetical index list will be split. # Minimum value: 1, maximum value: 20, default value: 5. # This tag requires that the tag ALPHABETICAL_INDEX is set to YES. COLS_IN_ALPHA_INDEX = 5 # In case all classes in a project start with a common prefix, all classes will # be put under the same header in the alphabetical index. The IGNORE_PREFIX tag # can be used to specify a prefix (or a list of prefixes) that should be ignored # while generating the index headers. # This tag requires that the tag ALPHABETICAL_INDEX is set to YES. IGNORE_PREFIX = #--------------------------------------------------------------------------- # Configuration options related to the HTML output #--------------------------------------------------------------------------- # If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output # The default value is: YES. GENERATE_HTML = YES # The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a # relative path is entered the value of OUTPUT_DIRECTORY will be put in front of # it. # The default directory is: html. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_OUTPUT = html # The HTML_FILE_EXTENSION tag can be used to specify the file extension for each # generated HTML page (for example: .htm, .php, .asp). # The default value is: .html. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FILE_EXTENSION = .html # The HTML_HEADER tag can be used to specify a user-defined HTML header file for # each generated HTML page. If the tag is left blank doxygen will generate a # standard header. # # To get valid HTML the header file that includes any scripts and style sheets # that doxygen needs, which is dependent on the configuration options used (e.g. # the setting GENERATE_TREEVIEW). It is highly recommended to start with a # default header using # doxygen -w html new_header.html new_footer.html new_stylesheet.css # YourConfigFile # and then modify the file new_header.html. See also section "Doxygen usage" # for information on how to generate the default header that doxygen normally # uses. # Note: The header is subject to change so you typically have to regenerate the # default header when upgrading to a newer version of doxygen. For a description # of the possible markers and block names see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_HEADER = # The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each # generated HTML page. If the tag is left blank doxygen will generate a standard # footer. See HTML_HEADER for more information on how to generate a default # footer and what special commands can be used inside the footer. See also # section "Doxygen usage" for information on how to generate the default footer # that doxygen normally uses. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_FOOTER = # The HTML_STYLESHEET tag can be used to specify a user-defined cascading style # sheet that is used by each HTML page. It can be used to fine-tune the look of # the HTML output. If left blank doxygen will generate a default style sheet. # See also section "Doxygen usage" for information on how to generate the style # sheet that doxygen normally uses. # Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as # it is more robust and this tag (HTML_STYLESHEET) will in the future become # obsolete. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_STYLESHEET = # The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined # cascading style sheets that are included after the standard style sheets # created by doxygen. Using this option one can overrule certain style aspects. # This is preferred over using HTML_STYLESHEET since it does not replace the # standard style sheet and is therefore more robust against future updates. # Doxygen will copy the style sheet files to the output directory. # Note: The order of the extra style sheet files is of importance (e.g. the last # style sheet in the list overrules the setting of the previous ones in the # list). For an example see the documentation. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_STYLESHEET = # The HTML_EXTRA_FILES tag can be used to specify one or more extra images or # other source files which should be copied to the HTML output directory. Note # that these files will be copied to the base HTML output directory. Use the # $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these # files. In the HTML_STYLESHEET file, use the file name only. Also note that the # files will be copied as-is; there are no commands or markers available. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_EXTRA_FILES = # The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen # will adjust the colors in the style sheet and background images according to # this color. Hue is specified as an angle on a colorwheel, see # https://en.wikipedia.org/wiki/Hue for more information. For instance the value # 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 # purple, and 360 is red again. # Minimum value: 0, maximum value: 359, default value: 220. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_HUE = 220 # The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors # in the HTML output. For a value of 0 the output will use grayscales only. A # value of 255 will produce the most vivid colors. # Minimum value: 0, maximum value: 255, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_SAT = 100 # The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the # luminance component of the colors in the HTML output. Values below 100 # gradually make the output lighter, whereas values above 100 make the output # darker. The value divided by 100 is the actual gamma applied, so 80 represents # a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not # change the gamma. # Minimum value: 40, maximum value: 240, default value: 80. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_COLORSTYLE_GAMMA = 80 # If the HTML_TIMESTAMP tag is set to YES then the footer of each generated HTML # page will contain the date and time when the page was generated. Setting this # to YES can help to show when doxygen was last run and thus if the # documentation is up to date. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_TIMESTAMP = NO # If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML # documentation will contain a main index with vertical navigation menus that # are dynamically created via Javascript. If disabled, the navigation index will # consists of multiple levels of tabs that are statically embedded in every HTML # page. Disable this option to support browsers that do not have Javascript, # like the Qt help browser. # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_DYNAMIC_MENUS = YES # If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML # documentation will contain sections that can be hidden and shown after the # page has loaded. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_DYNAMIC_SECTIONS = NO # With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries # shown in the various tree structured indices initially; the user can expand # and collapse entries dynamically later on. Doxygen will expand the tree to # such a level that at most the specified number of entries are visible (unless # a fully collapsed tree already exceeds this amount). So setting the number of # entries 1 will produce a full collapsed tree by default. 0 is a special value # representing an infinite number of entries and will result in a full expanded # tree by default. # Minimum value: 0, maximum value: 9999, default value: 100. # This tag requires that the tag GENERATE_HTML is set to YES. HTML_INDEX_NUM_ENTRIES = 100 # If the GENERATE_DOCSET tag is set to YES, additional index files will be # generated that can be used as input for Apple's Xcode 3 integrated development # environment (see: https://developer.apple.com/xcode/), introduced with OSX # 10.5 (Leopard). To create a documentation set, doxygen will generate a # Makefile in the HTML output directory. Running make will produce the docset in # that directory and running make install will install the docset in # ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at # startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy # genXcode/_index.html for more information. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_DOCSET = NO # This tag determines the name of the docset feed. A documentation feed provides # an umbrella under which multiple documentation sets from a single provider # (such as a company or product suite) can be grouped. # The default value is: Doxygen generated docs. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_FEEDNAME = "Doxygen generated docs" # This tag specifies a string that should uniquely identify the documentation # set bundle. This should be a reverse domain-name style string, e.g. # com.mycompany.MyDocSet. Doxygen will append .docset to the name. # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_BUNDLE_ID = org.doxygen.Project # The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify # the documentation publisher. This should be a reverse domain-name style # string, e.g. com.mycompany.MyDocSet.documentation. # The default value is: org.doxygen.Publisher. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_PUBLISHER_ID = org.doxygen.Publisher # The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. # The default value is: Publisher. # This tag requires that the tag GENERATE_DOCSET is set to YES. DOCSET_PUBLISHER_NAME = Publisher # If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three # additional HTML index files: index.hhp, index.hhc, and index.hhk. The # index.hhp is a project file that can be read by Microsoft's HTML Help Workshop # (see: https://www.microsoft.com/en-us/download/details.aspx?id=21138) on # Windows. # # The HTML Help Workshop contains a compiler that can convert all HTML output # generated by doxygen into a single compiled HTML file (.chm). Compiled HTML # files are now used as the Windows 98 help format, and will replace the old # Windows help format (.hlp) on all Windows platforms in the future. Compressed # HTML files also contain an index, a table of contents, and you can search for # words in the documentation. The HTML workshop also contains a viewer for # compressed HTML files. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_HTMLHELP = NO # The CHM_FILE tag can be used to specify the file name of the resulting .chm # file. You can add a path in front of the file if the result should not be # written to the html output directory. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. CHM_FILE = # The HHC_LOCATION tag can be used to specify the location (absolute path # including file name) of the HTML help compiler (hhc.exe). If non-empty, # doxygen will try to run the HTML help compiler on the generated index.hhp. # The file has to be specified with full path. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. HHC_LOCATION = # The GENERATE_CHI flag controls if a separate .chi index file is generated # (YES) or that it should be included in the master .chm file (NO). # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. GENERATE_CHI = NO # The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) # and project file content. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. CHM_INDEX_ENCODING = # The BINARY_TOC flag controls whether a binary table of contents is generated # (YES) or a normal table of contents (NO) in the .chm file. Furthermore it # enables the Previous and Next buttons. # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. BINARY_TOC = NO # The TOC_EXPAND flag can be set to YES to add extra items for group members to # the table of contents of the HTML help documentation and to the tree view. # The default value is: NO. # This tag requires that the tag GENERATE_HTMLHELP is set to YES. TOC_EXPAND = NO # If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and # QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that # can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help # (.qch) of the generated HTML documentation. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_QHP = NO # If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify # the file name of the resulting .qch file. The path specified is relative to # the HTML output folder. # This tag requires that the tag GENERATE_QHP is set to YES. QCH_FILE = # The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help # Project output. For more information please see Qt Help Project / Namespace # (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace). # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_QHP is set to YES. QHP_NAMESPACE = org.doxygen.Project # The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt # Help Project output. For more information please see Qt Help Project / Virtual # Folders (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual- # folders). # The default value is: doc. # This tag requires that the tag GENERATE_QHP is set to YES. QHP_VIRTUAL_FOLDER = doc # If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom # filter to add. For more information please see Qt Help Project / Custom # Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom- # filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_NAME = # The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the # custom filter to add. For more information please see Qt Help Project / Custom # Filters (see: https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom- # filters). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_CUST_FILTER_ATTRS = # The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this # project's filter section matches. Qt Help Project / Filter Attributes (see: # https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes). # This tag requires that the tag GENERATE_QHP is set to YES. QHP_SECT_FILTER_ATTRS = # The QHG_LOCATION tag can be used to specify the location of Qt's # qhelpgenerator. If non-empty doxygen will try to run qhelpgenerator on the # generated .qhp file. # This tag requires that the tag GENERATE_QHP is set to YES. QHG_LOCATION = # If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be # generated, together with the HTML files, they form an Eclipse help plugin. To # install this plugin and make it available under the help contents menu in # Eclipse, the contents of the directory containing the HTML and XML files needs # to be copied into the plugins directory of eclipse. The name of the directory # within the plugins directory should be the same as the ECLIPSE_DOC_ID value. # After copying Eclipse needs to be restarted before the help appears. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_ECLIPSEHELP = NO # A unique identifier for the Eclipse help plugin. When installing the plugin # the directory name containing the HTML and XML files should also have this # name. Each documentation set should have its own identifier. # The default value is: org.doxygen.Project. # This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. ECLIPSE_DOC_ID = org.doxygen.Project # If you want full control over the layout of the generated HTML pages it might # be necessary to disable the index and replace it with your own. The # DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top # of each HTML page. A value of NO enables the index and the value YES disables # it. Since the tabs in the index contain the same information as the navigation # tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. DISABLE_INDEX = NO # The GENERATE_TREEVIEW tag is used to specify whether a tree-like index # structure should be generated to display hierarchical information. If the tag # value is set to YES, a side panel will be generated containing a tree-like # index structure (just like the one that is generated for HTML Help). For this # to work a browser that supports JavaScript, DHTML, CSS and frames is required # (i.e. any modern browser). Windows users are probably better off using the # HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can # further fine-tune the look of the index. As an example, the default style # sheet generated by doxygen has an example that shows how to put an image at # the root of the tree instead of the PROJECT_NAME. Since the tree basically has # the same information as the tab index, you could consider setting # DISABLE_INDEX to YES when enabling this option. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. GENERATE_TREEVIEW = NO # The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that # doxygen will group on one line in the generated HTML documentation. # # Note that a value of 0 will completely suppress the enum values from appearing # in the overview section. # Minimum value: 0, maximum value: 20, default value: 4. # This tag requires that the tag GENERATE_HTML is set to YES. ENUM_VALUES_PER_LINE = 4 # If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used # to set the initial width (in pixels) of the frame in which the tree is shown. # Minimum value: 0, maximum value: 1500, default value: 250. # This tag requires that the tag GENERATE_HTML is set to YES. TREEVIEW_WIDTH = 250 # If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to # external symbols imported via tag files in a separate window. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. EXT_LINKS_IN_WINDOW = NO # Use this tag to change the font size of LaTeX formulas included as images in # the HTML documentation. When you change the font size after a successful # doxygen run you need to manually remove any form_*.png images from the HTML # output directory to force them to be regenerated. # Minimum value: 8, maximum value: 50, default value: 10. # This tag requires that the tag GENERATE_HTML is set to YES. FORMULA_FONTSIZE = 10 # Use the FORMULA_TRANSPARENT tag to determine whether or not the images # generated for formulas are transparent PNGs. Transparent PNGs are not # supported properly for IE 6.0, but are supported on all modern browsers. # # Note that when changing this option you need to delete any form_*.png files in # the HTML output directory before the changes have effect. # The default value is: YES. # This tag requires that the tag GENERATE_HTML is set to YES. FORMULA_TRANSPARENT = YES # Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see # https://www.mathjax.org) which uses client side Javascript for the rendering # instead of using pre-rendered bitmaps. Use this if you do not have LaTeX # installed or if you want to formulas look prettier in the HTML output. When # enabled you may also need to install MathJax separately and configure the path # to it using the MATHJAX_RELPATH option. # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. USE_MATHJAX = NO # When MathJax is enabled you can set the default output format to be used for # the MathJax output. See the MathJax site (see: # http://docs.mathjax.org/en/latest/output.html) for more details. # Possible values are: HTML-CSS (which is slower, but has the best # compatibility), NativeMML (i.e. MathML) and SVG. # The default value is: HTML-CSS. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_FORMAT = HTML-CSS # When MathJax is enabled you need to specify the location relative to the HTML # output directory using the MATHJAX_RELPATH option. The destination directory # should contain the MathJax.js script. For instance, if the mathjax directory # is located at the same level as the HTML output directory, then # MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax # Content Delivery Network so you can quickly see the result without installing # MathJax. However, it is strongly recommended to install a local copy of # MathJax from https://www.mathjax.org before deployment. # The default value is: https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_RELPATH = https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.5/ # The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax # extension names that should be enabled during MathJax rendering. For example # MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_EXTENSIONS = # The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces # of code that will be used on startup of the MathJax code. See the MathJax site # (see: http://docs.mathjax.org/en/latest/output.html) for more details. For an # example see the documentation. # This tag requires that the tag USE_MATHJAX is set to YES. MATHJAX_CODEFILE = # When the SEARCHENGINE tag is enabled doxygen will generate a search box for # the HTML output. The underlying search engine uses javascript and DHTML and # should work on any modern browser. Note that when using HTML help # (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) # there is already a search function so this one should typically be disabled. # For large projects the javascript based search engine can be slow, then # enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to # search using the keyboard; to jump to the search box use + S # (what the is depends on the OS and browser, but it is typically # , /